e21dbb6518
V8.33.0 absorbed our entire platform patch set as native features (discovery/env_for macro override/blocks.css priority/type-mapping fix/ frontend entries/locale override) and synced trade-pro from our remote; V8.33.1 adds HESU PIPE industrial-theme home fixes (1.3.1). Zero patch re-application this round — all verified native. Theme suites 35/35; full run 428 passed + same 10 known Windows env fails (FAILED set unchanged). Live: container 8.33.1-dev.1 healthy, hesupipe 192 entries intact, trade-pro build 203 pages 0 errors. Patch registry retired to archive — future upgrades are plain overlay. Co-Authored-By: Claude Code <noreply@anthropic.com>
63 lines
6.9 KiB
Python
63 lines
6.9 KiB
Python
"""Make a fully offline industrial-theme homepage preview from the real static builder."""
|
|
import os,sys,json,html,re,tempfile
|
|
from pathlib import Path
|
|
ROOT=Path(__file__).resolve().parents[1]
|
|
sys.path[:0]=[str(ROOT),str(ROOT/'tests')]
|
|
from tests.legacy import LegacyTestClient
|
|
from app import db,theme_store
|
|
from app.main import app
|
|
|
|
def generate():
|
|
target=ROOT/'examples'; target.mkdir(exist_ok=True)
|
|
with tempfile.TemporaryDirectory() as temporary:
|
|
db.DATA=Path(temporary);db.DB_FILE=db.DATA/'data.sqlite3';theme_store.THEME_ROOT=db.DATA/'theme_packages'
|
|
os.environ['SITEFORGE_ALLOW_LEGACY_DIRECT_DESIGN_WRITE']='1'
|
|
os.environ['SITEFORGE_ADMIN_PASSWORD']='demo-test-admin-secret-2026'
|
|
os.environ['SITEFORGE_SESSION_SECRET']='demo-secret-not-for-use-outside-test-2026'
|
|
with LegacyTestClient(app) as c:
|
|
assert c.post('/api/auth/login',json={'username':'admin','password':os.environ['SITEFORGE_ADMIN_PASSWORD']}).status_code==200
|
|
c.headers.update({'X-SiteForge-CSRF':c.get('/api/auth/me').json()['csrf']})
|
|
r=c.post('/api/sites',json={'name':'HESU PIPE · 排版验证样站','theme':'industrial','domain':'https://example.test','mode':'migration','tagline':'工业管道与工程材料 · 完整产品方案'})
|
|
assert r.status_code==201,r.text; sid=r.json()['id']
|
|
home=next(e for e in c.get(f'/api/sites/{sid}/entries').json() if e['route']=='/')
|
|
assert c.put(f'/api/factory/sites/{sid}/theme',json={'theme':'industrial','version':'1.3.1'}).status_code==200
|
|
products=[('HDPE 给水管','适用于市政给水及工业供水管网。'),('双壁波纹排水管','适用于市政雨污水管网和地下排水工程。'),('PVC-U 排水管','耐腐蚀、规格丰富的塑料管材系统。'),('PPR 冷热水管','建筑冷热水输送常用管道系列。'),('钢丝网骨架复合管','适用于承压输水与工程管线系统。'),('HDPE 管道连接件','用于热熔及电熔连接的各类管件。')]
|
|
for i,(title,summary) in enumerate(products,1):
|
|
res=c.post(f'/api/sites/{sid}/entries',json={'type':'product','title':title,'route':f'/products/pipe-{i}/','summary':summary})
|
|
assert res.status_code==201,res.text
|
|
blocks=[{'id':'features-hp-v8331','type':'features','title':'核心优势','items':[
|
|
{'title':'管道产品供应','text':'覆盖多种工程管材和配件,便于项目集中采购。'},
|
|
{'title':'规格与质量管理','text':'根据实际应用提供产品选型和规格资料。'},
|
|
{'title':'工程应用支持','text':'面向供水、排水及工业管网项目的应用场景。'},
|
|
{'title':'全球交付服务','text':'从订单沟通到物流交付,提供清晰的合作流程。'},
|
|
]}, {'id':'collection-hp-v8331','type':'collection','title':'动态内容列表 · 精选产品',
|
|
'subtitle':'PRODUCT COLLECTION','source':'product','limit':6,'columns':3,
|
|
'href':'/products/','button':'查看全部产品'}]
|
|
saved=c.put(f'/api/sites/{sid}/pages/{home["id"]}/blueprint',json={'blocks':blocks,'page_template':'home'})
|
|
assert saved.status_code==200,saved.text
|
|
built=c.post(f'/api/sites/{sid}/builds',json={})
|
|
assert built.status_code==200,built.text
|
|
bid=built.json()['version']; dist=db.DATA/'builds'/sid/bid/'dist'
|
|
assert (dist/'index.html').exists(),dist
|
|
source=(dist/'index.html').read_text()
|
|
# Inline exact packaged CSS/JS, so the demo is readable without a server or external requests.
|
|
def stylesheet(m):
|
|
path=m.group(1)
|
|
local=dist/path
|
|
return '<style>\n'+local.read_text(encoding='utf-8')+'\n</style>' if local.is_file() else ''
|
|
source=re.sub(r'<link rel="stylesheet" href="(assets/[A-Za-z0-9._-]+\.css)">',stylesheet,source)
|
|
def script(m):
|
|
local=dist/m.group(1)
|
|
if local.is_file():
|
|
return '<script>\n'+local.read_text(encoding='utf-8').replace('</script','<\\/script')+'\n</script>'
|
|
return ''
|
|
source=re.sub(r'<script defer src="(assets/[A-Za-z0-9._-]+\.js)"></script>',script,source)
|
|
# These paths are demonstrative and intentionally not published.
|
|
isolated=target/'V8_33_1_HesuPipe_首页布局真实构建预览.html'
|
|
snippet=json.dumps(source,ensure_ascii=False).replace('<','\\u003c')
|
|
wrapper='''<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>HesuPipe · 首页核心优势与产品列表</title><style>*{box-sizing:border-box}body{margin:0;background:#eef2f5;color:#1d3344;font-family:system-ui,-apple-system,sans-serif}.bar{display:flex;gap:15px;align-items:center;justify-content:space-between;padding:14px 23px;background:#fff;border-bottom:1px solid #dce4e9;flex-wrap:wrap}.bar strong{font-size:16px}.bar small{display:block;font-size:11px;color:#657a89;margin-top:4px}.buttons{display:flex;gap:6px}.buttons button{border:1px solid #cbdce4;background:#f6f9fa;padding:9px 14px;border-radius:6px;font-weight:650;color:#1e4359;cursor:pointer}.buttons button[aria-pressed=true]{background:#0f5e78;color:#fff}iframe{border:0;width:100%;height:calc(100vh - 74px);min-height:550px;background:#fff;display:block;margin:auto;transition:width .15s}.note{position:fixed;right:20px;bottom:18px;border:1px solid #d0dce3;border-radius:9px;background:#fff;padding:10px 13px;font-size:12px;box-shadow:0 4px 15px #0001}</style></head><body><div class="bar"><div><strong>HESU PIPE · 核心优势 / 动态内容列表</strong><small>V8.33.1 从实际静态构建生成的离线页面 · 示范文字与占位产品图</small></div><div class="buttons"><button data-size="100%" aria-pressed="true">电脑版</button><button data-size="820px" aria-pressed="false">平板</button><button data-size="390px" aria-pressed="false">手机版</button></div></div><iframe title="工业建材主题首页实际构建预览" sandbox="allow-scripts" id="preview"></iframe><span class="note">仅检查预览 · 不连接线上网站</span><script id="payload" type="application/json">PAYLOAD</script><script>const raw=JSON.parse(document.getElementById('payload').textContent);const target=document.getElementById('preview');target.srcdoc=raw;document.querySelectorAll('[data-size]').forEach(btn=>btn.addEventListener('click',()=>{target.style.width=btn.dataset.size;document.querySelectorAll('[data-size]').forEach(item=>item.setAttribute('aria-pressed',String(item===btn)));}));</script></body></html>'''
|
|
isolated.write_text(wrapper.replace('PAYLOAD',snippet),encoding='utf-8')
|
|
(target/'v8331-real-built-page.html').write_text(source,encoding='utf-8')
|
|
print('generated',isolated,'bytes',isolated.stat().st_size,'build',bid)
|
|
if __name__=='__main__':generate()
|