chore: import SiteForge Factory V5 source (baseline)

导入 SiteForge Factory V5 基线源码:
- FastAPI + SQLite + Docker Compose(Web + Worker)架构
- 四套商业主题 × 14 页面模板(1.1.0,含历史版本快照)
- 四种建站模式骨架 + 迁移审计 + 发布门禁
- 52 项 pytest 测试
- 补全计划主文档 docs/superpowers/plans/

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-10-22 10:42:19 +08:00
commit 3c57c167f2
264 changed files with 11682 additions and 0 deletions
+15
View File
@@ -0,0 +1,15 @@
<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>SiteForge · 智能网站工厂</title><link rel="stylesheet" href="/ui/admin.css"><script defer src="/ui/admin.js"></script><script defer src="/ui/enterprise-ui.js"></script></head><body>
<div class="workspace">
<aside class="sidebar"><div class="identity"><div class="logo">S<span>✳</span></div><div><strong>SiteForge</strong><small>WEBSITE FACTORY</small></div></div>
<div class="side-caption">工作空间</div><a class="side-link" href="/factory-studio" style="text-decoration:none;display:block">◈ &nbsp;网站工厂 V5 →</a><button class="side-link selected" id="nav-dashboard" onclick="App.openDashboard()"><span>▦</span> 网站项目</button><button class="side-link" onclick="App.openNewSite()"><span>⊕</span> 创建新网站</button><button class="side-link" onclick="EnterpriseUI.team()"><span>♙</span> 团队账号</button><button class="side-link" id="nav-template" onclick="App.openTemplates()"><span>▤</span> 模板资源库</button>
<div class="side-caption between">我的网站 <button title="新建网站" onclick="App.openNewSite()">+</button></div><div id="siteNav" class="site-nav"></div>
<div class="sidebar-bottom"><div class="status-dot"></div><div><strong>SiteForge Enterprise v1.0</strong><small>站点独立静态发布</small></div></div>
</aside>
<div class="shell"><header class="topbar"><div class="breadcrumb"><span>工作空间</span><span class="crumb-sep">/</span><strong id="topTitle">网站总览</strong></div><div class="top-actions"><span class="env-label">● ENTERPRISE EDITION</span><button class="btn light small" onclick="App.openTemplates()">浏览模板</button><button class="btn light small" onclick="App.logout()">退出登录</button><button class="btn primary small" onclick="App.openNewSite()">+ 创建网站</button></div></header>
<main class="main" id="view"><div class="loading">正在初始化 SiteForge...</div></main>
</div>
</div>
<div id="toast" class="toast" role="status" aria-live="polite"></div>
<dialog id="siteDialog" class="dialog"><form id="siteForm" method="dialog"><div class="dialog-head"><div><span class="eyebrow">CREATE WEBSITE</span><h2>创建全新网站</h2><p>选择模板并创建站点,后续可继续导入旧内容或添加新数据。</p></div><button type="button" class="icon-btn" onclick="App.closeDialog('siteDialog')">×</button></div><div class="form-grid"><label>网站名称 <input name="name" required maxlength="150" placeholder="例如:HESU Global"/></label><label>新网站域名 <input name="domain" placeholder="https://example.com(可稍后设置)"/></label><label class="full">网站简介 / 标语 <input name="tagline" placeholder="全球采购 · 本地服务 · 高效交付"/></label><label>建站模式 <select name="mode"><option value="new">新站搭建</option><option value="migration">旧站改版 / 迁移</option><option value="hybrid">混合建站</option></select></label><label>主题风格 <select name="theme" id="siteTheme"><option value="automotive">汽车与出行</option><option value="industrial">工业制造</option><option value="corporate" selected>企业品牌</option><option value="minimal">极简设计</option></select></label><label class="full">基于已保存模板 <select name="preset_id" id="presetSelector"><option value="">不使用自定义模板</option></select></label></div><div class="dialog-actions"><button class="btn light" type="button" onclick="App.closeDialog('siteDialog')">取消</button><button class="btn primary" type="submit">创建并进入工作台 →</button></div></form></dialog>
<dialog id="entryDialog" class="dialog wide"><form id="entryForm" method="dialog"><div class="dialog-head"><div><span class="eyebrow">CONTENT EDITOR</span><h2 id="entryHeading">新增页面 / 内容</h2><p>所有字段均属于当前网站,修改后重新构建即可更新静态文件。</p></div><button type="button" class="icon-btn" onclick="App.closeDialog('entryDialog')">×</button></div><input type="hidden" name="entry_id"><div class="form-grid"><label>内容类型 <select name="type" id="entryType" onchange="App.renderEntryFields()"><option value="page">页面 Page</option><option value="product">产品 Product</option><option value="post">文章 Post</option><option value="service">服务 Service</option><option value="case">案例 Case</option><option value="vehicle">车型 Vehicle</option><option value="brand">品牌 Brand</option><option value="other">自定义内容</option></select></label><label>发布状态 <select name="status"><option value="published">已发布</option><option value="draft">草稿</option></select></label><label class="full">标题 <input name="title" required placeholder="页面或产品名称"/></label><label class="full">URL 路径(不会自动更改既有 URL) <input name="route" placeholder="/product/example/"/><small>建议:/products/example/,若为迁移站必须填写原有路径</small></label><label class="full">摘要 <textarea name="summary" rows="2" placeholder="列表描述、首页标语等"></textarea></label><label class="full">内容 HTML <textarea name="body" rows="6" placeholder="<p>正文内容</p>"></textarea></label><label class="full">封面图片 URL <div class="inline-input"><input id="coverInput" name="cover" placeholder="media/xxx.jpg 或 https://..."/><button type="button" class="btn light small" onclick="document.querySelector('#coverFile').click()">上传图片</button><input id="coverFile" type="file" accept="image/png,image/jpeg,image/webp,image/gif,image/avif" hidden onchange="App.uploadCover(this.files[0])"></div></label><label>SEO Title <input name="seo_title" placeholder="留空则使用页面标题"/></label><label>Canonical <input name="canonical" placeholder="默认根据域名与路径生成"/></label><label class="full">Meta Description <textarea name="seo_description" rows="2"></textarea></label><label>Robots <select name="robots"><option value="index,follow">index,follow</option><option value="noindex,follow">noindex,follow</option><option value="noindex,nofollow">noindex,nofollow</option></select></label><label>其他结构化字段 JSON <textarea name="meta" id="entryMeta" rows="4" placeholder='{"blocks": []}'></textarea></label><div id="dynamicContentFields" class="full"></div><div class="full editor-tools"><strong>首页区块快捷添加</strong><button type="button" class="btn light tiny" onclick="App.addBlock('hero')">+ Hero</button><button type="button" class="btn light tiny" onclick="App.addBlock('features')">+ 优势</button><button type="button" class="btn light tiny" onclick="App.addBlock('stats')">+ 数字统计</button><button type="button" class="btn light tiny" onclick="App.addBlock('cta')">+ CTA</button><small>基础区块使用 JSON 配置;进入「可视化页面设计」可进行拖动排序和属性调整。</small></div></div><div class="dialog-actions"><button type="button" class="btn light" onclick="App.closeDialog('entryDialog')">取消</button><button type="submit" class="btn primary">保存内容</button></div></form></dialog>
</body></html>
+26
View File
@@ -0,0 +1,26 @@
<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>SiteForge Studio · 可视化建站</title><link rel="stylesheet" href="/ui/editor.css"><script defer src="/ui/editor.js"></script></head>
<body>
<div class="studio">
<header class="studio-top">
<div class="brand"><a href="/" title="返回网站管理">◈</a><div><b>SiteForge</b><small>VISUAL STUDIO</small></div></div>
<span class="divide"></span><div class="file-name"><span id="siteName">加载中</span><small> / </small><strong id="pageName">页面编辑器</strong></div>
<div class="top-center"><button id="deviceDesktop" class="icon-btn on" onclick="Studio.device('desktop')" title="PC预览">▣</button><button id="deviceTablet" class="icon-btn" onclick="Studio.device('tablet')" title="平板预览">▤</button><button id="devicePhone" class="icon-btn" onclick="Studio.device('phone')" title="手机预览">▯</button></div>
<span id="saveStatus" class="save-status">未修改</span><button class="ui-btn ghost" onclick="Studio.refreshPreview()">↻ 刷新预览</button><button class="ui-btn ghost" onclick="Studio.buildSite()">构建静态站</button><button class="ui-btn primary" onclick="Studio.save()">保存页面 <span>↗</span></button>
</header>
<div class="studio-body">
<aside class="left-pane"><div class="pane-heading"><div><span class="overline">STRUCTURE</span><h3>页面结构</h3></div><span class="chip" id="blockCount">0 区块</span></div>
<div class="field"><label>页面</label><select id="pageSelect" onchange="Studio.choosePage(this.value)"></select></div>
<div class="field"><label>页面模板</label><select id="pageTemplate" onchange="Studio.changeTemplateKind(this.value)"></select></div>
<div style="padding:0 19px 7px"><button class="ui-btn ghost" style="width:100%" onclick="Studio.resetPreset()">◈ 应用此页面模板的预设区块</button></div>
<div class="field"><label>页面布局模式</label><select id="layoutMode" onchange="Studio.setLayoutMode(this.value)"><option value="append">保留主题原页面 + 追加区块</option><option value="replace">全页自由排版(保留导航与页脚)</option></select></div><div class="section-label">内容区块 · 拖拽可调整排序</div><div id="blockList" class="block-list"></div>
<div class="section-label">添加功能区块</div><div class="palette" id="palette"></div><div class="section-label">可复用组件库</div><button class="ui-btn ghost" style="margin:10px" onclick="Studio.saveReusable()">将当前区块存为组件</button><div class="palette" id="reusablePalette"></div>
<div class="sidebar-foot">页面由区块驱动,所见预览使用服务端与正式构建相同的数据绑定引擎。</div>
</aside>
<section class="preview-workspace"><div class="preview-head"><span class="live-dot"></span><span>实时预览</span><code id="routeDisplay">/</code><span class="preview-note">正在展示正式主题中的完整页面,编辑后需保存并重新构建</span></div><div class="stage"><div class="device-frame" id="deviceFrame"><iframe id="sitePreview" title="页面预览" sandbox="allow-scripts" referrerpolicy="no-referrer"></iframe></div></div></section>
<aside class="right-pane"><div class="pane-heading"><div><span class="overline">PROPERTIES</span><h3>属性设置</h3></div><span class="chip" id="blockTypeLabel">页面</span></div>
<div class="inspector" id="inspector"><div class="no-selection">在左侧选择一个区块,在这里编辑它的文字、图片、布局及数据来源。</div></div>
<div class="right-foot"><div>动态数据绑定</div><p>支持 <code>{{site.name}}</code>、<code>{{page.title}}</code> 等变量;“动态内容列表”按类型自动查询当前网站的数据。</p></div>
</aside>
</div>
<div id="toast" class="toast"></div>
</div></body></html>
+12
View File
@@ -0,0 +1,12 @@
<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>SiteForge 3 · 网站生产中心</title><link rel="stylesheet" href="/ui/factory-v2.css"><script defer src="/ui/factory-v2.js"></script></head>
<body><div class="shell">
<aside class="sidebar"><a class="identity" href="/factory-studio"><span class="mark">SF</span><span><strong>SiteForge</strong><small>WEBSITE FACTORY V5</small></span></a>
<div class="side-title">生产中心 / WORKSPACE</div>
<nav id="navtabs"><button data-tab="overview" class="active">◫ &nbsp; 网站总览</button><button data-tab="themes">▧ &nbsp; 主题与版本</button><button data-tab="content">▤ &nbsp; 内容与数据装配</button><button data-tab="migration">⇄ &nbsp; 迁移与 SEO 验收</button><button data-tab="build">◈ &nbsp; 构建与发布</button></nav>
<div class="side-bottom"><a href="/theme-gallery">↗ 完整主题预览中心</a> · <a href="/">旧版后台</a><small>Docker · Static Sites · Multi Tenant</small></div></aside>
<div class="work"><header class="top"><div class="top-crumb">SiteForge / <span id="currentTab">网站总览</span></div><div class="top-controls"><label>当前网站 <select id="sitePicker" aria-label="当前网站"></select></label><button class="outline" id="reload">刷新</button><button class="primary" id="newSite">+ 新建网站</button></div></header>
<div class="content"><div id="alert" role="status"></div><div class="heading"><div><div class="eyebrow">MANAGE · ASSEMBLE · DEPLOY</div><h1 id="heading">网站生产工作台</h1><p id="description">将新内容或旧网站数据与主题组合,构建独立可部署的网站。</p></div><div class="head-actions"><a class="outline" id="openEditor" href="#">可视化编辑器 ↗</a><button class="primary" id="buildQuick">生成静态网站</button></div></div>
<section id="panel" aria-live="polite"></section></div>
</div></div>
<dialog id="createDialog"><form id="createForm" method="dialog"><h2>创建网站</h2><p>不需要旧网站,也能直接选择主题、添加内容、构建整套官网。</p><label>网站名称<input name="name" required placeholder="例如 Hesu Global"></label><label>域名<input name="domain" placeholder="https://example.com"></label><label>站点模式<select name="mode"><option value="new">全新网站</option><option value="migration">旧站迁移改版</option><option value="hybrid">混合建站</option></select></label><label>主题<select name="theme" id="newTheme"></select></label><div class="form-actions"><button type="button" class="outline" id="closeCreate">取消</button><button class="primary" type="submit">创建并初始化网站</button></div></form></dialog>
</body></html>
+5
View File
@@ -0,0 +1,5 @@
<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>登录 · SiteForge 网站工厂</title><style>
:root{font-family:Inter,system-ui,-apple-system,"Microsoft YaHei",sans-serif;color:#263952}*{box-sizing:border-box}body{margin:0;background:#f5f7fa;min-height:100vh;display:grid;place-items:center;padding:24px}main{width:min(430px,100%);background:#fff;border:1px solid #e1e8ef;border-radius:20px;box-shadow:0 25px 70px #19345512;padding:42px}.symbol{width:56px;height:56px;border-radius:15px;background:#173250;color:#fff;display:grid;place-items:center;font-size:26px;font-weight:700;margin-bottom:24px}h1{font-size:27px;margin:0 0 6px}p{font-size:13px;color:#7c8ca0;line-height:1.8;margin:0 0 29px}label{display:block;font-size:13px;font-weight:600;margin-bottom:9px}input{width:100%;height:48px;border:1px solid #d8e0e9;border-radius:10px;padding:0 14px;font-size:15px;outline:0}input:focus{border-color:#386aab;box-shadow:0 0 0 3px #417cbc1c}button{width:100%;margin-top:22px;background:#19446f;color:#fff;height:48px;border:0;border-radius:10px;font-size:14px;font-weight:650;cursor:pointer}button:disabled{opacity:.65}small{display:block;text-align:center;margin-top:23px;color:#96a4b6}#error{margin:12px 0 0;color:#b63d3d;min-height:18px;font-size:13px}
</style></head><body><main><div class="symbol">S✳</div><h1>欢迎使用 SiteForge</h1><p>登录网站生产工作台,管理网站、内容、主题和静态构建版本。</p><form id="auth"><label for="pass">账号密码</label><input id="username" type="text" value="admin" required autocomplete="username" placeholder="管理员或团队用户名"><label for="pass">管理员密码 / 团队密码</label><input id="pass" type="password" required autofocus autocomplete="current-password" placeholder="请输入部署时设置的密码"><div id="error" role="alert"></div><button id="submit" type="submit">登录工作台 →</button></form><small>SiteForge Web · Enterprise Edition v1.0</small></main><script>
document.getElementById('auth').addEventListener('submit',async e=>{e.preventDefault();const b=document.getElementById('submit'),err=document.getElementById('error');b.disabled=true;err.textContent='';try{const res=await fetch('/api/auth/login',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({username:document.getElementById('username').value,password:document.getElementById('pass').value})});if(!res.ok){const d=await res.json();throw new Error(d.detail||'登录失败')}location.replace('/')}catch(e){err.textContent=e.message;b.disabled=false}});
</script></body></html>
+15
View File
@@ -0,0 +1,15 @@
<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>SiteForge · 完整主题预览中心</title>
<style>*{box-sizing:border-box}body{margin:0;background:#eef1f5;font:14px/1.6 system-ui,'Microsoft YaHei',sans-serif;color:#172c42}header{display:flex;align-items:center;justify-content:space-between;padding:14px 26px;background:#fff;border-bottom:1px solid #dce3eb;gap:24px;flex-wrap:wrap}header strong{font-size:17px}header strong span{background:#15253e;color:#fff;border-radius:6px;padding:6px 8px;margin-right:8px}header a{color:#255b99;text-decoration:none}.controls{display:flex;align-items:end;flex-wrap:wrap;gap:12px;padding:18px 26px;border-bottom:1px solid #dce3eb;background:white}.controls label{font-weight:700;color:#5c7083;font-size:11px;display:grid;gap:6px}.controls select{border:1px solid #ccd5df;background:#fff;color:#192d41;padding:10px 14px;border-radius:7px;min-width:132px;font-size:13px}.devices{display:flex;gap:4px;margin-left:auto;background:#f0f3f8;border-radius:8px;padding:4px}.devices button{border:0;background:none;padding:9px 14px;border-radius:6px;cursor:pointer;color:#52697e}.devices button.active{background:#1e446c;color:white}.stage{padding:22px 18px 80px;display:flex;justify-content:center;overflow:auto}.frame{width:100%;height:calc(100vh - 210px);min-height:520px;background:white;box-shadow:0 16px 50px #12304720;border-radius:10px;overflow:hidden;transition:width .25s}.frame iframe{border:0;width:100%;height:100%;background:white}.help{text-align:center;color:#6d8296;font-size:12px;margin:-55px auto 28px}.error{color:#c14f45;display:none}@media(max-width:700px){header,.controls{padding:12px}header strong{font-size:14px}.devices{margin-left:0}.frame{min-height:450px}}</style></head>
<body><header><strong><span>SF</span> SiteForge · 主题预览中心</strong><a href="/factory-studio">返回网站工厂 →</a></header>
<section class="controls"><label>主题<select id="theme"><option value="automotive">Automotive · 汽车与出行</option><option value="industrial">Industrial · 工业制造</option><option value="corporate">Corporate · 企业品牌</option><option value="minimal">Minimal · 极简设计</option></select></label>
<label>页面类型<select id="page"><option value="home">首页</option><option value="about">关于我们</option><option value="listing">产品 / 分类列表</option><option value="category">分类详情</option><option value="product">产品详情</option><option value="brand">品牌详情</option><option value="article">新闻详情</option><option value="case">案例详情</option><option value="service">服务详情</option><option value="contact">联系我们</option><option value="faq">常见问题</option><option value="standard">标准页面</option><option value="notfound">404</option></select></label>
<label>主题版本<select id="version"><option value="">最新</option></select></label><div class="devices"><button data-size="100%" class="active">PC</button><button data-size="820px">平板</button><button data-size="390px">手机</button></div><span class="error" id="error">预览失败,请确认已登录。</span></section>
<div class="stage"><div class="frame" id="frame"><iframe id="preview" title="主题真实页面预览" loading="eager"></iframe></div></div>
<div class="help">预览使用真实页面模板与演示数据。实际网站构建后将使用你的品牌信息和内容。</div>
<script>
const theme=document.querySelector('#theme'),page=document.querySelector('#page'),version=document.querySelector('#version'),frame=document.querySelector('#frame'),iframe=document.querySelector('#preview');
function show(){iframe.src=`/api/factory/themes/${encodeURIComponent(theme.value)}/preview?page_type=${encodeURIComponent(page.value)}&version=${encodeURIComponent(version.value)}`;}
async function loadVersions(){const response=await fetch(`/api/factory/themes/${encodeURIComponent(theme.value)}/versions`,{credentials:'same-origin'});if(!response.ok)throw Error('无法读取主题版本');const arr=await response.json();version.replaceChildren();arr.forEach(x=>{const op=new Option('v'+x.version,x.version);version.append(op);});show();}
theme.addEventListener('change',()=>loadVersions().catch(()=>document.querySelector('#error').style.display='inline'));
[page,version].forEach(x=>x.addEventListener('change',show));document.querySelectorAll('.devices button').forEach(x=>x.addEventListener('click',()=>{document.querySelectorAll('.devices button').forEach(y=>y.classList.remove('active'));x.classList.add('active');frame.style.width=x.dataset.size;}));loadVersions().catch(()=>document.querySelector('#error').style.display='inline');
</script></body></html>