Mimingguang ee078b61bc feat: 后台管理系统完整重构 + 前后台数据联通
- 后台全面引入 Naive UI 组件库,统一 UI 规范
- 前台 usePublicApi 切换到 API 调用(GET /api/content/[key])
- 前后台数据源统一(site_content 表)
- 产品线统一管理(tour/camp/course 三套编辑器)
- 产品数据归一化(itinerary/faq/pricing 格式统一)
- 表单提交 API 改写入 submissions 表
- 新增 submissions 标记已读 API
- site-content PUT 支持 upsert
- 修复前台 bug(stories 路由冲突、占位符警告、selector breadcrumb)
- 消除 PageHero 类型警告(useSEO reactive 修复)
- 25 个前台页面全部 HTTP 200,展示效果不变

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 17:48:02 +08:00

57 行
1.2 KiB
JavaScript

/**
* 文件存储工具
* 将表单数据追加写入 server/storage/ 目录下的 JSON 文件
*/
import { promises as fs } from 'fs'
import { resolve } from 'path'
const STORAGE_DIR = resolve(process.cwd(), 'server/storage')
/**
* 确保存储目录存在
*/
async function ensureDir() {
try {
await fs.mkdir(STORAGE_DIR, { recursive: true })
} catch {
// 目录已存在,忽略
}
}
/**
* 将记录追加到指定文件
* @param {string} filename - 不含路径,如 'customize.json'
* @param {object} record
*/
export async function appendRecord(filename, record) {
await ensureDir()
const filepath = resolve(STORAGE_DIR, filename)
let records = []
try {
const raw = await fs.readFile(filepath, 'utf-8')
records = JSON.parse(raw)
} catch {
// 文件不存在或解析失败,从空数组开始
}
records.push(record)
await fs.writeFile(filepath, JSON.stringify(records, null, 2), 'utf-8')
return record
}
/**
* 读取所有记录(管理员用)
* @param {string} filename
*/
export async function readRecords(filename) {
const filepath = resolve(STORAGE_DIR, filename)
try {
const raw = await fs.readFile(filepath, 'utf-8')
return JSON.parse(raw)
} catch {
return []
}
}