145 行
3.7 KiB
JavaScript
145 行
3.7 KiB
JavaScript
/**
|
|
* 数据库初始化脚本
|
|
* 运行: node server/database/migrate.js
|
|
*
|
|
* 创建所有表(如果不存在)
|
|
*/
|
|
import Database from 'better-sqlite3'
|
|
import { join } from 'path'
|
|
import { fileURLToPath } from 'url'
|
|
import { dirname } from 'path'
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
const DB_PATH = join(__dirname, 'hulai.db')
|
|
|
|
const db = new Database(DB_PATH)
|
|
db.pragma('journal_mode = WAL')
|
|
db.pragma('foreign_keys = ON')
|
|
|
|
// ── 建表 SQL ────────────────────────────────────────
|
|
const tables = `
|
|
CREATE TABLE IF NOT EXISTS admin_users (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
username TEXT NOT NULL UNIQUE,
|
|
password_hash TEXT NOT NULL,
|
|
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS articles (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
slug TEXT NOT NULL UNIQUE,
|
|
title TEXT NOT NULL,
|
|
summary TEXT,
|
|
content TEXT,
|
|
category TEXT,
|
|
type TEXT NOT NULL DEFAULT 'news',
|
|
cover_image TEXT,
|
|
author TEXT,
|
|
tags TEXT,
|
|
seo_title TEXT,
|
|
seo_description TEXT,
|
|
seo_keywords TEXT,
|
|
published INTEGER DEFAULT 1,
|
|
sort_order INTEGER DEFAULT 0,
|
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS reviews (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
author TEXT NOT NULL,
|
|
rating INTEGER DEFAULT 5,
|
|
title TEXT,
|
|
content TEXT,
|
|
date TEXT,
|
|
screenshot TEXT,
|
|
scenes TEXT,
|
|
concerns TEXT,
|
|
verified INTEGER DEFAULT 0,
|
|
published INTEGER DEFAULT 1,
|
|
sort_order INTEGER DEFAULT 0,
|
|
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS faqs (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
question TEXT NOT NULL,
|
|
answer TEXT NOT NULL,
|
|
sort_order INTEGER DEFAULT 0,
|
|
published INTEGER DEFAULT 1
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS gallery_items (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
title TEXT NOT NULL,
|
|
category TEXT,
|
|
location TEXT,
|
|
description TEXT,
|
|
image TEXT NOT NULL,
|
|
orientation TEXT,
|
|
published INTEGER DEFAULT 1,
|
|
sort_order INTEGER DEFAULT 0
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS stories (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
slug TEXT NOT NULL UNIQUE,
|
|
title TEXT NOT NULL,
|
|
subtitle TEXT,
|
|
family_type TEXT,
|
|
children_age TEXT,
|
|
from_city TEXT,
|
|
trip_product TEXT,
|
|
travel_date TEXT,
|
|
cover_image TEXT,
|
|
sections TEXT,
|
|
key_moments TEXT,
|
|
seo_title TEXT,
|
|
seo_description TEXT,
|
|
published INTEGER DEFAULT 1,
|
|
sort_order INTEGER DEFAULT 0,
|
|
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS submissions (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
type TEXT NOT NULL,
|
|
data TEXT NOT NULL,
|
|
ip TEXT,
|
|
read INTEGER DEFAULT 0,
|
|
replied INTEGER DEFAULT 0,
|
|
replied_at TEXT,
|
|
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS site_content (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
key TEXT NOT NULL UNIQUE,
|
|
label TEXT NOT NULL,
|
|
data TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS sessions (
|
|
token TEXT PRIMARY KEY,
|
|
user_id INTEGER NOT NULL,
|
|
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
);
|
|
`
|
|
|
|
db.exec(tables)
|
|
console.log('✅ 数据库表创建完成:', DB_PATH)
|
|
|
|
// ── 增量迁移:为已有表补齐新列(幂等)───────────────
|
|
function addColumnIfMissing(table, column, ddl) {
|
|
const info = db.prepare(`PRAGMA table_info(${table})`).all()
|
|
if (!info.some(c => c.name === column)) {
|
|
db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${ddl}`)
|
|
console.log(` + ${table}.${column} 列已添加`)
|
|
}
|
|
}
|
|
addColumnIfMissing('submissions', 'replied', 'INTEGER DEFAULT 0')
|
|
addColumnIfMissing('submissions', 'replied_at', 'TEXT')
|
|
|
|
db.close()
|