Guanghu Domestic Migration d1e47f4565
Some checks are pending
自动更新代码和重启 / update-and-restart (push) Waiting to run
CI检查 + 自动部署 / check (push) Waiting to run
CI检查 + 自动部署 / deploy (push) Blocked by required conditions
重启聊天服务 / restart (push) Waiting to run
chore: import sanitized domestic snapshot for REPO-002
Source snapshot: ca48d3ddf926d79aa138306164169baf764bb829
2026-07-17 15:54:41 +08:00

90 lines
3.3 KiB
JavaScript

/**
* 铸渊封面工作室 · Web 服务 · v2.1
* 公开页面 + 模板列表 API + 生成 API + 意图链 + 下载端点
*/
import express from 'express'
import cors from 'cors'
import { generate, listTemplates } from './generate.js'
import { renderComponents } from './components/render.js'
import { renderToImage } from './renderer.js'
import { join, dirname } from 'path'
import { fileURLToPath } from 'url'
import { existsSync, mkdirSync } from 'fs'
const __dirname = dirname(fileURLToPath(import.meta.url))
const OUTPUT_DIR = join(__dirname, 'output')
const PUBLIC_DIR = join(__dirname, 'public')
const PORT = process.env.PORT || 3912
if (!existsSync(OUTPUT_DIR)) mkdirSync(OUTPUT_DIR, { recursive: true })
const app = express()
app.use(cors())
app.use(express.json({ limit: '1mb' }))
app.use('/output', express.static(OUTPUT_DIR))
app.use(express.static(PUBLIC_DIR))
app.get('/api/templates', (req, res) => {
try { res.json(listTemplates()) }
catch (err) { res.status(500).json({ error: '无法加载模板列表' }) }
})
app.post('/api/generate', async (req, res) => {
try {
const { templateId, presetId = '', title = '', body = '', subtitle = '', tag = '', layout = 'default' } = req.body
if (!title && !body) return res.status(400).json({ error: '请至少提供标题或正文内容' })
const result = await generate({ templateId, presetId, title, body, subtitle, tag, layout })
const filename = result.files[0].split('/').pop()
const url = `/output/${filename}`
res.json({
success: true,
url, filename,
templateId: result.templateId,
templateName: result.templateName,
presetId: result.presetId,
layout: result.layout,
intent_chain: result.intent_chain,
})
} catch (err) {
console.error('生成失败:', err.message)
res.status(500).json({ error: err.message || '生成失败' })
}
})
app.get('/api/health', (req, res) => {
res.json({ status: 'ok', timestamp: new Date().toISOString() })
})
// compose
app.post('/api/compose', async (req, res) => {
try {
const { canvas, stack } = req.body
if (!stack || !stack.length) return res.status(400).json({ error: 'need stack' })
const html = renderComponents({ canvas, stack })
const result = await renderToImage(html, { width: canvas?.width || 1080, height: canvas?.height || 1440, name: 'compose_'+Date.now(), format: 'png' })
res.json({ success:true, url:'/output/'+result.split('/').pop(), components: stack.map(c=>c.component) })
} catch(err) { console.error(err); res.status(500).json({ error: err.message }) }
})
app.get('*', (req, res) => {
res.sendFile(join(PUBLIC_DIR, 'index.html'))
})
app.post('/api/compose', async (req, res) => {
try {
const { canvas, stack } = req.body
if (!stack || !stack.length) return res.status(400).json({ error: 'need stack' })
const html = renderComponents({ canvas, stack })
const result = await renderToImage(html, { width: canvas?.width || 1080, height: canvas?.height || 1440, name: 'compose_'+Date.now(), format: 'png' })
res.json({ success:true, url:'/output/'+result.split('/').pop(), components: stack.map(c=>c.component) })
} catch(err) { console.error(err); res.status(500).json({ error: err.message }) }
})
app.listen(PORT, () => {
console.log(`🎨 铸渊封面工作室 v2.1 运行在 http://localhost:${PORT}`)
})