Initial commit: 5 Why Root Cause Analyzer v1.0.0
Phase 0 & Phase 2 completed: - Project structure setup - Environment configuration (.env, .gitignore) - Enterprise-grade dependencies (bcrypt, helmet, mysql2, etc.) - Complete database schema with 8 tables + 2 views - Database initialization scripts - Comprehensive documentation Database Tables: - users (user management with 3-tier permissions) - analyses (analysis records) - analysis_perspectives (multi-angle analysis) - analysis_whys (detailed 5 Why records) - llm_configs (LLM API configurations) - system_settings (system parameters) - audit_logs (security audit trail) - sessions (session management) Tech Stack: - Backend: Node.js + Express - Frontend: React 18 + Vite + Tailwind CSS - Database: MySQL 9.4.0 - AI: Ollama API (qwen2.5:3b) Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
75
scripts/init-database-simple.js
Normal file
75
scripts/init-database-simple.js
Normal file
@@ -0,0 +1,75 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import mysql from 'mysql2/promise';
|
||||
import dotenv from 'dotenv';
|
||||
import bcrypt from 'bcryptjs';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
const dbConfig = {
|
||||
host: process.env.DB_HOST,
|
||||
port: parseInt(process.env.DB_PORT),
|
||||
user: process.env.DB_USER,
|
||||
password: process.env.DB_PASSWORD,
|
||||
database: process.env.DB_NAME,
|
||||
multipleStatements: true
|
||||
};
|
||||
|
||||
async function initDatabase() {
|
||||
let connection;
|
||||
|
||||
try {
|
||||
console.log('\n╔════════════════════════════════════════════╗');
|
||||
console.log('║ 5 Why Analyzer - Database Initialization ║');
|
||||
console.log('╚════════════════════════════════════════════╝\n');
|
||||
|
||||
console.log('🔄 Connecting...');
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
console.log('✅ Connected\n');
|
||||
|
||||
// 生成密碼 hash
|
||||
const adminPassword = process.env.ADMIN_PASSWORD || 'Admin@123456';
|
||||
const adminHash = await bcrypt.hash(adminPassword, 10);
|
||||
|
||||
// 讀取並執行 SQL
|
||||
const sqlPath = path.join(__dirname, '../docs/db_schema.sql');
|
||||
let sqlContent = fs.readFileSync(sqlPath, 'utf8');
|
||||
sqlContent = sqlContent.replace(/\$2a\$10\$YourBcryptHashHere/g, adminHash);
|
||||
|
||||
console.log('📝 Executing SQL statements...\n');
|
||||
await connection.query(sqlContent);
|
||||
|
||||
console.log('\n✅ Database initialized successfully!\n');
|
||||
|
||||
// 列出資料表
|
||||
const [tables] = await connection.execute(
|
||||
"SELECT table_name FROM information_schema.tables WHERE table_schema = ? AND table_name LIKE '%' ORDER BY table_name",
|
||||
[dbConfig.database]
|
||||
);
|
||||
|
||||
const fiveWhyTables = tables.filter(t => !t.table_name.startsWith('hr_') && !t.table_name.startsWith('pm_'));
|
||||
|
||||
if (fiveWhyTables.length > 0) {
|
||||
console.log('📊 5 Why Analyzer Tables:');
|
||||
fiveWhyTables.forEach((table, index) => {
|
||||
console.log(` ${index + 1}. ${table.table_name}`);
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`\n🔐 Admin credentials:`);
|
||||
console.log(` Email: ${process.env.ADMIN_EMAIL || 'admin@example.com'}`);
|
||||
console.log(` Password: ${adminPassword}\n`);
|
||||
|
||||
} catch (error) {
|
||||
console.error('\n❌ Error:', error.message);
|
||||
process.exit(1);
|
||||
} finally {
|
||||
if (connection) await connection.end();
|
||||
}
|
||||
}
|
||||
|
||||
initDatabase();
|
||||
134
scripts/init-database.js
Normal file
134
scripts/init-database.js
Normal file
@@ -0,0 +1,134 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import mysql from 'mysql2/promise';
|
||||
import dotenv from 'dotenv';
|
||||
import bcrypt from 'bcryptjs';
|
||||
|
||||
// 載入環境變數
|
||||
dotenv.config();
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
// 資料庫配置
|
||||
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',
|
||||
multipleStatements: true
|
||||
};
|
||||
|
||||
async function initDatabase() {
|
||||
let connection;
|
||||
|
||||
try {
|
||||
console.log('🔄 Connecting to database...');
|
||||
console.log(` Host: ${dbConfig.host}:${dbConfig.port}`);
|
||||
console.log(` Database: ${dbConfig.database}`);
|
||||
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
console.log('✅ Connected successfully\n');
|
||||
|
||||
// 讀取 SQL 檔案
|
||||
const sqlPath = path.join(__dirname, '../docs/db_schema.sql');
|
||||
console.log('📖 Reading SQL file:', sqlPath);
|
||||
|
||||
let sqlContent = fs.readFileSync(sqlPath, 'utf8');
|
||||
|
||||
// 生成管理者密碼 hash
|
||||
const adminPassword = process.env.ADMIN_PASSWORD || 'Admin@123456';
|
||||
const adminHash = await bcrypt.hash(adminPassword, 10);
|
||||
|
||||
console.log('🔐 Generated admin password hash');
|
||||
|
||||
// 替換 SQL 中的密碼 hash
|
||||
sqlContent = sqlContent.replace(/\$2a\$10\$YourBcryptHashHere/g, adminHash);
|
||||
|
||||
// 分割 SQL 語句
|
||||
const statements = sqlContent
|
||||
.split(';')
|
||||
.map(stmt => stmt.trim())
|
||||
.filter(stmt => stmt.length > 0 && !stmt.startsWith('--'));
|
||||
|
||||
console.log(`\n📝 Found ${statements.length} SQL statements\n`);
|
||||
|
||||
// 執行每個語句
|
||||
for (let i = 0; i < statements.length; i++) {
|
||||
const statement = statements[i];
|
||||
|
||||
// 跳過註解和空白
|
||||
if (statement.startsWith('--') || statement.trim() === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
// 顯示正在執行的語句類型
|
||||
let action = 'Executing';
|
||||
if (statement.toUpperCase().includes('CREATE TABLE')) {
|
||||
const match = statement.match(/CREATE TABLE(?:\s+IF NOT EXISTS)?\s+`?(\w+)`?/i);
|
||||
if (match) {
|
||||
action = `Creating table: ${match[1]}`;
|
||||
}
|
||||
} else if (statement.toUpperCase().includes('CREATE VIEW')) {
|
||||
const match = statement.match(/CREATE.*VIEW\s+`?(\w+)`?/i);
|
||||
if (match) {
|
||||
action = `Creating view: ${match[1]}`;
|
||||
}
|
||||
} else if (statement.toUpperCase().includes('INSERT INTO')) {
|
||||
const match = statement.match(/INSERT INTO\s+`?(\w+)`?/i);
|
||||
if (match) {
|
||||
action = `Inserting data into: ${match[1]}`;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(` [${i + 1}/${statements.length}] ${action}`);
|
||||
await connection.execute(statement);
|
||||
} catch (error) {
|
||||
// 如果是 "already exists" 錯誤,只顯示警告
|
||||
if (error.message.includes('already exists')) {
|
||||
console.log(` ⚠️ Already exists, skipping...`);
|
||||
} else {
|
||||
console.error(` ❌ Error: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n✅ Database initialization completed successfully!\n');
|
||||
|
||||
// 顯示建立的資料表
|
||||
const [tables] = await connection.execute('SHOW TABLES');
|
||||
console.log('📊 Tables in database:');
|
||||
tables.forEach((table, index) => {
|
||||
const tableName = Object.values(table)[0];
|
||||
console.log(` ${index + 1}. ${tableName}`);
|
||||
});
|
||||
|
||||
console.log('\n🔐 Default admin credentials:');
|
||||
console.log(` Email: ${process.env.ADMIN_EMAIL || 'admin@example.com'}`);
|
||||
console.log(` Password: ${adminPassword}`);
|
||||
console.log('\n⚠️ Please change the admin password after first login!\n');
|
||||
|
||||
} catch (error) {
|
||||
console.error('\n❌ Database initialization failed:');
|
||||
console.error(' Error:', error.message);
|
||||
if (error.code) {
|
||||
console.error(' Error Code:', error.code);
|
||||
}
|
||||
process.exit(1);
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
console.log('🔌 Database connection closed\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 執行初始化
|
||||
console.log('\n╔════════════════════════════════════════════╗');
|
||||
console.log('║ 5 Why Analyzer - Database Initialization ║');
|
||||
console.log('╚════════════════════════════════════════════╝\n');
|
||||
|
||||
initDatabase();
|
||||
89
scripts/test-db-connection.js
Normal file
89
scripts/test-db-connection.js
Normal file
@@ -0,0 +1,89 @@
|
||||
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'
|
||||
};
|
||||
|
||||
async function testConnection() {
|
||||
let connection;
|
||||
|
||||
try {
|
||||
console.log('\n╔═══════════════════════════════════════╗');
|
||||
console.log('║ Database Connection Test ║');
|
||||
console.log('╚═══════════════════════════════════════╝\n');
|
||||
|
||||
console.log('📡 Attempting to connect to database...');
|
||||
console.log(` Host: ${dbConfig.host}`);
|
||||
console.log(` Port: ${dbConfig.port}`);
|
||||
console.log(` Database: ${dbConfig.database}`);
|
||||
console.log(` User: ${dbConfig.user}\n`);
|
||||
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
|
||||
console.log('✅ Connection successful!\n');
|
||||
|
||||
// 測試基本查詢
|
||||
console.log('🔍 Testing basic queries...\n');
|
||||
|
||||
// 1. 檢查資料庫版本
|
||||
const [versionResult] = await connection.execute('SELECT VERSION() as version');
|
||||
console.log(` MySQL Version: ${versionResult[0].version}`);
|
||||
|
||||
// 2. 列出所有資料表
|
||||
const [tables] = await connection.execute('SHOW TABLES');
|
||||
console.log(` Tables found: ${tables.length}`);
|
||||
|
||||
if (tables.length > 0) {
|
||||
console.log('\n📊 Available tables:');
|
||||
tables.forEach((table, index) => {
|
||||
const tableName = Object.values(table)[0];
|
||||
console.log(` ${index + 1}. ${tableName}`);
|
||||
});
|
||||
|
||||
// 3. 檢查每個資料表的記錄數
|
||||
console.log('\n📈 Table statistics:');
|
||||
for (const table of tables) {
|
||||
const tableName = Object.values(table)[0];
|
||||
const [countResult] = await connection.execute(`SELECT COUNT(*) as count FROM \`${tableName}\``);
|
||||
console.log(` ${tableName}: ${countResult[0].count} rows`);
|
||||
}
|
||||
} else {
|
||||
console.log('\n⚠️ No tables found. Run "npm run db:init" to initialize the database.');
|
||||
}
|
||||
|
||||
console.log('\n✅ All tests passed!\n');
|
||||
|
||||
} catch (error) {
|
||||
console.error('\n❌ Connection failed!');
|
||||
console.error(` Error: ${error.message}`);
|
||||
if (error.code) {
|
||||
console.error(` Error Code: ${error.code}`);
|
||||
}
|
||||
if (error.errno) {
|
||||
console.error(` Error Number: ${error.errno}`);
|
||||
}
|
||||
|
||||
console.log('\n💡 Troubleshooting tips:');
|
||||
console.log(' 1. Check if MySQL server is running');
|
||||
console.log(' 2. Verify host and port in .env file');
|
||||
console.log(' 3. Confirm database credentials');
|
||||
console.log(' 4. Check firewall settings');
|
||||
console.log(' 5. Ensure database exists\n');
|
||||
|
||||
process.exit(1);
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
console.log('🔌 Connection closed\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testConnection();
|
||||
Reference in New Issue
Block a user