實作註冊、登入功能

This commit is contained in:
2025-09-28 23:34:32 +08:00
parent 36eb088983
commit 9b224fa7e1
17 changed files with 4468 additions and 57 deletions

View File

@@ -0,0 +1,77 @@
import mysql from 'mysql2/promise'
// 資料庫連接配置
const dbConfig = {
host: process.env.DB_HOST || 'mysql.theaken.com',
port: parseInt(process.env.DB_PORT || '33306'),
user: process.env.DB_USER || 'hr_assessment',
password: process.env.DB_PASSWORD || 'QFOts8FlibiI',
database: process.env.DB_NAME || 'db_hr_assessment',
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0,
acquireTimeout: 60000,
timeout: 60000,
reconnect: true,
}
// 建立連接池
let pool: mysql.Pool | null = null
export function getConnectionPool(): mysql.Pool {
if (!pool) {
pool = mysql.createPool(dbConfig)
}
return pool
}
// 測試資料庫連接
export async function testConnection(): Promise<boolean> {
try {
const connection = await getConnectionPool().getConnection()
await connection.ping()
connection.release()
console.log('✅ 資料庫連接成功')
return true
} catch (error) {
console.error('❌ 資料庫連接失敗:', error)
return false
}
}
// 關閉連接池
export async function closeConnectionPool(): Promise<void> {
if (pool) {
await pool.end()
pool = null
}
}
// 執行查詢的輔助函數
export async function executeQuery<T = any>(
query: string,
params: any[] = []
): Promise<T[]> {
try {
const [rows] = await getConnectionPool().execute(query, params)
return rows as T[]
} catch (error) {
console.error('查詢執行失敗:', error)
throw error
}
}
// 執行單一查詢的輔助函數
export async function executeQueryOne<T = any>(
query: string,
params: any[] = []
): Promise<T | null> {
try {
const [rows] = await getConnectionPool().execute(query, params)
const results = rows as T[]
return results.length > 0 ? results[0] : null
} catch (error) {
console.error('查詢執行失敗:', error)
throw error
}
}

31
lib/database/init.ts Normal file
View File

@@ -0,0 +1,31 @@
import { testConnection } from './connection'
import { createUsersTable } from './models/user'
// 初始化資料庫
export async function initializeDatabase(): Promise<boolean> {
try {
console.log('🔄 正在初始化資料庫...')
// 測試連接
const isConnected = await testConnection()
if (!isConnected) {
console.error('❌ 無法連接到資料庫')
return false
}
// 建立用戶表
await createUsersTable()
console.log('✅ 資料庫初始化完成')
return true
} catch (error) {
console.error('❌ 資料庫初始化失敗:', error)
return false
}
}
// 在應用程式啟動時自動初始化
if (typeof window === 'undefined') {
// 只在伺服器端執行
initializeDatabase().catch(console.error)
}

152
lib/database/models/user.ts Normal file
View File

@@ -0,0 +1,152 @@
import { executeQuery, executeQueryOne } from '../connection'
import { hashPassword, verifyPassword } from '../../utils/password'
export interface User {
id: string
name: string
email: string
password: string
department: string
role: 'admin' | 'user'
created_at: string
updated_at: string
}
export interface CreateUserData {
name: string
email: string
password: string
department: string
role: 'admin' | 'user'
}
export interface UpdateUserData {
name?: string
email?: string
password?: string
department?: string
role?: 'admin' | 'user'
}
// 建立用戶表(如果不存在)
export async function createUsersTable(): Promise<void> {
const createTableQuery = `
CREATE TABLE IF NOT EXISTS users (
id VARCHAR(36) PRIMARY KEY,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
department VARCHAR(100) NOT NULL,
role ENUM('admin', 'user') NOT NULL DEFAULT 'user',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
)
`
await executeQuery(createTableQuery)
console.log('✅ 用戶表建立成功')
}
// 根據 email 查找用戶
export async function findUserByEmail(email: string): Promise<User | null> {
const query = 'SELECT * FROM users WHERE email = ?'
return await executeQueryOne<User>(query, [email])
}
// 根據 ID 查找用戶
export async function findUserById(id: string): Promise<User | null> {
const query = 'SELECT * FROM users WHERE id = ?'
return await executeQueryOne<User>(query, [id])
}
// 建立新用戶
export async function createUser(userData: CreateUserData): Promise<User | null> {
const query = `
INSERT INTO users (id, name, email, password, department, role)
VALUES (?, ?, ?, ?, ?, ?)
`
const { name, email, password, department, role } = userData
try {
// 生成簡單的 UUID
const userId = `user-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
// 雜湊密碼
const hashedPassword = await hashPassword(password)
await executeQuery(query, [userId, name, email, hashedPassword, department, role])
return await findUserByEmail(email)
} catch (error) {
console.error('建立用戶失敗:', error)
return null
}
}
// 更新用戶
export async function updateUser(id: string, userData: UpdateUserData): Promise<User | null> {
const fields = Object.keys(userData).filter(key => userData[key as keyof UpdateUserData] !== undefined)
if (fields.length === 0) {
return await findUserById(id)
}
const setClause = fields.map(field => `${field} = ?`).join(', ')
const query = `UPDATE users SET ${setClause}, updated_at = CURRENT_TIMESTAMP WHERE id = ?`
const values = fields.map(field => userData[field as keyof UpdateUserData])
values.push(id)
try {
await executeQuery(query, values)
return await findUserById(id)
} catch (error) {
console.error('更新用戶失敗:', error)
return null
}
}
// 刪除用戶
export async function deleteUser(id: string): Promise<boolean> {
const query = 'DELETE FROM users WHERE id = ?'
try {
await executeQuery(query, [id])
return true
} catch (error) {
console.error('刪除用戶失敗:', error)
return false
}
}
// 獲取所有用戶
export async function getAllUsers(): Promise<User[]> {
const query = 'SELECT * FROM users ORDER BY created_at DESC'
return await executeQuery<User>(query)
}
// 根據部門獲取用戶
export async function getUsersByDepartment(department: string): Promise<User[]> {
const query = 'SELECT * FROM users WHERE department = ? ORDER BY created_at DESC'
return await executeQuery<User>(query, [department])
}
// 檢查 email 是否已存在
export async function isEmailExists(email: string): Promise<boolean> {
const user = await findUserByEmail(email)
return user !== null
}
// 驗證用戶密碼
export async function verifyUserPassword(email: string, password: string): Promise<User | null> {
const user = await findUserByEmail(email)
if (!user) {
return null
}
const isValidPassword = await verifyPassword(password, user.password)
if (!isValidPassword) {
return null
}
return user
}

View File

@@ -0,0 +1,88 @@
import { executeQuery } from './connection'
import { initializeDatabase } from './init'
import { hashPasswordSync } from '../utils/password'
// 重新建立用戶數據(使用明文密碼)
const resetUsers = [
{
name: "系統管理員",
email: "admin@company.com",
password: "admin123",
department: "人力資源部",
role: "admin",
},
{
name: "張小明",
email: "user@company.com",
password: "user123",
department: "資訊技術部",
role: "user",
},
{
name: "李經理",
email: "manager@company.com",
password: "manager123",
department: "管理部",
role: "admin",
},
{
name: "王測試",
email: "test@company.com",
password: "test123",
department: "測試部",
role: "user",
}
]
export async function resetUsersData(): Promise<void> {
try {
console.log('🔄 正在重新建立用戶數據...')
// 確保資料庫已初始化
await initializeDatabase()
// 清空現有用戶數據
await executeQuery('DELETE FROM users')
console.log('✅ 已清空現有用戶數據')
// 重新插入用戶數據
for (const user of resetUsers) {
// 生成簡單的 UUID
const userId = `user-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
// 雜湊密碼
const hashedPassword = hashPasswordSync(user.password)
const query = `
INSERT INTO users (id, name, email, password, department, role)
VALUES (?, ?, ?, ?, ?, ?)
`
await executeQuery(query, [
userId,
user.name,
user.email,
hashedPassword,
user.department,
user.role
])
console.log(`✅ 建立用戶: ${user.name} (${user.email}) - 密碼已雜湊`)
}
console.log('✅ 用戶數據重新建立完成')
console.log('\n📋 可用帳號:')
resetUsers.forEach(user => {
console.log(` ${user.name}: ${user.email} / ${user.password} (${user.role})`)
})
} catch (error) {
console.error('❌ 重新建立用戶數據失敗:', error)
throw error
}
}
// 如果直接執行此檔案,則執行重置
if (require.main === module) {
resetUsersData().catch(console.error)
}

51
lib/database/seed.ts Normal file
View File

@@ -0,0 +1,51 @@
import { createUser, isEmailExists } from './models/user'
import { initializeDatabase } from './init'
// 預設用戶數據
const defaultUsers = [
{
name: "系統管理員",
email: "admin@company.com",
password: "admin123",
department: "人力資源部",
role: "admin" as const,
},
{
name: "張小明",
email: "user@company.com",
password: "user123",
department: "資訊技術部",
role: "user" as const,
},
]
// 種子資料庫
export async function seedDatabase(): Promise<void> {
try {
console.log('🔄 正在種子資料庫...')
// 確保資料庫已初始化
await initializeDatabase()
// 檢查並建立預設用戶
for (const userData of defaultUsers) {
const exists = await isEmailExists(userData.email)
if (!exists) {
await createUser(userData)
console.log(`✅ 建立預設用戶: ${userData.name} (${userData.email})`)
} else {
console.log(`⏭️ 用戶已存在: ${userData.name} (${userData.email})`)
}
}
console.log('✅ 資料庫種子完成')
} catch (error) {
console.error('❌ 資料庫種子失敗:', error)
throw error
}
}
// 如果直接執行此檔案,則執行種子
if (require.main === module) {
seedDatabase().catch(console.error)
}