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
+33 -6
View File
@@ -3,6 +3,7 @@ and durable worker-backed REST scanning. All flows require explicit authorizatio
and remain subject to the existing SiteForge release-review gates.
"""
from __future__ import annotations
import re
import hashlib
import json
import uuid
@@ -160,15 +161,36 @@ def _srcset_urls(value):
return result
# CSS background images are common in Elementor containers and ACF galleries.
# Restrict to syntactically explicit remote URLs, never evaluate arbitrary CSS.
CSS_URL = re.compile(r'''url\(\s*([\"\']?)((?:https?:)?//[^\"\')\s]+)\1\s*\)''', re.I)
def _css_urls(raw):
return [match.group(2) for match in CSS_URL.finditer(str(raw or ''))]
def _rewrite_css_urls(raw,converted):
def replace(match):
url=match.group(2)
if url not in converted:return match.group(0)
quote=match.group(1)
return f'url({quote}{converted[url]}{quote})'
updated=CSS_URL.sub(replace,str(raw or ''))
return updated,updated!=str(raw or '')
def _media_urls(content):
"""Collect all remote media references, including responsive and lazy images."""
"""Collect images plus responsive/lazy/CSS backgrounds for explicit review."""
urls=[content.get('cover') or '']
soup=BeautifulSoup(content.get('body') or '','html.parser')
for tag in soup.find_all(['img','source','video']):
for key in ('src','data-src','poster'):
for tag in soup.find_all(True):
for key in ('src','data-src','poster','data-background','data-background-image'):
if tag.get(key):urls.append(tag[key])
for key in ('srcset','data-srcset'):
urls.extend(_srcset_urls(tag.get(key)))
if tag.get('style'):urls.extend(_css_urls(tag['style']))
if tag.name=='style':urls.extend(_css_urls(tag.get_text()))
return list(dict.fromkeys(u for u in urls if isinstance(u,str)
and u.startswith(('https://','http://','//'))))
@@ -187,8 +209,8 @@ def _rewrite_srcset(value,converted):
def _rewrite_media_html(body,converted):
soup=BeautifulSoup(body,'html.parser');changed=False
for tag in soup.find_all(['img','source','video']):
for key in ('src','data-src','poster'):
for tag in soup.find_all(True):
for key in ('src','data-src','poster','data-background','data-background-image'):
if tag.get(key) in converted:
tag[key]=converted[tag[key]];changed=True
for key in ('srcset','data-srcset'):
@@ -196,11 +218,16 @@ def _rewrite_media_html(body,converted):
new,updated=_rewrite_srcset(tag[key],converted)
if updated:
tag[key]=new;changed=True
# A single redundant srcset has no value and can be removed.
if tag.name=='img' and key=='srcset' and len(new.split(','))==1:
fields=new.strip().split()
if fields and fields[0]==tag.get('src'):
tag.attrs.pop(key,None)
if tag.get('style'):
value,updated=_rewrite_css_urls(tag['style'],converted)
if updated:tag['style']=value;changed=True
if tag.name=='style':
value,updated=_rewrite_css_urls(tag.string if tag.string is not None else tag.get_text(),converted)
if updated:tag.string=value;changed=True
return str(soup) if changed else body,changed