整合資料庫、完成登入註冊忘記密碼功能

This commit is contained in:
2025-09-09 12:00:22 +08:00
parent af88c0f037
commit 32b19e9a0f
85 changed files with 11672 additions and 2350 deletions

View File

@@ -1,3 +1,7 @@
// =====================================================
// 資料庫連接配置
// =====================================================
import mysql from 'mysql2/promise';
// 資料庫配置
@@ -9,205 +13,114 @@ const dbConfig = {
database: process.env.DB_NAME || 'db_AI_Platform',
charset: 'utf8mb4',
timezone: '+08:00',
// 連接池配置
connectionLimit: 10,
acquireTimeout: 60000,
timeout: 60000,
reconnect: true
reconnect: true,
connectionLimit: 10,
queueLimit: 0,
};
// 創建連接池
let pool: mysql.Pool | null = null;
const pool = mysql.createPool(dbConfig);
// 資料庫連接類
export class Database {
private static instance: Database;
private constructor() {}
private pool: mysql.Pool;
private constructor() {
this.pool = pool;
}
public static getInstance(): Database {
if (!Database.instance) {
Database.instance = new Database();
}
return Database.instance;
}
// 獲取連接
public async getPool(): Promise<mysql.Pool> {
if (!pool) {
pool = mysql.createPool(dbConfig);
// 測試連接
try {
const connection = await pool.getConnection();
console.log('✅ 資料庫連接池建立成功');
connection.release();
} catch (error) {
console.error('❌ 資料庫連接池建立失敗:', error);
throw error;
}
}
return pool;
// 獲取連接
public async getConnection(): Promise<mysql.PoolConnection> {
return await this.pool.getConnection();
}
// 執行查詢
public async query<T = any>(sql: string, params?: any[]): Promise<T[]> {
const pool = await this.getPool();
const connection = await this.getConnection();
try {
const [rows] = await pool.execute(sql, params);
const [rows] = await connection.execute(sql, params);
return rows as T[];
} catch (error) {
console.error('查詢執行失敗:', error);
throw error;
} finally {
connection.release();
}
}
// 執行單一查詢 (返回第一筆結果)
// 執行單一查詢
public async queryOne<T = any>(sql: string, params?: any[]): Promise<T | null> {
const results = await this.query<T>(sql, params);
return results.length > 0 ? results[0] : null;
}
// 執行插入
public async insert(table: string, data: Record<string, any>): Promise<number> {
const columns = Object.keys(data);
const values = Object.values(data);
const placeholders = columns.map(() => '?').join(', ');
const sql = `INSERT INTO ${table} (${columns.join(', ')}) VALUES (${placeholders})`;
const pool = await this.getPool();
public async insert(sql: string, params?: any[]): Promise<mysql.ResultSetHeader> {
const connection = await this.getConnection();
try {
const [result] = await pool.execute(sql, values);
return (result as any).insertId;
} catch (error) {
console.error('插入執行失敗:', error);
throw error;
const [result] = await connection.execute(sql, params);
return result as mysql.ResultSetHeader;
} finally {
connection.release();
}
}
// 執行更新
public async update(table: string, data: Record<string, any>, where: Record<string, any>): Promise<number> {
const setColumns = Object.keys(data).map(col => `${col} = ?`).join(', ');
const whereColumns = Object.keys(where).map(col => `${col} = ?`).join(' AND ');
const sql = `UPDATE ${table} SET ${setColumns} WHERE ${whereColumns}`;
const values = [...Object.values(data), ...Object.values(where)];
const pool = await this.getPool();
public async update(sql: string, params?: any[]): Promise<mysql.ResultSetHeader> {
const connection = await this.getConnection();
try {
const [result] = await pool.execute(sql, values);
return (result as any).affectedRows;
} catch (error) {
console.error('更新執行失敗:', error);
throw error;
const [result] = await connection.execute(sql, params);
return result as mysql.ResultSetHeader;
} finally {
connection.release();
}
}
// 執行刪除
public async delete(table: string, where: Record<string, any>): Promise<number> {
const whereColumns = Object.keys(where).map(col => `${col} = ?`).join(' AND ');
const sql = `DELETE FROM ${table} WHERE ${whereColumns}`;
const values = Object.values(where);
const pool = await this.getPool();
public async delete(sql: string, params?: any[]): Promise<mysql.ResultSetHeader> {
const connection = await this.getConnection();
try {
const [result] = await pool.execute(sql, values);
return (result as any).affectedRows;
} catch (error) {
console.error('刪除執行失敗:', error);
throw error;
const [result] = await connection.execute(sql, params);
return result as mysql.ResultSetHeader;
} finally {
connection.release();
}
}
// 開始事務
public async beginTransaction(): Promise<mysql.PoolConnection> {
const pool = await this.getPool();
const connection = await pool.getConnection();
const connection = await this.getConnection();
await connection.beginTransaction();
return connection;
}
// 提交事務
public async commitTransaction(connection: mysql.PoolConnection): Promise<void> {
public async commit(connection: mysql.PoolConnection): Promise<void> {
await connection.commit();
connection.release();
}
// 回滾事務
public async rollbackTransaction(connection: mysql.PoolConnection): Promise<void> {
public async rollback(connection: mysql.PoolConnection): Promise<void> {
await connection.rollback();
connection.release();
}
// 關閉連接池
public async close(): Promise<void> {
if (pool) {
await pool.end();
pool = null;
console.log('🔌 資料庫連接池已關閉');
}
}
// 健康檢查
public async healthCheck(): Promise<boolean> {
try {
const result = await this.queryOne('SELECT 1 as health');
return result?.health === 1;
} catch (error) {
console.error('資料庫健康檢查失敗:', error);
return false;
}
}
// 獲取資料庫統計
public async getDatabaseStats(): Promise<{
tables: number;
users: number;
competitions: number;
apps: number;
judges: number;
}> {
try {
const tablesResult = await this.queryOne(`
SELECT COUNT(*) as count
FROM information_schema.tables
WHERE table_schema = ?
`, [dbConfig.database]);
const usersResult = await this.queryOne('SELECT COUNT(*) as count FROM users');
const competitionsResult = await this.queryOne('SELECT COUNT(*) as count FROM competitions');
const appsResult = await this.queryOne('SELECT COUNT(*) as count FROM apps');
const judgesResult = await this.queryOne('SELECT COUNT(*) as count FROM judges');
return {
tables: tablesResult?.count || 0,
users: usersResult?.count || 0,
competitions: competitionsResult?.count || 0,
apps: appsResult?.count || 0,
judges: judgesResult?.count || 0
};
} catch (error) {
console.error('獲取資料庫統計失敗:', error);
throw error;
}
await this.pool.end();
}
}
// 導出單例實例
export const db = Database.getInstance();
import bcrypt from 'bcrypt';
// 工具函數
export const generateId = (): string => {
return Date.now().toString(36) + Math.random().toString(36).substr(2);
};
export const hashPassword = async (password: string): Promise<string> => {
const saltRounds = 12;
return bcrypt.hash(password, saltRounds);
};
export const comparePassword = async (password: string, hash: string): Promise<boolean> => {
return bcrypt.compare(password, hash);
};
// 導出類型
export type { PoolConnection } from 'mysql2/promise';