feat: Add 5Why_ prefix to all database tables
- Rename all tables with 5Why_ prefix for namespace isolation - Update models: User.js, Analysis.js, AuditLog.js - Update routes: llmConfig.js - Update scripts: seed-test-users.js, add-deepseek-config.js, add-ollama-config.js - Add migrate-table-prefix.js script for database migration - Update db_schema.sql with new table names - Update views: 5Why_user_analysis_stats, 5Why_recent_analyses Tables renamed: - users -> 5Why_users - analyses -> 5Why_analyses - analysis_perspectives -> 5Why_analysis_perspectives - analysis_whys -> 5Why_analysis_whys - llm_configs -> 5Why_llm_configs - system_settings -> 5Why_system_settings - audit_logs -> 5Why_audit_logs - sessions -> 5Why_sessions 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -17,7 +17,7 @@ async function addDeepSeekConfig() {
|
||||
try {
|
||||
// Check if DeepSeek config already exists
|
||||
const existing = await query(
|
||||
`SELECT id FROM llm_configs WHERE provider_name = 'DeepSeek' LIMIT 1`
|
||||
`SELECT id FROM 5Why_llm_configs WHERE provider = 'DeepSeek' LIMIT 1`
|
||||
);
|
||||
|
||||
if (existing.length > 0) {
|
||||
@@ -35,12 +35,12 @@ async function addDeepSeekConfig() {
|
||||
}
|
||||
|
||||
// First, deactivate all existing configs
|
||||
await query('UPDATE llm_configs SET is_active = 0');
|
||||
await query('UPDATE 5Why_llm_configs SET is_active = 0');
|
||||
|
||||
// Insert DeepSeek configuration
|
||||
const result = await query(
|
||||
`INSERT INTO llm_configs
|
||||
(provider_name, api_endpoint, api_key, model_name, temperature, max_tokens, timeout_seconds, is_active)
|
||||
`INSERT INTO 5Why_llm_configs
|
||||
(provider, api_url, api_key, model_name, temperature, max_tokens, timeout, is_active)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
[
|
||||
'DeepSeek',
|
||||
@@ -49,7 +49,7 @@ async function addDeepSeekConfig() {
|
||||
process.env.DEEPSEEK_MODEL || 'deepseek-chat',
|
||||
0.7,
|
||||
6000,
|
||||
120,
|
||||
120000,
|
||||
1 // Set as active
|
||||
]
|
||||
);
|
||||
|
||||
81
scripts/add-ollama-config.js
Normal file
81
scripts/add-ollama-config.js
Normal file
@@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Add Ollama LLM Configuration
|
||||
* This script adds Ollama configuration to the llm_configs table
|
||||
*/
|
||||
|
||||
import { pool, query } from '../config.js';
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
async function addOllamaConfig() {
|
||||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
console.log(' Adding Ollama LLM Configuration');
|
||||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
||||
|
||||
try {
|
||||
// Check if Ollama config already exists
|
||||
const existing = await query(
|
||||
`SELECT id FROM 5Why_llm_configs WHERE provider = 'Ollama' LIMIT 1`
|
||||
);
|
||||
|
||||
if (existing.length > 0) {
|
||||
console.log('✅ Ollama configuration already exists (ID:', existing[0].id, ')');
|
||||
console.log(' Skipping...\n');
|
||||
return;
|
||||
}
|
||||
|
||||
// First, deactivate all existing configs
|
||||
await query('UPDATE 5Why_llm_configs SET is_active = 0');
|
||||
|
||||
// Insert Ollama configuration
|
||||
const result = await query(
|
||||
`INSERT INTO 5Why_llm_configs
|
||||
(provider, api_url, api_key, model_name, temperature, max_tokens, timeout, is_active)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
[
|
||||
'Ollama',
|
||||
process.env.OLLAMA_API_URL || 'https://ollama_pjapi.theaken.com',
|
||||
null, // Ollama doesn't need API key
|
||||
process.env.OLLAMA_MODEL || 'qwen2.5:3b',
|
||||
0.7,
|
||||
6000,
|
||||
120000,
|
||||
1 // Set as active
|
||||
]
|
||||
);
|
||||
|
||||
console.log('✅ Ollama configuration added successfully!');
|
||||
console.log(' Config ID:', result.insertId);
|
||||
console.log(' Provider: Ollama');
|
||||
console.log(' Model: qwen2.5:3b');
|
||||
console.log(' API URL:', process.env.OLLAMA_API_URL || 'https://ollama_pjapi.theaken.com');
|
||||
console.log(' Status: Active\n');
|
||||
|
||||
console.log('📝 Notes:');
|
||||
console.log(' - Ollama is FREE and runs on your infrastructure');
|
||||
console.log(' - No API key required');
|
||||
console.log(' - Current model: qwen2.5:3b');
|
||||
console.log(' - You can manage it in Admin Panel > LLM 配置\n');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error adding Ollama configuration:', error.message);
|
||||
process.exit(1);
|
||||
} finally {
|
||||
await pool.end();
|
||||
}
|
||||
}
|
||||
|
||||
// Run the script
|
||||
addOllamaConfig()
|
||||
.then(() => {
|
||||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
console.log(' Configuration Complete');
|
||||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
||||
process.exit(0);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Fatal error:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
177
scripts/migrate-table-prefix.js
Normal file
177
scripts/migrate-table-prefix.js
Normal file
@@ -0,0 +1,177 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Database Migration Script
|
||||
* Renames all tables to add 5Why_ prefix
|
||||
*
|
||||
* Run: node scripts/migrate-table-prefix.js
|
||||
*/
|
||||
|
||||
import mysql from 'mysql2/promise';
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const dbConfig = {
|
||||
host: process.env.DB_HOST || 'mysql.theaken.com',
|
||||
port: parseInt(process.env.DB_PORT) || 33306,
|
||||
user: process.env.DB_USER || 'A102',
|
||||
password: process.env.DB_PASSWORD || 'Bb123456',
|
||||
database: process.env.DB_NAME || 'db_A102'
|
||||
};
|
||||
|
||||
// 要重新命名的表(舊名 -> 新名)
|
||||
const tableRenames = [
|
||||
{ old: 'users', new: '5Why_users' },
|
||||
{ old: 'analyses', new: '5Why_analyses' },
|
||||
{ old: 'analysis_perspectives', new: '5Why_analysis_perspectives' },
|
||||
{ old: 'analysis_whys', new: '5Why_analysis_whys' },
|
||||
{ old: 'llm_configs', new: '5Why_llm_configs' },
|
||||
{ old: 'system_settings', new: '5Why_system_settings' },
|
||||
{ old: 'audit_logs', new: '5Why_audit_logs' },
|
||||
{ old: 'sessions', new: '5Why_sessions' }
|
||||
];
|
||||
|
||||
// 要重新建立的視圖
|
||||
const viewRenames = [
|
||||
{ old: 'user_analysis_stats', new: '5Why_user_analysis_stats' },
|
||||
{ old: 'recent_analyses', new: '5Why_recent_analyses' }
|
||||
];
|
||||
|
||||
async function migrateTablePrefix() {
|
||||
let connection;
|
||||
|
||||
try {
|
||||
console.log('\n╔════════════════════════════════════════════════════════╗');
|
||||
console.log('║ 5 Why Analyzer - Table Prefix Migration ║');
|
||||
console.log('╚════════════════════════════════════════════════════════╝\n');
|
||||
|
||||
console.log('🔄 Connecting to database...');
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
console.log('✅ Connected successfully\n');
|
||||
|
||||
// 先刪除舊的視圖(因為它們依賴表)
|
||||
console.log('📋 Dropping old views...');
|
||||
for (const view of viewRenames) {
|
||||
try {
|
||||
await connection.execute(`DROP VIEW IF EXISTS ${view.old}`);
|
||||
console.log(` ✓ Dropped view: ${view.old}`);
|
||||
} catch (err) {
|
||||
console.log(` ⚠ View ${view.old} not found or already dropped`);
|
||||
}
|
||||
}
|
||||
console.log('');
|
||||
|
||||
// 重新命名表
|
||||
console.log('📋 Renaming tables...');
|
||||
for (const table of tableRenames) {
|
||||
try {
|
||||
// 檢查舊表是否存在
|
||||
const [oldExists] = await connection.execute(
|
||||
`SELECT COUNT(*) as count FROM information_schema.tables
|
||||
WHERE table_schema = ? AND table_name = ?`,
|
||||
[dbConfig.database, table.old]
|
||||
);
|
||||
|
||||
// 檢查新表是否已存在
|
||||
const [newExists] = await connection.execute(
|
||||
`SELECT COUNT(*) as count FROM information_schema.tables
|
||||
WHERE table_schema = ? AND table_name = ?`,
|
||||
[dbConfig.database, table.new]
|
||||
);
|
||||
|
||||
if (oldExists[0].count > 0 && newExists[0].count === 0) {
|
||||
await connection.execute(`RENAME TABLE \`${table.old}\` TO \`${table.new}\``);
|
||||
console.log(` ✓ Renamed: ${table.old} -> ${table.new}`);
|
||||
} else if (newExists[0].count > 0) {
|
||||
console.log(` ⚠ Table ${table.new} already exists, skipping...`);
|
||||
} else {
|
||||
console.log(` ⚠ Table ${table.old} not found, skipping...`);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(` ✗ Error renaming ${table.old}: ${err.message}`);
|
||||
}
|
||||
}
|
||||
console.log('');
|
||||
|
||||
// 建立新的視圖
|
||||
console.log('📋 Creating new views...');
|
||||
|
||||
// user_analysis_stats 視圖
|
||||
try {
|
||||
await connection.execute(`
|
||||
CREATE OR REPLACE VIEW 5Why_user_analysis_stats AS
|
||||
SELECT
|
||||
u.id AS user_id,
|
||||
u.username,
|
||||
u.employee_id,
|
||||
u.department,
|
||||
COUNT(a.id) AS total_analyses,
|
||||
COUNT(CASE WHEN a.status = 'completed' THEN 1 END) AS completed_analyses,
|
||||
COUNT(CASE WHEN a.status = 'failed' THEN 1 END) AS failed_analyses,
|
||||
AVG(a.processing_time) AS avg_processing_time,
|
||||
MAX(a.created_at) AS last_analysis_at
|
||||
FROM 5Why_users u
|
||||
LEFT JOIN 5Why_analyses a ON u.id = a.user_id
|
||||
GROUP BY u.id, u.username, u.employee_id, u.department
|
||||
`);
|
||||
console.log(' ✓ Created view: 5Why_user_analysis_stats');
|
||||
} catch (err) {
|
||||
console.error(` ✗ Error creating view 5Why_user_analysis_stats: ${err.message}`);
|
||||
}
|
||||
|
||||
// recent_analyses 視圖
|
||||
try {
|
||||
await connection.execute(`
|
||||
CREATE OR REPLACE VIEW 5Why_recent_analyses AS
|
||||
SELECT
|
||||
a.id,
|
||||
a.finding,
|
||||
u.username,
|
||||
u.employee_id,
|
||||
a.output_language,
|
||||
a.status,
|
||||
a.processing_time,
|
||||
a.created_at
|
||||
FROM 5Why_analyses a
|
||||
JOIN 5Why_users u ON a.user_id = u.id
|
||||
ORDER BY a.created_at DESC
|
||||
LIMIT 100
|
||||
`);
|
||||
console.log(' ✓ Created view: 5Why_recent_analyses');
|
||||
} catch (err) {
|
||||
console.error(` ✗ Error creating view 5Why_recent_analyses: ${err.message}`);
|
||||
}
|
||||
|
||||
console.log('\n╔════════════════════════════════════════════════════════╗');
|
||||
console.log('║ Migration Complete! ║');
|
||||
console.log('╚════════════════════════════════════════════════════════╝\n');
|
||||
|
||||
// 列出所有 5Why_ 表
|
||||
const [tables] = await connection.execute(
|
||||
`SELECT table_name FROM information_schema.tables
|
||||
WHERE table_schema = ? AND table_name LIKE '5Why_%'
|
||||
ORDER BY table_name`,
|
||||
[dbConfig.database]
|
||||
);
|
||||
|
||||
if (tables.length > 0) {
|
||||
console.log('📊 5Why tables in database:');
|
||||
tables.forEach((table, index) => {
|
||||
console.log(` ${index + 1}. ${table.TABLE_NAME || table.table_name}`);
|
||||
});
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('\n❌ Migration failed:');
|
||||
console.error(' Error:', error.message);
|
||||
process.exit(1);
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
console.log('\n🔌 Database connection closed\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 執行遷移
|
||||
migrateTablePrefix();
|
||||
@@ -60,7 +60,7 @@ async function seedTestUsers() {
|
||||
for (const user of testUsers) {
|
||||
// 檢查使用者是否已存在 (只用 email 精確匹配)
|
||||
const [existing] = await connection.execute(
|
||||
'SELECT id FROM users WHERE email = ?',
|
||||
'SELECT id FROM 5Why_users WHERE email = ?',
|
||||
[user.email]
|
||||
);
|
||||
|
||||
@@ -68,7 +68,7 @@ async function seedTestUsers() {
|
||||
// 更新現有使用者的密碼
|
||||
const passwordHash = await bcrypt.hash(user.password, 10);
|
||||
await connection.execute(
|
||||
'UPDATE users SET password_hash = ?, role = ?, is_active = 1, employee_id = ? WHERE email = ?',
|
||||
'UPDATE 5Why_users SET password_hash = ?, role = ?, is_active = 1, employee_id = ? WHERE email = ?',
|
||||
[passwordHash, user.role, user.employee_id, user.email]
|
||||
);
|
||||
console.log(`🔄 Updated: ${user.email} (${user.role})`);
|
||||
@@ -76,7 +76,7 @@ async function seedTestUsers() {
|
||||
// 建立新使用者
|
||||
const passwordHash = await bcrypt.hash(user.password, 10);
|
||||
await connection.execute(
|
||||
`INSERT INTO users (employee_id, username, email, password_hash, role, department, position, is_active)
|
||||
`INSERT INTO 5Why_users (employee_id, username, email, password_hash, role, department, position, is_active)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, 1)`,
|
||||
[user.employee_id, user.username, user.email, passwordHash, user.role, user.department, user.position]
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user