135 lines
3.6 KiB
JavaScript
135 lines
3.6 KiB
JavaScript
/**
|
||
* ═══════════════════════════════════════════════════
|
||
* 铸渊封面工作室 · Web 服务 · v2.0
|
||
* ═══════════════════════════════════════════════════
|
||
*
|
||
* 公开页面 + 模板列表 API + 生成 API + 下载端点
|
||
*/
|
||
|
||
import express from 'express'
|
||
import cors from 'cors'
|
||
import { generate, listTemplates } from './generate.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))
|
||
|
||
|
||
/* ═══════════════════════════════════════════════════
|
||
* API
|
||
* ═══════════════════════════════════════════════════ */
|
||
|
||
/**
|
||
* GET /api/templates
|
||
* 获取所有可用模板列表
|
||
*/
|
||
app.get('/api/templates', (req, res) => {
|
||
try {
|
||
const templates = listTemplates()
|
||
res.json(templates)
|
||
} catch (err) {
|
||
res.status(500).json({ error: '无法加载模板列表' })
|
||
}
|
||
})
|
||
|
||
|
||
/**
|
||
* POST /api/generate
|
||
* 生成封面图片
|
||
*
|
||
* Body:
|
||
* templateId - 模板ID(默认第一个)
|
||
* presetId - 预设风格ID
|
||
* title - 标题
|
||
* body - 正文
|
||
* subtitle - 副标题(可选)
|
||
* tag - 标签(可选)
|
||
* layout - 布局(default/hero/quote)
|
||
*/
|
||
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,
|
||
})
|
||
|
||
// 构造可访问的 URL
|
||
const filename = result.files[0].split('/').pop()
|
||
const url = `/output/${filename}`
|
||
|
||
res.json({
|
||
success: true,
|
||
url,
|
||
files: result.files,
|
||
templateId: result.templateId,
|
||
templateName: result.templateName,
|
||
presetId: result.presetId,
|
||
layout: result.layout,
|
||
filename,
|
||
})
|
||
} catch (err) {
|
||
console.error('生成失败:', err.message)
|
||
res.status(500).json({ error: err.message || '生成失败' })
|
||
}
|
||
})
|
||
|
||
|
||
/**
|
||
* GET /api/health
|
||
* 健康检查
|
||
*/
|
||
app.get('/api/health', (req, res) => {
|
||
res.json({ status: 'ok', timestamp: new Date().toISOString() })
|
||
})
|
||
|
||
|
||
// ── 兜底:SPA 路由 ──
|
||
app.get('*', (req, res) => {
|
||
res.sendFile(join(PUBLIC_DIR, 'index.html'))
|
||
})
|
||
|
||
|
||
// ── 启动 ──
|
||
app.listen(PORT, () => {
|
||
console.log(`🎨 铸渊封面工作室 v2.0 运行在 http://localhost:${PORT}`)
|
||
console.log(` 📋 模板API: http://localhost:${PORT}/api/templates`)
|
||
console.log(` 🎯 生成API: http://localhost:${PORT}/api/generate`)
|
||
})
|