feat(framework): 回灌 OpenFlare 分层、安全与运行时改进

将平台域持久化收敛为 repository 唯一入口,model 去掉 IO。
邮件头写入前清除 CR/LF,防止 header 注入。
httppool 支持可配置 Transport;batchwriter 增加 MinBatchSize/Stats,flush 失败交回批次;任务 PermanentError 作为 SkipRetry 终态。
设置与推送页的确认改为 AlertDialog;axios 去尾斜杠并按 Gin 数组序列化查询参数。
升级共享 Go 依赖(Gin、Asynq、OTel、GORM、Redis 等)。
This commit is contained in:
ryan
2026-08-16 11:07:20 +08:00
parent b9b42e3174
commit 6a53619dd2
58 changed files with 2201 additions and 1175 deletions
@@ -23,6 +23,16 @@ import {
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from '@/components/ui/alert-dialog';
import services from '@/lib/services';
import type { Template } from '@/lib/services/admin/types';
import { toast } from 'sonner';
@@ -35,6 +45,7 @@ export function TemplatesManager() {
const [selectedTemplate, setSelectedTemplate] = useState<Template | null>(
null,
);
const [deleteTarget, setDeleteTarget] = useState<Template | null>(null);
// Form states
const [key, setKey] = useState('');
@@ -95,6 +106,7 @@ export function TemplatesManager() {
await services.adminTemplate.deleteTemplate(key);
},
onSuccess: async () => {
setDeleteTarget(null);
await queryClient.invalidateQueries({ queryKey: ['admin', 'templates'] });
toast.success(t('templateDeleted'));
},
@@ -225,15 +237,7 @@ export function TemplatesManager() {
disabled={
tmpl.is_system || deleteTemplateMutation.isPending
}
onClick={() => {
if (
window.confirm(
t('deleteTemplateConfirm', { name: tmpl.name }),
)
) {
deleteTemplateMutation.mutate(tmpl.key);
}
}}
onClick={() => setDeleteTarget(tmpl)}
>
<Trash2 className='size-4' />
</Button>
@@ -401,6 +405,35 @@ export function TemplatesManager() {
</form>
</DialogContent>
</Dialog>
<AlertDialog
open={Boolean(deleteTarget)}
onOpenChange={(open) => !open && setDeleteTarget(null)}
>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>{t('deleteTemplateTitle')}</AlertDialogTitle>
<AlertDialogDescription>
{t('deleteTemplateConfirm', { name: deleteTarget?.name ?? '' })}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel disabled={deleteTemplateMutation.isPending}>
{t('cancel')}
</AlertDialogCancel>
<AlertDialogAction
disabled={deleteTemplateMutation.isPending}
onClick={() =>
deleteTarget && deleteTemplateMutation.mutate(deleteTarget.key)
}
>
{deleteTemplateMutation.isPending
? t('deleting')
: t('confirmDelete')}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
);
}