135 lines
3.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* ═══════════════════════════════════════════════════
* 铸渊封面工作室 · 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`)
})