diff --git a/Dockerfile b/Dockerfile index a98dc18..89d08fb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -238,6 +238,10 @@ RUN addgroup --system --gid 1001 nodejs && \ COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static +# standalone 可能会把构建期的 prisma/engine-cache、dev.db 一起追踪进来; +# 运行镜像只保留 schema.prisma 和 migrations,避免客户每次升级重复拉无用缓存。 +RUN rm -rf ./prisma + # 复制必要的额外文件 # 1. public 目录(静态资源) COPY --from=builder --chown=nextjs:nodejs /app/public ./public diff --git a/README.md b/README.md index fde2101..2db4644 100644 --- a/README.md +++ b/README.md @@ -213,7 +213,7 @@ docker compose -f docker-compose.prod.yml up -d - `发布管理.ps1` - `license-server/deploy.ps1` -其中 `scripts/customer-install.ps1` 是客户首装分步骤入口,内部调用 `scripts/smoke-install.ps1` 做预检、启动和验收;`scripts/backup-restore.ps1` 用于客户环境备份、恢复和恢复后验收,`scripts/measure-image-size.ps1` 用于发布前记录镜像体积和分层。其它脚本执行前必须先读内容和参数,不应把内部固定服务器或固定镜像仓库当成客户默认交付配置。客户交付时要显式写入版本化 `APP_IMAGE` / `IMAGE_TAG`,`latest` 只保留给内部测试或兼容场景。当前新 Registry 发布脚本是主线,旧远程部署脚本只作历史或内部维护参考。AI、钉钉、内网穿透等可选能力依赖真实外部服务和密钥,不能只凭页面入口当成客户验收已完成。 +其中 `scripts/customer-install.ps1` 是客户首装分步骤入口,内部调用 `scripts/smoke-install.ps1` 做预检、启动和验收;`scripts/backup-restore.ps1` 用于客户环境备份、恢复和恢复后验收;`scripts/measure-image-size.ps1` 用于发布前记录镜像体积和分层,也可以设置体积/层数阈值,超限时阻止发布继续。其它脚本执行前必须先读内容和参数,不应把内部固定服务器或固定镜像仓库当成客户默认交付配置。客户交付时要显式写入版本化 `APP_IMAGE` / `IMAGE_TAG`,`latest` 只保留给内部测试或兼容场景。当前新 Registry 发布脚本是主线,旧远程部署脚本只作历史或内部维护参考。AI、钉钉、内网穿透等可选能力依赖真实外部服务和密钥,不能只凭页面入口当成客户验收已完成。 ## 10. 文档入口 diff --git a/__tests__/production-build-hardening.test.ts b/__tests__/production-build-hardening.test.ts index 3e3065c..9da5784 100644 --- a/__tests__/production-build-hardening.test.ts +++ b/__tests__/production-build-hardening.test.ts @@ -20,6 +20,7 @@ describe('production build hardening', () => { }) it('生产运行镜像只复制 Prisma 运行迁移所需文件', () => { + expect(dockerfile).toContain('RUN rm -rf ./prisma') expect(dockerfile).toContain('/app/prisma/schema.prisma ./prisma/schema.prisma') expect(dockerfile).toContain('/app/prisma/migrations ./prisma/migrations') expect(dockerfile).not.toContain('/app/prisma ./prisma') diff --git a/app/admin/_components/AppearanceSettingsPanel.tsx b/app/admin/_components/AppearanceSettingsPanel.tsx index b967197..752fa2b 100644 --- a/app/admin/_components/AppearanceSettingsPanel.tsx +++ b/app/admin/_components/AppearanceSettingsPanel.tsx @@ -274,23 +274,20 @@ export default function AppearanceSettingsPanel({ sessionUser, variant = 'embedd } return ( -
+
-
-
-

- 外观 -

-

品牌、标识与系统开关

-

- 这里集中处理品牌主题、LOGO、功能开关与对象存储。保存后会同步到后台界面与素材上传入口。 +

+
+

外观设置

+

+ 管理站点名称、LOGO、功能开关和对象存储配置。

{lastSavedAt && (

@@ -298,7 +295,7 @@ export default function AppearanceSettingsPanel({ sessionUser, variant = 'embedd

)}
-
+
{variant === 'standalone' && ( -
@@ -469,9 +432,16 @@ export default function AppearanceSettingsPanel({ sessionUser, variant = 'embedd onChange={(e) => setHeaderBgColor(e.target.value)} className={cn(styles.inputStyles.base, 'flex-1 text-sm font-mono')} /> +
- {['#1f2937', '#4f46e5', '#1e40af', '#7c3aed', '#0891b2', '#059669', '#dc2626', '#d97706'].map((color) => ( + {['#1f2937', '#334155', '#0f766e', '#166534', '#b45309', '#b91c1c'].map((color) => (
{hasDisabledTemplates && onEnableAll && ( @@ -54,32 +54,24 @@ export default function BuiltInTemplateGrid({ )}
-
+
{presets.map((preset) => { const fieldCount = preset.getFields().length return (
onPreview(preset.key)} > -
-
+
+

{preset.name}

{preset.description}

- - 预制 - -
-
-
- - - - {fieldCount} 个字段 -
-
+
+ + {fieldCount} 个字段 + {onToggleEnabled && (
{loading ? ( @@ -71,57 +88,26 @@ export default function CustomTemplateSection({ ) : error ? (
{error}
) : ( -
- - +
{templates.length === 0 ? ( -
暂无自定义模板,点击上方卡片开始创建。
+
暂无自定义模板。
) : filteredTemplates.length === 0 ? ( -
未找到匹配的模板名称,请调整搜索关键词。
+
未找到匹配的模板名称,请调整搜索关键词。
) : ( filteredTemplates.map((tpl) => (
onPreview(tpl)} - className={cn(styles.cardStyles.hover, 'group relative overflow-hidden cursor-pointer')} + className="group relative cursor-pointer rounded-lg border border-slate-200 bg-white px-4 py-3 transition-colors hover:border-slate-300 hover:bg-slate-50 dark:border-slate-700 dark:bg-slate-900 dark:hover:border-slate-600 dark:hover:bg-slate-800/70" > -
-
+
+

{tpl.name}

{tpl.description && (

{tpl.description}

)}
-
+
{tpl.statusLabel} -
-
-
+ + {tpl.updatedLabel} · 版本 {tpl.version} + {tpl.status === 'active' ? ( - - {tpl.updatedLabel} · 版本 {tpl.version} -
+
)) )}
diff --git a/app/admin/_components/template-manager/TemplateManagerHeader.tsx b/app/admin/_components/template-manager/TemplateManagerHeader.tsx index ebad934..b021792 100644 --- a/app/admin/_components/template-manager/TemplateManagerHeader.tsx +++ b/app/admin/_components/template-manager/TemplateManagerHeader.tsx @@ -19,10 +19,10 @@ export default function TemplateManagerHeader({ return (
diff --git a/app/admin/_components/template-manager/editor/FieldConditionsConfig.tsx b/app/admin/_components/template-manager/editor/FieldConditionsConfig.tsx index 355aa3d..97600e1 100644 --- a/app/admin/_components/template-manager/editor/FieldConditionsConfig.tsx +++ b/app/admin/_components/template-manager/editor/FieldConditionsConfig.tsx @@ -164,7 +164,7 @@ export default function FieldConditionsConfig({ {index === 0 &&
} {/* 条件配置 */} -
+
{/* 字段选择 */} +
{typeof rules.minLength === 'number' && (
@@ -213,7 +213,7 @@ export default function FieldValidationConfig({ {/* 数值范围验证 - number */} {supportedValidations.includes('min') && ( -
+
+
{typeof rules.min === 'number' && (
diff --git a/app/admin/_components/template-manager/editor/TemplateFieldsEditor.tsx b/app/admin/_components/template-manager/editor/TemplateFieldsEditor.tsx index 93f7c8d..f176264 100644 --- a/app/admin/_components/template-manager/editor/TemplateFieldsEditor.tsx +++ b/app/admin/_components/template-manager/editor/TemplateFieldsEditor.tsx @@ -327,7 +327,7 @@ export default function TemplateFieldsEditor({
)} -
+