mirror of
https://github.com/ZeroCatDev/ClassworksKV.git
synced 2025-07-01 20:09:23 +00:00
Update .gitignore
to exclude Prisma database data directory and modify database setup in classworks.js
to use prisma migrate deploy
for migrations, enhancing deployment process.
This commit is contained in:
parent
93b9b1c6f5
commit
09875b35c1
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
node_modules
|
||||
# Keep environment variables out of version control
|
||||
.env
|
||||
prisma/database/data
|
||||
|
192
batchMigrate.js
Normal file
192
batchMigrate.js
Normal file
@ -0,0 +1,192 @@
|
||||
#!/usr/bin/env node
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { execSync } from 'child_process';
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
// 加载环境变量
|
||||
dotenv.config();
|
||||
|
||||
const PRISMA_DIR = path.join(process.cwd(), 'prisma');
|
||||
const DATABASE_DIR = path.join(PRISMA_DIR, 'database');
|
||||
const MIGRATIONS_DIR = path.join(PRISMA_DIR, 'migrations');
|
||||
|
||||
// 数据库 URL 环境变量映射
|
||||
const DB_URL_VARS = {
|
||||
mysql: 'MYSQL_DATABASE_URL',
|
||||
postgres: 'PG_DATABASE_URL'
|
||||
};
|
||||
|
||||
function copyDirectory(source, destination) {
|
||||
// 如果目标目录不存在,创建它
|
||||
if (!fs.existsSync(destination)) {
|
||||
fs.mkdirSync(destination, { recursive: true });
|
||||
}
|
||||
|
||||
// 读取源目录中的所有内容
|
||||
const items = fs.readdirSync(source);
|
||||
|
||||
for (const item of items) {
|
||||
const sourcePath = path.join(source, item);
|
||||
const destPath = path.join(destination, item);
|
||||
|
||||
const stats = fs.statSync(sourcePath);
|
||||
if (stats.isDirectory()) {
|
||||
// 如果是目录,递归复制
|
||||
copyDirectory(sourcePath, destPath);
|
||||
} else {
|
||||
// 如果是文件,直接复制
|
||||
fs.copyFileSync(sourcePath, destPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function deleteMigrationsDir() {
|
||||
if (fs.existsSync(MIGRATIONS_DIR)) {
|
||||
console.log('🗑️ 删除现有的 migrations 目录...');
|
||||
fs.rmSync(MIGRATIONS_DIR, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
// 修改 schema 文件中的数据库配置
|
||||
function updateSchemaConfig(schemaPath, dbType) {
|
||||
console.log(`📝 更新 schema 文件配置...`);
|
||||
|
||||
// 读取原始内容
|
||||
let content = fs.readFileSync(schemaPath, 'utf8');
|
||||
const originalContent = content;
|
||||
|
||||
if (dbType === 'sqlite') {
|
||||
// 修改 SQLite 数据库路径为 ../../data/db.db(用于迁移)
|
||||
content = content.replace(
|
||||
/url\s*=\s*"file:..\/data\/db.db"/,
|
||||
'url = "file:../../data/db.db"'
|
||||
);
|
||||
} else {
|
||||
// 获取对应的环境变量名
|
||||
const urlEnvVar = DB_URL_VARS[dbType];
|
||||
if (!urlEnvVar) {
|
||||
throw new Error(`未找到 ${dbType} 的数据库 URL 环境变量映射`);
|
||||
}
|
||||
|
||||
// 替换 env("DATABASE_URL") 为对应的环境变量
|
||||
content = content.replace(
|
||||
/env\s*\(\s*"DATABASE_URL"\s*\)/,
|
||||
`env("${urlEnvVar}")`
|
||||
);
|
||||
}
|
||||
|
||||
// 写入修改后的内容
|
||||
fs.writeFileSync(schemaPath, content, 'utf8');
|
||||
|
||||
return originalContent;
|
||||
}
|
||||
|
||||
// 恢复 schema 文件的原始内容,对于 SQLite 恢复为 ../data/db.db
|
||||
function restoreSchema(schemaPath, dbType, originalContent) {
|
||||
if (originalContent) {
|
||||
console.log(`📝 恢复 schema 文件的原始内容...`);
|
||||
if (dbType === 'sqlite') {
|
||||
// 确保恢复为 ../data/db.db
|
||||
let content = originalContent;
|
||||
if (content.includes('../../data/db.db')) {
|
||||
content = content.replace(
|
||||
/url\s*=\s*"file:..\/..\/data\/db.db"/,
|
||||
'url = "file:../data/db.db"'
|
||||
);
|
||||
}
|
||||
fs.writeFileSync(schemaPath, content, 'utf8');
|
||||
} else {
|
||||
fs.writeFileSync(schemaPath, originalContent, 'utf8');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function processDatabaseType(dbType) {
|
||||
const schemaPath = path.join(DATABASE_DIR, dbType, 'schema.prisma');
|
||||
const dbMigrationsDir = path.join(DATABASE_DIR, dbType, 'migrations');
|
||||
|
||||
if (!fs.existsSync(schemaPath)) {
|
||||
console.log(`⚠️ 跳过 ${dbType}: schema.prisma 文件不存在`);
|
||||
return;
|
||||
}
|
||||
|
||||
let originalContent;
|
||||
try {
|
||||
console.log(`\n🔄 处理 ${dbType} 数据库迁移...`);
|
||||
|
||||
// 删除旧的迁移目录
|
||||
deleteMigrationsDir();
|
||||
|
||||
// 修改 schema 文件配置
|
||||
originalContent = updateSchemaConfig(schemaPath, dbType);
|
||||
|
||||
// 先尝试部署现有迁移
|
||||
console.log(`📦 部署现有迁移...`);
|
||||
try {
|
||||
execSync(`npx prisma migrate deploy --schema=${schemaPath}`, {
|
||||
stdio: 'inherit'
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(`⚠️ 部署现有迁移失败,将创建新迁移`);
|
||||
}
|
||||
|
||||
// 执行新迁移
|
||||
console.log(`📦 创建新迁移...`);
|
||||
execSync(`npx prisma migrate dev --name ${new Date().toISOString().split('T')[0]} --schema=${schemaPath}`, {
|
||||
stdio: 'inherit'
|
||||
});
|
||||
|
||||
// 复制迁移文件到数据库特定目录
|
||||
if (fs.existsSync(MIGRATIONS_DIR)) {
|
||||
console.log(`📋 复制迁移文件到 ${dbType} 目录...`);
|
||||
copyDirectory(MIGRATIONS_DIR, dbMigrationsDir);
|
||||
}
|
||||
|
||||
console.log(`✅ ${dbType} 迁移完成`);
|
||||
} catch (error) {
|
||||
console.error(`❌ ${dbType} 迁移失败:`, error.message);
|
||||
} finally {
|
||||
// 确保无论成功还是失败都恢复原始内容,对于 SQLite 恢复为 ../data/db.db
|
||||
restoreSchema(schemaPath, dbType, originalContent);
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
// 确保数据库目录存在
|
||||
if (!fs.existsSync(DATABASE_DIR)) {
|
||||
console.error('❌ database 目录不存在');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// 获取所有数据库类型目录
|
||||
const dbTypes = fs.readdirSync(DATABASE_DIR).filter(item => {
|
||||
const itemPath = path.join(DATABASE_DIR, item);
|
||||
return fs.statSync(itemPath).isDirectory();
|
||||
});
|
||||
|
||||
console.log('📊 发现的数据库类型:', dbTypes.join(', '));
|
||||
console.log('🔑 数据库配置:');
|
||||
for (const [dbType, envVar] of Object.entries(DB_URL_VARS)) {
|
||||
console.log(` - ${dbType}: 使用环境变量 ${envVar}`);
|
||||
}
|
||||
console.log(' - sqlite: 迁移时使用 ../../data/db.db,完成后恢复为 ../data/db.db');
|
||||
|
||||
// 依次处理每个数据库类型
|
||||
for (const dbType of dbTypes) {
|
||||
await processDatabaseType(dbType);
|
||||
}
|
||||
|
||||
console.log('\n🎉 所有数据库迁移处理完成!');
|
||||
} catch (error) {
|
||||
console.error('❌ 批量迁移失败:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// 执行主函数
|
||||
main().catch(error => {
|
||||
console.error('❌ 程序执行失败:', error);
|
||||
process.exit(1);
|
||||
});
|
@ -46,11 +46,7 @@ function setupDatabase() {
|
||||
process.env.DATABASE_URL = DATABASE_URL;
|
||||
|
||||
// 检查数据库表并执行必要的迁移
|
||||
execSync(
|
||||
"npx prisma migrate dev --name update-" +
|
||||
new Date().toISOString().split("T")[0],
|
||||
{ stdio: "inherit" }
|
||||
);
|
||||
execSync("npx prisma migrate deploy", { stdio: "inherit" });
|
||||
} catch (error) {
|
||||
console.error("❌ 数据库初始化失败:", error.message);
|
||||
process.exit(1);
|
||||
|
69
docker/docker-compose.yml
Normal file
69
docker/docker-compose.yml
Normal file
@ -0,0 +1,69 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
mysql:
|
||||
image: mysql:8.0
|
||||
container_name: classworks_mysql
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-classworks}
|
||||
MYSQL_DATABASE: ${MYSQL_DATABASE:-classworks}
|
||||
MYSQL_USER: ${MYSQL_USER:-classworks}
|
||||
MYSQL_PASSWORD: ${MYSQL_PASSWORD:-classworks}
|
||||
TZ: Asia/Shanghai
|
||||
ports:
|
||||
- "3306:3306"
|
||||
volumes:
|
||||
- mysql_data:/var/lib/mysql
|
||||
- ./mysql/conf.d:/etc/mysql/conf.d:ro
|
||||
- ./mysql/initdb.d:/docker-entrypoint-initdb.d:ro
|
||||
command:
|
||||
- --character-set-server=utf8mb4
|
||||
- --collation-server=utf8mb4_unicode_ci
|
||||
- --default-authentication-plugin=mysql_native_password
|
||||
healthcheck:
|
||||
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u$$MYSQL_USER", "-p$$MYSQL_PASSWORD"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
networks:
|
||||
- classworks_net
|
||||
|
||||
postgres:
|
||||
image: postgres:15-alpine
|
||||
container_name: classworks_postgres
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_DB: ${POSTGRES_DB:-classworks}
|
||||
POSTGRES_USER: ${POSTGRES_USER:-classworks}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-classworks}
|
||||
TZ: Asia/Shanghai
|
||||
ports:
|
||||
- "5432:5432"
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
- ./postgres/initdb.d:/docker-entrypoint-initdb.d:ro
|
||||
command:
|
||||
- "postgres"
|
||||
- "-c"
|
||||
- "max_connections=100"
|
||||
- "-c"
|
||||
- "shared_buffers=128MB"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
networks:
|
||||
- classworks_net
|
||||
|
||||
volumes:
|
||||
mysql_data:
|
||||
name: classworks_mysql_data
|
||||
postgres_data:
|
||||
name: classworks_postgres_data
|
||||
|
||||
networks:
|
||||
classworks_net:
|
||||
name: classworks_network
|
||||
driver: bridge
|
33
docker/mysql/conf.d/my.cnf
Normal file
33
docker/mysql/conf.d/my.cnf
Normal file
@ -0,0 +1,33 @@
|
||||
[mysqld]
|
||||
# 字符集设置
|
||||
character-set-server=utf8mb4
|
||||
collation-server=utf8mb4_unicode_ci
|
||||
|
||||
# 连接设置
|
||||
max_connections=100
|
||||
max_allowed_packet=64M
|
||||
|
||||
# InnoDB设置
|
||||
innodb_buffer_pool_size=256M
|
||||
innodb_log_file_size=64M
|
||||
innodb_flush_log_at_trx_commit=2
|
||||
innodb_flush_method=O_DIRECT
|
||||
|
||||
# 优化设置
|
||||
query_cache_type=1
|
||||
query_cache_size=32M
|
||||
sort_buffer_size=4M
|
||||
read_buffer_size=2M
|
||||
read_rnd_buffer_size=4M
|
||||
join_buffer_size=2M
|
||||
|
||||
# 日志设置
|
||||
slow_query_log=1
|
||||
slow_query_log_file=/var/log/mysql/slow.log
|
||||
long_query_time=2
|
||||
|
||||
[client]
|
||||
default-character-set=utf8mb4
|
||||
|
||||
[mysql]
|
||||
default-character-set=utf8mb4
|
12
docker/mysql/initdb.d/init.sql
Normal file
12
docker/mysql/initdb.d/init.sql
Normal file
@ -0,0 +1,12 @@
|
||||
-- 设置时区
|
||||
SET GLOBAL time_zone = '+8:00';
|
||||
SET time_zone = '+8:00';
|
||||
|
||||
-- 创建数据库(如果不存在)
|
||||
CREATE DATABASE IF NOT EXISTS classworks
|
||||
CHARACTER SET utf8mb4
|
||||
COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- 设置权限
|
||||
GRANT ALL PRIVILEGES ON classworks.* TO 'classworks'@'%';
|
||||
FLUSH PRIVILEGES;
|
10
docker/postgres/initdb.d/init.sql
Normal file
10
docker/postgres/initdb.d/init.sql
Normal file
@ -0,0 +1,10 @@
|
||||
-- 创建扩展
|
||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||
CREATE EXTENSION IF NOT EXISTS "pg_trgm";
|
||||
|
||||
-- 设置时区
|
||||
SET timezone = 'Asia/Shanghai';
|
||||
|
||||
-- 设置默认权限
|
||||
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO classworks;
|
||||
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT USAGE, SELECT ON SEQUENCES TO classworks;
|
BIN
prisma/data/db.db
Normal file
BIN
prisma/data/db.db
Normal file
Binary file not shown.
BIN
prisma/data/db.db-journal
Normal file
BIN
prisma/data/db.db-journal
Normal file
Binary file not shown.
@ -0,0 +1,24 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE `KVStore` (
|
||||
`namespace` VARCHAR(191) NOT NULL,
|
||||
`key` VARCHAR(191) NOT NULL,
|
||||
`value` JSON NOT NULL,
|
||||
`creatorIp` VARCHAR(191) NULL DEFAULT '',
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updatedAt` DATETIME(3) NOT NULL,
|
||||
|
||||
PRIMARY KEY (`namespace`, `key`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `Device` (
|
||||
`uuid` VARCHAR(191) NOT NULL,
|
||||
`password` VARCHAR(191) NULL,
|
||||
`passwordHint` VARCHAR(191) NULL,
|
||||
`name` VARCHAR(191) NULL,
|
||||
`accessType` ENUM('PUBLIC', 'PROTECTED', 'PRIVATE') NOT NULL DEFAULT 'PUBLIC',
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updatedAt` DATETIME(3) NOT NULL,
|
||||
|
||||
PRIMARY KEY (`uuid`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
3
prisma/database/mysql/migrations/migration_lock.toml
Normal file
3
prisma/database/mysql/migrations/migration_lock.toml
Normal file
@ -0,0 +1,3 @@
|
||||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (e.g., Git)
|
||||
provider = "mysql"
|
@ -0,0 +1,27 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "AccessType" AS ENUM ('PUBLIC', 'PROTECTED', 'PRIVATE');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "KVStore" (
|
||||
"namespace" TEXT NOT NULL,
|
||||
"key" TEXT NOT NULL,
|
||||
"value" JSONB NOT NULL,
|
||||
"creatorIp" TEXT DEFAULT '',
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "KVStore_pkey" PRIMARY KEY ("namespace","key")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Device" (
|
||||
"uuid" TEXT NOT NULL,
|
||||
"password" TEXT,
|
||||
"passwordHint" TEXT,
|
||||
"name" TEXT,
|
||||
"accessType" "AccessType" NOT NULL DEFAULT 'PUBLIC',
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "Device_pkey" PRIMARY KEY ("uuid")
|
||||
);
|
3
prisma/database/postgres/migrations/migration_lock.toml
Normal file
3
prisma/database/postgres/migrations/migration_lock.toml
Normal file
@ -0,0 +1,3 @@
|
||||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (e.g., Git)
|
||||
provider = "postgresql"
|
@ -0,0 +1,22 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "KVStore" (
|
||||
"namespace" TEXT NOT NULL,
|
||||
"key" TEXT NOT NULL,
|
||||
"value" JSONB NOT NULL,
|
||||
"creatorIp" TEXT DEFAULT '',
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL,
|
||||
|
||||
PRIMARY KEY ("namespace", "key")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Device" (
|
||||
"uuid" TEXT NOT NULL PRIMARY KEY,
|
||||
"password" TEXT,
|
||||
"passwordHint" TEXT,
|
||||
"name" TEXT,
|
||||
"accessType" TEXT NOT NULL DEFAULT 'PUBLIC',
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL
|
||||
);
|
3
prisma/database/sqlite/migrations/migration_lock.toml
Normal file
3
prisma/database/sqlite/migrations/migration_lock.toml
Normal file
@ -0,0 +1,3 @@
|
||||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (e.g., Git)
|
||||
provider = "sqlite"
|
Loading…
x
Reference in New Issue
Block a user