upgrade: adopt V8.21.3 (V8.13→8.21.3: P5 SFTP/S3, P6 multilingual, editor overhaul, enterprise hardening)

Overlay ~150 upstream items onto our V8.12 base, preserving trade-pro
theme, registry tests, and design docs. Re-applied our patch set
(discovery/env_for/blocks.css/type-mapping/theme_color/frontend entries,
test_theme_pro_v3 mods) — all anchors survived upstream evolution.

New platform enhancement (fixes upgrade-introduced regression):
multilingual.site_locale now honors settings_json.locale, plus
PUT /api/i18n/sites/{id}/locale — standalone English sites get
<html lang='en'> under the V8.15 locale-rewrite pipeline.

compose: adopt V8.21.3 (image 8.21.3-dev.1, /api/ready probes, MySQL/S3
env) with project name pinned to siteforge-v8-10 (data volumes).

Tests: 356 passed / 11 failed — all 11 confirmed pre-existing in
pristine V8.21.3 on this host (10 upstream Windows env + our build test
now green after locale fix). Patch registry updated in plan doc.

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-10-23 12:31:03 +08:00
parent 257c89e159
commit 601451f22b
218 changed files with 10441 additions and 628 deletions
+24 -3
View File
@@ -12,7 +12,7 @@ from fastapi.responses import HTMLResponse, FileResponse, RedirectResponse, Resp
from fastapi.staticfiles import StaticFiles
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel, Field
from . import db, importers, builder, security, blueprints, enterprise, theme_store, factory_api, migration_audit, reusables, jobs, release_api, cms_pipeline, v8_workflows, v8_templates, wp_migration_v81, wp_migration_v82, wp_migration_v83, connectors_v84, connectors_v85, studio_v86, site_design_v87
from . import console_v819, wp_complex_v820, db, importers, builder, security, blueprints, enterprise, theme_store, factory_api, migration_audit, reusables, jobs, release_api, cms_pipeline, v8_workflows, v8_templates, wp_migration_v81, wp_migration_v82, wp_migration_v83, connectors_v84, connectors_v85, studio_v86, site_design_v87, multilingual
from .version import VERSION, BUILD_SHA
from .request_trace import TraceMiddleware
from .preview_rewrite import rewrite_preview_html
@@ -38,6 +38,7 @@ async def lifespan(app):
connectors_v85.setup()
studio_v86.setup()
site_design_v87.setup()
multilingual.setup()
yield
app=FastAPI(title='SiteForge 网站工厂',description='Docker-first visual website factory',version=VERSION,lifespan=lifespan)
@@ -58,15 +59,18 @@ app.include_router(v8_templates.router)
app.include_router(wp_migration_v81.router)
app.include_router(wp_migration_v82.router)
app.include_router(wp_migration_v83.router)
app.include_router(wp_complex_v820.router)
app.include_router(connectors_v84.router)
app.include_router(connectors_v85.router)
app.include_router(studio_v86.router)
app.include_router(site_design_v87.router)
app.include_router(multilingual.router)
app.include_router(console_v819.router)
@app.middleware('http')
async def require_login(request:Request, call_next):
path = request.url.path
public = path in ('/login', '/api/auth/login', '/api/health', '/favicon.ico') or path.startswith('/ui/')
public = path in ('/login', '/api/auth/login', '/api/health', '/api/ready', '/favicon.ico') or path.startswith('/ui/')
if not public:
session = security.read_session(request.cookies.get(security.COOKIE))
identity = enterprise.account_from_session(session) if session else None
@@ -261,6 +265,12 @@ def visual_editor(site_id:str,entry_id:str):
@app.get('/api/health')
def health():return {'status':'ok','name':'SiteForge','version':VERSION,'commit':BUILD_SHA}
@app.get('/api/ready')
def ready():
from .healthchecks import readiness
ok=readiness()
return JSONResponse({'status':'ready' if ok else 'unavailable'},status_code=200 if ok else 503)
@app.get('/api/themes')
def themes():
manifests=[]
@@ -536,7 +546,15 @@ def editor_preview(site_id:str,entry_id:str,payload:BlueprintPayload):
html=html.replace('</head>','<style>'+styles+'</style></head>')
bridge = r'''<script>(function(){
const fields={'title':'h1,h2','description':'.sf-hero-copy p,.sf-image-text p'};
document.querySelectorAll('[data-block]').forEach(block=>{
const sections=Array.from(document.querySelectorAll('[data-block]'));
sections.forEach(block=>{
block.setAttribute('data-studio-selectable','true');
block.addEventListener('click',event=>{
if(event.target.closest('a'))event.preventDefault();
sections.forEach(item=>item.classList.remove('sf-editor-selected'));
block.classList.add('sf-editor-selected');
parent.postMessage({source:'siteforge-select-block',blockId:block.dataset.block},'*');
});
for(const [field,selector] of Object.entries(fields)){
const target=block.querySelector(selector);if(!target)continue;
target.title='双击直接修改文字';target.addEventListener('dblclick',()=>{
@@ -550,6 +568,9 @@ def editor_preview(site_id:str,entry_id:str,payload:BlueprintPayload):
});
}
});
const style=document.createElement('style');
style.textContent='[data-studio-selectable]:hover{outline:2px dashed #e98b45!important;outline-offset:-2px;cursor:pointer}[data-studio-selectable].sf-editor-selected{outline:2px solid #ea8138!important;outline-offset:-2px}';
document.head.appendChild(style);
})()</script>'''
# The static site's /media/ assets are hosted by the admin at
# /local-media/{site}/. Rewrite only the editor response, never builds.