Initial clean project import
This commit is contained in:
@@ -0,0 +1,838 @@
|
||||
import StorageMain from "../storage/main";
|
||||
import Files from "../file/main";
|
||||
|
||||
const versions = [
|
||||
{
|
||||
version: 0,
|
||||
up: async (db: DB) => {
|
||||
// await db.execute(`CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)`);
|
||||
// console.log('db.insert', await db.insert(`INSERT INTO users (name, email) VALUES (?, ?)`,['Alice', 'alice@example.com']));
|
||||
// console.log('db.select', await db.select(`SELECT * FROM users`));
|
||||
// console.log('db.first', await db.first(`SELECT * FROM users`));
|
||||
},
|
||||
},
|
||||
{
|
||||
version: 1,
|
||||
up: async (db: DB) => {
|
||||
await db.execute(`CREATE TABLE IF NOT EXISTS data_sound_tts (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
serverName TEXT,
|
||||
serverTitle TEXT,
|
||||
serverVersion TEXT,
|
||||
text TEXT,
|
||||
param TEXT,
|
||||
|
||||
status TEXT,
|
||||
statusMsg TEXT,
|
||||
jobResult TEXT,
|
||||
startTime INTEGER,
|
||||
endTime INTEGER,
|
||||
resultWav TEXT
|
||||
)`);
|
||||
await db.execute(`CREATE TABLE IF NOT EXISTS data_sound_clone (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
serverName TEXT,
|
||||
serverTitle TEXT,
|
||||
serverVersion TEXT,
|
||||
promptName TEXT,
|
||||
promptWav TEXT,
|
||||
promptText TEXT,
|
||||
text TEXT,
|
||||
param TEXT,
|
||||
|
||||
status TEXT,
|
||||
statusMsg TEXT,
|
||||
jobResult TEXT,
|
||||
startTime INTEGER,
|
||||
endTime INTEGER,
|
||||
resultWav TEXT
|
||||
)`);
|
||||
await db.execute(`CREATE TABLE IF NOT EXISTS data_video_template (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
name TEXT,
|
||||
video TEXT
|
||||
)`);
|
||||
await db.execute(`CREATE TABLE IF NOT EXISTS data_video_gen (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
serverName TEXT,
|
||||
serverTitle TEXT,
|
||||
serverVersion TEXT,
|
||||
videoTemplateId INTEGER,
|
||||
videoTemplateName TEXT,
|
||||
soundType TEXT,
|
||||
soundTtsId INTEGER,
|
||||
soundTtsText TEXT,
|
||||
soundCloneId INTEGER,
|
||||
soundCloneText TEXT,
|
||||
|
||||
param TEXT,
|
||||
|
||||
status TEXT,
|
||||
statusMsg TEXT,
|
||||
jobResult TEXT,
|
||||
startTime INTEGER,
|
||||
endTime INTEGER,
|
||||
resultMp4 TEXT
|
||||
)`);
|
||||
},
|
||||
},
|
||||
{
|
||||
version: 2,
|
||||
up: async (db: DB) => {
|
||||
await db.execute(`ALTER TABLE data_sound_tts ADD COLUMN result TEXT`);
|
||||
await db.execute(`ALTER TABLE data_sound_clone ADD COLUMN result TEXT`);
|
||||
await db.execute(`ALTER TABLE data_video_gen ADD COLUMN result TEXT`);
|
||||
},
|
||||
},
|
||||
{
|
||||
version: 3,
|
||||
up: async (db: DB) => {
|
||||
await db.execute(`ALTER TABLE data_video_gen ADD COLUMN soundCustomFile TEXT`);
|
||||
},
|
||||
},
|
||||
{
|
||||
version: 4,
|
||||
up: async (db: DB) => {
|
||||
await db.execute(`CREATE TABLE IF NOT EXISTS data_task (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
createdAt INTEGER DEFAULT (strftime('%s', 'now')),
|
||||
updatedAt INTEGER DEFAULT (strftime('%s', 'now')),
|
||||
|
||||
biz TEXT,
|
||||
|
||||
status TEXT,
|
||||
statusMsg TEXT,
|
||||
startTime INTEGER,
|
||||
endTime INTEGER,
|
||||
|
||||
serverName TEXT,
|
||||
serverTitle TEXT,
|
||||
serverVersion TEXT,
|
||||
|
||||
param TEXT,
|
||||
jobResult TEXT,
|
||||
modelConfig TEXT,
|
||||
result TEXT
|
||||
|
||||
)`);
|
||||
},
|
||||
},
|
||||
{
|
||||
version: 5,
|
||||
up: async (db: DB) => {
|
||||
// await db.execute(`DELETE FROM data_task where 1=1`);
|
||||
// SoundClone
|
||||
let records = await db.select(`SELECT * FROM data_sound_clone`);
|
||||
for (const r of records) {
|
||||
const values = [
|
||||
"SoundClone",
|
||||
r.status,
|
||||
r.statusMsg,
|
||||
r.startTime,
|
||||
r.endTime,
|
||||
r.serverName,
|
||||
r.serverTitle,
|
||||
r.serverVersion,
|
||||
r.param,
|
||||
r.jobResult,
|
||||
JSON.stringify({
|
||||
promptName: r.promptName,
|
||||
promptWav: r.promptWav,
|
||||
promptText: r.promptText,
|
||||
text: r.text,
|
||||
}),
|
||||
JSON.stringify({
|
||||
url: r.resultWav,
|
||||
}),
|
||||
];
|
||||
await db.insert(
|
||||
`INSERT INTO data_task
|
||||
(biz, status, statusMsg, startTime, endTime, serverName, serverTitle, serverVersion, param, jobResult, modelConfig, result)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
values
|
||||
);
|
||||
}
|
||||
// SoundTts
|
||||
records = await db.select(`SELECT * FROM data_sound_tts`);
|
||||
for (const r of records) {
|
||||
const values = [
|
||||
"SoundTts",
|
||||
r.status,
|
||||
r.statusMsg,
|
||||
r.startTime,
|
||||
r.endTime,
|
||||
r.serverName,
|
||||
r.serverTitle,
|
||||
r.serverVersion,
|
||||
r.param,
|
||||
r.jobResult,
|
||||
JSON.stringify({
|
||||
text: r.text,
|
||||
}),
|
||||
JSON.stringify({
|
||||
url: r.resultWav,
|
||||
}),
|
||||
];
|
||||
await db.insert(
|
||||
`INSERT INTO data_task
|
||||
(biz, status, statusMsg, startTime, endTime, serverName, serverTitle, serverVersion, param, jobResult, modelConfig, result)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
values
|
||||
);
|
||||
}
|
||||
// VideoGen
|
||||
records = await db.select(`SELECT * FROM data_video_gen`);
|
||||
for (const r of records) {
|
||||
const values = [
|
||||
"VideoGen",
|
||||
r.status,
|
||||
r.statusMsg,
|
||||
r.startTime,
|
||||
r.endTime,
|
||||
r.serverName,
|
||||
r.serverTitle,
|
||||
r.serverVersion,
|
||||
r.param,
|
||||
r.jobResult,
|
||||
JSON.stringify({
|
||||
videoTemplateId: r.videoTemplateId,
|
||||
videoTemplateName: r.videoTemplateName,
|
||||
soundType: r.soundType,
|
||||
soundTtsId: r.soundTtsId,
|
||||
soundTtsText: r.soundTtsText,
|
||||
soundCloneId: r.soundCloneId,
|
||||
soundCloneText: r.soundCloneText,
|
||||
soundCustomFile: r.soundCustomFile,
|
||||
}),
|
||||
JSON.stringify({
|
||||
url: r.resultMp4,
|
||||
}),
|
||||
];
|
||||
await db.insert(
|
||||
`INSERT INTO data_task
|
||||
(biz, status, statusMsg, startTime, endTime, serverName, serverTitle, serverVersion, param, jobResult, modelConfig, result)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
values
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
version: 6,
|
||||
up: async (db: DB) => {
|
||||
await db.execute(`CREATE TABLE IF NOT EXISTS data_storage (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
createdAt INTEGER DEFAULT (strftime('%s', 'now')),
|
||||
updatedAt INTEGER DEFAULT (strftime('%s', 'now')),
|
||||
|
||||
biz TEXT,
|
||||
|
||||
title TEXT,
|
||||
sort INTEGER,
|
||||
content TEXT
|
||||
|
||||
)`);
|
||||
},
|
||||
},
|
||||
{
|
||||
version: 7,
|
||||
up: async (db: DB) => {
|
||||
const records = await StorageMain.get("soundClonePrompt", "records", []);
|
||||
for (const r of records) {
|
||||
const values = [
|
||||
"SoundPrompt",
|
||||
r.name,
|
||||
JSON.stringify({
|
||||
url: r.promptWav,
|
||||
promptText: r.promptText,
|
||||
}),
|
||||
];
|
||||
await db.insert(
|
||||
`INSERT INTO data_storage (biz, title, content)
|
||||
VALUES (?, ?, ?)`,
|
||||
values
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
version: 8,
|
||||
up: async (db: DB) => {
|
||||
await db.execute(`ALTER TABLE data_task ADD COLUMN title TEXT`);
|
||||
const records = await db.select(`SELECT * FROM data_task`);
|
||||
for (const r of records) {
|
||||
let modelConfig: any = {};
|
||||
try {
|
||||
modelConfig = JSON.parse(r.modelConfig);
|
||||
} catch (e) {
|
||||
modelConfig = {};
|
||||
}
|
||||
let title = "";
|
||||
if (r.biz === "SoundTts" || r.biz === "SoundClone") {
|
||||
title = Files.textToName(modelConfig.text);
|
||||
} else if (r.biz === "VideoGen") {
|
||||
title = Files.textToName([modelConfig.videoTemplateName, modelConfig.soundTtsText].join("_"));
|
||||
} else if (r.biz === "VideoGenFlow") {
|
||||
title = Files.textToName([modelConfig.videoTemplateName, modelConfig.text].join("_"));
|
||||
}
|
||||
await db.execute(`UPDATE data_task SET title = ? WHERE id = ?`, [title, r.id]);
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
version: 9,
|
||||
up: async (db: DB) => {
|
||||
const records = await db.select(`SELECT * FROM data_task where biz in ('SoundTts', 'SoundClone')`);
|
||||
for (const r of records) {
|
||||
const modelConfigOld = JSON.parse(r.modelConfig);
|
||||
const paramOld = JSON.parse(r.param);
|
||||
const modelConfig: any = {
|
||||
type: r.biz,
|
||||
ttsParam: r.biz === "SoundTts" ? paramOld : undefined,
|
||||
cloneParam: r.biz === "SoundClone" ? paramOld : undefined,
|
||||
...modelConfigOld,
|
||||
};
|
||||
const values = [
|
||||
"SoundGenerate",
|
||||
r.title,
|
||||
r.status,
|
||||
r.statusMsg,
|
||||
r.startTime,
|
||||
r.endTime,
|
||||
r.serverName,
|
||||
r.serverTitle,
|
||||
r.serverVersion,
|
||||
JSON.stringify({}),
|
||||
r.jobResult,
|
||||
JSON.stringify(modelConfig),
|
||||
r.result,
|
||||
];
|
||||
await db.insert(
|
||||
`INSERT INTO data_task
|
||||
(biz, title, status, statusMsg, startTime, endTime, serverName, serverTitle, serverVersion, param, jobResult, modelConfig, result)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
values
|
||||
);
|
||||
await db.execute(`DELETE FROM data_task WHERE id = ?`, [r.id]);
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
version: 10,
|
||||
up: async (db: DB) => {
|
||||
await db.execute(`ALTER TABLE data_task
|
||||
ADD COLUMN type INTEGER DEFAULT 1`);
|
||||
},
|
||||
},
|
||||
{
|
||||
version: 11,
|
||||
up: async (db: DB) => {
|
||||
await db.execute(`ALTER TABLE data_video_template
|
||||
ADD COLUMN info TEXT`);
|
||||
},
|
||||
},
|
||||
{
|
||||
version: 12,
|
||||
up: async (db: DB) => {
|
||||
// 平台账号表
|
||||
await db.execute(`CREATE TABLE IF NOT EXISTS platform_accounts (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
platform TEXT NOT NULL,
|
||||
nickname TEXT,
|
||||
uid TEXT,
|
||||
avatar TEXT,
|
||||
cookies TEXT,
|
||||
tokens TEXT,
|
||||
loginStatus TEXT DEFAULT 'pending',
|
||||
expiresAt INTEGER,
|
||||
createdAt INTEGER,
|
||||
updatedAt INTEGER
|
||||
)`);
|
||||
|
||||
// 发布任务表
|
||||
await db.execute(`CREATE TABLE IF NOT EXISTS publish_tasks (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
title TEXT,
|
||||
videoPath TEXT,
|
||||
coverPath TEXT,
|
||||
platforms TEXT,
|
||||
publishConfig TEXT,
|
||||
scheduledTime INTEGER,
|
||||
status TEXT DEFAULT 'pending',
|
||||
createdAt INTEGER,
|
||||
updatedAt INTEGER
|
||||
)`);
|
||||
|
||||
// 发布历史表
|
||||
await db.execute(`CREATE TABLE IF NOT EXISTS publish_history (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
taskId INTEGER,
|
||||
platform TEXT,
|
||||
accountId INTEGER,
|
||||
videoUrl TEXT,
|
||||
videoId TEXT,
|
||||
status TEXT,
|
||||
errorMessage TEXT,
|
||||
publishedAt INTEGER,
|
||||
viewCount INTEGER DEFAULT 0,
|
||||
likeCount INTEGER DEFAULT 0
|
||||
)`);
|
||||
|
||||
// 创建索引
|
||||
await db.execute(`CREATE INDEX IF NOT EXISTS idx_platform_accounts_platform ON platform_accounts(platform)`);
|
||||
await db.execute(`CREATE INDEX IF NOT EXISTS idx_publish_tasks_status ON publish_tasks(status)`);
|
||||
await db.execute(`CREATE INDEX IF NOT EXISTS idx_publish_history_taskId ON publish_history(taskId)`);
|
||||
await db.execute(`CREATE INDEX IF NOT EXISTS idx_publish_history_platform ON publish_history(platform)`);
|
||||
},
|
||||
},
|
||||
{
|
||||
version: 13,
|
||||
up: async (db: DB) => {
|
||||
// 封面模板表
|
||||
await db.execute(`CREATE TABLE IF NOT EXISTS cover_templates (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
thumbnail_path TEXT,
|
||||
config TEXT NOT NULL,
|
||||
created_at INTEGER NOT NULL,
|
||||
updated_at INTEGER NOT NULL
|
||||
)`);
|
||||
|
||||
// 创建索引以提升查询性能
|
||||
await db.execute(`CREATE INDEX IF NOT EXISTS idx_cover_templates_created_at ON cover_templates(created_at DESC)`);
|
||||
await db.execute(`CREATE INDEX IF NOT EXISTS idx_cover_templates_name ON cover_templates(name)`);
|
||||
},
|
||||
},
|
||||
{
|
||||
version: 14,
|
||||
up: async (db: DB) => {
|
||||
// 关键词分组表 - 用于字幕特效管理
|
||||
await db.execute(`CREATE TABLE IF NOT EXISTS keyword_groups (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
keywords TEXT NOT NULL,
|
||||
color TEXT,
|
||||
effectId TEXT,
|
||||
styleOverride TEXT,
|
||||
effectConfig TEXT,
|
||||
styleConfig TEXT,
|
||||
createdAt INTEGER,
|
||||
updatedAt INTEGER
|
||||
)`);
|
||||
|
||||
// 创建索引以提升查询性能
|
||||
await db.execute(`CREATE INDEX IF NOT EXISTS idx_keyword_groups_name ON keyword_groups(name)`);
|
||||
await db.execute(`CREATE INDEX IF NOT EXISTS idx_keyword_groups_createdAt ON keyword_groups(createdAt DESC)`);
|
||||
},
|
||||
},
|
||||
{
|
||||
version: 15,
|
||||
up: async (db: DB) => {
|
||||
// 字幕样式表 - 用于保存字幕样式配置
|
||||
await db.execute(`CREATE TABLE IF NOT EXISTS subtitle_styles (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
preview TEXT,
|
||||
fontName TEXT NOT NULL,
|
||||
fontSize INTEGER,
|
||||
fontColor TEXT,
|
||||
outlineColor TEXT,
|
||||
outlineWidth INTEGER,
|
||||
shadowOffset INTEGER,
|
||||
backgroundColor TEXT,
|
||||
backgroundOpacity REAL,
|
||||
position TEXT,
|
||||
alignment TEXT,
|
||||
createdAt INTEGER,
|
||||
updatedAt INTEGER
|
||||
)`);
|
||||
|
||||
// 创建索引以提升查询性能
|
||||
await db.execute(`CREATE INDEX IF NOT EXISTS idx_subtitle_styles_name ON subtitle_styles(name)`);
|
||||
await db.execute(`CREATE INDEX IF NOT EXISTS idx_subtitle_styles_createdAt ON subtitle_styles(createdAt DESC)`);
|
||||
},
|
||||
},
|
||||
{
|
||||
version: 16,
|
||||
up: async (db: DB) => {
|
||||
// 为 cover_templates 表添加 is_system 字段,标识系统默认模板
|
||||
await db.execute(`
|
||||
ALTER TABLE cover_templates
|
||||
ADD COLUMN is_system INTEGER DEFAULT 0
|
||||
`);
|
||||
|
||||
// 为 subtitle_styles 表添加 is_system 字段,标识系统默认样式
|
||||
await db.execute(`
|
||||
ALTER TABLE subtitle_styles
|
||||
ADD COLUMN is_system INTEGER DEFAULT 0
|
||||
`);
|
||||
|
||||
console.log('[Migration v16] 已添加 is_system 字段到 cover_templates 和 subtitle_styles 表');
|
||||
},
|
||||
},
|
||||
{
|
||||
version: 17,
|
||||
up: async (db: DB) => {
|
||||
// 为 subtitle_styles 表添加 shadowColor 和 shadowBlur 字段
|
||||
try {
|
||||
await db.execute(`
|
||||
ALTER TABLE subtitle_styles
|
||||
ADD COLUMN shadowColor TEXT DEFAULT '#000000'
|
||||
`);
|
||||
console.log('[Migration v17] 已添加 shadowColor 字段到 subtitle_styles 表');
|
||||
} catch (error: any) {
|
||||
if (!error.message?.includes('duplicate column')) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await db.execute(`
|
||||
ALTER TABLE subtitle_styles
|
||||
ADD COLUMN shadowBlur INTEGER DEFAULT 4
|
||||
`);
|
||||
console.log('[Migration v17] 已添加 shadowBlur 字段到 subtitle_styles 表');
|
||||
} catch (error: any) {
|
||||
if (!error.message?.includes('duplicate column')) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
version: 18,
|
||||
up: async (db: DB) => {
|
||||
console.log('[Migration v18] 开始处理封面模板系统迁移');
|
||||
|
||||
try {
|
||||
// 第一步:删除旧的系统模板(system-cover-01/02/03/04)
|
||||
const oldSystemTemplates = [
|
||||
'system-cover-01',
|
||||
'system-cover-02',
|
||||
'system-cover-03',
|
||||
'system-cover-04'
|
||||
];
|
||||
|
||||
for (const templateId of oldSystemTemplates) {
|
||||
try {
|
||||
await db.execute(
|
||||
`DELETE FROM cover_templates WHERE id = ?`,
|
||||
[templateId]
|
||||
);
|
||||
console.log(`[Migration v18] ✅ 已删除旧系统模板: ${templateId}`);
|
||||
} catch (error) {
|
||||
// 如果模板不存在,忽略错误(新用户的情况)
|
||||
console.log(`[Migration v18] ℹ️ 旧系统模板不存在(预期行为): ${templateId}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 第二步:将4个自定义模板标记为新的系统模板(仅对现有模板操作)
|
||||
const newSystemTemplates = [
|
||||
'485183be-6eed-4eae-b74a-8591f08b69fa', // 黄白实描
|
||||
'8f17c467-c69c-45b2-8011-94a6c3071d16', // 斜黄白
|
||||
'6404718a-1b1c-4148-9807-ad3c57a53e0c', // 蓝底白字
|
||||
'705fa010-5bf3-4041-b624-0fd364bea5ad' // 大四字报
|
||||
];
|
||||
|
||||
for (const templateId of newSystemTemplates) {
|
||||
try {
|
||||
const result = await db.execute(
|
||||
`UPDATE cover_templates SET is_system = 1 WHERE id = ?`,
|
||||
[templateId]
|
||||
);
|
||||
console.log(`[Migration v18] ✅ 已标记为系统模板: ${templateId}`);
|
||||
} catch (error) {
|
||||
// 如果模板不存在,忽略(新用户的情况)
|
||||
console.log(`[Migration v18] ℹ️ 模板不存在,跳过标记: ${templateId}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('[Migration v18] ✅ 封面模板系统迁移完成(仅对现有模板生效)');
|
||||
console.log('[Migration v18] 💡 提示:新用户请运行 \"ipAgent:exportCoverTemplatesToConfig\" 来导出这4个模板到配置文件');
|
||||
} catch (error: any) {
|
||||
console.error('[Migration v18] ❌ 迁移失败:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
version: 19,
|
||||
up: async (db: DB) => {
|
||||
console.log('[Migration v19] 开始音效系统初始化');
|
||||
|
||||
try {
|
||||
// 第一步:创建音效库表
|
||||
await db.execute(`CREATE TABLE IF NOT EXISTS sound_effects (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
category TEXT,
|
||||
type TEXT,
|
||||
audio_source TEXT,
|
||||
volume REAL DEFAULT 0.7,
|
||||
duration REAL,
|
||||
metadata TEXT,
|
||||
created_at INTEGER,
|
||||
updated_at INTEGER,
|
||||
is_system INTEGER DEFAULT 0
|
||||
)`);
|
||||
console.log('[Migration v19] ✅ 创建 sound_effects 表');
|
||||
|
||||
// 第二步:创建用户自定义音效表
|
||||
await db.execute(`CREATE TABLE IF NOT EXISTS user_sound_effects (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
description TEXT,
|
||||
file_path TEXT NOT NULL,
|
||||
duration REAL,
|
||||
file_size INTEGER,
|
||||
created_at INTEGER,
|
||||
updated_at INTEGER
|
||||
)`);
|
||||
console.log('[Migration v19] ✅ 创建 user_sound_effects 表');
|
||||
|
||||
// 第三步:检查并添加 keyword_groups 新字段
|
||||
try {
|
||||
await db.execute(`ALTER TABLE keyword_groups ADD COLUMN sound_effect_id TEXT`);
|
||||
console.log('[Migration v19] ✅ 为 keyword_groups 添加 sound_effect_id 列');
|
||||
} catch (error: any) {
|
||||
if (error.message?.includes('duplicate')) {
|
||||
console.log('[Migration v19] ℹ️ sound_effect_id 列已存在,跳过');
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await db.execute(`ALTER TABLE keyword_groups ADD COLUMN sound_effect_config TEXT`);
|
||||
console.log('[Migration v19] ✅ 为 keyword_groups 添加 sound_effect_config 列');
|
||||
} catch (error: any) {
|
||||
if (error.message?.includes('duplicate')) {
|
||||
console.log('[Migration v19] ℹ️ sound_effect_config 列已存在,跳过');
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
console.log('[Migration v19] ✅ 音效系统初始化完成');
|
||||
} catch (error: any) {
|
||||
console.error('[Migration v19] ❌ 迁移失败:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
version: 20,
|
||||
up: async (db: DB) => {
|
||||
console.log('[Migration v20] 开始贴纸系统初始化');
|
||||
|
||||
try {
|
||||
// 添加 stickerId 列
|
||||
try {
|
||||
await db.execute(`ALTER TABLE keyword_groups ADD COLUMN stickerId TEXT`);
|
||||
console.log('[Migration v20] ✅ 为 keyword_groups 添加 stickerId 列');
|
||||
} catch (error: any) {
|
||||
if (error.message?.includes('duplicate')) {
|
||||
console.log('[Migration v20] ℹ️ stickerId 列已存在,跳过');
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 添加 stickerConfig 列
|
||||
try {
|
||||
await db.execute(`ALTER TABLE keyword_groups ADD COLUMN stickerConfig TEXT`);
|
||||
console.log('[Migration v20] ✅ 为 keyword_groups 添加 stickerConfig 列');
|
||||
} catch (error: any) {
|
||||
if (error.message?.includes('duplicate')) {
|
||||
console.log('[Migration v20] ℹ️ stickerConfig 列已存在,跳过');
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 添加 effectDuration 列
|
||||
try {
|
||||
await db.execute(`ALTER TABLE keyword_groups ADD COLUMN effectDuration REAL`);
|
||||
console.log('[Migration v20] ✅ 为 keyword_groups 添加 effectDuration 列');
|
||||
} catch (error: any) {
|
||||
if (error.message?.includes('duplicate')) {
|
||||
console.log('[Migration v20] ℹ️ effectDuration 列已存在,跳过');
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
console.log('[Migration v20] ✅ 贴纸系统初始化完成');
|
||||
} catch (error: any) {
|
||||
console.error('[Migration v20] ❌ 迁移失败:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
version: 21,
|
||||
up: async (db: DB) => {
|
||||
console.log('[Migration v21] 开始更新平台账号昵称');
|
||||
|
||||
try {
|
||||
// 更新所有平台账号的昵称为"用户"
|
||||
await db.execute(`
|
||||
UPDATE platform_accounts
|
||||
SET nickname = '用户'
|
||||
WHERE nickname IN ('抖音用户', '快手用户', '视频号用户', '小红书用户')
|
||||
`);
|
||||
|
||||
console.log('[Migration v21] ✅ 已更新所有平台账号昵称为"用户"');
|
||||
} catch (error: any) {
|
||||
console.error('[Migration v21] ❌ 迁移失败:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
version: 22,
|
||||
up: async (db: DB) => {
|
||||
console.log('[Migration v22] 开始创建用户认证系统表');
|
||||
|
||||
try {
|
||||
// 创建 users 表 - 用户基本信息
|
||||
await db.execute(`CREATE TABLE IF NOT EXISTS users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username TEXT UNIQUE NOT NULL,
|
||||
email TEXT UNIQUE NOT NULL,
|
||||
password TEXT NOT NULL,
|
||||
avatar TEXT,
|
||||
role TEXT DEFAULT 'user',
|
||||
status TEXT DEFAULT 'active',
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
last_login_at DATETIME
|
||||
)`);
|
||||
console.log('[Migration v22] ✅ 创建 users 表');
|
||||
|
||||
// 创建 user_subscriptions 表 - 用户订阅时长管理
|
||||
await db.execute(`CREATE TABLE IF NOT EXISTS user_subscriptions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER UNIQUE NOT NULL,
|
||||
subscription_type TEXT DEFAULT 'trial',
|
||||
expires_at DATETIME NOT NULL,
|
||||
trial_started_at DATETIME,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
)`);
|
||||
console.log('[Migration v22] ✅ 创建 user_subscriptions 表');
|
||||
|
||||
// 创建 user_time_logs 表 - 时长变更日志
|
||||
await db.execute(`CREATE TABLE IF NOT EXISTS user_time_logs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
operator_id INTEGER,
|
||||
action TEXT NOT NULL,
|
||||
days_added INTEGER,
|
||||
previous_expires_at DATETIME,
|
||||
new_expires_at DATETIME,
|
||||
reason TEXT,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (operator_id) REFERENCES users(id)
|
||||
)`);
|
||||
console.log('[Migration v22] ✅ 创建 user_time_logs 表');
|
||||
|
||||
// 创建 password_resets 表 - 密码重置记录(管理员手动重置)
|
||||
await db.execute(`CREATE TABLE IF NOT EXISTS password_resets (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
operator_id INTEGER,
|
||||
old_password_hash TEXT,
|
||||
reset_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
reason TEXT,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (operator_id) REFERENCES users(id)
|
||||
)`);
|
||||
console.log('[Migration v22] ✅ 创建 password_resets 表');
|
||||
|
||||
console.log('[Migration v22] ✅ 用户认证系统表创建完成');
|
||||
} catch (error: any) {
|
||||
console.error('[Migration v22] ❌ 迁移失败:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
version: 23,
|
||||
up: async (db: DB) => {
|
||||
console.log('[Migration v23] 开始创建字幕模板表和添加 readonly 列');
|
||||
|
||||
try {
|
||||
// 创建 subtitle_templates 表 - 用于保存字幕模板
|
||||
await db.execute(`CREATE TABLE IF NOT EXISTS subtitle_templates (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
config TEXT,
|
||||
is_system INTEGER DEFAULT 0,
|
||||
readonly INTEGER DEFAULT 0,
|
||||
created_at INTEGER,
|
||||
updated_at INTEGER
|
||||
)`);
|
||||
console.log('[Migration v23] ✅ 创建 subtitle_templates 表');
|
||||
|
||||
// 为 cover_templates 表添加 readonly 列(如果不存在)
|
||||
try {
|
||||
await db.execute(`
|
||||
ALTER TABLE cover_templates
|
||||
ADD COLUMN readonly INTEGER DEFAULT 0
|
||||
`);
|
||||
console.log('[Migration v23] ✅ 为 cover_templates 表添加 readonly 列');
|
||||
} catch (error: any) {
|
||||
if (error.message && error.message.includes('duplicate column name')) {
|
||||
console.log('[Migration v23] ℹ️ cover_templates 表已有 readonly 列');
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 为 subtitle_templates 表创建索引
|
||||
await db.execute(`CREATE INDEX IF NOT EXISTS idx_subtitle_templates_name ON subtitle_templates(name)`);
|
||||
await db.execute(`CREATE INDEX IF NOT EXISTS idx_subtitle_templates_is_system ON subtitle_templates(is_system)`);
|
||||
console.log('[Migration v23] ✅ 创建 subtitle_templates 表的索引');
|
||||
|
||||
console.log('[Migration v23] ✅ 字幕模板表创建完成');
|
||||
} catch (error: any) {
|
||||
console.error('[Migration v23] ❌ 迁移失败:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
version: 24,
|
||||
up: async (db: DB) => {
|
||||
console.log('[Migration v24] 开始创建素材库表');
|
||||
await db.execute(`CREATE TABLE IF NOT EXISTS data_video_material (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
path TEXT NOT NULL,
|
||||
type TEXT NOT NULL,
|
||||
info TEXT,
|
||||
createdAt INTEGER,
|
||||
updatedAt INTEGER
|
||||
)`);
|
||||
await db.execute(`CREATE INDEX IF NOT EXISTS idx_video_material_updatedAt ON data_video_material(updatedAt DESC)`);
|
||||
console.log('[Migration v24] 素材库表创建完成');
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export default {
|
||||
versions,
|
||||
};
|
||||
Reference in New Issue
Block a user