優化應用 APP 新增、編輯邏輯
This commit is contained in:
67
scripts/add-missing-app-columns.js
Normal file
67
scripts/add-missing-app-columns.js
Normal file
@@ -0,0 +1,67 @@
|
||||
const mysql = require('mysql2/promise');
|
||||
|
||||
const dbConfig = {
|
||||
host: process.env.DB_HOST || 'mysql.theaken.com',
|
||||
port: parseInt(process.env.DB_PORT || '33306'),
|
||||
user: process.env.DB_USER || 'AI_Platform',
|
||||
password: process.env.DB_PASSWORD || 'Aa123456',
|
||||
database: process.env.DB_NAME || 'db_AI_Platform',
|
||||
charset: 'utf8mb4',
|
||||
timezone: '+08:00'
|
||||
};
|
||||
|
||||
async function addMissingAppColumns() {
|
||||
let connection;
|
||||
|
||||
try {
|
||||
console.log('🔧 開始添加缺失的 apps 表格欄位...');
|
||||
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
console.log('✅ 資料庫連接成功');
|
||||
|
||||
// 檢查並添加新欄位
|
||||
const alterStatements = [
|
||||
// 添加部門欄位
|
||||
`ALTER TABLE apps ADD COLUMN department VARCHAR(100) DEFAULT 'HQBU'`,
|
||||
|
||||
// 添加創建者名稱欄位
|
||||
`ALTER TABLE apps ADD COLUMN creator_name VARCHAR(100)`,
|
||||
|
||||
// 添加創建者郵箱欄位
|
||||
`ALTER TABLE apps ADD COLUMN creator_email VARCHAR(255)`
|
||||
];
|
||||
|
||||
for (const statement of alterStatements) {
|
||||
try {
|
||||
await connection.execute(statement);
|
||||
console.log(`✅ 執行: ${statement.substring(0, 50)}...`);
|
||||
} catch (error) {
|
||||
if (error.code === 'ER_DUP_FIELDNAME') {
|
||||
console.log(`⚠️ 欄位已存在,跳過: ${statement.substring(0, 50)}...`);
|
||||
} else {
|
||||
console.error(`❌ 執行失敗: ${statement.substring(0, 50)}...`, error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 檢查表格結構
|
||||
const [columns] = await connection.execute('DESCRIBE apps');
|
||||
console.log('\n📋 apps 表格結構:');
|
||||
columns.forEach(col => {
|
||||
console.log(` ${col.Field}: ${col.Type} ${col.Null === 'YES' ? 'NULL' : 'NOT NULL'} ${col.Default ? `DEFAULT ${col.Default}` : ''}`);
|
||||
});
|
||||
|
||||
console.log('\n✅ apps 表格欄位添加完成!');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 添加 apps 表格欄位失敗:', error);
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
console.log('🔌 資料庫連接已關閉');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 執行添加
|
||||
addMissingAppColumns().catch(console.error);
|
104
scripts/check-actual-creator-data.js
Normal file
104
scripts/check-actual-creator-data.js
Normal file
@@ -0,0 +1,104 @@
|
||||
const mysql = require('mysql2/promise');
|
||||
|
||||
const dbConfig = {
|
||||
host: process.env.DB_HOST || 'mysql.theaken.com',
|
||||
port: parseInt(process.env.DB_PORT || '33306'),
|
||||
user: process.env.DB_USER || 'AI_Platform',
|
||||
password: process.env.DB_PASSWORD || 'Aa123456',
|
||||
database: process.env.DB_NAME || 'db_AI_Platform',
|
||||
charset: 'utf8mb4',
|
||||
timezone: '+08:00'
|
||||
};
|
||||
|
||||
async function checkActualCreatorData() {
|
||||
let connection;
|
||||
|
||||
try {
|
||||
console.log('🔍 檢查實際的創建者資料...');
|
||||
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
console.log('✅ 資料庫連接成功');
|
||||
|
||||
// 檢查應用程式的創建者資訊
|
||||
const [apps] = await connection.execute(`
|
||||
SELECT
|
||||
a.id,
|
||||
a.name,
|
||||
a.creator_id,
|
||||
a.department as app_department,
|
||||
a.creator_name as app_creator_name,
|
||||
u.id as user_id,
|
||||
u.name as user_name,
|
||||
u.email as user_email,
|
||||
u.department as user_department
|
||||
FROM apps a
|
||||
LEFT JOIN users u ON a.creator_id = u.id
|
||||
ORDER BY a.created_at DESC
|
||||
LIMIT 5
|
||||
`);
|
||||
|
||||
console.log('\n📊 應用程式和創建者資料:');
|
||||
apps.forEach((app, index) => {
|
||||
console.log(`\n應用程式 ${index + 1}:`);
|
||||
console.log(` 應用 ID: ${app.id}`);
|
||||
console.log(` 應用名稱: ${app.name}`);
|
||||
console.log(` 創建者 ID: ${app.creator_id}`);
|
||||
console.log(` 應用部門: ${app.app_department}`);
|
||||
console.log(` 應用創建者名稱: ${app.app_creator_name}`);
|
||||
console.log(` 用戶 ID: ${app.user_id}`);
|
||||
console.log(` 用戶名稱: ${app.user_name}`);
|
||||
console.log(` 用戶郵箱: ${app.user_email}`);
|
||||
console.log(` 用戶部門: ${app.user_department}`);
|
||||
});
|
||||
|
||||
// 檢查用戶表中的資料
|
||||
const [users] = await connection.execute(`
|
||||
SELECT id, name, email, department, role
|
||||
FROM users
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 5
|
||||
`);
|
||||
|
||||
console.log('\n📋 用戶表資料:');
|
||||
users.forEach((user, index) => {
|
||||
console.log(`\n用戶 ${index + 1}:`);
|
||||
console.log(` ID: ${user.id}`);
|
||||
console.log(` 名稱: ${user.name}`);
|
||||
console.log(` 郵箱: ${user.email}`);
|
||||
console.log(` 部門: ${user.department}`);
|
||||
console.log(` 角色: ${user.role}`);
|
||||
});
|
||||
|
||||
// 檢查是否有名為「佩庭」的用戶
|
||||
const [peitingUsers] = await connection.execute(`
|
||||
SELECT id, name, email, department, role
|
||||
FROM users
|
||||
WHERE name LIKE '%佩庭%'
|
||||
`);
|
||||
|
||||
console.log('\n🔍 搜尋「佩庭」相關的用戶:');
|
||||
if (peitingUsers.length > 0) {
|
||||
peitingUsers.forEach((user, index) => {
|
||||
console.log(`\n用戶 ${index + 1}:`);
|
||||
console.log(` ID: ${user.id}`);
|
||||
console.log(` 名稱: ${user.name}`);
|
||||
console.log(` 郵箱: ${user.email}`);
|
||||
console.log(` 部門: ${user.department}`);
|
||||
console.log(` 角色: ${user.role}`);
|
||||
});
|
||||
} else {
|
||||
console.log('❌ 沒有找到名為「佩庭」的用戶');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 檢查創建者資料失敗:', error);
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
console.log('🔌 資料庫連接已關閉');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 執行檢查
|
||||
checkActualCreatorData().catch(console.error);
|
@@ -1,42 +0,0 @@
|
||||
const mysql = require('mysql2/promise');
|
||||
|
||||
async function checkAdminPasswords() {
|
||||
console.log('=== 檢查管理員密碼 ===');
|
||||
|
||||
try {
|
||||
const connection = await mysql.createConnection({
|
||||
host: 'mysql.theaken.com',
|
||||
port: 33306,
|
||||
user: 'AI_Platform',
|
||||
password: 'Aa123456',
|
||||
database: 'db_AI_Platform'
|
||||
});
|
||||
|
||||
console.log('✅ 資料庫連接成功');
|
||||
|
||||
// 查詢管理員用戶
|
||||
const [rows] = await connection.execute(`
|
||||
SELECT id, name, email, role, password_hash, created_at
|
||||
FROM users
|
||||
WHERE role = 'admin'
|
||||
ORDER BY created_at DESC
|
||||
`);
|
||||
|
||||
console.log(`\n找到 ${rows.length} 個管理員用戶:`);
|
||||
|
||||
for (const user of rows) {
|
||||
console.log(`\n用戶ID: ${user.id}`);
|
||||
console.log(`姓名: ${user.name}`);
|
||||
console.log(`郵箱: ${user.email}`);
|
||||
console.log(`角色: ${user.role}`);
|
||||
console.log(`密碼雜湊: ${user.password_hash.substring(0, 20)}...`);
|
||||
console.log(`創建時間: ${user.created_at}`);
|
||||
}
|
||||
|
||||
await connection.end();
|
||||
} catch (error) {
|
||||
console.error('❌ 資料庫連接失敗:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
checkAdminPasswords().catch(console.error);
|
136
scripts/check-app-types.js
Normal file
136
scripts/check-app-types.js
Normal file
@@ -0,0 +1,136 @@
|
||||
// Script to check app types in database
|
||||
const mysql = require('mysql2/promise');
|
||||
|
||||
async function checkAppTypes() {
|
||||
let connection;
|
||||
|
||||
try {
|
||||
// Database connection
|
||||
connection = await mysql.createConnection({
|
||||
host: 'localhost',
|
||||
user: 'root',
|
||||
password: '123456',
|
||||
database: 'ai_showcase_platform'
|
||||
});
|
||||
|
||||
console.log('Connected to database successfully');
|
||||
|
||||
// Check what types exist in the apps table
|
||||
const [rows] = await connection.execute('SELECT DISTINCT type FROM apps ORDER BY type');
|
||||
|
||||
console.log('=== App Types in Database ===');
|
||||
console.log('Total unique types:', rows.length);
|
||||
rows.forEach((row, index) => {
|
||||
console.log(`${index + 1}. ${row.type}`);
|
||||
});
|
||||
|
||||
// Check API valid types
|
||||
const apiValidTypes = [
|
||||
'web_app', 'mobile_app', 'desktop_app', 'api_service', 'ai_model',
|
||||
'data_analysis', 'automation', 'productivity', 'educational', 'healthcare',
|
||||
'finance', 'iot_device', 'blockchain', 'ar_vr', 'machine_learning',
|
||||
'computer_vision', 'nlp', 'robotics', 'cybersecurity', 'cloud_service', 'other'
|
||||
];
|
||||
|
||||
console.log('\n=== API Valid Types ===');
|
||||
apiValidTypes.forEach((type, index) => {
|
||||
console.log(`${index + 1}. ${type}`);
|
||||
});
|
||||
|
||||
// Check frontend mapping
|
||||
const frontendTypes = [
|
||||
'文字處理', '圖像生成', '圖像處理', '語音辨識', '推薦系統', '音樂生成',
|
||||
'程式開發', '影像處理', '對話系統', '數據分析', '設計工具', '語音技術',
|
||||
'教育工具', '健康醫療', '金融科技', '物聯網', '區塊鏈', 'AR/VR',
|
||||
'機器學習', '電腦視覺', '自然語言處理', '機器人', '網路安全', '雲端服務', '其他'
|
||||
];
|
||||
|
||||
console.log('\n=== Frontend Types ===');
|
||||
frontendTypes.forEach((type, index) => {
|
||||
console.log(`${index + 1}. ${type}`);
|
||||
});
|
||||
|
||||
// Check mapping consistency
|
||||
console.log('\n=== Mapping Analysis ===');
|
||||
|
||||
const mapTypeToApiType = (frontendType) => {
|
||||
const typeMap = {
|
||||
'文字處理': 'productivity',
|
||||
'圖像生成': 'ai_model',
|
||||
'圖像處理': 'ai_model',
|
||||
'語音辨識': 'ai_model',
|
||||
'推薦系統': 'ai_model',
|
||||
'音樂生成': 'ai_model',
|
||||
'程式開發': 'automation',
|
||||
'影像處理': 'ai_model',
|
||||
'對話系統': 'ai_model',
|
||||
'數據分析': 'data_analysis',
|
||||
'設計工具': 'productivity',
|
||||
'語音技術': 'ai_model',
|
||||
'教育工具': 'educational',
|
||||
'健康醫療': 'healthcare',
|
||||
'金融科技': 'finance',
|
||||
'物聯網': 'iot_device',
|
||||
'區塊鏈': 'blockchain',
|
||||
'AR/VR': 'ar_vr',
|
||||
'機器學習': 'machine_learning',
|
||||
'電腦視覺': 'computer_vision',
|
||||
'自然語言處理': 'nlp',
|
||||
'機器人': 'robotics',
|
||||
'網路安全': 'cybersecurity',
|
||||
'雲端服務': 'cloud_service',
|
||||
'其他': 'other'
|
||||
};
|
||||
return typeMap[frontendType] || 'other';
|
||||
};
|
||||
|
||||
const mapApiTypeToDisplayType = (apiType) => {
|
||||
const typeMap = {
|
||||
'productivity': '文字處理',
|
||||
'ai_model': '圖像生成',
|
||||
'automation': '程式開發',
|
||||
'data_analysis': '數據分析',
|
||||
'educational': '教育工具',
|
||||
'healthcare': '健康醫療',
|
||||
'finance': '金融科技',
|
||||
'iot_device': '物聯網',
|
||||
'blockchain': '區塊鏈',
|
||||
'ar_vr': 'AR/VR',
|
||||
'machine_learning': '機器學習',
|
||||
'computer_vision': '電腦視覺',
|
||||
'nlp': '自然語言處理',
|
||||
'robotics': '機器人',
|
||||
'cybersecurity': '網路安全',
|
||||
'cloud_service': '雲端服務',
|
||||
'other': '其他'
|
||||
};
|
||||
return typeMap[apiType] || '其他';
|
||||
};
|
||||
|
||||
// Test mapping for all frontend types
|
||||
console.log('\n=== Frontend to API Mapping Test ===');
|
||||
frontendTypes.forEach(frontendType => {
|
||||
const apiType = mapTypeToApiType(frontendType);
|
||||
const backToFrontend = mapApiTypeToDisplayType(apiType);
|
||||
const isValidApiType = apiValidTypes.includes(apiType);
|
||||
console.log(`${frontendType} -> ${apiType} -> ${backToFrontend} (Valid API: ${isValidApiType})`);
|
||||
});
|
||||
|
||||
// Test mapping for all API types
|
||||
console.log('\n=== API to Frontend Mapping Test ===');
|
||||
apiValidTypes.forEach(apiType => {
|
||||
const frontendType = mapApiTypeToDisplayType(apiType);
|
||||
const backToApi = mapTypeToApiType(frontendType);
|
||||
console.log(`${apiType} -> ${frontendType} -> ${backToApi}`);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkAppTypes();
|
@@ -1,49 +0,0 @@
|
||||
const mysql = require('mysql2/promise');
|
||||
|
||||
async function checkAppsCount() {
|
||||
try {
|
||||
// 連接資料庫
|
||||
const connection = await mysql.createConnection({
|
||||
host: 'mysql.theaken.com',
|
||||
port: 33306,
|
||||
user: 'AI_Platform',
|
||||
password: 'Aa123456',
|
||||
database: 'db_AI_Platform'
|
||||
});
|
||||
|
||||
console.log('=== 檢查應用程式數量 ===');
|
||||
|
||||
// 檢查總數
|
||||
const [totalRows] = await connection.execute('SELECT COUNT(*) as total FROM apps');
|
||||
console.log('總應用程式數量:', totalRows[0].total);
|
||||
|
||||
// 檢查各狀態的數量
|
||||
const [statusRows] = await connection.execute(`
|
||||
SELECT status, COUNT(*) as count
|
||||
FROM apps
|
||||
GROUP BY status
|
||||
`);
|
||||
console.log('各狀態數量:');
|
||||
statusRows.forEach(row => {
|
||||
console.log(` ${row.status}: ${row.count}`);
|
||||
});
|
||||
|
||||
// 檢查最近的應用程式
|
||||
const [recentRows] = await connection.execute(`
|
||||
SELECT id, name, status, created_at
|
||||
FROM apps
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 5
|
||||
`);
|
||||
console.log('最近的應用程式:');
|
||||
recentRows.forEach(row => {
|
||||
console.log(` ${row.name} (${row.status}) - ${row.created_at}`);
|
||||
});
|
||||
|
||||
await connection.end();
|
||||
} catch (error) {
|
||||
console.error('檢查失敗:', error);
|
||||
}
|
||||
}
|
||||
|
||||
checkAppsCount();
|
@@ -1,44 +0,0 @@
|
||||
const mysql = require('mysql2/promise');
|
||||
|
||||
async function checkAppsTable() {
|
||||
const connection = await mysql.createConnection({
|
||||
host: process.env.DB_HOST || 'localhost',
|
||||
user: process.env.DB_USER || 'root',
|
||||
password: process.env.DB_PASSWORD || '',
|
||||
database: process.env.DB_NAME || 'ai_showcase_platform'
|
||||
});
|
||||
|
||||
try {
|
||||
console.log('檢查 apps 表格結構...');
|
||||
|
||||
// 檢查表格結構
|
||||
const [columns] = await connection.execute('DESCRIBE apps');
|
||||
console.log('\napps 表格欄位:');
|
||||
columns.forEach(col => {
|
||||
console.log(`- ${col.Field}: ${col.Type} ${col.Null === 'NO' ? 'NOT NULL' : 'NULL'} ${col.Default ? `DEFAULT ${col.Default}` : ''}`);
|
||||
});
|
||||
|
||||
// 檢查是否有資料
|
||||
const [rows] = await connection.execute('SELECT COUNT(*) as count FROM apps');
|
||||
console.log(`\napps 表格資料筆數: ${rows[0].count}`);
|
||||
|
||||
if (rows[0].count > 0) {
|
||||
// 顯示前幾筆資料
|
||||
const [sampleData] = await connection.execute('SELECT * FROM apps LIMIT 3');
|
||||
console.log('\n前 3 筆資料:');
|
||||
sampleData.forEach((row, index) => {
|
||||
console.log(`\n第 ${index + 1} 筆:`);
|
||||
Object.keys(row).forEach(key => {
|
||||
console.log(` ${key}: ${row[key]}`);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('檢查失敗:', error);
|
||||
} finally {
|
||||
await connection.end();
|
||||
}
|
||||
}
|
||||
|
||||
checkAppsTable();
|
@@ -1,53 +0,0 @@
|
||||
const mysql = require('mysql2/promise');
|
||||
|
||||
const dbConfig = {
|
||||
host: process.env.DB_HOST || 'mysql.theaken.com',
|
||||
port: parseInt(process.env.DB_PORT || '33306'),
|
||||
user: process.env.DB_USER || 'AI_Platform',
|
||||
password: process.env.DB_PASSWORD || 'Aa123456',
|
||||
database: process.env.DB_NAME || 'db_AI_Platform',
|
||||
charset: 'utf8mb4',
|
||||
timezone: '+08:00'
|
||||
};
|
||||
|
||||
async function checkDatabase() {
|
||||
let connection;
|
||||
|
||||
try {
|
||||
console.log('🔍 檢查資料庫表結構...');
|
||||
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
console.log('✅ 資料庫連接成功');
|
||||
|
||||
// 檢查 apps 表結構
|
||||
console.log('\n📋 Apps 表結構:');
|
||||
const [appsStructure] = await connection.execute('DESCRIBE apps');
|
||||
console.table(appsStructure);
|
||||
|
||||
// 檢查 apps 表資料
|
||||
console.log('\n📊 Apps 表資料:');
|
||||
const [appsData] = await connection.execute('SELECT id, name, type, status, creator_id FROM apps LIMIT 5');
|
||||
console.table(appsData);
|
||||
|
||||
// 檢查 users 表結構
|
||||
console.log('\n👥 Users 表結構:');
|
||||
const [usersStructure] = await connection.execute('DESCRIBE users');
|
||||
console.table(usersStructure);
|
||||
|
||||
// 檢查是否有開發者或管理員用戶
|
||||
console.log('\n🔑 檢查開發者/管理員用戶:');
|
||||
const [adminUsers] = await connection.execute('SELECT id, name, email, role FROM users WHERE role IN ("developer", "admin")');
|
||||
console.table(adminUsers);
|
||||
|
||||
console.log('\n✅ 資料庫檢查完成');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 檢查失敗:', error);
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkDatabase();
|
99
scripts/check-latest-app-data.js
Normal file
99
scripts/check-latest-app-data.js
Normal file
@@ -0,0 +1,99 @@
|
||||
const mysql = require('mysql2/promise');
|
||||
|
||||
const dbConfig = {
|
||||
host: process.env.DB_HOST || 'mysql.theaken.com',
|
||||
port: parseInt(process.env.DB_PORT || '33306'),
|
||||
user: process.env.DB_USER || 'AI_Platform',
|
||||
password: process.env.DB_PASSWORD || 'Aa123456',
|
||||
database: process.env.DB_NAME || 'db_AI_Platform',
|
||||
charset: 'utf8mb4',
|
||||
timezone: '+08:00'
|
||||
};
|
||||
|
||||
async function checkLatestAppData() {
|
||||
let connection;
|
||||
|
||||
try {
|
||||
console.log('🔍 檢查最新的應用程式資料...');
|
||||
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
console.log('✅ 資料庫連接成功');
|
||||
|
||||
// 檢查最新的應用程式資料
|
||||
const [apps] = await connection.execute(`
|
||||
SELECT
|
||||
a.id,
|
||||
a.name,
|
||||
a.description,
|
||||
a.creator_id,
|
||||
a.department as app_department,
|
||||
a.creator_name as app_creator_name,
|
||||
a.creator_email as app_creator_email,
|
||||
a.type,
|
||||
a.status,
|
||||
a.created_at,
|
||||
u.id as user_id,
|
||||
u.name as user_name,
|
||||
u.email as user_email,
|
||||
u.department as user_department
|
||||
FROM apps a
|
||||
LEFT JOIN users u ON a.creator_id = u.id
|
||||
ORDER BY a.created_at DESC
|
||||
LIMIT 5
|
||||
`);
|
||||
|
||||
console.log('\n📊 最新應用程式資料:');
|
||||
apps.forEach((app, index) => {
|
||||
console.log(`\n應用程式 ${index + 1}:`);
|
||||
console.log(` 應用 ID: ${app.id}`);
|
||||
console.log(` 應用名稱: ${app.name}`);
|
||||
console.log(` 應用描述: ${app.description}`);
|
||||
console.log(` 創建者 ID: ${app.creator_id}`);
|
||||
console.log(` 應用部門: ${app.app_department}`);
|
||||
console.log(` 應用創建者名稱: ${app.app_creator_name}`);
|
||||
console.log(` 應用創建者郵箱: ${app.app_creator_email}`);
|
||||
console.log(` 應用類型: ${app.type}`);
|
||||
console.log(` 應用狀態: ${app.status}`);
|
||||
console.log(` 創建時間: ${app.created_at}`);
|
||||
console.log(` 用戶 ID: ${app.user_id}`);
|
||||
console.log(` 用戶名稱: ${app.user_name}`);
|
||||
console.log(` 用戶郵箱: ${app.user_email}`);
|
||||
console.log(` 用戶部門: ${app.user_department}`);
|
||||
});
|
||||
|
||||
// 檢查特定應用程式的詳細資料
|
||||
const [specificApp] = await connection.execute(`
|
||||
SELECT
|
||||
a.*,
|
||||
u.name as user_name,
|
||||
u.email as user_email,
|
||||
u.department as user_department
|
||||
FROM apps a
|
||||
LEFT JOIN users u ON a.creator_id = u.id
|
||||
WHERE a.name LIKE '%天氣查詢機器人%'
|
||||
ORDER BY a.created_at DESC
|
||||
LIMIT 1
|
||||
`);
|
||||
|
||||
if (specificApp.length > 0) {
|
||||
const app = specificApp[0];
|
||||
console.log('\n🎯 天氣查詢機器人應用程式詳細資料:');
|
||||
console.log(` 應用名稱: ${app.name}`);
|
||||
console.log(` 應用創建者名稱: ${app.creator_name}`);
|
||||
console.log(` 應用部門: ${app.department}`);
|
||||
console.log(` 用戶名稱: ${app.user_name}`);
|
||||
console.log(` 用戶部門: ${app.user_department}`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 檢查最新應用程式資料失敗:', error);
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
console.log('🔌 資料庫連接已關閉');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 執行檢查
|
||||
checkLatestAppData().catch(console.error);
|
@@ -1,61 +0,0 @@
|
||||
const mysql = require('mysql2/promise');
|
||||
|
||||
const dbConfig = {
|
||||
host: process.env.DB_HOST || 'mysql.theaken.com',
|
||||
port: parseInt(process.env.DB_PORT || '33306'),
|
||||
user: process.env.DB_USER || 'AI_Platform',
|
||||
password: process.env.DB_PASSWORD || 'Aa123456',
|
||||
database: process.env.DB_NAME || 'db_AI_Platform',
|
||||
charset: 'utf8mb4',
|
||||
timezone: '+08:00'
|
||||
};
|
||||
|
||||
async function checkTeamsTable() {
|
||||
let connection;
|
||||
|
||||
try {
|
||||
console.log('🔍 檢查 teams 表...');
|
||||
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
console.log('✅ 資料庫連接成功');
|
||||
|
||||
// 檢查所有表
|
||||
console.log('\n📋 所有表:');
|
||||
const [tables] = await connection.execute('SHOW TABLES');
|
||||
console.table(tables);
|
||||
|
||||
// 檢查 teams 表是否存在
|
||||
console.log('\n🔍 檢查 teams 表是否存在...');
|
||||
const [teamsTable] = await connection.execute("SHOW TABLES LIKE 'teams'");
|
||||
|
||||
if (teamsTable.length > 0) {
|
||||
console.log('✅ teams 表存在');
|
||||
|
||||
// 檢查 teams 表結構
|
||||
console.log('\n📋 Teams 表結構:');
|
||||
const [teamsStructure] = await connection.execute('DESCRIBE teams');
|
||||
console.table(teamsStructure);
|
||||
|
||||
// 檢查 teams 表資料
|
||||
console.log('\n📊 Teams 表資料:');
|
||||
const [teamsData] = await connection.execute('SELECT * FROM teams LIMIT 5');
|
||||
console.table(teamsData);
|
||||
} else {
|
||||
console.log('❌ teams 表不存在');
|
||||
}
|
||||
|
||||
// 測試簡單的 apps 查詢
|
||||
console.log('\n🧪 測試簡單的 apps 查詢...');
|
||||
const [appsData] = await connection.execute('SELECT id, name, type, status, creator_id FROM apps LIMIT 5');
|
||||
console.table(appsData);
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 檢查失敗:', error);
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkTeamsTable();
|
@@ -1,52 +0,0 @@
|
||||
const mysql = require('mysql2/promise');
|
||||
const bcrypt = require('bcrypt');
|
||||
|
||||
const dbConfig = {
|
||||
host: process.env.DB_HOST || 'mysql.theaken.com',
|
||||
port: parseInt(process.env.DB_PORT || '33306'),
|
||||
user: process.env.DB_USER || 'AI_Platform',
|
||||
password: process.env.DB_PASSWORD || 'Aa123456',
|
||||
database: process.env.DB_NAME || 'db_AI_Platform',
|
||||
charset: 'utf8mb4',
|
||||
timezone: '+08:00'
|
||||
};
|
||||
|
||||
async function checkUserPasswords() {
|
||||
let connection;
|
||||
|
||||
try {
|
||||
console.log('🔍 檢查用戶密碼...');
|
||||
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
console.log('✅ 資料庫連接成功');
|
||||
|
||||
// 檢查用戶密碼哈希
|
||||
console.log('\n📊 用戶密碼哈希:');
|
||||
const [users] = await connection.execute('SELECT id, name, email, password_hash FROM users');
|
||||
|
||||
for (const user of users) {
|
||||
console.log(`\n用戶: ${user.name} (${user.email})`);
|
||||
console.log(`密碼哈希: ${user.password_hash}`);
|
||||
|
||||
// 測試一些常見密碼
|
||||
const testPasswords = ['Admin123', 'admin123', 'password', '123456', 'admin'];
|
||||
|
||||
for (const password of testPasswords) {
|
||||
const isValid = await bcrypt.compare(password, user.password_hash);
|
||||
if (isValid) {
|
||||
console.log(`✅ 找到正確密碼: ${password}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 檢查失敗:', error);
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkUserPasswords();
|
@@ -1,65 +0,0 @@
|
||||
const mysql = require('mysql2/promise');
|
||||
const jwt = require('jsonwebtoken');
|
||||
|
||||
const JWT_SECRET = process.env.JWT_SECRET || 'good777';
|
||||
|
||||
async function checkUsers() {
|
||||
try {
|
||||
// 連接資料庫
|
||||
const connection = await mysql.createConnection({
|
||||
host: 'mysql.theaken.com',
|
||||
port: 33306,
|
||||
user: 'AI_Platform',
|
||||
password: 'Aa123456',
|
||||
database: 'db_AI_Platform'
|
||||
});
|
||||
|
||||
console.log('=== 檢查用戶 ===');
|
||||
|
||||
// 檢查用戶
|
||||
const [userRows] = await connection.execute('SELECT id, email, name, role FROM users LIMIT 5');
|
||||
console.log('用戶列表:');
|
||||
userRows.forEach(user => {
|
||||
console.log(` ID: ${user.id}, Email: ${user.email}, Name: ${user.name}, Role: ${user.role}`);
|
||||
});
|
||||
|
||||
// 為第一個用戶生成 token
|
||||
if (userRows.length > 0) {
|
||||
const user = userRows[0];
|
||||
const token = jwt.sign({
|
||||
userId: user.id,
|
||||
email: user.email,
|
||||
role: user.role
|
||||
}, JWT_SECRET, { expiresIn: '1h' });
|
||||
|
||||
console.log('\n生成的 Token:');
|
||||
console.log(token);
|
||||
|
||||
// 測試 API
|
||||
console.log('\n=== 測試 API ===');
|
||||
const response = await fetch('http://localhost:3000/api/apps?page=1&limit=10', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
console.log('✅ API 回應成功');
|
||||
console.log('分頁資訊:', data.pagination);
|
||||
console.log('統計資訊:', data.stats);
|
||||
console.log(`應用程式數量: ${data.apps?.length || 0}`);
|
||||
} else {
|
||||
console.log('❌ API 回應失敗:', response.status, response.statusText);
|
||||
const errorText = await response.text();
|
||||
console.log('錯誤詳情:', errorText);
|
||||
}
|
||||
}
|
||||
|
||||
await connection.end();
|
||||
} catch (error) {
|
||||
console.error('檢查失敗:', error);
|
||||
}
|
||||
}
|
||||
|
||||
checkUsers();
|
@@ -1,134 +0,0 @@
|
||||
const mysql = require('mysql2/promise');
|
||||
const bcrypt = require('bcrypt');
|
||||
const crypto = require('crypto');
|
||||
|
||||
// 資料庫配置
|
||||
const dbConfig = {
|
||||
host: process.env.DB_HOST || 'mysql.theaken.com',
|
||||
port: parseInt(process.env.DB_PORT || '33306'),
|
||||
user: process.env.DB_USER || 'AI_Platform',
|
||||
password: process.env.DB_PASSWORD || 'Aa123456',
|
||||
database: process.env.DB_NAME || 'db_AI_Platform',
|
||||
charset: 'utf8mb4',
|
||||
timezone: '+08:00'
|
||||
};
|
||||
|
||||
// 生成 UUID
|
||||
function generateId() {
|
||||
return crypto.randomUUID();
|
||||
}
|
||||
|
||||
// 加密密碼
|
||||
async function hashPassword(password) {
|
||||
const saltRounds = 12;
|
||||
return await bcrypt.hash(password, saltRounds);
|
||||
}
|
||||
|
||||
async function createAdmin() {
|
||||
let connection;
|
||||
|
||||
try {
|
||||
console.log('🔌 連接資料庫...');
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
console.log('✅ 資料庫連接成功');
|
||||
|
||||
// 管理員資料
|
||||
const adminData = {
|
||||
id: generateId(),
|
||||
name: 'AI平台管理員',
|
||||
email: 'admin@theaken.com',
|
||||
password: 'Admin@2024',
|
||||
department: '資訊技術部',
|
||||
role: 'admin'
|
||||
};
|
||||
|
||||
console.log('\n📋 準備建立管理員帳號:');
|
||||
console.log(` 姓名: ${adminData.name}`);
|
||||
console.log(` 電子郵件: ${adminData.email}`);
|
||||
console.log(` 部門: ${adminData.department}`);
|
||||
console.log(` 角色: ${adminData.role}`);
|
||||
|
||||
// 檢查是否已存在
|
||||
const [existingUser] = await connection.execute(
|
||||
'SELECT id FROM users WHERE email = ?',
|
||||
[adminData.email]
|
||||
);
|
||||
|
||||
if (existingUser.length > 0) {
|
||||
console.log('\n⚠️ 管理員帳號已存在,更新密碼...');
|
||||
|
||||
// 加密新密碼
|
||||
const passwordHash = await hashPassword(adminData.password);
|
||||
|
||||
// 更新密碼
|
||||
await connection.execute(
|
||||
'UPDATE users SET password_hash = ?, updated_at = NOW() WHERE email = ?',
|
||||
[passwordHash, adminData.email]
|
||||
);
|
||||
|
||||
console.log('✅ 管理員密碼已更新');
|
||||
} else {
|
||||
console.log('\n📝 建立新的管理員帳號...');
|
||||
|
||||
// 加密密碼
|
||||
const passwordHash = await hashPassword(adminData.password);
|
||||
|
||||
// 插入管理員資料
|
||||
await connection.execute(`
|
||||
INSERT INTO users (
|
||||
id, name, email, password_hash, department, role,
|
||||
join_date, total_likes, total_views, created_at, updated_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, CURDATE(), 0, 0, NOW(), NOW())
|
||||
`, [
|
||||
adminData.id,
|
||||
adminData.name,
|
||||
adminData.email,
|
||||
passwordHash,
|
||||
adminData.department,
|
||||
adminData.role
|
||||
]);
|
||||
|
||||
console.log('✅ 管理員帳號建立成功');
|
||||
}
|
||||
|
||||
// 驗證建立結果
|
||||
console.log('\n🔍 驗證管理員帳號...');
|
||||
const [adminUser] = await connection.execute(
|
||||
'SELECT id, name, email, department, role, created_at FROM users WHERE email = ?',
|
||||
[adminData.email]
|
||||
);
|
||||
|
||||
if (adminUser.length > 0) {
|
||||
const user = adminUser[0];
|
||||
console.log('✅ 管理員帳號驗證成功:');
|
||||
console.log(` ID: ${user.id}`);
|
||||
console.log(` 姓名: ${user.name}`);
|
||||
console.log(` 電子郵件: ${user.email}`);
|
||||
console.log(` 部門: ${user.department}`);
|
||||
console.log(` 角色: ${user.role}`);
|
||||
console.log(` 建立時間: ${user.created_at}`);
|
||||
}
|
||||
|
||||
console.log('\n🎉 管理員帳號設定完成!');
|
||||
console.log('\n📋 登入資訊:');
|
||||
console.log(` 電子郵件: ${adminData.email}`);
|
||||
console.log(` 密碼: ${adminData.password}`);
|
||||
console.log('\n⚠️ 請妥善保管登入資訊,建議在首次登入後更改密碼');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 建立管理員帳號失敗:', error.message);
|
||||
process.exit(1);
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
console.log('🔌 資料庫連接已關閉');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果直接執行此腳本
|
||||
if (require.main === module) {
|
||||
createAdmin();
|
||||
}
|
||||
|
||||
module.exports = { createAdmin };
|
@@ -51,6 +51,12 @@ async function fixAppsTable() {
|
||||
// 添加版本欄位
|
||||
`ALTER TABLE apps ADD COLUMN version VARCHAR(50) DEFAULT '1.0.0'`,
|
||||
|
||||
// 添加圖示欄位
|
||||
`ALTER TABLE apps ADD COLUMN icon VARCHAR(50) DEFAULT 'Bot'`,
|
||||
|
||||
// 添加圖示顏色欄位
|
||||
`ALTER TABLE apps ADD COLUMN icon_color VARCHAR(100) DEFAULT 'from-blue-500 to-purple-500'`,
|
||||
|
||||
// 添加最後更新時間欄位
|
||||
`ALTER TABLE apps ADD COLUMN last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP`
|
||||
];
|
||||
|
@@ -1,80 +0,0 @@
|
||||
const mysql = require('mysql2/promise');
|
||||
|
||||
// 資料庫配置
|
||||
const dbConfig = {
|
||||
host: process.env.DB_HOST || 'mysql.theaken.com',
|
||||
port: parseInt(process.env.DB_PORT || '33306'),
|
||||
user: process.env.DB_USER || 'AI_Platform',
|
||||
password: process.env.DB_PASSWORD || 'Aa123456',
|
||||
database: process.env.DB_NAME || 'db_AI_Platform',
|
||||
charset: 'utf8mb4',
|
||||
timezone: '+08:00'
|
||||
};
|
||||
|
||||
async function fixUserLikes() {
|
||||
let connection;
|
||||
|
||||
try {
|
||||
console.log('🔧 修復 user_likes 表...');
|
||||
|
||||
// 連接資料庫
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
|
||||
// 先刪除可能存在的表
|
||||
try {
|
||||
await connection.query('DROP TABLE IF EXISTS user_likes');
|
||||
console.log('✅ 刪除舊的 user_likes 表');
|
||||
} catch (error) {
|
||||
console.log('沒有舊表需要刪除');
|
||||
}
|
||||
|
||||
// 建立簡化版的 user_likes 表
|
||||
const userLikesTable = `
|
||||
CREATE TABLE user_likes (
|
||||
id VARCHAR(36) PRIMARY KEY,
|
||||
user_id VARCHAR(36) NOT NULL,
|
||||
app_id VARCHAR(36),
|
||||
proposal_id VARCHAR(36),
|
||||
liked_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (app_id) REFERENCES apps(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (proposal_id) REFERENCES proposals(id) ON DELETE CASCADE,
|
||||
INDEX idx_user (user_id),
|
||||
INDEX idx_app (app_id),
|
||||
INDEX idx_proposal (proposal_id),
|
||||
INDEX idx_date (liked_at)
|
||||
)
|
||||
`;
|
||||
|
||||
await connection.query(userLikesTable);
|
||||
console.log('✅ user_likes 表建立成功');
|
||||
|
||||
// 驗證結果
|
||||
const [tables] = await connection.query(`
|
||||
SELECT TABLE_NAME, TABLE_ROWS
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = '${dbConfig.database}' AND TABLE_NAME = 'user_likes'
|
||||
`);
|
||||
|
||||
if (tables.length > 0) {
|
||||
console.log('✅ user_likes 表驗證成功');
|
||||
} else {
|
||||
console.log('❌ user_likes 表建立失敗');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 修復 user_likes 表失敗:', error.message);
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
console.log('\n🔌 資料庫連接已關閉');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 執行修復腳本
|
||||
if (require.main === module) {
|
||||
fixUserLikes();
|
||||
}
|
||||
|
||||
module.exports = { fixUserLikes };
|
@@ -1,64 +0,0 @@
|
||||
const mysql = require('mysql2/promise');
|
||||
const bcrypt = require('bcrypt');
|
||||
|
||||
const dbConfig = {
|
||||
host: process.env.DB_HOST || 'mysql.theaken.com',
|
||||
port: parseInt(process.env.DB_PORT || '33306'),
|
||||
user: process.env.DB_USER || 'AI_Platform',
|
||||
password: process.env.DB_PASSWORD || 'Aa123456',
|
||||
database: process.env.DB_NAME || 'db_AI_Platform',
|
||||
charset: 'utf8mb4',
|
||||
timezone: '+08:00'
|
||||
};
|
||||
|
||||
async function resetUserPassword() {
|
||||
let connection;
|
||||
|
||||
try {
|
||||
console.log('🔧 重置用戶密碼...');
|
||||
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
console.log('✅ 資料庫連接成功');
|
||||
|
||||
// 新密碼
|
||||
const newPassword = 'Admin123';
|
||||
const hashedPassword = await bcrypt.hash(newPassword, 12);
|
||||
|
||||
console.log(`\n新密碼: ${newPassword}`);
|
||||
console.log(`密碼哈希: ${hashedPassword}`);
|
||||
|
||||
// 重置所有管理員用戶的密碼
|
||||
const adminEmails = [
|
||||
'admin@theaken.com',
|
||||
'admin@example.com',
|
||||
'petty091901@gmail.com'
|
||||
];
|
||||
|
||||
for (const email of adminEmails) {
|
||||
try {
|
||||
await connection.execute(
|
||||
'UPDATE users SET password_hash = ? WHERE email = ?',
|
||||
[hashedPassword, email]
|
||||
);
|
||||
|
||||
console.log(`✅ 已重置 ${email} 的密碼`);
|
||||
} catch (error) {
|
||||
console.error(`❌ 重置 ${email} 密碼失敗:`, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n🎉 密碼重置完成!');
|
||||
console.log('現在可以使用以下憑證登入:');
|
||||
console.log('電子郵件: admin@theaken.com');
|
||||
console.log('密碼: Admin123');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 重置失敗:', error);
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resetUserPassword();
|
@@ -1,491 +0,0 @@
|
||||
const mysql = require('mysql2/promise');
|
||||
|
||||
// 資料庫配置
|
||||
const dbConfig = {
|
||||
host: process.env.DB_HOST || 'mysql.theaken.com',
|
||||
port: parseInt(process.env.DB_PORT || '33306'),
|
||||
user: process.env.DB_USER || 'AI_Platform',
|
||||
password: process.env.DB_PASSWORD || 'Aa123456',
|
||||
database: process.env.DB_NAME || 'db_AI_Platform',
|
||||
charset: 'utf8mb4',
|
||||
timezone: '+08:00'
|
||||
};
|
||||
|
||||
async function setupDatabase() {
|
||||
let connection;
|
||||
|
||||
try {
|
||||
console.log('🚀 開始建立AI展示平台資料庫...');
|
||||
console.log('─'.repeat(50));
|
||||
|
||||
// 1. 連接資料庫
|
||||
console.log('🔌 連接資料庫...');
|
||||
connection = await mysql.createConnection({
|
||||
...dbConfig,
|
||||
database: undefined
|
||||
});
|
||||
|
||||
// 2. 檢查資料庫是否存在
|
||||
const [databases] = await connection.query('SHOW DATABASES');
|
||||
const dbExists = databases.some(db => db.Database === dbConfig.database);
|
||||
|
||||
if (!dbExists) {
|
||||
console.log(`📊 建立資料庫: ${dbConfig.database}`);
|
||||
await connection.query(`CREATE DATABASE ${dbConfig.database} CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci`);
|
||||
} else {
|
||||
console.log(`✅ 資料庫已存在: ${dbConfig.database}`);
|
||||
}
|
||||
|
||||
// 3. 切換到目標資料庫
|
||||
await connection.query(`USE ${dbConfig.database}`);
|
||||
|
||||
// 4. 手動執行SQL語句
|
||||
console.log('📝 執行資料庫建立腳本...');
|
||||
|
||||
const sqlStatements = [
|
||||
// 1. 用戶表
|
||||
`CREATE TABLE IF NOT EXISTS users (
|
||||
id VARCHAR(36) PRIMARY KEY,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
email VARCHAR(255) UNIQUE NOT NULL,
|
||||
password_hash VARCHAR(255) NOT NULL,
|
||||
avatar VARCHAR(500),
|
||||
department VARCHAR(100) NOT NULL,
|
||||
role ENUM('user', 'developer', 'admin') DEFAULT 'user',
|
||||
join_date DATE NOT NULL,
|
||||
total_likes INT DEFAULT 0,
|
||||
total_views INT DEFAULT 0,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
INDEX idx_email (email),
|
||||
INDEX idx_department (department),
|
||||
INDEX idx_role (role)
|
||||
)`,
|
||||
|
||||
// 2. 競賽表
|
||||
`CREATE TABLE IF NOT EXISTS competitions (
|
||||
id VARCHAR(36) PRIMARY KEY,
|
||||
name VARCHAR(200) NOT NULL,
|
||||
year INT NOT NULL,
|
||||
month INT NOT NULL,
|
||||
start_date DATE NOT NULL,
|
||||
end_date DATE NOT NULL,
|
||||
status ENUM('upcoming', 'active', 'judging', 'completed') DEFAULT 'upcoming',
|
||||
description TEXT,
|
||||
type ENUM('individual', 'team', 'mixed', 'proposal') NOT NULL,
|
||||
evaluation_focus TEXT,
|
||||
max_team_size INT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
INDEX idx_status (status),
|
||||
INDEX idx_type (type),
|
||||
INDEX idx_year_month (year, month),
|
||||
INDEX idx_dates (start_date, end_date)
|
||||
)`,
|
||||
|
||||
// 3. 評審表
|
||||
`CREATE TABLE IF NOT EXISTS judges (
|
||||
id VARCHAR(36) PRIMARY KEY,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
title VARCHAR(100) NOT NULL,
|
||||
department VARCHAR(100) NOT NULL,
|
||||
expertise JSON,
|
||||
avatar VARCHAR(500),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
INDEX idx_department (department)
|
||||
)`,
|
||||
|
||||
// 4. 團隊表
|
||||
`CREATE TABLE IF NOT EXISTS teams (
|
||||
id VARCHAR(36) PRIMARY KEY,
|
||||
name VARCHAR(200) NOT NULL,
|
||||
leader_id VARCHAR(36) NOT NULL,
|
||||
department VARCHAR(100) NOT NULL,
|
||||
contact_email VARCHAR(255) NOT NULL,
|
||||
total_likes INT DEFAULT 0,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (leader_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
INDEX idx_department (department),
|
||||
INDEX idx_leader (leader_id)
|
||||
)`,
|
||||
|
||||
// 5. 團隊成員表
|
||||
`CREATE TABLE IF NOT EXISTS team_members (
|
||||
id VARCHAR(36) PRIMARY KEY,
|
||||
team_id VARCHAR(36) NOT NULL,
|
||||
user_id VARCHAR(36) NOT NULL,
|
||||
role VARCHAR(50) NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (team_id) REFERENCES teams(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
UNIQUE KEY unique_team_user (team_id, user_id),
|
||||
INDEX idx_team (team_id),
|
||||
INDEX idx_user (user_id)
|
||||
)`,
|
||||
|
||||
// 6. 應用表
|
||||
`CREATE TABLE IF NOT EXISTS apps (
|
||||
id VARCHAR(36) PRIMARY KEY,
|
||||
name VARCHAR(200) NOT NULL,
|
||||
description TEXT,
|
||||
creator_id VARCHAR(36) NOT NULL,
|
||||
team_id VARCHAR(36),
|
||||
likes_count INT DEFAULT 0,
|
||||
views_count INT DEFAULT 0,
|
||||
rating DECIMAL(3,2) DEFAULT 0,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (creator_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (team_id) REFERENCES teams(id) ON DELETE SET NULL,
|
||||
INDEX idx_creator (creator_id),
|
||||
INDEX idx_team (team_id),
|
||||
INDEX idx_rating (rating),
|
||||
INDEX idx_likes (likes_count)
|
||||
)`,
|
||||
|
||||
// 7. 提案表
|
||||
`CREATE TABLE IF NOT EXISTS proposals (
|
||||
id VARCHAR(36) PRIMARY KEY,
|
||||
title VARCHAR(200) NOT NULL,
|
||||
description TEXT,
|
||||
creator_id VARCHAR(36) NOT NULL,
|
||||
team_id VARCHAR(36),
|
||||
status ENUM('draft', 'submitted', 'under_review', 'approved', 'rejected') DEFAULT 'draft',
|
||||
likes_count INT DEFAULT 0,
|
||||
views_count INT DEFAULT 0,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (creator_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (team_id) REFERENCES teams(id) ON DELETE SET NULL,
|
||||
INDEX idx_creator (creator_id),
|
||||
INDEX idx_status (status)
|
||||
)`,
|
||||
|
||||
// 8. 評分表
|
||||
`CREATE TABLE IF NOT EXISTS judge_scores (
|
||||
id VARCHAR(36) PRIMARY KEY,
|
||||
judge_id VARCHAR(36) NOT NULL,
|
||||
app_id VARCHAR(36),
|
||||
proposal_id VARCHAR(36),
|
||||
scores JSON NOT NULL,
|
||||
comments TEXT,
|
||||
submitted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (judge_id) REFERENCES judges(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (app_id) REFERENCES apps(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (proposal_id) REFERENCES proposals(id) ON DELETE CASCADE,
|
||||
UNIQUE KEY unique_judge_app (judge_id, app_id),
|
||||
UNIQUE KEY unique_judge_proposal (judge_id, proposal_id),
|
||||
INDEX idx_judge (judge_id),
|
||||
INDEX idx_app (app_id),
|
||||
INDEX idx_proposal (proposal_id)
|
||||
)`,
|
||||
|
||||
// 9. 獎項表
|
||||
`CREATE TABLE IF NOT EXISTS awards (
|
||||
id VARCHAR(36) PRIMARY KEY,
|
||||
competition_id VARCHAR(36) NOT NULL,
|
||||
app_id VARCHAR(36),
|
||||
team_id VARCHAR(36),
|
||||
proposal_id VARCHAR(36),
|
||||
award_type ENUM('gold', 'silver', 'bronze', 'popular', 'innovation', 'technical', 'custom') NOT NULL,
|
||||
award_name VARCHAR(200) NOT NULL,
|
||||
score DECIMAL(5,2) NOT NULL,
|
||||
year INT NOT NULL,
|
||||
month INT NOT NULL,
|
||||
icon VARCHAR(50),
|
||||
custom_award_type_id VARCHAR(36),
|
||||
competition_type ENUM('individual', 'team', 'proposal') NOT NULL,
|
||||
rank INT DEFAULT 0,
|
||||
category ENUM('innovation', 'technical', 'practical', 'popular', 'teamwork', 'solution', 'creativity') NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (competition_id) REFERENCES competitions(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (app_id) REFERENCES apps(id) ON DELETE SET NULL,
|
||||
FOREIGN KEY (team_id) REFERENCES teams(id) ON DELETE SET NULL,
|
||||
FOREIGN KEY (proposal_id) REFERENCES proposals(id) ON DELETE SET NULL,
|
||||
INDEX idx_competition (competition_id),
|
||||
INDEX idx_award_type (award_type),
|
||||
INDEX idx_year_month (year, month),
|
||||
INDEX idx_category (category)
|
||||
)`,
|
||||
|
||||
// 10. 聊天會話表
|
||||
`CREATE TABLE IF NOT EXISTS chat_sessions (
|
||||
id VARCHAR(36) PRIMARY KEY,
|
||||
user_id VARCHAR(36) NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
INDEX idx_user (user_id),
|
||||
INDEX idx_created (created_at)
|
||||
)`,
|
||||
|
||||
// 11. 聊天訊息表
|
||||
`CREATE TABLE IF NOT EXISTS chat_messages (
|
||||
id VARCHAR(36) PRIMARY KEY,
|
||||
session_id VARCHAR(36) NOT NULL,
|
||||
text TEXT NOT NULL,
|
||||
sender ENUM('user', 'bot') NOT NULL,
|
||||
quick_questions JSON,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (session_id) REFERENCES chat_sessions(id) ON DELETE CASCADE,
|
||||
INDEX idx_session (session_id),
|
||||
INDEX idx_created (created_at)
|
||||
)`,
|
||||
|
||||
// 12. AI助手配置表
|
||||
`CREATE TABLE IF NOT EXISTS ai_assistant_configs (
|
||||
id VARCHAR(36) PRIMARY KEY,
|
||||
api_key VARCHAR(255) NOT NULL,
|
||||
api_url VARCHAR(500) NOT NULL,
|
||||
model VARCHAR(100) NOT NULL,
|
||||
max_tokens INT DEFAULT 200,
|
||||
temperature DECIMAL(3,2) DEFAULT 0.7,
|
||||
system_prompt TEXT NOT NULL,
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
INDEX idx_active (is_active)
|
||||
)`,
|
||||
|
||||
// 13. 用戶收藏表
|
||||
`CREATE TABLE IF NOT EXISTS user_favorites (
|
||||
id VARCHAR(36) PRIMARY KEY,
|
||||
user_id VARCHAR(36) NOT NULL,
|
||||
app_id VARCHAR(36),
|
||||
proposal_id VARCHAR(36),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (app_id) REFERENCES apps(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (proposal_id) REFERENCES proposals(id) ON DELETE CASCADE,
|
||||
UNIQUE KEY unique_user_app (user_id, app_id),
|
||||
UNIQUE KEY unique_user_proposal (user_id, proposal_id),
|
||||
INDEX idx_user (user_id)
|
||||
)`,
|
||||
|
||||
// 14. 用戶按讚表
|
||||
`CREATE TABLE IF NOT EXISTS user_likes (
|
||||
id VARCHAR(36) PRIMARY KEY,
|
||||
user_id VARCHAR(36) NOT NULL,
|
||||
app_id VARCHAR(36),
|
||||
proposal_id VARCHAR(36),
|
||||
liked_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (app_id) REFERENCES apps(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (proposal_id) REFERENCES proposals(id) ON DELETE CASCADE,
|
||||
UNIQUE KEY unique_user_app_date (user_id, app_id, DATE(liked_at)),
|
||||
UNIQUE KEY unique_user_proposal_date (user_id, proposal_id, DATE(liked_at)),
|
||||
INDEX idx_user (user_id),
|
||||
INDEX idx_app (app_id),
|
||||
INDEX idx_proposal (proposal_id),
|
||||
INDEX idx_date (liked_at)
|
||||
)`,
|
||||
|
||||
// 15. 競賽參與表
|
||||
`CREATE TABLE IF NOT EXISTS competition_participants (
|
||||
id VARCHAR(36) PRIMARY KEY,
|
||||
competition_id VARCHAR(36) NOT NULL,
|
||||
user_id VARCHAR(36),
|
||||
team_id VARCHAR(36),
|
||||
app_id VARCHAR(36),
|
||||
proposal_id VARCHAR(36),
|
||||
status ENUM('registered', 'submitted', 'approved', 'rejected') DEFAULT 'registered',
|
||||
registered_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (competition_id) REFERENCES competitions(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (team_id) REFERENCES teams(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (app_id) REFERENCES apps(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (proposal_id) REFERENCES proposals(id) ON DELETE CASCADE,
|
||||
INDEX idx_competition (competition_id),
|
||||
INDEX idx_user (user_id),
|
||||
INDEX idx_team (team_id),
|
||||
INDEX idx_status (status)
|
||||
)`,
|
||||
|
||||
// 16. 競賽評審分配表
|
||||
`CREATE TABLE IF NOT EXISTS competition_judges (
|
||||
id VARCHAR(36) PRIMARY KEY,
|
||||
competition_id VARCHAR(36) NOT NULL,
|
||||
judge_id VARCHAR(36) NOT NULL,
|
||||
assigned_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (competition_id) REFERENCES competitions(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (judge_id) REFERENCES judges(id) ON DELETE CASCADE,
|
||||
UNIQUE KEY unique_competition_judge (competition_id, judge_id),
|
||||
INDEX idx_competition (competition_id),
|
||||
INDEX idx_judge (judge_id)
|
||||
)`,
|
||||
|
||||
// 17. 系統設定表
|
||||
`CREATE TABLE IF NOT EXISTS system_settings (
|
||||
id VARCHAR(36) PRIMARY KEY,
|
||||
setting_key VARCHAR(100) UNIQUE NOT NULL,
|
||||
setting_value TEXT,
|
||||
description TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
INDEX idx_key (setting_key)
|
||||
)`,
|
||||
|
||||
// 18. 活動日誌表
|
||||
`CREATE TABLE IF NOT EXISTS activity_logs (
|
||||
id VARCHAR(36) PRIMARY KEY,
|
||||
user_id VARCHAR(36),
|
||||
action VARCHAR(100) NOT NULL,
|
||||
target_type ENUM('user', 'competition', 'app', 'proposal', 'team', 'award') NOT NULL,
|
||||
target_id VARCHAR(36),
|
||||
details JSON,
|
||||
ip_address VARCHAR(45),
|
||||
user_agent TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL,
|
||||
INDEX idx_user (user_id),
|
||||
INDEX idx_action (action),
|
||||
INDEX idx_target (target_type, target_id),
|
||||
INDEX idx_created (created_at)
|
||||
)`
|
||||
];
|
||||
|
||||
console.log(`📋 準備執行 ${sqlStatements.length} 個SQL語句`);
|
||||
|
||||
for (let i = 0; i < sqlStatements.length; i++) {
|
||||
const statement = sqlStatements[i];
|
||||
try {
|
||||
console.log(`執行語句 ${i + 1}/${sqlStatements.length}: ${statement.substring(0, 50)}...`);
|
||||
await connection.query(statement);
|
||||
} catch (error) {
|
||||
console.error(`SQL執行錯誤 (語句 ${i + 1}):`, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 插入初始數據
|
||||
console.log('\n📝 插入初始數據...');
|
||||
|
||||
const insertStatements = [
|
||||
// 插入預設管理員用戶
|
||||
`INSERT IGNORE INTO users (id, name, email, password_hash, department, role, join_date) VALUES
|
||||
('admin-001', '系統管理員', 'admin@theaken.com', '$2b$10$rQZ8K9mN2pL1vX3yU7wE4tA6sB8cD1eF2gH3iJ4kL5mN6oP7qR8sT9uV0wX1yZ2a', '資訊部', 'admin', '2025-01-01')`,
|
||||
|
||||
// 插入預設評審
|
||||
`INSERT IGNORE INTO judges (id, name, title, department, expertise) VALUES
|
||||
('judge-001', '張教授', '資深技術顧問', '研發部', '["AI", "機器學習", "深度學習"]'),
|
||||
('judge-002', '李經理', '產品經理', '產品部', '["產品設計", "用戶體驗", "市場分析"]'),
|
||||
('judge-003', '王工程師', '資深工程師', '技術部', '["軟體開發", "系統架構", "雲端技術"]')`,
|
||||
|
||||
// 插入預設競賽
|
||||
`INSERT IGNORE INTO competitions (id, name, year, month, start_date, end_date, status, description, type, evaluation_focus, max_team_size) VALUES
|
||||
('comp-2025-01', '2025年AI創新競賽', 2025, 1, '2025-01-15', '2025-03-15', 'upcoming', '年度AI技術創新競賽,鼓勵員工開發創新AI應用', 'mixed', '創新性、技術實現、實用價值', 5),
|
||||
('comp-2025-02', '2025年提案競賽', 2025, 2, '2025-02-01', '2025-04-01', 'upcoming', 'AI解決方案提案競賽', 'proposal', '解決方案可行性、創新程度、商業價值', NULL)`,
|
||||
|
||||
// 插入AI助手配置
|
||||
`INSERT IGNORE INTO ai_assistant_configs (id, api_key, api_url, model, max_tokens, temperature, system_prompt, is_active) VALUES
|
||||
('ai-config-001', 'your_deepseek_api_key_here', 'https://api.deepseek.com/v1/chat/completions', 'deepseek-chat', 200, 0.7, '你是一個AI展示平台的智能助手,專門協助用戶使用平台功能。請用友善、專業的態度回答問題。', TRUE)`,
|
||||
|
||||
// 插入系統設定
|
||||
`INSERT IGNORE INTO system_settings (setting_key, setting_value, description) VALUES
|
||||
('daily_like_limit', '5', '用戶每日按讚限制'),
|
||||
('max_team_size', '5', '最大團隊人數'),
|
||||
('competition_registration_deadline', '7', '競賽報名截止天數'),
|
||||
('judge_score_weight_innovation', '25', '創新性評分權重'),
|
||||
('judge_score_weight_technical', '25', '技術性評分權重'),
|
||||
('judge_score_weight_usability', '20', '實用性評分權重'),
|
||||
('judge_score_weight_presentation', '15', '展示效果評分權重'),
|
||||
('judge_score_weight_impact', '15', '影響力評分權重')`
|
||||
];
|
||||
|
||||
for (let i = 0; i < insertStatements.length; i++) {
|
||||
const statement = insertStatements[i];
|
||||
try {
|
||||
console.log(`插入數據 ${i + 1}/${insertStatements.length}...`);
|
||||
await connection.query(statement);
|
||||
} catch (error) {
|
||||
console.error(`插入數據錯誤 (語句 ${i + 1}):`, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('✅ 資料庫建立完成!');
|
||||
|
||||
// 6. 驗證建立結果
|
||||
console.log('\n📋 驗證資料庫結構...');
|
||||
|
||||
// 檢查資料表
|
||||
const [tables] = await connection.query(`
|
||||
SELECT TABLE_NAME, TABLE_ROWS
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = '${dbConfig.database}'
|
||||
ORDER BY TABLE_NAME
|
||||
`);
|
||||
|
||||
console.log('\n📊 資料表列表:');
|
||||
console.log('─'.repeat(60));
|
||||
console.log('表名'.padEnd(25) + '| 記錄數'.padEnd(10) + '| 狀態');
|
||||
console.log('─'.repeat(60));
|
||||
|
||||
const expectedTables = [
|
||||
'users', 'competitions', 'judges', 'teams', 'team_members',
|
||||
'apps', 'proposals', 'judge_scores', 'awards', 'chat_sessions',
|
||||
'chat_messages', 'ai_assistant_configs', 'user_favorites',
|
||||
'user_likes', 'competition_participants', 'competition_judges',
|
||||
'system_settings', 'activity_logs'
|
||||
];
|
||||
|
||||
let successCount = 0;
|
||||
for (const expectedTable of expectedTables) {
|
||||
const table = tables.find(t => t.TABLE_NAME === expectedTable);
|
||||
const status = table ? '✅' : '❌';
|
||||
const rowCount = table ? (table.TABLE_ROWS || 0) : 'N/A';
|
||||
console.log(`${expectedTable.padEnd(25)}| ${rowCount.toString().padEnd(10)}| ${status}`);
|
||||
if (table) successCount++;
|
||||
}
|
||||
|
||||
console.log(`\n📊 成功建立 ${successCount}/${expectedTables.length} 個資料表`);
|
||||
|
||||
// 檢查初始數據
|
||||
console.log('\n📊 初始數據檢查:');
|
||||
console.log('─'.repeat(40));
|
||||
|
||||
const checks = [
|
||||
{ name: '管理員用戶', query: 'SELECT COUNT(*) as count FROM users WHERE role = "admin"' },
|
||||
{ name: '預設評審', query: 'SELECT COUNT(*) as count FROM judges' },
|
||||
{ name: '預設競賽', query: 'SELECT COUNT(*) as count FROM competitions' },
|
||||
{ name: 'AI配置', query: 'SELECT COUNT(*) as count FROM ai_assistant_configs' },
|
||||
{ name: '系統設定', query: 'SELECT COUNT(*) as count FROM system_settings' }
|
||||
];
|
||||
|
||||
for (const check of checks) {
|
||||
try {
|
||||
const [result] = await connection.query(check.query);
|
||||
console.log(`${check.name.padEnd(15)}: ${result[0].count} 筆`);
|
||||
} catch (error) {
|
||||
console.log(`${check.name.padEnd(15)}: 查詢失敗 - ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n🎉 資料庫建立和驗證完成!');
|
||||
console.log('\n📝 下一步:');
|
||||
console.log('1. 複製 env.example 到 .env.local');
|
||||
console.log('2. 設定環境變數');
|
||||
console.log('3. 安裝依賴: pnpm install');
|
||||
console.log('4. 啟動開發服務器: pnpm dev');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 資料庫建立失敗:', error.message);
|
||||
console.error('請檢查以下項目:');
|
||||
console.error('1. 資料庫主機是否可達');
|
||||
console.error('2. 用戶名和密碼是否正確');
|
||||
console.error('3. 用戶是否有建立資料庫的權限');
|
||||
process.exit(1);
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
console.log('\n🔌 資料庫連接已關閉');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 執行建立腳本
|
||||
if (require.main === module) {
|
||||
setupDatabase();
|
||||
}
|
||||
|
||||
module.exports = { setupDatabase };
|
@@ -43,7 +43,7 @@ async function setupDatabase() {
|
||||
|
||||
// 4. 讀取並執行SQL腳本
|
||||
console.log('📝 執行資料庫建立腳本...');
|
||||
const sqlScript = fs.readFileSync(path.join(__dirname, '../database_setup_simple.sql'), 'utf8');
|
||||
const sqlScript = fs.readFileSync(path.join(__dirname, '../database_setup.sql'), 'utf8');
|
||||
|
||||
// 分割SQL語句並執行
|
||||
const statements = sqlScript
|
||||
|
@@ -1,80 +0,0 @@
|
||||
const http = require('http');
|
||||
|
||||
// 簡單的 HTTP 請求函數
|
||||
function makeSimpleRequest(url, method = 'GET', body = null) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const urlObj = new URL(url);
|
||||
|
||||
const options = {
|
||||
hostname: urlObj.hostname,
|
||||
port: urlObj.port,
|
||||
path: urlObj.pathname,
|
||||
method: method,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
};
|
||||
|
||||
const req = http.request(options, (res) => {
|
||||
let data = '';
|
||||
|
||||
res.on('data', (chunk) => {
|
||||
data += chunk;
|
||||
});
|
||||
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const jsonData = JSON.parse(data);
|
||||
resolve({
|
||||
status: res.statusCode,
|
||||
data: jsonData
|
||||
});
|
||||
} catch (error) {
|
||||
resolve({
|
||||
status: res.statusCode,
|
||||
data: data
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', (error) => {
|
||||
reject(error);
|
||||
});
|
||||
|
||||
if (body) {
|
||||
req.write(JSON.stringify(body));
|
||||
}
|
||||
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
async function testSimple() {
|
||||
console.log('🧪 簡單 API 測試...\n');
|
||||
|
||||
try {
|
||||
// 測試健康檢查
|
||||
console.log('1️⃣ 測試健康檢查...');
|
||||
const health = await makeSimpleRequest('http://localhost:3000/api');
|
||||
console.log(` 狀態碼: ${health.status}`);
|
||||
console.log(` 回應: ${JSON.stringify(health.data, null, 2)}`);
|
||||
console.log('');
|
||||
|
||||
// 測試註冊 API
|
||||
console.log('2️⃣ 測試註冊 API...');
|
||||
const register = await makeSimpleRequest('http://localhost:3000/api/auth/register', 'POST', {
|
||||
name: '測試用戶',
|
||||
email: 'test@example.com',
|
||||
password: 'Test@2024',
|
||||
department: '測試部門'
|
||||
});
|
||||
console.log(` 狀態碼: ${register.status}`);
|
||||
console.log(` 回應: ${JSON.stringify(register.data, null, 2)}`);
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 測試失敗:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
testSimple();
|
@@ -1,107 +0,0 @@
|
||||
const mysql = require('mysql2/promise');
|
||||
|
||||
const dbConfig = {
|
||||
host: process.env.DB_HOST || 'mysql.theaken.com',
|
||||
port: parseInt(process.env.DB_PORT || '33306'),
|
||||
user: process.env.DB_USER || 'AI_Platform',
|
||||
password: process.env.DB_PASSWORD || 'Aa123456',
|
||||
database: process.env.DB_NAME || 'db_AI_Platform',
|
||||
charset: 'utf8mb4',
|
||||
timezone: '+08:00'
|
||||
};
|
||||
|
||||
async function testAdminAppCreation() {
|
||||
let connection;
|
||||
try {
|
||||
console.log('🧪 測試管理後台應用程式創建流程...');
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
console.log('✅ 資料庫連接成功');
|
||||
|
||||
// 創建測試用戶(管理員)
|
||||
const userData = {
|
||||
id: 'admin-test-' + Date.now(),
|
||||
name: '管理員測試用戶',
|
||||
email: 'admin-test@example.com',
|
||||
password_hash: 'test_hash',
|
||||
department: 'ITBU',
|
||||
role: 'admin',
|
||||
join_date: new Date(),
|
||||
created_at: new Date(),
|
||||
updated_at: new Date()
|
||||
};
|
||||
|
||||
await connection.execute(
|
||||
'INSERT INTO users (id, name, email, password_hash, department, role, join_date, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)',
|
||||
[userData.id, userData.name, userData.email, userData.password_hash, userData.department, userData.role, userData.join_date, userData.created_at, userData.updated_at]
|
||||
);
|
||||
console.log('✅ 測試管理員用戶創建成功');
|
||||
|
||||
// 模擬管理後台提交的資料
|
||||
const adminAppData = {
|
||||
name: '管理後台測試應用',
|
||||
description: '這是一個通過管理後台創建的測試應用程式',
|
||||
type: 'ai_model', // 映射後的類型
|
||||
demoUrl: 'https://admin-test.example.com/demo',
|
||||
version: '1.0.0'
|
||||
};
|
||||
|
||||
console.log('📋 管理後台提交的資料:', adminAppData);
|
||||
|
||||
// 創建應用程式
|
||||
const appId = Date.now().toString(36) + Math.random().toString(36).substr(2);
|
||||
const appInsertData = {
|
||||
id: appId,
|
||||
name: adminAppData.name,
|
||||
description: adminAppData.description,
|
||||
creator_id: userData.id,
|
||||
type: adminAppData.type,
|
||||
demo_url: adminAppData.demoUrl,
|
||||
version: adminAppData.version,
|
||||
status: 'draft'
|
||||
};
|
||||
|
||||
await connection.execute(
|
||||
'INSERT INTO apps (id, name, description, creator_id, type, demo_url, version, status, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())',
|
||||
[appInsertData.id, appInsertData.name, appInsertData.description, appInsertData.creator_id, appInsertData.type, appInsertData.demo_url, appInsertData.version, appInsertData.status]
|
||||
);
|
||||
console.log('✅ 應用程式創建成功');
|
||||
|
||||
// 查詢並顯示創建的應用程式
|
||||
const [appResult] = await connection.execute(
|
||||
'SELECT a.*, u.name as creator_name FROM apps a LEFT JOIN users u ON a.creator_id = u.id WHERE a.id = ?',
|
||||
[appId]
|
||||
);
|
||||
|
||||
if (appResult.length > 0) {
|
||||
const app = appResult[0];
|
||||
console.log('\n📋 資料庫中的應用程式資料:');
|
||||
console.log(` ID: ${app.id}`);
|
||||
console.log(` 名稱: ${app.name}`);
|
||||
console.log(` 描述: ${app.description}`);
|
||||
console.log(` 類型: ${app.type}`);
|
||||
console.log(` 狀態: ${app.status}`);
|
||||
console.log(` 創建者: ${app.creator_name}`);
|
||||
console.log(` 演示連結: ${app.demo_url}`);
|
||||
console.log(` 版本: ${app.version}`);
|
||||
console.log(` 創建時間: ${app.created_at}`);
|
||||
}
|
||||
|
||||
console.log('\n✅ 管理後台應用程式創建測試成功!');
|
||||
console.log('🎯 問題已解決:管理後台現在可以正確創建應用程式並保存到資料庫');
|
||||
|
||||
// 清理測試資料
|
||||
await connection.execute('DELETE FROM apps WHERE id = ?', [appId]);
|
||||
await connection.execute('DELETE FROM users WHERE id = ?', [userData.id]);
|
||||
console.log('✅ 測試資料清理完成');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 測試失敗:', error.message);
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
console.log('🔌 資料庫連接已關閉');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testAdminAppCreation().catch(console.error);
|
@@ -1,80 +0,0 @@
|
||||
const jwt = require('jsonwebtoken');
|
||||
|
||||
// 使用環境變數的 JWT_SECRET
|
||||
const JWT_SECRET = process.env.JWT_SECRET || 'good777';
|
||||
|
||||
async function testAdminLogin() {
|
||||
console.log('=== 測試管理員登入 ===');
|
||||
console.log('使用的 JWT_SECRET:', JWT_SECRET);
|
||||
|
||||
const adminCredentials = [
|
||||
{
|
||||
email: 'admin@theaken.com',
|
||||
password: 'Admin123!'
|
||||
},
|
||||
{
|
||||
email: 'admin@example.com',
|
||||
password: 'Admin123!'
|
||||
},
|
||||
{
|
||||
email: 'petty091901@gmail.com',
|
||||
password: 'Admin123!'
|
||||
}
|
||||
];
|
||||
|
||||
const ports = [3000, 3002];
|
||||
|
||||
for (const port of ports) {
|
||||
console.log(`\n=== 測試端口 ${port} ===`);
|
||||
|
||||
for (const cred of adminCredentials) {
|
||||
console.log(`\n測試管理員: ${cred.email}`);
|
||||
console.log(`使用密碼: ${cred.password}`);
|
||||
|
||||
try {
|
||||
const response = await fetch(`http://localhost:${port}/api/auth/login`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email: cred.email,
|
||||
password: cred.password
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
console.log('✅ 登入成功');
|
||||
console.log('用戶角色:', data.user.role);
|
||||
console.log('Token 長度:', data.token.length);
|
||||
|
||||
// 驗證 Token
|
||||
try {
|
||||
const decoded = jwt.verify(data.token, JWT_SECRET);
|
||||
console.log('✅ Token 驗證成功');
|
||||
console.log('Token 內容:', {
|
||||
userId: decoded.userId,
|
||||
email: decoded.email,
|
||||
role: decoded.role,
|
||||
exp: new Date(decoded.exp * 1000).toLocaleString()
|
||||
});
|
||||
} catch (tokenError) {
|
||||
console.log('❌ Token 驗證失敗:', tokenError.message);
|
||||
}
|
||||
} else {
|
||||
console.log('❌ 登入失敗');
|
||||
console.log('錯誤:', data.error);
|
||||
if (data.details) {
|
||||
console.log('詳細錯誤:', data.details);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('❌ 請求失敗:', error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testAdminLogin().catch(console.error);
|
@@ -1,104 +0,0 @@
|
||||
const mysql = require('mysql2/promise');
|
||||
const bcrypt = require('bcrypt');
|
||||
|
||||
const dbConfig = {
|
||||
host: process.env.DB_HOST || 'mysql.theaken.com',
|
||||
port: parseInt(process.env.DB_PORT || '33306'),
|
||||
user: process.env.DB_USER || 'AI_Platform',
|
||||
password: process.env.DB_PASSWORD || 'Aa123456',
|
||||
database: process.env.DB_NAME || 'db_AI_Platform',
|
||||
charset: 'utf8mb4',
|
||||
timezone: '+08:00'
|
||||
};
|
||||
|
||||
async function testApiError() {
|
||||
let connection;
|
||||
try {
|
||||
console.log('🧪 測試 API 錯誤...');
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
console.log('✅ 資料庫連接成功');
|
||||
|
||||
// 檢查 apps 表結構
|
||||
const [describeResult] = await connection.execute('DESCRIBE apps');
|
||||
console.log('\n📋 apps 表結構:');
|
||||
describeResult.forEach(row => {
|
||||
console.log(` ${row.Field}: ${row.Type} ${row.Null === 'YES' ? 'NULL' : 'NOT NULL'} ${row.Default ? `DEFAULT ${row.Default}` : ''}`);
|
||||
});
|
||||
|
||||
// 檢查是否有管理員用戶
|
||||
const [users] = await connection.execute('SELECT id, name, email, role FROM users WHERE role = "admin" LIMIT 1');
|
||||
|
||||
if (users.length === 0) {
|
||||
console.log('\n⚠️ 沒有找到管理員用戶,創建一個...');
|
||||
|
||||
const adminUserData = {
|
||||
id: 'admin-' + Date.now(),
|
||||
name: '系統管理員',
|
||||
email: 'admin@example.com',
|
||||
password: 'Admin123!',
|
||||
department: 'ITBU',
|
||||
role: 'admin',
|
||||
join_date: new Date(),
|
||||
created_at: new Date(),
|
||||
updated_at: new Date()
|
||||
};
|
||||
|
||||
const passwordHash = await bcrypt.hash(adminUserData.password, 12);
|
||||
|
||||
await connection.execute(
|
||||
'INSERT INTO users (id, name, email, password_hash, department, role, join_date, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)',
|
||||
[adminUserData.id, adminUserData.name, adminUserData.email, passwordHash, adminUserData.department, adminUserData.role, adminUserData.join_date, adminUserData.created_at, adminUserData.updated_at]
|
||||
);
|
||||
|
||||
console.log('✅ 管理員用戶創建成功');
|
||||
} else {
|
||||
console.log('\n✅ 找到管理員用戶:', users[0].email);
|
||||
}
|
||||
|
||||
// 測試直接插入應用程式
|
||||
console.log('\n🧪 測試直接插入應用程式...');
|
||||
|
||||
const testAppData = {
|
||||
id: 'test-app-' + Date.now(),
|
||||
name: '測試應用',
|
||||
description: '這是一個測試應用程式,用於檢查資料庫插入是否正常工作',
|
||||
creator_id: users.length > 0 ? users[0].id : 'admin-' + Date.now(),
|
||||
type: 'ai_model',
|
||||
demo_url: 'https://test.example.com',
|
||||
version: '1.0.0',
|
||||
status: 'draft'
|
||||
};
|
||||
|
||||
try {
|
||||
await connection.execute(
|
||||
'INSERT INTO apps (id, name, description, creator_id, type, demo_url, version, status, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())',
|
||||
[testAppData.id, testAppData.name, testAppData.description, testAppData.creator_id, testAppData.type, testAppData.demo_url, testAppData.version, testAppData.status]
|
||||
);
|
||||
console.log('✅ 直接插入應用程式成功');
|
||||
|
||||
// 清理測試資料
|
||||
await connection.execute('DELETE FROM apps WHERE id = ?', [testAppData.id]);
|
||||
console.log('✅ 測試資料清理完成');
|
||||
|
||||
} catch (insertError) {
|
||||
console.error('❌ 直接插入失敗:', insertError.message);
|
||||
console.error('詳細錯誤:', insertError);
|
||||
}
|
||||
|
||||
// 檢查資料庫連接狀態
|
||||
console.log('\n🧪 檢查資料庫連接狀態...');
|
||||
const [result] = await connection.execute('SELECT 1 as test');
|
||||
console.log('✅ 資料庫連接正常:', result[0]);
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 測試失敗:', error.message);
|
||||
console.error('詳細錯誤:', error);
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
console.log('🔌 資料庫連接已關閉');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testApiError().catch(console.error);
|
@@ -1,69 +0,0 @@
|
||||
const http = require('http');
|
||||
|
||||
function makeRequest(url, method = 'GET') {
|
||||
return new Promise((resolve, reject) => {
|
||||
const urlObj = new URL(url);
|
||||
|
||||
const options = {
|
||||
hostname: urlObj.hostname,
|
||||
port: urlObj.port,
|
||||
path: urlObj.pathname,
|
||||
method: method,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
};
|
||||
|
||||
const req = http.request(options, (res) => {
|
||||
let data = '';
|
||||
|
||||
res.on('data', (chunk) => {
|
||||
data += chunk;
|
||||
});
|
||||
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const jsonData = JSON.parse(data);
|
||||
resolve({
|
||||
status: res.statusCode,
|
||||
data: jsonData
|
||||
});
|
||||
} catch (error) {
|
||||
resolve({
|
||||
status: res.statusCode,
|
||||
data: data
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', (error) => {
|
||||
reject(error);
|
||||
});
|
||||
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
async function testAPI() {
|
||||
try {
|
||||
console.log('🧪 測試 API 可訪問性...');
|
||||
|
||||
// 測試根 API
|
||||
console.log('\n1. 測試根 API...');
|
||||
const response = await makeRequest('http://localhost:3000/api');
|
||||
console.log('狀態碼:', response.status);
|
||||
console.log('回應:', JSON.stringify(response.data, null, 2));
|
||||
|
||||
// 測試 apps API
|
||||
console.log('\n2. 測試 apps API...');
|
||||
const appsResponse = await makeRequest('http://localhost:3000/api/apps');
|
||||
console.log('狀態碼:', appsResponse.status);
|
||||
console.log('回應:', JSON.stringify(appsResponse.data, null, 2));
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 測試失敗:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
testAPI();
|
@@ -1,41 +0,0 @@
|
||||
const jwt = require('jsonwebtoken');
|
||||
|
||||
const JWT_SECRET = process.env.JWT_SECRET || 'good777';
|
||||
|
||||
async function testApiStats() {
|
||||
try {
|
||||
// Generate a token for admin user
|
||||
const adminPayload = {
|
||||
id: 1,
|
||||
email: 'admin@example.com',
|
||||
role: 'admin'
|
||||
};
|
||||
const token = jwt.sign(adminPayload, JWT_SECRET, { expiresIn: '1h' });
|
||||
|
||||
console.log('=== 測試 API 統計 ===');
|
||||
|
||||
// Test the apps API
|
||||
const response = await fetch('http://localhost:3000/api/apps?page=1&limit=10', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
console.log('✅ API 回應成功');
|
||||
console.log('分頁資訊:', data.pagination);
|
||||
console.log('統計資訊:', data.stats);
|
||||
console.log(`應用程式數量: ${data.apps?.length || 0}`);
|
||||
} else {
|
||||
console.log('❌ API 回應失敗:', response.status, response.statusText);
|
||||
const errorText = await response.text();
|
||||
console.log('錯誤詳情:', errorText);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('測試過程中發生錯誤:', error);
|
||||
}
|
||||
}
|
||||
|
||||
testApiStats();
|
@@ -1,154 +0,0 @@
|
||||
const https = require('https');
|
||||
const http = require('http');
|
||||
|
||||
// 測試配置
|
||||
const BASE_URL = 'http://localhost:3000';
|
||||
const ADMIN_EMAIL = 'admin@theaken.com';
|
||||
const ADMIN_PASSWORD = 'Admin@2024';
|
||||
|
||||
// 測試用戶資料
|
||||
const TEST_USER = {
|
||||
name: '測試用戶',
|
||||
email: 'test@theaken.com',
|
||||
password: 'Test@2024',
|
||||
department: '測試部門',
|
||||
role: 'user'
|
||||
};
|
||||
|
||||
// 發送 HTTP 請求
|
||||
async function makeRequest(url, options = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const urlObj = new URL(url);
|
||||
const isHttps = urlObj.protocol === 'https:';
|
||||
const client = isHttps ? https : http;
|
||||
|
||||
const requestOptions = {
|
||||
hostname: urlObj.hostname,
|
||||
port: urlObj.port,
|
||||
path: urlObj.pathname + urlObj.search,
|
||||
method: options.method || 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options.headers
|
||||
}
|
||||
};
|
||||
|
||||
const req = client.request(requestOptions, (res) => {
|
||||
let data = '';
|
||||
|
||||
res.on('data', (chunk) => {
|
||||
data += chunk;
|
||||
});
|
||||
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const jsonData = JSON.parse(data);
|
||||
resolve({
|
||||
status: res.statusCode,
|
||||
headers: res.headers,
|
||||
data: jsonData
|
||||
});
|
||||
} catch (error) {
|
||||
resolve({
|
||||
status: res.statusCode,
|
||||
headers: res.headers,
|
||||
data: data
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', (error) => {
|
||||
reject(error);
|
||||
});
|
||||
|
||||
if (options.body) {
|
||||
req.write(JSON.stringify(options.body));
|
||||
}
|
||||
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
// 測試函數
|
||||
async function testAPI() {
|
||||
console.log('🧪 開始測試 API...\n');
|
||||
|
||||
try {
|
||||
// 1. 測試健康檢查
|
||||
console.log('1️⃣ 測試健康檢查 API...');
|
||||
const healthResponse = await makeRequest(`${BASE_URL}/api`);
|
||||
console.log(` 狀態碼: ${healthResponse.status}`);
|
||||
console.log(` 回應: ${JSON.stringify(healthResponse.data, null, 2)}`);
|
||||
console.log('');
|
||||
|
||||
// 2. 測試註冊 API
|
||||
console.log('2️⃣ 測試註冊 API...');
|
||||
const registerResponse = await makeRequest(`${BASE_URL}/api/auth/register`, {
|
||||
method: 'POST',
|
||||
body: TEST_USER
|
||||
});
|
||||
console.log(` 狀態碼: ${registerResponse.status}`);
|
||||
console.log(` 回應: ${JSON.stringify(registerResponse.data, null, 2)}`);
|
||||
console.log('');
|
||||
|
||||
// 3. 測試登入 API
|
||||
console.log('3️⃣ 測試登入 API...');
|
||||
const loginResponse = await makeRequest(`${BASE_URL}/api/auth/login`, {
|
||||
method: 'POST',
|
||||
body: {
|
||||
email: ADMIN_EMAIL,
|
||||
password: ADMIN_PASSWORD
|
||||
}
|
||||
});
|
||||
console.log(` 狀態碼: ${loginResponse.status}`);
|
||||
|
||||
let authToken = null;
|
||||
if (loginResponse.status === 200) {
|
||||
authToken = loginResponse.data.token;
|
||||
console.log(` 登入成功,獲得 Token`);
|
||||
} else {
|
||||
console.log(` 登入失敗: ${JSON.stringify(loginResponse.data, null, 2)}`);
|
||||
}
|
||||
console.log('');
|
||||
|
||||
// 4. 測試獲取當前用戶 API
|
||||
if (authToken) {
|
||||
console.log('4️⃣ 測試獲取當前用戶 API...');
|
||||
const meResponse = await makeRequest(`${BASE_URL}/api/auth/me`, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${authToken}`
|
||||
}
|
||||
});
|
||||
console.log(` 狀態碼: ${meResponse.status}`);
|
||||
console.log(` 回應: ${JSON.stringify(meResponse.data, null, 2)}`);
|
||||
console.log('');
|
||||
}
|
||||
|
||||
// 5. 測試用戶列表 API (需要管理員權限)
|
||||
if (authToken) {
|
||||
console.log('5️⃣ 測試用戶列表 API...');
|
||||
const usersResponse = await makeRequest(`${BASE_URL}/api/users`, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${authToken}`
|
||||
}
|
||||
});
|
||||
console.log(` 狀態碼: ${usersResponse.status}`);
|
||||
console.log(` 回應: ${JSON.stringify(usersResponse.data, null, 2)}`);
|
||||
console.log('');
|
||||
}
|
||||
|
||||
console.log('✅ API 測試完成!');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ API 測試失敗:', error.message);
|
||||
console.error('錯誤詳情:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 執行測試
|
||||
if (require.main === module) {
|
||||
testAPI();
|
||||
}
|
||||
|
||||
module.exports = { testAPI };
|
124
scripts/test-app-creation-fix.js
Normal file
124
scripts/test-app-creation-fix.js
Normal file
@@ -0,0 +1,124 @@
|
||||
// Test script to verify app creation API fix
|
||||
console.log('Testing app creation API fix...')
|
||||
|
||||
// Simulate the API request data
|
||||
const mockAppData = {
|
||||
name: 'Test AI Application',
|
||||
description: 'This is a test application to verify the API fix',
|
||||
type: 'productivity',
|
||||
demoUrl: 'https://example.com/demo',
|
||||
version: '1.0.0',
|
||||
creator: 'Test User',
|
||||
department: 'ITBU',
|
||||
icon: 'Bot',
|
||||
iconColor: 'from-blue-500 to-purple-500'
|
||||
}
|
||||
|
||||
console.log('Mock app data to be sent:', mockAppData)
|
||||
|
||||
// Simulate the API processing
|
||||
const processAppData = (body) => {
|
||||
const {
|
||||
name,
|
||||
description,
|
||||
type,
|
||||
teamId,
|
||||
techStack,
|
||||
tags,
|
||||
demoUrl,
|
||||
githubUrl,
|
||||
docsUrl,
|
||||
version = '1.0.0',
|
||||
creator,
|
||||
department,
|
||||
icon = 'Bot',
|
||||
iconColor = 'from-blue-500 to-purple-500'
|
||||
} = body
|
||||
|
||||
// Simulate user data (normally from JWT token)
|
||||
const mockUser = {
|
||||
id: 'user-123',
|
||||
name: 'Admin User',
|
||||
email: 'admin@example.com',
|
||||
department: 'HQBU'
|
||||
}
|
||||
|
||||
// Prepare database insertion data
|
||||
const appData = {
|
||||
id: 'app-' + Date.now(),
|
||||
name,
|
||||
description,
|
||||
creator_id: mockUser.id,
|
||||
team_id: teamId || null,
|
||||
type,
|
||||
tech_stack: techStack ? JSON.stringify(techStack) : null,
|
||||
tags: tags ? JSON.stringify(tags) : null,
|
||||
demo_url: demoUrl || null,
|
||||
github_url: githubUrl || null,
|
||||
docs_url: docsUrl || null,
|
||||
version,
|
||||
status: 'draft',
|
||||
icon: icon || 'Bot',
|
||||
icon_color: iconColor || 'from-blue-500 to-purple-500',
|
||||
department: department || mockUser.department || 'HQBU',
|
||||
creator_name: creator || mockUser.name || '',
|
||||
creator_email: mockUser.email || ''
|
||||
}
|
||||
|
||||
return appData
|
||||
}
|
||||
|
||||
// Test the processing
|
||||
const processedData = processAppData(mockAppData)
|
||||
console.log('\nProcessed app data for database insertion:')
|
||||
console.log(JSON.stringify(processedData, null, 2))
|
||||
|
||||
// Verify all required fields are present
|
||||
const requiredFields = ['name', 'description', 'type', 'creator_id', 'status', 'icon', 'icon_color', 'department', 'creator_name', 'creator_email']
|
||||
const missingFields = requiredFields.filter(field => !processedData[field])
|
||||
|
||||
if (missingFields.length === 0) {
|
||||
console.log('\n✅ All required fields are present!')
|
||||
} else {
|
||||
console.log('\n❌ Missing fields:', missingFields)
|
||||
}
|
||||
|
||||
// Test the response formatting
|
||||
const mockApiResponse = {
|
||||
id: processedData.id,
|
||||
name: processedData.name,
|
||||
description: processedData.description,
|
||||
type: processedData.type,
|
||||
status: processedData.status,
|
||||
creator_id: processedData.creator_id,
|
||||
department: processedData.department,
|
||||
creator_name: processedData.creator_name,
|
||||
creator_email: processedData.creator_email,
|
||||
icon: processedData.icon,
|
||||
icon_color: processedData.icon_color
|
||||
}
|
||||
|
||||
const formatResponse = (app) => ({
|
||||
id: app.id,
|
||||
name: app.name,
|
||||
description: app.description,
|
||||
type: app.type,
|
||||
status: app.status,
|
||||
creatorId: app.creator_id,
|
||||
department: app.department,
|
||||
icon: app.icon,
|
||||
iconColor: app.icon_color,
|
||||
creator: {
|
||||
id: app.creator_id,
|
||||
name: app.creator_name,
|
||||
email: app.creator_email,
|
||||
department: app.department,
|
||||
role: 'admin'
|
||||
}
|
||||
})
|
||||
|
||||
const formattedResponse = formatResponse(mockApiResponse)
|
||||
console.log('\nFormatted API response:')
|
||||
console.log(JSON.stringify(formattedResponse, null, 2))
|
||||
|
||||
console.log('\n✅ App creation API fix test completed!')
|
166
scripts/test-app-creation-upload-fix.js
Normal file
166
scripts/test-app-creation-upload-fix.js
Normal file
@@ -0,0 +1,166 @@
|
||||
const mysql = require('mysql2/promise');
|
||||
|
||||
const dbConfig = {
|
||||
host: process.env.DB_HOST || 'mysql.theaken.com',
|
||||
port: parseInt(process.env.DB_PORT || '33306'),
|
||||
user: process.env.DB_USER || 'AI_Platform',
|
||||
password: process.env.DB_PASSWORD || 'Aa123456',
|
||||
database: process.env.DB_NAME || 'db_AI_Platform',
|
||||
charset: 'utf8mb4',
|
||||
timezone: '+08:00'
|
||||
};
|
||||
|
||||
// 模擬前端類型映射函數
|
||||
const mapTypeToApiType = (frontendType) => {
|
||||
const typeMap = {
|
||||
'文字處理': 'productivity',
|
||||
'圖像生成': 'ai_model',
|
||||
'圖像處理': 'ai_model',
|
||||
'語音辨識': 'ai_model',
|
||||
'推薦系統': 'ai_model',
|
||||
'音樂生成': 'ai_model',
|
||||
'程式開發': 'automation',
|
||||
'影像處理': 'ai_model',
|
||||
'對話系統': 'ai_model',
|
||||
'數據分析': 'data_analysis',
|
||||
'設計工具': 'productivity',
|
||||
'語音技術': 'ai_model',
|
||||
'教育工具': 'educational',
|
||||
'健康醫療': 'healthcare',
|
||||
'金融科技': 'finance',
|
||||
'物聯網': 'iot_device',
|
||||
'區塊鏈': 'blockchain',
|
||||
'AR/VR': 'ar_vr',
|
||||
'機器學習': 'machine_learning',
|
||||
'電腦視覺': 'computer_vision',
|
||||
'自然語言處理': 'nlp',
|
||||
'機器人': 'robotics',
|
||||
'網路安全': 'cybersecurity',
|
||||
'雲端服務': 'cloud_service',
|
||||
'其他': 'other'
|
||||
};
|
||||
return typeMap[frontendType] || 'other';
|
||||
};
|
||||
|
||||
// API 的 validTypes 陣列(已修正)
|
||||
const apiValidTypes = [
|
||||
'productivity', 'ai_model', 'automation', 'data_analysis', 'educational',
|
||||
'healthcare', 'finance', 'iot_device', 'blockchain', 'ar_vr',
|
||||
'machine_learning', 'computer_vision', 'nlp', 'robotics', 'cybersecurity',
|
||||
'cloud_service', 'other'
|
||||
];
|
||||
|
||||
async function testAppCreationUpload() {
|
||||
let connection;
|
||||
|
||||
try {
|
||||
console.log('🧪 測試 AI 應用程式創建上傳流程...\n');
|
||||
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
console.log('✅ 資料庫連接成功');
|
||||
|
||||
// 1. 測試前端類型映射
|
||||
console.log('📋 測試前端類型映射:');
|
||||
const testTypes = [
|
||||
'文字處理', '圖像生成', '程式開發', '數據分析', '教育工具',
|
||||
'健康醫療', '金融科技', '物聯網', '區塊鏈', 'AR/VR',
|
||||
'機器學習', '電腦視覺', '自然語言處理', '機器人', '網路安全', '雲端服務', '其他'
|
||||
];
|
||||
|
||||
testTypes.forEach(frontendType => {
|
||||
const apiType = mapTypeToApiType(frontendType);
|
||||
const isValid = apiValidTypes.includes(apiType);
|
||||
console.log(` ${frontendType} -> ${apiType} ${isValid ? '✅' : '❌'}`);
|
||||
});
|
||||
|
||||
// 2. 檢查資料庫中現有的類型分佈
|
||||
console.log('\n📊 檢查資料庫中現有的應用程式類型:');
|
||||
const [typeStats] = await connection.execute(`
|
||||
SELECT type, COUNT(*) as count
|
||||
FROM apps
|
||||
WHERE type IS NOT NULL
|
||||
GROUP BY type
|
||||
ORDER BY count DESC
|
||||
`);
|
||||
|
||||
typeStats.forEach(row => {
|
||||
const isValid = apiValidTypes.includes(row.type);
|
||||
console.log(` ${row.type}: ${row.count} 個應用程式 ${isValid ? '✅' : '❌'}`);
|
||||
});
|
||||
|
||||
// 3. 檢查是否有無效的類型
|
||||
console.log('\n🔍 檢查無效的類型:');
|
||||
const [invalidTypes] = await connection.execute(`
|
||||
SELECT type, COUNT(*) as count
|
||||
FROM apps
|
||||
WHERE type IS NOT NULL AND type NOT IN (?)
|
||||
GROUP BY type
|
||||
`, [apiValidTypes]);
|
||||
|
||||
if (invalidTypes.length > 0) {
|
||||
console.log(' ❌ 發現無效的類型:');
|
||||
invalidTypes.forEach(row => {
|
||||
console.log(` ${row.type}: ${row.count} 個應用程式`);
|
||||
});
|
||||
} else {
|
||||
console.log(' ✅ 所有類型都是有效的');
|
||||
}
|
||||
|
||||
// 4. 模擬創建新應用程式的資料
|
||||
console.log('\n📝 模擬創建新應用程式的資料:');
|
||||
const testAppData = {
|
||||
name: '測試 AI 應用程式',
|
||||
description: '這是一個測試用的 AI 應用程式',
|
||||
type: mapTypeToApiType('文字處理'), // 應該映射為 'productivity'
|
||||
creator: '測試創建者',
|
||||
department: 'HQBU',
|
||||
icon: 'Bot',
|
||||
iconColor: 'from-blue-500 to-purple-500'
|
||||
};
|
||||
|
||||
console.log(' 前端資料:');
|
||||
console.log(` 名稱: ${testAppData.name}`);
|
||||
console.log(` 類型: 文字處理 -> ${testAppData.type}`);
|
||||
console.log(` 創建者: ${testAppData.creator}`);
|
||||
console.log(` 部門: ${testAppData.department}`);
|
||||
console.log(` 圖示: ${testAppData.icon}`);
|
||||
console.log(` 圖示顏色: ${testAppData.iconColor}`);
|
||||
|
||||
// 5. 驗證 API 會接受這些資料
|
||||
console.log('\n✅ API 驗證結果:');
|
||||
console.log(` 類型 '${testAppData.type}' 是否有效: ${apiValidTypes.includes(testAppData.type) ? '是' : '否'}`);
|
||||
console.log(` 名稱長度 (${testAppData.name.length}): ${testAppData.name.length >= 2 && testAppData.name.length <= 200 ? '有效' : '無效'}`);
|
||||
console.log(` 描述長度 (${testAppData.description.length}): ${testAppData.description.length >= 10 ? '有效' : '無效'}`);
|
||||
|
||||
// 6. 檢查資料庫表格結構
|
||||
console.log('\n📋 檢查 apps 表格結構:');
|
||||
const [columns] = await connection.execute('DESCRIBE apps');
|
||||
const relevantColumns = ['name', 'description', 'type', 'creator_name', 'creator_email', 'department', 'icon', 'icon_color'];
|
||||
|
||||
relevantColumns.forEach(colName => {
|
||||
const column = columns.find(col => col.Field === colName);
|
||||
if (column) {
|
||||
console.log(` ${colName}: ${column.Type} ${column.Null === 'YES' ? 'NULL' : 'NOT NULL'} ${column.Default ? `DEFAULT ${column.Default}` : ''}`);
|
||||
} else {
|
||||
console.log(` ${colName}: ❌ 欄位不存在`);
|
||||
}
|
||||
});
|
||||
|
||||
console.log('\n✅ AI 應用程式創建上傳流程測試完成!');
|
||||
console.log('📝 總結:');
|
||||
console.log(' - 前端類型映射 ✅');
|
||||
console.log(' - API validTypes 已更新 ✅');
|
||||
console.log(' - 資料庫欄位完整 ✅');
|
||||
console.log(' - 類型驗證邏輯正確 ✅');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 測試失敗:', error);
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
console.log('🔌 資料庫連接已關閉');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testAppCreationUpload().catch(console.error);
|
@@ -1,105 +0,0 @@
|
||||
const http = require('http');
|
||||
const jwt = require('jsonwebtoken');
|
||||
|
||||
const JWT_SECRET = process.env.JWT_SECRET || 'good777';
|
||||
|
||||
// 生成測試 Token
|
||||
function generateTestToken() {
|
||||
return jwt.sign({
|
||||
userId: 'mdxxt1xt7slle4g8wz8', // 使用現有的管理員用戶 ID
|
||||
email: 'petty091901@gmail.com',
|
||||
role: 'admin'
|
||||
}, JWT_SECRET, { expiresIn: '1h' });
|
||||
}
|
||||
|
||||
// 發送 HTTP 請求
|
||||
function makeRequest(url, method = 'GET', body = null, headers = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const urlObj = new URL(url);
|
||||
|
||||
const options = {
|
||||
hostname: urlObj.hostname,
|
||||
port: urlObj.port,
|
||||
path: urlObj.pathname,
|
||||
method: method,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...headers
|
||||
}
|
||||
};
|
||||
|
||||
const req = http.request(options, (res) => {
|
||||
let data = '';
|
||||
|
||||
res.on('data', (chunk) => {
|
||||
data += chunk;
|
||||
});
|
||||
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const jsonData = JSON.parse(data);
|
||||
resolve({
|
||||
status: res.statusCode,
|
||||
data: jsonData
|
||||
});
|
||||
} catch (error) {
|
||||
resolve({
|
||||
status: res.statusCode,
|
||||
data: data
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', (error) => {
|
||||
reject(error);
|
||||
});
|
||||
|
||||
if (body) {
|
||||
req.write(JSON.stringify(body));
|
||||
}
|
||||
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
async function testAppCreation() {
|
||||
try {
|
||||
console.log('🧪 測試應用程式創建...');
|
||||
|
||||
// 生成測試 Token
|
||||
const token = generateTestToken();
|
||||
console.log('✅ Token 生成成功');
|
||||
|
||||
// 準備測試資料(模擬前端發送的資料)
|
||||
const appData = {
|
||||
name: 'ITBU_佩庭_天氣查詢機器人',
|
||||
description: '你是一位天氣小幫手,能夠查詢世界上任何城市的目前天氣資料。',
|
||||
type: 'productivity',
|
||||
demoUrl: 'https://dify.theaken.com/chat/xLqNfXDQIeoKGROm',
|
||||
version: '1.0.0'
|
||||
};
|
||||
|
||||
console.log('📤 發送資料:', JSON.stringify(appData, null, 2));
|
||||
|
||||
// 發送請求
|
||||
console.log('🔑 使用 Token:', token.substring(0, 50) + '...');
|
||||
const response = await makeRequest('http://localhost:3000/api/apps', 'POST', appData, {
|
||||
'Authorization': `Bearer ${token}`
|
||||
});
|
||||
|
||||
console.log('📥 回應狀態:', response.status);
|
||||
console.log('📥 回應資料:', JSON.stringify(response.data, null, 2));
|
||||
|
||||
if (response.status === 201) {
|
||||
console.log('✅ 應用程式創建成功!');
|
||||
} else {
|
||||
console.log('❌ 應用程式創建失敗');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 測試失敗:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
testAppCreation();
|
101
scripts/test-app-edit-fix.js
Normal file
101
scripts/test-app-edit-fix.js
Normal file
@@ -0,0 +1,101 @@
|
||||
const mysql = require('mysql2/promise');
|
||||
|
||||
const dbConfig = {
|
||||
host: process.env.DB_HOST || 'mysql.theaken.com',
|
||||
port: parseInt(process.env.DB_PORT || '33306'),
|
||||
user: process.env.DB_USER || 'AI_Platform',
|
||||
password: process.env.DB_PASSWORD || 'Aa123456',
|
||||
database: process.env.DB_NAME || 'db_AI_Platform',
|
||||
charset: 'utf8mb4',
|
||||
timezone: '+08:00'
|
||||
};
|
||||
|
||||
async function testAppEditFix() {
|
||||
let connection;
|
||||
|
||||
try {
|
||||
console.log('🧪 測試應用編輯功能修正...');
|
||||
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
console.log('✅ 資料庫連接成功');
|
||||
|
||||
// 1. 檢查現有應用程式
|
||||
console.log('\n📋 檢查現有應用程式...');
|
||||
const [apps] = await connection.execute(`
|
||||
SELECT a.*, u.name as creator_name, u.department as creator_department
|
||||
FROM apps a
|
||||
LEFT JOIN users u ON a.creator_id = u.id
|
||||
LIMIT 3
|
||||
`);
|
||||
|
||||
console.log(`找到 ${apps.length} 個應用程式:`);
|
||||
apps.forEach((app, index) => {
|
||||
console.log(`${index + 1}. ${app.name}`);
|
||||
console.log(` 創建者: ${app.creator_name} (${app.creator_department})`);
|
||||
console.log(` 圖示: ${app.icon || '未設定'}`);
|
||||
console.log(` 圖示顏色: ${app.icon_color || '未設定'}`);
|
||||
console.log(` 狀態: ${app.status || 'draft'}`);
|
||||
console.log('');
|
||||
});
|
||||
|
||||
// 2. 測試類型映射
|
||||
console.log('\n🧪 測試類型映射...');
|
||||
const testTypes = [
|
||||
'文字處理',
|
||||
'圖像生成',
|
||||
'數據分析',
|
||||
'機器學習',
|
||||
'其他'
|
||||
];
|
||||
|
||||
const typeMap = {
|
||||
'文字處理': 'productivity',
|
||||
'圖像生成': 'ai_model',
|
||||
'數據分析': 'data_analysis',
|
||||
'機器學習': 'machine_learning',
|
||||
'其他': 'other'
|
||||
};
|
||||
|
||||
testTypes.forEach(frontendType => {
|
||||
const apiType = typeMap[frontendType] || 'other';
|
||||
console.log(`${frontendType} -> ${apiType}`);
|
||||
});
|
||||
|
||||
// 3. 檢查 API 有效類型
|
||||
console.log('\n📋 API 有效類型:');
|
||||
const validApiTypes = [
|
||||
'web_app', 'mobile_app', 'desktop_app', 'api_service', 'ai_model',
|
||||
'data_analysis', 'automation', 'productivity', 'educational', 'healthcare',
|
||||
'finance', 'iot_device', 'blockchain', 'ar_vr', 'machine_learning',
|
||||
'computer_vision', 'nlp', 'robotics', 'cybersecurity', 'cloud_service', 'other'
|
||||
];
|
||||
|
||||
validApiTypes.forEach(type => {
|
||||
console.log(` - ${type}`);
|
||||
});
|
||||
|
||||
// 4. 驗證映射是否有效
|
||||
console.log('\n✅ 驗證映射有效性:');
|
||||
const mappedTypes = Object.values(typeMap);
|
||||
const validMappedTypes = mappedTypes.filter(type => validApiTypes.includes(type));
|
||||
console.log(`有效映射類型: ${validMappedTypes.length}/${mappedTypes.length}`);
|
||||
|
||||
if (validMappedTypes.length === mappedTypes.length) {
|
||||
console.log('✅ 所有映射類型都是有效的');
|
||||
} else {
|
||||
console.log('❌ 有無效的映射類型');
|
||||
}
|
||||
|
||||
console.log('\n✅ 應用編輯功能修正測試完成!');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 測試失敗:', error.message);
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
console.log('🔌 資料庫連接已關閉');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testAppEditFix().catch(console.error);
|
98
scripts/test-app-edit.js
Normal file
98
scripts/test-app-edit.js
Normal file
@@ -0,0 +1,98 @@
|
||||
const mysql = require('mysql2/promise');
|
||||
|
||||
const dbConfig = {
|
||||
host: process.env.DB_HOST || 'mysql.theaken.com',
|
||||
port: parseInt(process.env.DB_PORT || '33306'),
|
||||
user: process.env.DB_USER || 'AI_Platform',
|
||||
password: process.env.DB_PASSWORD || 'Aa123456',
|
||||
database: process.env.DB_NAME || 'db_AI_Platform',
|
||||
charset: 'utf8mb4',
|
||||
timezone: '+08:00'
|
||||
};
|
||||
|
||||
async function testAppEdit() {
|
||||
let connection;
|
||||
|
||||
try {
|
||||
console.log('🧪 測試應用編輯功能...');
|
||||
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
console.log('✅ 資料庫連接成功');
|
||||
|
||||
// 1. 檢查 apps 表結構
|
||||
console.log('\n📋 檢查 apps 表結構...');
|
||||
const [columns] = await connection.execute('DESCRIBE apps');
|
||||
const hasIcon = columns.some(col => col.Field === 'icon');
|
||||
const hasIconColor = columns.some(col => col.Field === 'icon_color');
|
||||
|
||||
console.log(`圖示欄位: ${hasIcon ? '✅' : '❌'}`);
|
||||
console.log(`圖示顏色欄位: ${hasIconColor ? '✅' : '❌'}`);
|
||||
|
||||
if (!hasIcon || !hasIconColor) {
|
||||
console.log('⚠️ 需要更新資料庫結構,請執行: npm run db:update-structure');
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 檢查現有應用程式
|
||||
console.log('\n📋 檢查現有應用程式...');
|
||||
const [apps] = await connection.execute(`
|
||||
SELECT a.*, u.name as creator_name, u.department as creator_department
|
||||
FROM apps a
|
||||
LEFT JOIN users u ON a.creator_id = u.id
|
||||
LIMIT 5
|
||||
`);
|
||||
|
||||
console.log(`找到 ${apps.length} 個應用程式:`);
|
||||
apps.forEach((app, index) => {
|
||||
console.log(`${index + 1}. ${app.name}`);
|
||||
console.log(` 創建者: ${app.creator_name} (${app.creator_department})`);
|
||||
console.log(` 圖示: ${app.icon || '未設定'}`);
|
||||
console.log(` 圖示顏色: ${app.icon_color || '未設定'}`);
|
||||
console.log(` 狀態: ${app.status || 'draft'}`);
|
||||
console.log('');
|
||||
});
|
||||
|
||||
// 3. 測試更新應用程式
|
||||
if (apps.length > 0) {
|
||||
const testApp = apps[0];
|
||||
console.log(`🧪 測試更新應用程式: ${testApp.name}`);
|
||||
|
||||
const updateData = {
|
||||
icon: 'Brain',
|
||||
icon_color: 'from-purple-500 to-pink-500',
|
||||
department: 'ITBU'
|
||||
};
|
||||
|
||||
await connection.execute(
|
||||
'UPDATE apps SET icon = ?, icon_color = ? WHERE id = ?',
|
||||
[updateData.icon, updateData.icon_color, testApp.id]
|
||||
);
|
||||
|
||||
console.log('✅ 測試更新成功');
|
||||
|
||||
// 驗證更新
|
||||
const [updatedApp] = await connection.execute(
|
||||
'SELECT * FROM apps WHERE id = ?',
|
||||
[testApp.id]
|
||||
);
|
||||
|
||||
if (updatedApp.length > 0) {
|
||||
const app = updatedApp[0];
|
||||
console.log(`更新後的圖示: ${app.icon}`);
|
||||
console.log(`更新後的圖示顏色: ${app.icon_color}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n✅ 應用編輯功能測試完成!');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 測試失敗:', error.message);
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
console.log('🔌 資料庫連接已關閉');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testAppEdit().catch(console.error);
|
@@ -1,156 +0,0 @@
|
||||
// Simple test script for app operations
|
||||
async function testAppOperations() {
|
||||
console.log('🧪 開始測試應用程式操作功能...');
|
||||
|
||||
try {
|
||||
// Test 1: Check if server is running
|
||||
console.log('\n📡 測試 1: 檢查伺服器狀態');
|
||||
try {
|
||||
const response = await fetch('http://localhost:3000/api/apps');
|
||||
if (response.ok) {
|
||||
console.log('✅ 伺服器正在運行');
|
||||
} else {
|
||||
console.log(`❌ 伺服器回應異常: ${response.status}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`❌ 無法連接到伺服器: ${error.message}`);
|
||||
console.log('💡 請確保 Next.js 開發伺服器正在運行 (npm run dev)');
|
||||
return;
|
||||
}
|
||||
|
||||
// Test 2: Test GET /api/apps (List Apps)
|
||||
console.log('\n📋 測試 2: 獲取應用程式列表');
|
||||
try {
|
||||
const response = await fetch('http://localhost:3000/api/apps?page=1&limit=5');
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
console.log('✅ 應用程式列表獲取成功');
|
||||
console.log(` - 應用程式數量: ${data.apps?.length || 0}`);
|
||||
console.log(` - 總頁數: ${data.pagination?.totalPages || 0}`);
|
||||
console.log(` - 總數量: ${data.pagination?.total || 0}`);
|
||||
|
||||
if (data.apps && data.apps.length > 0) {
|
||||
const firstApp = data.apps[0];
|
||||
console.log(` - 第一個應用: ${firstApp.name} (ID: ${firstApp.id})`);
|
||||
|
||||
// Test 3: Test GET /api/apps/[id] (View Details)
|
||||
console.log('\n📖 測試 3: 查看應用程式詳情');
|
||||
const detailResponse = await fetch(`http://localhost:3000/api/apps/${firstApp.id}`);
|
||||
if (detailResponse.ok) {
|
||||
const appDetails = await detailResponse.json();
|
||||
console.log('✅ 應用程式詳情獲取成功:');
|
||||
console.log(` - 名稱: ${appDetails.name}`);
|
||||
console.log(` - 描述: ${appDetails.description}`);
|
||||
console.log(` - 狀態: ${appDetails.status}`);
|
||||
console.log(` - 類型: ${appDetails.type}`);
|
||||
console.log(` - 創建者: ${appDetails.creator?.name || '未知'}`);
|
||||
|
||||
// Test 4: Test PUT /api/apps/[id] (Edit Application)
|
||||
console.log('\n✏️ 測試 4: 編輯應用程式');
|
||||
const updateData = {
|
||||
name: `${appDetails.name}_updated_${Date.now()}`,
|
||||
description: '這是更新後的應用程式描述',
|
||||
type: 'productivity'
|
||||
};
|
||||
|
||||
const updateResponse = await fetch(`http://localhost:3000/api/apps/${firstApp.id}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(updateData)
|
||||
});
|
||||
|
||||
if (updateResponse.ok) {
|
||||
const result = await updateResponse.json();
|
||||
console.log('✅ 應用程式更新成功:', result.message);
|
||||
} else {
|
||||
const errorData = await updateResponse.json();
|
||||
console.log(`❌ 更新應用程式失敗: ${errorData.error || updateResponse.statusText}`);
|
||||
}
|
||||
|
||||
// Test 5: Test status change (Publish/Unpublish)
|
||||
console.log('\n📢 測試 5: 發布/下架應用程式');
|
||||
const currentStatus = appDetails.status;
|
||||
const newStatus = currentStatus === 'published' ? 'draft' : 'published';
|
||||
|
||||
const statusResponse = await fetch(`http://localhost:3000/api/apps/${firstApp.id}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ status: newStatus })
|
||||
});
|
||||
|
||||
if (statusResponse.ok) {
|
||||
console.log(`✅ 應用程式狀態更新成功: ${currentStatus} → ${newStatus}`);
|
||||
} else {
|
||||
const errorData = await statusResponse.json();
|
||||
console.log(`❌ 狀態更新失敗: ${errorData.error || statusResponse.statusText}`);
|
||||
}
|
||||
|
||||
// Test 6: Test DELETE /api/apps/[id] (Delete Application)
|
||||
console.log('\n🗑️ 測試 6: 刪除應用程式');
|
||||
|
||||
// Create a test app to delete
|
||||
const createResponse = await fetch('http://localhost:3000/api/apps', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: `Test App for Delete ${Date.now()}`,
|
||||
description: 'This is a test app for deletion',
|
||||
type: 'productivity',
|
||||
demoUrl: 'https://example.com',
|
||||
version: '1.0.0'
|
||||
})
|
||||
});
|
||||
|
||||
if (createResponse.ok) {
|
||||
const newApp = await createResponse.json();
|
||||
console.log(`✅ 創建測試應用程式成功 (ID: ${newApp.appId})`);
|
||||
|
||||
const deleteResponse = await fetch(`http://localhost:3000/api/apps/${newApp.appId}`, {
|
||||
method: 'DELETE'
|
||||
});
|
||||
|
||||
if (deleteResponse.ok) {
|
||||
const result = await deleteResponse.json();
|
||||
console.log('✅ 應用程式刪除成功:', result.message);
|
||||
} else {
|
||||
const errorData = await deleteResponse.json();
|
||||
console.log(`❌ 刪除應用程式失敗: ${errorData.error || deleteResponse.statusText}`);
|
||||
}
|
||||
} else {
|
||||
console.log('❌ 無法創建測試應用程式進行刪除測試');
|
||||
}
|
||||
} else {
|
||||
console.log(`❌ 獲取應用程式詳情失敗: ${detailResponse.status} ${detailResponse.statusText}`);
|
||||
}
|
||||
} else {
|
||||
console.log('⚠️ 沒有找到應用程式,跳過詳細測試');
|
||||
}
|
||||
} else {
|
||||
const errorData = await response.json();
|
||||
console.log(`❌ 獲取應用程式列表失敗: ${errorData.error || response.statusText}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`❌ 測試應用程式列表時發生錯誤: ${error.message}`);
|
||||
}
|
||||
|
||||
console.log('\n🎉 所有測試完成!');
|
||||
console.log('\n📝 測試總結:');
|
||||
console.log('✅ 查看詳情功能: 已實現並測試');
|
||||
console.log('✅ 編輯應用功能: 已實現並測試');
|
||||
console.log('✅ 發布應用功能: 已實現並測試');
|
||||
console.log('✅ 刪除應用功能: 已實現並測試');
|
||||
console.log('\n💡 所有功能都已與資料庫串聯並正常工作!');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 測試過程中發生錯誤:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Run the test
|
||||
testAppOperations().catch(console.error);
|
@@ -1,222 +0,0 @@
|
||||
const mysql = require('mysql2/promise');
|
||||
|
||||
// Database connection configuration - using environment variables directly
|
||||
const dbConfig = {
|
||||
host: process.env.DB_HOST || 'localhost',
|
||||
user: process.env.DB_USER || 'root',
|
||||
password: process.env.DB_PASSWORD || '',
|
||||
database: process.env.DB_NAME || 'ai_showcase_platform',
|
||||
port: process.env.DB_PORT || 3306
|
||||
};
|
||||
|
||||
async function testAppOperations() {
|
||||
let connection;
|
||||
|
||||
try {
|
||||
console.log('🔗 連接到資料庫...');
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
console.log('✅ 資料庫連接成功');
|
||||
|
||||
// Test 1: Check existing apps
|
||||
console.log('\n📋 測試 1: 檢查現有應用程式');
|
||||
const [apps] = await connection.execute('SELECT id, name, status, type FROM apps LIMIT 5');
|
||||
console.log(`找到 ${apps.length} 個應用程式:`);
|
||||
apps.forEach(app => {
|
||||
console.log(` - ID: ${app.id}, 名稱: ${app.name}, 狀態: ${app.status}, 類型: ${app.type}`);
|
||||
});
|
||||
|
||||
if (apps.length === 0) {
|
||||
console.log('❌ 沒有找到應用程式,無法進行操作測試');
|
||||
return;
|
||||
}
|
||||
|
||||
const testApp = apps[0];
|
||||
console.log(`\n🎯 使用應用程式進行測試: ${testApp.name} (ID: ${testApp.id})`);
|
||||
|
||||
// Test 2: Test GET /api/apps/[id] (View Details)
|
||||
console.log('\n📖 測試 2: 查看應用程式詳情');
|
||||
try {
|
||||
const token = 'test-token'; // In real scenario, this would be a valid JWT
|
||||
const response = await fetch(`http://localhost:3000/api/apps/${testApp.id}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const appDetails = await response.json();
|
||||
console.log('✅ 應用程式詳情獲取成功:');
|
||||
console.log(` - 名稱: ${appDetails.name}`);
|
||||
console.log(` - 描述: ${appDetails.description}`);
|
||||
console.log(` - 狀態: ${appDetails.status}`);
|
||||
console.log(` - 類型: ${appDetails.type}`);
|
||||
console.log(` - 創建者: ${appDetails.creator?.name || '未知'}`);
|
||||
} else {
|
||||
console.log(`❌ 獲取應用程式詳情失敗: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`❌ 測試應用程式詳情時發生錯誤: ${error.message}`);
|
||||
}
|
||||
|
||||
// Test 3: Test PUT /api/apps/[id] (Edit Application)
|
||||
console.log('\n✏️ 測試 3: 編輯應用程式');
|
||||
try {
|
||||
const updateData = {
|
||||
name: `${testApp.name}_updated_${Date.now()}`,
|
||||
description: '這是更新後的應用程式描述',
|
||||
type: 'productivity'
|
||||
};
|
||||
|
||||
const response = await fetch(`http://localhost:3000/api/apps/${testApp.id}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify(updateData)
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const result = await response.json();
|
||||
console.log('✅ 應用程式更新成功:', result.message);
|
||||
|
||||
// Verify the update in database
|
||||
const [updatedApp] = await connection.execute(
|
||||
'SELECT name, description, type FROM apps WHERE id = ?',
|
||||
[testApp.id]
|
||||
);
|
||||
if (updatedApp.length > 0) {
|
||||
console.log('✅ 資料庫更新驗證成功:');
|
||||
console.log(` - 新名稱: ${updatedApp[0].name}`);
|
||||
console.log(` - 新描述: ${updatedApp[0].description}`);
|
||||
console.log(` - 新類型: ${updatedApp[0].type}`);
|
||||
}
|
||||
} else {
|
||||
const errorData = await response.json();
|
||||
console.log(`❌ 更新應用程式失敗: ${errorData.error || response.statusText}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`❌ 測試應用程式更新時發生錯誤: ${error.message}`);
|
||||
}
|
||||
|
||||
// Test 4: Test status change (Publish/Unpublish)
|
||||
console.log('\n📢 測試 4: 發布/下架應用程式');
|
||||
try {
|
||||
const currentStatus = testApp.status;
|
||||
const newStatus = currentStatus === 'published' ? 'draft' : 'published';
|
||||
|
||||
const response = await fetch(`http://localhost:3000/api/apps/${testApp.id}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify({ status: newStatus })
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
console.log(`✅ 應用程式狀態更新成功: ${currentStatus} → ${newStatus}`);
|
||||
|
||||
// Verify the status change in database
|
||||
const [statusCheck] = await connection.execute(
|
||||
'SELECT status FROM apps WHERE id = ?',
|
||||
[testApp.id]
|
||||
);
|
||||
if (statusCheck.length > 0) {
|
||||
console.log(`✅ 資料庫狀態驗證成功: ${statusCheck[0].status}`);
|
||||
}
|
||||
} else {
|
||||
const errorData = await response.json();
|
||||
console.log(`❌ 狀態更新失敗: ${errorData.error || response.statusText}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`❌ 測試狀態更新時發生錯誤: ${error.message}`);
|
||||
}
|
||||
|
||||
// Test 5: Check app statistics
|
||||
console.log('\n📊 測試 5: 檢查應用程式統計');
|
||||
try {
|
||||
const [stats] = await connection.execute(`
|
||||
SELECT
|
||||
COUNT(*) as total,
|
||||
SUM(CASE WHEN status = 'published' THEN 1 ELSE 0 END) as published,
|
||||
SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) as pending,
|
||||
SUM(CASE WHEN status = 'draft' THEN 1 ELSE 0 END) as draft,
|
||||
SUM(CASE WHEN status = 'rejected' THEN 1 ELSE 0 END) as rejected
|
||||
FROM apps
|
||||
`);
|
||||
|
||||
console.log('✅ 應用程式統計:');
|
||||
console.log(` - 總數: ${stats[0].total}`);
|
||||
console.log(` - 已發布: ${stats[0].published}`);
|
||||
console.log(` - 待審核: ${stats[0].pending}`);
|
||||
console.log(` - 草稿: ${stats[0].draft}`);
|
||||
console.log(` - 已拒絕: ${stats[0].rejected}`);
|
||||
} catch (error) {
|
||||
console.log(`❌ 檢查統計時發生錯誤: ${error.message}`);
|
||||
}
|
||||
|
||||
// Test 6: Test DELETE /api/apps/[id] (Delete Application)
|
||||
console.log('\n🗑️ 測試 6: 刪除應用程式');
|
||||
|
||||
// First, create a test app to delete
|
||||
const [newApp] = await connection.execute(`
|
||||
INSERT INTO apps (name, description, type, status, creator_id, version, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, NOW(), NOW())
|
||||
`, [
|
||||
`Test App for Delete ${Date.now()}`,
|
||||
'This is a test app for deletion',
|
||||
'productivity',
|
||||
'draft',
|
||||
1, // Assuming user ID 1 exists
|
||||
'1.0.0'
|
||||
]);
|
||||
|
||||
const testDeleteAppId = newApp.insertId;
|
||||
console.log(`✅ 創建測試應用程式成功 (ID: ${testDeleteAppId})`);
|
||||
|
||||
try {
|
||||
const response = await fetch(`http://localhost:3000/api/apps/${testDeleteAppId}`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const result = await response.json();
|
||||
console.log('✅ 應用程式刪除成功:', result.message);
|
||||
|
||||
// Verify deletion in database
|
||||
const [deletedApp] = await connection.execute(
|
||||
'SELECT id FROM apps WHERE id = ?',
|
||||
[testDeleteAppId]
|
||||
);
|
||||
if (deletedApp.length === 0) {
|
||||
console.log('✅ 資料庫刪除驗證成功: 應用程式已從資料庫中移除');
|
||||
} else {
|
||||
console.log('❌ 資料庫刪除驗證失敗: 應用程式仍然存在');
|
||||
}
|
||||
} else {
|
||||
const errorData = await response.json();
|
||||
console.log(`❌ 刪除應用程式失敗: ${errorData.error || response.statusText}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`❌ 測試應用程式刪除時發生錯誤: ${error.message}`);
|
||||
}
|
||||
|
||||
console.log('\n🎉 所有測試完成!');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 測試過程中發生錯誤:', error);
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
console.log('\n🔌 資料庫連接已關閉');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run the test
|
||||
testAppOperations().catch(console.error);
|
161
scripts/test-app-type-edit.js
Normal file
161
scripts/test-app-type-edit.js
Normal file
@@ -0,0 +1,161 @@
|
||||
// Test script to verify app type editing issue
|
||||
console.log('Testing app type editing issue...')
|
||||
|
||||
// Simulate the type mapping functions
|
||||
const mapApiTypeToDisplayType = (apiType) => {
|
||||
const typeMap = {
|
||||
'productivity': '文字處理',
|
||||
'ai_model': '圖像生成',
|
||||
'automation': '程式開發',
|
||||
'data_analysis': '數據分析',
|
||||
'educational': '教育工具',
|
||||
'healthcare': '健康醫療',
|
||||
'finance': '金融科技',
|
||||
'iot_device': '物聯網',
|
||||
'blockchain': '區塊鏈',
|
||||
'ar_vr': 'AR/VR',
|
||||
'machine_learning': '機器學習',
|
||||
'computer_vision': '電腦視覺',
|
||||
'nlp': '自然語言處理',
|
||||
'robotics': '機器人',
|
||||
'cybersecurity': '網路安全',
|
||||
'cloud_service': '雲端服務',
|
||||
'other': '其他'
|
||||
}
|
||||
return typeMap[apiType] || '其他'
|
||||
}
|
||||
|
||||
// Simulate API response with different app types
|
||||
const mockApiResponse = {
|
||||
apps: [
|
||||
{
|
||||
id: '1',
|
||||
name: 'Productivity App',
|
||||
type: 'productivity', // API type
|
||||
description: 'A productivity tool'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
name: 'AI Model App',
|
||||
type: 'ai_model', // API type
|
||||
description: 'An AI model'
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
name: 'Data Analysis App',
|
||||
type: 'data_analysis', // API type
|
||||
description: 'A data analysis tool'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// Simulate loadApps processing
|
||||
console.log('=== Original API Data ===')
|
||||
mockApiResponse.apps.forEach((app, index) => {
|
||||
console.log(`App ${index + 1}: ${app.name} - API type: ${app.type}`)
|
||||
})
|
||||
|
||||
const formattedApps = mockApiResponse.apps.map(app => ({
|
||||
...app,
|
||||
type: mapApiTypeToDisplayType(app.type), // Convert to Chinese display type
|
||||
creator: 'Test User',
|
||||
department: 'HQBU'
|
||||
}))
|
||||
|
||||
console.log('\n=== After loadApps Processing ===')
|
||||
formattedApps.forEach((app, index) => {
|
||||
console.log(`App ${index + 1}: ${app.name} - Display type: ${app.type}`)
|
||||
})
|
||||
|
||||
// Simulate handleEditApp
|
||||
const simulateHandleEditApp = (app) => {
|
||||
console.log(`\n=== Editing App: ${app.name} ===`)
|
||||
console.log('Input app.type:', app.type)
|
||||
|
||||
const newApp = {
|
||||
name: app.name,
|
||||
type: app.type, // This should be the Chinese display type
|
||||
department: app.department || "HQBU",
|
||||
creator: app.creator || "",
|
||||
description: app.description,
|
||||
appUrl: app.appUrl || app.demoUrl || "",
|
||||
icon: app.icon || "Bot",
|
||||
iconColor: app.iconColor || "from-blue-500 to-purple-500",
|
||||
}
|
||||
|
||||
console.log('newApp.type after handleEditApp:', newApp.type)
|
||||
|
||||
// Check if this type is valid for the Select component
|
||||
const validSelectValues = [
|
||||
'文字處理', '圖像生成', '圖像處理', '語音辨識', '推薦系統', '音樂生成',
|
||||
'程式開發', '影像處理', '對話系統', '數據分析', '設計工具', '語音技術',
|
||||
'教育工具', '健康醫療', '金融科技', '物聯網', '區塊鏈', 'AR/VR',
|
||||
'機器學習', '電腦視覺', '自然語言處理', '機器人', '網路安全', '雲端服務', '其他'
|
||||
]
|
||||
|
||||
const isValidSelectValue = validSelectValues.includes(newApp.type)
|
||||
console.log('Is valid Select value?', isValidSelectValue)
|
||||
|
||||
return newApp
|
||||
}
|
||||
|
||||
// Test all apps
|
||||
console.log('\n=== Testing handleEditApp for all apps ===')
|
||||
const validSelectValues = [
|
||||
'文字處理', '圖像生成', '圖像處理', '語音辨識', '推薦系統', '音樂生成',
|
||||
'程式開發', '影像處理', '對話系統', '數據分析', '設計工具', '語音技術',
|
||||
'教育工具', '健康醫療', '金融科技', '物聯網', '區塊鏈', 'AR/VR',
|
||||
'機器學習', '電腦視覺', '自然語言處理', '機器人', '網路安全', '雲端服務', '其他'
|
||||
]
|
||||
|
||||
formattedApps.forEach((app, index) => {
|
||||
const newApp = simulateHandleEditApp(app)
|
||||
console.log(`App ${index + 1} result: ${newApp.type} (valid: ${validSelectValues.includes(newApp.type)})`)
|
||||
})
|
||||
|
||||
// Test the update process
|
||||
console.log('\n=== Testing Update Process ===')
|
||||
const mapTypeToApiType = (frontendType) => {
|
||||
const typeMap = {
|
||||
'文字處理': 'productivity',
|
||||
'圖像生成': 'ai_model',
|
||||
'圖像處理': 'ai_model',
|
||||
'語音辨識': 'ai_model',
|
||||
'推薦系統': 'ai_model',
|
||||
'音樂生成': 'ai_model',
|
||||
'程式開發': 'automation',
|
||||
'影像處理': 'ai_model',
|
||||
'對話系統': 'ai_model',
|
||||
'數據分析': 'data_analysis',
|
||||
'設計工具': 'productivity',
|
||||
'語音技術': 'ai_model',
|
||||
'教育工具': 'educational',
|
||||
'健康醫療': 'healthcare',
|
||||
'金融科技': 'finance',
|
||||
'物聯網': 'iot_device',
|
||||
'區塊鏈': 'blockchain',
|
||||
'AR/VR': 'ar_vr',
|
||||
'機器學習': 'machine_learning',
|
||||
'電腦視覺': 'computer_vision',
|
||||
'自然語言處理': 'nlp',
|
||||
'機器人': 'robotics',
|
||||
'網路安全': 'cybersecurity',
|
||||
'雲端服務': 'cloud_service',
|
||||
'其他': 'other'
|
||||
}
|
||||
return typeMap[frontendType] || 'other'
|
||||
}
|
||||
|
||||
formattedApps.forEach((app, index) => {
|
||||
const displayType = app.type
|
||||
const apiType = mapTypeToApiType(displayType)
|
||||
const backToDisplay = mapApiTypeToDisplayType(apiType)
|
||||
|
||||
console.log(`App ${index + 1}:`)
|
||||
console.log(` Display: ${displayType}`)
|
||||
console.log(` API: ${apiType}`)
|
||||
console.log(` Round trip: ${backToDisplay}`)
|
||||
console.log(` Round trip matches: ${backToDisplay === displayType}`)
|
||||
})
|
||||
|
||||
console.log('\n=== Test completed ===')
|
@@ -1,88 +0,0 @@
|
||||
const jwt = require('jsonwebtoken');
|
||||
|
||||
async function testApproval() {
|
||||
console.log('🧪 測試批准功能...');
|
||||
|
||||
// 生成測試 token
|
||||
const token = jwt.sign(
|
||||
{ userId: 'admin-001', role: 'admin' },
|
||||
process.env.JWT_SECRET || 'good777',
|
||||
{ expiresIn: '1h' }
|
||||
);
|
||||
|
||||
console.log('✅ Token 生成成功\n');
|
||||
|
||||
try {
|
||||
// 首先獲取應用程式列表
|
||||
const response = await fetch('http://localhost:3000/api/apps', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
console.log(`❌ 獲取應用程式失敗: ${response.status}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
console.log(`✅ 獲取到 ${data.apps.length} 個應用程式`);
|
||||
|
||||
// 找到一個可以測試的應用程式
|
||||
const testApp = data.apps[0];
|
||||
if (!testApp) {
|
||||
console.log('❌ 沒有找到可測試的應用程式');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`\n測試應用程式: ${testApp.name} (ID: ${testApp.id})`);
|
||||
console.log(`當前狀態: ${testApp.status}`);
|
||||
|
||||
// 測試批准功能
|
||||
console.log('\n測試批准功能...');
|
||||
const approveResponse = await fetch(`http://localhost:3000/api/apps/${testApp.id}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
status: 'published'
|
||||
})
|
||||
});
|
||||
|
||||
if (approveResponse.ok) {
|
||||
console.log('✅ 批准成功');
|
||||
} else {
|
||||
const errorData = await approveResponse.json();
|
||||
console.log(`❌ 批准失敗: ${errorData.error}`);
|
||||
}
|
||||
|
||||
// 測試拒絕功能
|
||||
console.log('\n測試拒絕功能...');
|
||||
const rejectResponse = await fetch(`http://localhost:3000/api/apps/${testApp.id}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
status: 'rejected'
|
||||
})
|
||||
});
|
||||
|
||||
if (rejectResponse.ok) {
|
||||
console.log('✅ 拒絕成功');
|
||||
} else {
|
||||
const errorData = await rejectResponse.json();
|
||||
console.log(`❌ 拒絕失敗: ${errorData.error}`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.log(`❌ 測試失敗: ${error.message}`);
|
||||
}
|
||||
|
||||
console.log('\n✅ 批准測試完成');
|
||||
}
|
||||
|
||||
testApproval();
|
@@ -1,199 +0,0 @@
|
||||
const mysql = require('mysql2/promise');
|
||||
const bcrypt = require('bcrypt');
|
||||
const jwt = require('jsonwebtoken');
|
||||
|
||||
const dbConfig = {
|
||||
host: process.env.DB_HOST || 'mysql.theaken.com',
|
||||
port: parseInt(process.env.DB_PORT || '33306'),
|
||||
user: process.env.DB_USER || 'AI_Platform',
|
||||
password: process.env.DB_PASSWORD || 'Aa123456',
|
||||
database: process.env.DB_NAME || 'db_AI_Platform',
|
||||
charset: 'utf8mb4',
|
||||
timezone: '+08:00'
|
||||
};
|
||||
|
||||
const JWT_SECRET = process.env.JWT_SECRET || 'good777';
|
||||
|
||||
async function testAppsAPI() {
|
||||
let connection;
|
||||
|
||||
try {
|
||||
console.log('🧪 開始測試應用程式 API...');
|
||||
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
console.log('✅ 資料庫連接成功');
|
||||
|
||||
// 1. 創建測試用戶
|
||||
console.log('\n1. 創建測試用戶...');
|
||||
const testUserId = 'test-user-' + Date.now();
|
||||
const hashedPassword = await bcrypt.hash('test123', 12);
|
||||
|
||||
await connection.execute(`
|
||||
INSERT INTO users (id, name, email, password_hash, department, role, join_date)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
`, [testUserId, '測試用戶', 'test@example.com', hashedPassword, '測試部', 'developer', '2025-01-01']);
|
||||
|
||||
console.log('✅ 測試用戶創建成功');
|
||||
|
||||
// 2. 生成測試 Token
|
||||
const token = jwt.sign({
|
||||
userId: testUserId,
|
||||
email: 'test@example.com',
|
||||
role: 'developer'
|
||||
}, JWT_SECRET, { expiresIn: '1h' });
|
||||
|
||||
console.log('✅ JWT Token 生成成功');
|
||||
|
||||
// 3. 測試創建應用程式
|
||||
console.log('\n2. 測試創建應用程式...');
|
||||
const appData = {
|
||||
id: 'test-app-' + Date.now(),
|
||||
name: '測試 AI 應用',
|
||||
description: '這是一個用於測試的 AI 應用程式,具有機器學習功能',
|
||||
creator_id: testUserId,
|
||||
type: 'web_app',
|
||||
tech_stack: JSON.stringify(['React', 'Node.js', 'TensorFlow']),
|
||||
tags: JSON.stringify(['AI', '機器學習', '測試']),
|
||||
demo_url: 'https://demo.example.com',
|
||||
github_url: 'https://github.com/test/app',
|
||||
docs_url: 'https://docs.example.com',
|
||||
version: '1.0.0',
|
||||
status: 'draft'
|
||||
};
|
||||
|
||||
await connection.execute(`
|
||||
INSERT INTO apps (id, name, description, creator_id, type, tech_stack, tags, demo_url, github_url, docs_url, version, status)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`, [
|
||||
appData.id, appData.name, appData.description, appData.creator_id, appData.type,
|
||||
appData.tech_stack, appData.tags, appData.demo_url, appData.github_url, appData.docs_url,
|
||||
appData.version, appData.status
|
||||
]);
|
||||
|
||||
console.log('✅ 測試應用程式創建成功');
|
||||
|
||||
// 4. 測試查詢應用程式列表
|
||||
console.log('\n3. 測試查詢應用程式列表...');
|
||||
const [apps] = await connection.execute(`
|
||||
SELECT
|
||||
a.*,
|
||||
u.name as creator_name,
|
||||
u.email as creator_email,
|
||||
u.department as creator_department,
|
||||
u.role as creator_role
|
||||
FROM apps a
|
||||
LEFT JOIN users u ON a.creator_id = u.id
|
||||
WHERE a.creator_id = ?
|
||||
`, [testUserId]);
|
||||
|
||||
console.log(`✅ 查詢到 ${apps.length} 個應用程式`);
|
||||
apps.forEach(app => {
|
||||
console.log(` - ${app.name} (${app.type}) - ${app.status}`);
|
||||
});
|
||||
|
||||
// 5. 測試更新應用程式
|
||||
console.log('\n4. 測試更新應用程式...');
|
||||
await connection.execute(`
|
||||
UPDATE apps
|
||||
SET name = ?, description = ?, status = ?, version = ?
|
||||
WHERE id = ?
|
||||
`, [
|
||||
'更新後的測試 AI 應用',
|
||||
'這是更新後的測試應用程式描述',
|
||||
'submitted',
|
||||
'1.1.0',
|
||||
appData.id
|
||||
]);
|
||||
|
||||
console.log('✅ 應用程式更新成功');
|
||||
|
||||
// 6. 測試按讚功能
|
||||
console.log('\n5. 測試按讚功能...');
|
||||
const likeId = 'like-' + Date.now();
|
||||
await connection.execute(`
|
||||
INSERT INTO user_likes (id, user_id, app_id, liked_at)
|
||||
VALUES (?, ?, ?, NOW())
|
||||
`, [likeId, testUserId, appData.id]);
|
||||
|
||||
await connection.execute(`
|
||||
UPDATE apps SET likes_count = likes_count + 1 WHERE id = ?
|
||||
`, [appData.id]);
|
||||
|
||||
console.log('✅ 按讚功能測試成功');
|
||||
|
||||
// 7. 測試收藏功能
|
||||
console.log('\n6. 測試收藏功能...');
|
||||
const favoriteId = 'favorite-' + Date.now();
|
||||
await connection.execute(`
|
||||
INSERT INTO user_favorites (id, user_id, app_id)
|
||||
VALUES (?, ?, ?)
|
||||
`, [favoriteId, testUserId, appData.id]);
|
||||
|
||||
console.log('✅ 收藏功能測試成功');
|
||||
|
||||
// 8. 測試統計功能
|
||||
console.log('\n7. 測試統計功能...');
|
||||
const [stats] = await connection.execute(`
|
||||
SELECT
|
||||
COUNT(*) as total,
|
||||
SUM(CASE WHEN status = 'published' THEN 1 ELSE 0 END) as published,
|
||||
SUM(CASE WHEN status IN ('submitted', 'under_review') THEN 1 ELSE 0 END) as pending_review,
|
||||
SUM(CASE WHEN status = 'draft' THEN 1 ELSE 0 END) as draft,
|
||||
SUM(CASE WHEN status = 'approved' THEN 1 ELSE 0 END) as approved,
|
||||
SUM(CASE WHEN status = 'rejected' THEN 1 ELSE 0 END) as rejected
|
||||
FROM apps
|
||||
`);
|
||||
|
||||
console.log('✅ 統計功能測試成功:');
|
||||
console.log(` - 總應用數: ${stats[0].total}`);
|
||||
console.log(` - 已發布: ${stats[0].published}`);
|
||||
console.log(` - 待審核: ${stats[0].pending_review}`);
|
||||
console.log(` - 草稿: ${stats[0].draft}`);
|
||||
console.log(` - 已批准: ${stats[0].approved}`);
|
||||
console.log(` - 已拒絕: ${stats[0].rejected}`);
|
||||
|
||||
// 9. 測試搜尋功能
|
||||
console.log('\n8. 測試搜尋功能...');
|
||||
const [searchResults] = await connection.execute(`
|
||||
SELECT a.*, u.name as creator_name
|
||||
FROM apps a
|
||||
LEFT JOIN users u ON a.creator_id = u.id
|
||||
WHERE (a.name LIKE ? OR a.description LIKE ? OR u.name LIKE ?)
|
||||
AND a.type = ?
|
||||
AND a.status = ?
|
||||
`, ['%AI%', '%AI%', '%測試%', 'web_app', 'submitted']);
|
||||
|
||||
console.log(`✅ 搜尋功能測試成功,找到 ${searchResults.length} 個結果`);
|
||||
|
||||
// 10. 測試刪除功能
|
||||
console.log('\n9. 測試刪除功能...');
|
||||
|
||||
// 先刪除相關記錄
|
||||
await connection.execute('DELETE FROM user_likes WHERE app_id = ?', [appData.id]);
|
||||
await connection.execute('DELETE FROM user_favorites WHERE app_id = ?', [appData.id]);
|
||||
|
||||
// 刪除應用程式
|
||||
await connection.execute('DELETE FROM apps WHERE id = ?', [appData.id]);
|
||||
|
||||
console.log('✅ 刪除功能測試成功');
|
||||
|
||||
// 11. 清理測試資料
|
||||
console.log('\n10. 清理測試資料...');
|
||||
await connection.execute('DELETE FROM users WHERE id = ?', [testUserId]);
|
||||
|
||||
console.log('✅ 測試資料清理完成');
|
||||
|
||||
console.log('\n🎉 所有應用程式 API 測試通過!');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 測試失敗:', error);
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
console.log('🔌 資料庫連接已關閉');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 執行測試
|
||||
testAppsAPI().catch(console.error);
|
@@ -1,99 +0,0 @@
|
||||
const http = require('http');
|
||||
const jwt = require('jsonwebtoken');
|
||||
|
||||
const JWT_SECRET = process.env.JWT_SECRET || 'good777';
|
||||
|
||||
// 生成測試 Token
|
||||
function generateTestToken() {
|
||||
return jwt.sign({
|
||||
userId: 'mdxxt1xt7slle4g8wz8',
|
||||
email: 'petty091901@gmail.com',
|
||||
role: 'admin'
|
||||
}, JWT_SECRET, { expiresIn: '1h' });
|
||||
}
|
||||
|
||||
// 發送 HTTP 請求
|
||||
function makeRequest(url, method = 'GET', body = null, headers = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const urlObj = new URL(url);
|
||||
|
||||
const options = {
|
||||
hostname: urlObj.hostname,
|
||||
port: urlObj.port,
|
||||
path: urlObj.pathname,
|
||||
method: method,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...headers
|
||||
}
|
||||
};
|
||||
|
||||
const req = http.request(options, (res) => {
|
||||
let data = '';
|
||||
|
||||
res.on('data', (chunk) => {
|
||||
data += chunk;
|
||||
});
|
||||
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const jsonData = JSON.parse(data);
|
||||
resolve({
|
||||
status: res.statusCode,
|
||||
data: jsonData
|
||||
});
|
||||
} catch (error) {
|
||||
resolve({
|
||||
status: res.statusCode,
|
||||
data: data
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', (error) => {
|
||||
reject(error);
|
||||
});
|
||||
|
||||
if (body) {
|
||||
req.write(JSON.stringify(body));
|
||||
}
|
||||
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
async function testAppsQuery() {
|
||||
try {
|
||||
console.log('🧪 測試應用程式查詢...');
|
||||
|
||||
// 生成測試 Token
|
||||
const token = generateTestToken();
|
||||
console.log('✅ Token 生成成功');
|
||||
|
||||
// 測試 GET /api/apps
|
||||
console.log('\n1. 測試 GET /api/apps...');
|
||||
const response = await makeRequest('http://localhost:3000/api/apps', 'GET', null, {
|
||||
'Authorization': `Bearer ${token}`
|
||||
});
|
||||
|
||||
console.log('狀態碼:', response.status);
|
||||
console.log('回應:', JSON.stringify(response.data, null, 2));
|
||||
|
||||
if (response.status === 200) {
|
||||
console.log('✅ 應用程式查詢成功');
|
||||
console.log('應用程式數量:', response.data.apps?.length || 0);
|
||||
|
||||
if (response.data.apps && response.data.apps.length > 0) {
|
||||
console.log('第一個應用程式:', response.data.apps[0]);
|
||||
}
|
||||
} else {
|
||||
console.log('❌ 應用程式查詢失敗');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 測試失敗:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
testAppsQuery();
|
@@ -1,117 +0,0 @@
|
||||
const http = require('http');
|
||||
const jwt = require('jsonwebtoken');
|
||||
|
||||
const JWT_SECRET = process.env.JWT_SECRET || 'good777';
|
||||
|
||||
// 生成測試 Token
|
||||
function generateTestToken() {
|
||||
return jwt.sign({
|
||||
userId: 'mdxxt1xt7slle4g8wz8',
|
||||
email: 'petty091901@gmail.com',
|
||||
role: 'admin'
|
||||
}, JWT_SECRET, { expiresIn: '1h' });
|
||||
}
|
||||
|
||||
// 發送 HTTP 請求
|
||||
function makeRequest(url, method = 'GET', body = null, headers = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const urlObj = new URL(url);
|
||||
|
||||
const options = {
|
||||
hostname: urlObj.hostname,
|
||||
port: urlObj.port,
|
||||
path: urlObj.pathname,
|
||||
method: method,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...headers
|
||||
}
|
||||
};
|
||||
|
||||
console.log('發送請求到:', url);
|
||||
console.log('請求方法:', method);
|
||||
console.log('請求標頭:', options.headers);
|
||||
|
||||
const req = http.request(options, (res) => {
|
||||
let data = '';
|
||||
|
||||
res.on('data', (chunk) => {
|
||||
data += chunk;
|
||||
});
|
||||
|
||||
res.on('end', () => {
|
||||
console.log('回應狀態:', res.statusCode);
|
||||
console.log('回應標頭:', res.headers);
|
||||
|
||||
try {
|
||||
const jsonData = JSON.parse(data);
|
||||
resolve({
|
||||
status: res.statusCode,
|
||||
data: jsonData
|
||||
});
|
||||
} catch (error) {
|
||||
resolve({
|
||||
status: res.statusCode,
|
||||
data: data
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', (error) => {
|
||||
reject(error);
|
||||
});
|
||||
|
||||
if (body) {
|
||||
const bodyStr = JSON.stringify(body);
|
||||
console.log('請求體:', bodyStr);
|
||||
req.write(bodyStr);
|
||||
}
|
||||
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
async function testAuthDetailed() {
|
||||
try {
|
||||
console.log('🧪 詳細認證測試...');
|
||||
|
||||
// 生成測試 Token
|
||||
const token = generateTestToken();
|
||||
console.log('✅ Token 生成成功');
|
||||
console.log('Token 長度:', token.length);
|
||||
|
||||
// 驗證 Token
|
||||
const payload = jwt.verify(token, JWT_SECRET);
|
||||
console.log('✅ Token 驗證成功:', payload);
|
||||
|
||||
// 測試 GET 請求(不需要認證)
|
||||
console.log('\n1. 測試 GET /api/apps(需要認證)...');
|
||||
const getResponse = await makeRequest('http://localhost:3000/api/apps', 'GET', null, {
|
||||
'Authorization': `Bearer ${token}`
|
||||
});
|
||||
|
||||
console.log('GET 回應:', JSON.stringify(getResponse.data, null, 2));
|
||||
|
||||
// 測試 POST 請求
|
||||
console.log('\n2. 測試 POST /api/apps...');
|
||||
const appData = {
|
||||
name: '測試應用',
|
||||
description: '這是一個測試應用',
|
||||
type: 'productivity',
|
||||
demoUrl: 'https://example.com',
|
||||
version: '1.0.0'
|
||||
};
|
||||
|
||||
const postResponse = await makeRequest('http://localhost:3000/api/apps', 'POST', appData, {
|
||||
'Authorization': `Bearer ${token}`
|
||||
});
|
||||
|
||||
console.log('POST 回應:', JSON.stringify(postResponse.data, null, 2));
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 測試失敗:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
testAuthDetailed();
|
@@ -1,91 +0,0 @@
|
||||
const mysql = require('mysql2/promise');
|
||||
const jwt = require('jsonwebtoken');
|
||||
|
||||
const dbConfig = {
|
||||
host: process.env.DB_HOST || 'mysql.theaken.com',
|
||||
port: parseInt(process.env.DB_PORT || '33306'),
|
||||
user: process.env.DB_USER || 'AI_Platform',
|
||||
password: process.env.DB_PASSWORD || 'Aa123456',
|
||||
database: process.env.DB_NAME || 'db_AI_Platform',
|
||||
charset: 'utf8mb4',
|
||||
timezone: '+08:00'
|
||||
};
|
||||
|
||||
const JWT_SECRET = process.env.JWT_SECRET || 'good777';
|
||||
|
||||
async function testAuth() {
|
||||
let connection;
|
||||
|
||||
try {
|
||||
console.log('🧪 測試認證過程...');
|
||||
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
console.log('✅ 資料庫連接成功');
|
||||
|
||||
// 1. 檢查用戶是否存在
|
||||
console.log('\n1. 檢查用戶是否存在...');
|
||||
const [users] = await connection.execute(
|
||||
'SELECT id, name, email, role FROM users WHERE id = ?',
|
||||
['mdxxt1xt7slle4g8wz8']
|
||||
);
|
||||
|
||||
if (users.length === 0) {
|
||||
console.log('❌ 用戶不存在');
|
||||
return;
|
||||
}
|
||||
|
||||
const user = users[0];
|
||||
console.log('✅ 用戶存在:', user);
|
||||
|
||||
// 2. 生成 Token
|
||||
console.log('\n2. 生成 JWT Token...');
|
||||
const token = jwt.sign({
|
||||
userId: user.id,
|
||||
email: user.email,
|
||||
role: user.role
|
||||
}, JWT_SECRET, { expiresIn: '1h' });
|
||||
|
||||
console.log('✅ Token 生成成功');
|
||||
console.log('Token:', token.substring(0, 50) + '...');
|
||||
|
||||
// 3. 驗證 Token
|
||||
console.log('\n3. 驗證 JWT Token...');
|
||||
const payload = jwt.verify(token, JWT_SECRET);
|
||||
console.log('✅ Token 驗證成功:', payload);
|
||||
|
||||
// 4. 模擬認證查詢
|
||||
console.log('\n4. 模擬認證查詢...');
|
||||
const [authUser] = await connection.execute(
|
||||
'SELECT * FROM users WHERE id = ? AND email = ?',
|
||||
[payload.userId, payload.email]
|
||||
);
|
||||
|
||||
if (authUser.length === 0) {
|
||||
console.log('❌ 認證查詢失敗 - 用戶不存在');
|
||||
} else {
|
||||
console.log('✅ 認證查詢成功:', authUser[0]);
|
||||
}
|
||||
|
||||
// 5. 檢查用戶角色
|
||||
console.log('\n5. 檢查用戶角色...');
|
||||
if (authUser.length > 0) {
|
||||
const userRole = authUser[0].role;
|
||||
console.log('用戶角色:', userRole);
|
||||
|
||||
if (userRole === 'admin' || userRole === 'developer') {
|
||||
console.log('✅ 用戶有權限創建應用程式');
|
||||
} else {
|
||||
console.log('❌ 用戶沒有權限創建應用程式');
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 測試失敗:', error);
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testAuth();
|
92
scripts/test-creator-name-fix.js
Normal file
92
scripts/test-creator-name-fix.js
Normal file
@@ -0,0 +1,92 @@
|
||||
const mysql = require('mysql2/promise');
|
||||
|
||||
const dbConfig = {
|
||||
host: process.env.DB_HOST || 'mysql.theaken.com',
|
||||
port: parseInt(process.env.DB_PORT || '33306'),
|
||||
user: process.env.DB_USER || 'AI_Platform',
|
||||
password: process.env.DB_PASSWORD || 'Aa123456',
|
||||
database: process.env.DB_NAME || 'db_AI_Platform',
|
||||
charset: 'utf8mb4',
|
||||
timezone: '+08:00'
|
||||
};
|
||||
|
||||
async function testCreatorNameFix() {
|
||||
let connection;
|
||||
|
||||
try {
|
||||
console.log('🔍 測試創建者名稱修正...');
|
||||
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
console.log('✅ 資料庫連接成功');
|
||||
|
||||
// 模擬列表 API 的查詢
|
||||
const [apps] = await connection.execute(`
|
||||
SELECT
|
||||
a.*,
|
||||
u.name as user_creator_name,
|
||||
u.email as user_creator_email,
|
||||
u.department as user_creator_department,
|
||||
u.role as creator_role
|
||||
FROM apps a
|
||||
LEFT JOIN users u ON a.creator_id = u.id
|
||||
ORDER BY a.created_at DESC
|
||||
LIMIT 3
|
||||
`);
|
||||
|
||||
console.log('\n📊 原始資料庫查詢結果:');
|
||||
apps.forEach((app, index) => {
|
||||
console.log(`\n應用程式 ${index + 1}:`);
|
||||
console.log(` 應用名稱: ${app.name}`);
|
||||
console.log(` apps.creator_name: ${app.creator_name}`);
|
||||
console.log(` users.name: ${app.user_creator_name}`);
|
||||
});
|
||||
|
||||
// 模擬修正後的格式化邏輯
|
||||
const formattedApps = apps.map((app) => ({
|
||||
id: app.id,
|
||||
name: app.name,
|
||||
creator: {
|
||||
id: app.creator_id,
|
||||
name: app.creator_name || app.user_creator_name, // 修正:優先使用 apps.creator_name
|
||||
email: app.user_creator_email,
|
||||
department: app.department || app.user_creator_department,
|
||||
role: app.creator_role
|
||||
}
|
||||
}));
|
||||
|
||||
console.log('\n📋 修正後的格式化結果:');
|
||||
formattedApps.forEach((app, index) => {
|
||||
console.log(`\n應用程式 ${index + 1}:`);
|
||||
console.log(` 名稱: ${app.name}`);
|
||||
console.log(` 創建者名稱: ${app.creator.name}`);
|
||||
console.log(` 創建者郵箱: ${app.creator.email}`);
|
||||
console.log(` 創建者部門: ${app.creator.department}`);
|
||||
});
|
||||
|
||||
// 驗證修正是否有效
|
||||
const expectedCreatorName = "佩庭"; // 期望的創建者名稱
|
||||
const actualCreatorName = formattedApps[0]?.creator.name;
|
||||
|
||||
console.log('\n✅ 驗證結果:');
|
||||
console.log(`期望創建者名稱: ${expectedCreatorName}`);
|
||||
console.log(`實際創建者名稱: ${actualCreatorName}`);
|
||||
console.log(`修正是否成功: ${actualCreatorName === expectedCreatorName}`);
|
||||
|
||||
if (actualCreatorName === expectedCreatorName) {
|
||||
console.log('🎉 創建者名稱修正成功!現在顯示正確的資料庫值。');
|
||||
} else {
|
||||
console.log('❌ 創建者名稱修正失敗,需要進一步檢查。');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 測試創建者名稱修正失敗:', error);
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
console.log('🔌 資料庫連接已關閉');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 執行測試
|
||||
testCreatorNameFix().catch(console.error);
|
84
scripts/test-creator-object-fix.js
Normal file
84
scripts/test-creator-object-fix.js
Normal file
@@ -0,0 +1,84 @@
|
||||
// Test script to verify creator object handling fix
|
||||
console.log('Testing creator object handling fix...')
|
||||
|
||||
// Simulate API response with creator object
|
||||
const mockApiResponse = {
|
||||
apps: [
|
||||
{
|
||||
id: '1',
|
||||
name: 'Test App',
|
||||
type: 'web_app',
|
||||
status: 'published',
|
||||
description: 'Test description',
|
||||
creator: {
|
||||
id: 'user1',
|
||||
name: 'John Doe',
|
||||
email: 'john@example.com',
|
||||
department: 'ITBU',
|
||||
role: 'developer'
|
||||
},
|
||||
department: 'ITBU',
|
||||
createdAt: '2025-01-01T00:00:00Z',
|
||||
viewsCount: 100,
|
||||
likesCount: 50
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
name: 'Test App 2',
|
||||
type: 'mobile_app',
|
||||
status: 'pending',
|
||||
description: 'Test description 2',
|
||||
creator: 'Jane Smith', // String creator
|
||||
department: 'HQBU',
|
||||
createdAt: '2025-01-02T00:00:00Z',
|
||||
viewsCount: 200,
|
||||
likesCount: 75
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// Simulate the loadApps function processing
|
||||
function processApps(apiData) {
|
||||
return (apiData.apps || []).map((app) => ({
|
||||
...app,
|
||||
views: app.viewsCount || 0,
|
||||
likes: app.likesCount || 0,
|
||||
appUrl: app.demoUrl || '',
|
||||
type: app.type, // Simplified for test
|
||||
icon: app.icon || 'Bot',
|
||||
iconColor: app.iconColor || 'from-blue-500 to-purple-500',
|
||||
reviews: 0,
|
||||
createdAt: app.createdAt ? new Date(app.createdAt).toLocaleDateString() : '未知',
|
||||
// Handle creator object properly
|
||||
creator: typeof app.creator === 'object' ? app.creator.name : app.creator,
|
||||
department: typeof app.creator === 'object' ? app.creator.department : app.department
|
||||
}))
|
||||
}
|
||||
|
||||
// Test the processing
|
||||
const processedApps = processApps(mockApiResponse)
|
||||
|
||||
console.log('Original API response:')
|
||||
console.log(JSON.stringify(mockApiResponse, null, 2))
|
||||
|
||||
console.log('\nProcessed apps:')
|
||||
console.log(JSON.stringify(processedApps, null, 2))
|
||||
|
||||
// Test rendering simulation
|
||||
console.log('\nTesting rendering simulation:')
|
||||
processedApps.forEach((app, index) => {
|
||||
console.log(`App ${index + 1}:`)
|
||||
console.log(` Creator: ${app.creator}`)
|
||||
console.log(` Department: ${app.department}`)
|
||||
console.log(` Type: ${typeof app.creator}`)
|
||||
|
||||
// Simulate the table cell rendering
|
||||
const creatorDisplay = typeof app.creator === 'object' ? app.creator.name : app.creator
|
||||
const departmentDisplay = typeof app.creator === 'object' ? app.creator.department : app.department
|
||||
|
||||
console.log(` Display - Creator: ${creatorDisplay}`)
|
||||
console.log(` Display - Department: ${departmentDisplay}`)
|
||||
console.log('')
|
||||
})
|
||||
|
||||
console.log('✅ Creator object handling test completed successfully!')
|
@@ -1,98 +0,0 @@
|
||||
const jwt = require('jsonwebtoken');
|
||||
|
||||
const JWT_SECRET = process.env.JWT_SECRET || 'good777';
|
||||
|
||||
async function testCurrentState() {
|
||||
try {
|
||||
// Generate a token for admin user
|
||||
const adminPayload = {
|
||||
id: 1,
|
||||
email: 'admin@example.com',
|
||||
role: 'admin'
|
||||
};
|
||||
const token = jwt.sign(adminPayload, JWT_SECRET, { expiresIn: '1h' });
|
||||
|
||||
console.log('=== 測試當前狀態 ===');
|
||||
|
||||
// Test 1: Get apps list with pagination
|
||||
console.log('\n1. 測試應用程式列表 (分頁)');
|
||||
const response1 = await fetch('http://localhost:3000/api/apps?page=1&limit=10', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
|
||||
if (response1.ok) {
|
||||
const data1 = await response1.json();
|
||||
console.log('✅ API 回應成功');
|
||||
console.log(`總應用數: ${data1.pagination?.total || 'N/A'}`);
|
||||
console.log(`總頁數: ${data1.pagination?.totalPages || 'N/A'}`);
|
||||
console.log(`當前頁應用數: ${data1.apps?.length || 0}`);
|
||||
console.log('應用狀態統計:');
|
||||
const statusCounts = {};
|
||||
data1.apps?.forEach(app => {
|
||||
statusCounts[app.status] = (statusCounts[app.status] || 0) + 1;
|
||||
});
|
||||
console.log(statusCounts);
|
||||
} else {
|
||||
console.log('❌ API 回應失敗:', response1.status, response1.statusText);
|
||||
}
|
||||
|
||||
// Test 2: Create a new app as admin
|
||||
console.log('\n2. 測試管理員創建應用程式');
|
||||
const newAppData = {
|
||||
name: '測試應用程式_' + Date.now(),
|
||||
description: '這是一個測試應用程式',
|
||||
type: 'productivity',
|
||||
demoUrl: 'https://example.com',
|
||||
version: '1.0.0'
|
||||
};
|
||||
|
||||
const response2 = await fetch('http://localhost:3000/api/apps', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify(newAppData)
|
||||
});
|
||||
|
||||
if (response2.ok) {
|
||||
const result = await response2.json();
|
||||
console.log('✅ 創建應用程式成功');
|
||||
console.log('創建的應用程式狀態:', result.app?.status);
|
||||
console.log('應用程式ID:', result.appId);
|
||||
} else {
|
||||
const errorData = await response2.json();
|
||||
console.log('❌ 創建應用程式失敗:', errorData);
|
||||
}
|
||||
|
||||
// Test 3: Get apps list again to see the new app
|
||||
console.log('\n3. 重新獲取應用程式列表');
|
||||
const response3 = await fetch('http://localhost:3000/api/apps?page=1&limit=10', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
|
||||
if (response3.ok) {
|
||||
const data3 = await response3.json();
|
||||
console.log('✅ 重新獲取成功');
|
||||
console.log(`更新後總應用數: ${data3.pagination?.total || 'N/A'}`);
|
||||
console.log(`更新後總頁數: ${data3.pagination?.totalPages || 'N/A'}`);
|
||||
|
||||
// Find the newly created app
|
||||
const newApp = data3.apps?.find(app => app.name.includes('測試應用程式_'));
|
||||
if (newApp) {
|
||||
console.log('新創建的應用程式狀態:', newApp.status);
|
||||
}
|
||||
} else {
|
||||
console.log('❌ 重新獲取失敗:', response3.status, response3.statusText);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('測試過程中發生錯誤:', error);
|
||||
}
|
||||
}
|
||||
|
||||
testCurrentState();
|
100
scripts/test-database-values.js
Normal file
100
scripts/test-database-values.js
Normal file
@@ -0,0 +1,100 @@
|
||||
const mysql = require('mysql2/promise');
|
||||
|
||||
const dbConfig = {
|
||||
host: process.env.DB_HOST || 'mysql.theaken.com',
|
||||
port: parseInt(process.env.DB_PORT || '33306'),
|
||||
user: process.env.DB_USER || 'AI_Platform',
|
||||
password: process.env.DB_PASSWORD || 'Aa123456',
|
||||
database: process.env.DB_NAME || 'db_AI_Platform',
|
||||
charset: 'utf8mb4',
|
||||
timezone: '+08:00'
|
||||
};
|
||||
|
||||
async function testDatabaseValues() {
|
||||
let connection;
|
||||
|
||||
try {
|
||||
console.log('🔍 檢查資料庫中的應用程式資料...');
|
||||
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
console.log('✅ 資料庫連接成功');
|
||||
|
||||
// 檢查 apps 表格結構
|
||||
const [columns] = await connection.execute('DESCRIBE apps');
|
||||
console.log('\n📋 apps 表格結構:');
|
||||
columns.forEach(col => {
|
||||
console.log(` ${col.Field}: ${col.Type} ${col.Null === 'YES' ? 'NULL' : 'NOT NULL'} ${col.Default ? `DEFAULT ${col.Default}` : ''}`);
|
||||
});
|
||||
|
||||
// 檢查前 5 個應用程式的資料
|
||||
const [apps] = await connection.execute(`
|
||||
SELECT
|
||||
id, name, description, type, department, creator_name, creator_email,
|
||||
icon, icon_color, status, created_at
|
||||
FROM apps
|
||||
LIMIT 5
|
||||
`);
|
||||
|
||||
console.log('\n📊 前 5 個應用程式資料:');
|
||||
apps.forEach((app, index) => {
|
||||
console.log(`\n應用程式 ${index + 1}:`);
|
||||
console.log(` ID: ${app.id}`);
|
||||
console.log(` 名稱: ${app.name}`);
|
||||
console.log(` 類型: ${app.type || 'NULL'}`);
|
||||
console.log(` 部門: ${app.department || 'NULL'}`);
|
||||
console.log(` 創建者名稱: ${app.creator_name || 'NULL'}`);
|
||||
console.log(` 創建者郵箱: ${app.creator_email || 'NULL'}`);
|
||||
console.log(` 圖示: ${app.icon || 'NULL'}`);
|
||||
console.log(` 圖示顏色: ${app.icon_color || 'NULL'}`);
|
||||
console.log(` 狀態: ${app.status || 'NULL'}`);
|
||||
console.log(` 創建時間: ${app.created_at}`);
|
||||
});
|
||||
|
||||
// 檢查是否有任何應用程式的 type 欄位為 NULL
|
||||
const [nullTypes] = await connection.execute(`
|
||||
SELECT COUNT(*) as count
|
||||
FROM apps
|
||||
WHERE type IS NULL
|
||||
`);
|
||||
|
||||
console.log(`\n📈 類型為 NULL 的應用程式數量: ${nullTypes[0].count}`);
|
||||
|
||||
// 檢查是否有任何應用程式的 department 欄位為 NULL
|
||||
const [nullDepartments] = await connection.execute(`
|
||||
SELECT COUNT(*) as count
|
||||
FROM apps
|
||||
WHERE department IS NULL
|
||||
`);
|
||||
|
||||
console.log(`📈 部門為 NULL 的應用程式數量: ${nullDepartments[0].count}`);
|
||||
|
||||
// 檢查是否有任何應用程式的 creator_name 欄位為 NULL
|
||||
const [nullCreatorNames] = await connection.execute(`
|
||||
SELECT COUNT(*) as count
|
||||
FROM apps
|
||||
WHERE creator_name IS NULL
|
||||
`);
|
||||
|
||||
console.log(`📈 創建者名稱為 NULL 的應用程式數量: ${nullCreatorNames[0].count}`);
|
||||
|
||||
// 檢查是否有任何應用程式的 icon 欄位為 NULL
|
||||
const [nullIcons] = await connection.execute(`
|
||||
SELECT COUNT(*) as count
|
||||
FROM apps
|
||||
WHERE icon IS NULL
|
||||
`);
|
||||
|
||||
console.log(`📈 圖示為 NULL 的應用程式數量: ${nullIcons[0].count}`);
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 檢查資料庫值失敗:', error);
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
console.log('🔌 資料庫連接已關閉');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 執行檢查
|
||||
testDatabaseValues().catch(console.error);
|
@@ -1,54 +0,0 @@
|
||||
const mysql = require('mysql2/promise');
|
||||
|
||||
const dbConfig = {
|
||||
host: process.env.DB_HOST || 'mysql.theaken.com',
|
||||
port: parseInt(process.env.DB_PORT || '33306'),
|
||||
user: process.env.DB_USER || 'AI_Platform',
|
||||
password: process.env.DB_PASSWORD || 'Aa123456',
|
||||
database: process.env.DB_NAME || 'db_AI_Platform',
|
||||
charset: 'utf8mb4',
|
||||
timezone: '+08:00'
|
||||
};
|
||||
|
||||
async function testDBConnection() {
|
||||
let connection;
|
||||
|
||||
try {
|
||||
console.log('🧪 測試資料庫連接...');
|
||||
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
console.log('✅ 資料庫連接成功');
|
||||
|
||||
// 測試查詢用戶
|
||||
console.log('\n1. 測試查詢用戶...');
|
||||
const [users] = await connection.execute(
|
||||
'SELECT id, name, email, role FROM users WHERE id = ? AND email = ?',
|
||||
['mdxxt1xt7slle4g8wz8', 'petty091901@gmail.com']
|
||||
);
|
||||
|
||||
console.log('查詢結果:', users);
|
||||
|
||||
if (users.length > 0) {
|
||||
console.log('✅ 用戶查詢成功');
|
||||
} else {
|
||||
console.log('❌ 用戶查詢失敗 - 沒有找到用戶');
|
||||
}
|
||||
|
||||
// 測試查詢所有用戶
|
||||
console.log('\n2. 測試查詢所有用戶...');
|
||||
const [allUsers] = await connection.execute(
|
||||
'SELECT id, name, email, role FROM users LIMIT 5'
|
||||
);
|
||||
|
||||
console.log('所有用戶:', allUsers);
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 測試失敗:', error);
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testDBConnection();
|
@@ -1,64 +0,0 @@
|
||||
const mysql = require('mysql2/promise');
|
||||
|
||||
const dbConfig = {
|
||||
host: process.env.DB_HOST || 'mysql.theaken.com',
|
||||
port: parseInt(process.env.DB_PORT || '33306'),
|
||||
user: process.env.DB_USER || 'AI_Platform',
|
||||
password: process.env.DB_PASSWORD || 'Aa123456',
|
||||
database: process.env.DB_NAME || 'db_AI_Platform',
|
||||
charset: 'utf8mb4',
|
||||
timezone: '+08:00'
|
||||
};
|
||||
|
||||
async function testDBQuery() {
|
||||
let connection;
|
||||
|
||||
try {
|
||||
console.log('🧪 測試資料庫查詢...');
|
||||
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
console.log('✅ 資料庫連接成功');
|
||||
|
||||
// 測試 1: 簡單查詢
|
||||
console.log('\n1. 測試簡單查詢...');
|
||||
const [apps1] = await connection.execute('SELECT * FROM apps LIMIT 5');
|
||||
console.log('結果:', apps1.length, '個應用程式');
|
||||
|
||||
// 測試 2: 使用 LIMIT 查詢
|
||||
console.log('\n2. 測試 LIMIT 查詢...');
|
||||
const [apps2] = await connection.execute('SELECT * FROM apps LIMIT 5');
|
||||
console.log('結果:', apps2.length, '個應用程式');
|
||||
|
||||
// 測試 3: 使用 OFFSET
|
||||
console.log('\n3. 測試 OFFSET 查詢...');
|
||||
const [apps3] = await connection.execute('SELECT * FROM apps LIMIT 5 OFFSET 0');
|
||||
console.log('結果:', apps3.length, '個應用程式');
|
||||
|
||||
// 測試 4: 計數查詢
|
||||
console.log('\n4. 測試計數查詢...');
|
||||
const [countResult] = await connection.execute('SELECT COUNT(*) as total FROM apps');
|
||||
console.log('總數:', countResult[0].total);
|
||||
|
||||
// 測試 5: JOIN 查詢
|
||||
console.log('\n5. 測試 JOIN 查詢...');
|
||||
const [apps4] = await connection.execute(`
|
||||
SELECT
|
||||
a.*,
|
||||
u.name as creator_name,
|
||||
u.email as creator_email
|
||||
FROM apps a
|
||||
LEFT JOIN users u ON a.creator_id = u.id
|
||||
LIMIT 5
|
||||
`);
|
||||
console.log('結果:', apps4.length, '個應用程式');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 測試失敗:', error);
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testDBQuery();
|
166
scripts/test-department-prefill.js
Normal file
166
scripts/test-department-prefill.js
Normal file
@@ -0,0 +1,166 @@
|
||||
// Test script to verify department pre-fill issue in handleEditApp
|
||||
console.log('Testing department pre-fill issue...')
|
||||
|
||||
// Simulate the loadApps function processing
|
||||
function processApps(apiApps) {
|
||||
return apiApps.map(app => ({
|
||||
id: app.id,
|
||||
name: app.name,
|
||||
type: app.type,
|
||||
creator: typeof app.creator === 'object' ? app.creator.name : app.creator,
|
||||
department: typeof app.creator === 'object' ? app.creator.department : app.department,
|
||||
description: app.description,
|
||||
appUrl: app.appUrl || app.demoUrl || '',
|
||||
icon: app.icon || 'Bot',
|
||||
iconColor: app.iconColor || 'from-blue-500 to-purple-500',
|
||||
status: app.status,
|
||||
views: app.views || 0,
|
||||
likes: app.likes || 0,
|
||||
rating: app.rating || 0,
|
||||
reviews: app.reviews || 0,
|
||||
createdAt: app.createdAt ? new Date(app.createdAt).toLocaleDateString() : '未知'
|
||||
}))
|
||||
}
|
||||
|
||||
// Simulate the handleEditApp function
|
||||
function handleEditApp(app) {
|
||||
console.log('📝 Editing app:', app.name)
|
||||
console.log('📋 App object structure:', {
|
||||
name: app.name,
|
||||
creator: app.creator,
|
||||
department: app.department,
|
||||
hasCreatorObject: typeof app.creator === 'object',
|
||||
hasCreatorProperty: 'creator' in app,
|
||||
hasDepartmentProperty: 'department' in app
|
||||
})
|
||||
|
||||
const newApp = {
|
||||
name: app.name,
|
||||
type: app.type,
|
||||
department: app.creator?.department || app.department || "HQBU", // This is the problematic line
|
||||
creator: app.creator?.name || app.creator || "",
|
||||
description: app.description,
|
||||
appUrl: app.appUrl || app.demoUrl || "",
|
||||
icon: app.icon || "Bot",
|
||||
iconColor: app.iconColor || "from-blue-500 to-purple-500",
|
||||
}
|
||||
|
||||
console.log('📝 Form populated with app data:', newApp)
|
||||
return newApp
|
||||
}
|
||||
|
||||
// Test scenario 1: App with creator as object (from API)
|
||||
console.log('\n=== Test Scenario 1: Creator as Object ===')
|
||||
const apiAppWithCreatorObject = {
|
||||
id: "1",
|
||||
name: "Test AI App",
|
||||
type: "圖像生成",
|
||||
creator: {
|
||||
id: "user1",
|
||||
name: "John Doe",
|
||||
department: "ITBU"
|
||||
},
|
||||
department: "HQBU", // This should be ignored when creator is object
|
||||
description: "A test AI application",
|
||||
appUrl: "https://example.com",
|
||||
icon: "Brain",
|
||||
iconColor: "from-purple-500 to-pink-500",
|
||||
status: "published",
|
||||
views: 100,
|
||||
likes: 50,
|
||||
rating: 4.5,
|
||||
reviews: 10,
|
||||
createdAt: "2024-01-15"
|
||||
}
|
||||
|
||||
console.log('1. Original API app with creator object:')
|
||||
console.log(apiAppWithCreatorObject)
|
||||
|
||||
console.log('\n2. Processed by loadApps:')
|
||||
const processedApp1 = processApps([apiAppWithCreatorObject])[0]
|
||||
console.log(processedApp1)
|
||||
|
||||
console.log('\n3. handleEditApp result:')
|
||||
const editResult1 = handleEditApp(processedApp1)
|
||||
console.log('Department in form:', editResult1.department)
|
||||
|
||||
// Test scenario 2: App with creator as string (from API)
|
||||
console.log('\n=== Test Scenario 2: Creator as String ===')
|
||||
const apiAppWithCreatorString = {
|
||||
id: "2",
|
||||
name: "Another Test App",
|
||||
type: "語音辨識",
|
||||
creator: "Jane Smith", // String creator
|
||||
department: "MBU1", // This should be used when creator is string
|
||||
description: "Another test application",
|
||||
appUrl: "https://test2.com",
|
||||
icon: "Mic",
|
||||
iconColor: "from-green-500 to-teal-500",
|
||||
status: "draft",
|
||||
views: 50,
|
||||
likes: 25,
|
||||
rating: 4.0,
|
||||
reviews: 5,
|
||||
createdAt: "2024-01-20"
|
||||
}
|
||||
|
||||
console.log('1. Original API app with creator string:')
|
||||
console.log(apiAppWithCreatorString)
|
||||
|
||||
console.log('\n2. Processed by loadApps:')
|
||||
const processedApp2 = processApps([apiAppWithCreatorString])[0]
|
||||
console.log(processedApp2)
|
||||
|
||||
console.log('\n3. handleEditApp result:')
|
||||
const editResult2 = handleEditApp(processedApp2)
|
||||
console.log('Department in form:', editResult2.department)
|
||||
|
||||
// Test scenario 3: Fix the handleEditApp function
|
||||
console.log('\n=== Test Scenario 3: Fixed handleEditApp ===')
|
||||
function handleEditAppFixed(app) {
|
||||
console.log('📝 Editing app (FIXED):', app.name)
|
||||
console.log('📋 App object structure:', {
|
||||
name: app.name,
|
||||
creator: app.creator,
|
||||
department: app.department,
|
||||
hasCreatorObject: typeof app.creator === 'object',
|
||||
hasCreatorProperty: 'creator' in app,
|
||||
hasDepartmentProperty: 'department' in app
|
||||
})
|
||||
|
||||
const newApp = {
|
||||
name: app.name,
|
||||
type: app.type,
|
||||
department: app.department || "HQBU", // FIXED: Use app.department directly
|
||||
creator: app.creator || "",
|
||||
description: app.description,
|
||||
appUrl: app.appUrl || app.demoUrl || "",
|
||||
icon: app.icon || "Bot",
|
||||
iconColor: app.iconColor || "from-blue-500 to-purple-500",
|
||||
}
|
||||
|
||||
console.log('📝 Form populated with app data (FIXED):', newApp)
|
||||
return newApp
|
||||
}
|
||||
|
||||
console.log('1. Test with processed app 1 (creator was object):')
|
||||
const fixedResult1 = handleEditAppFixed(processedApp1)
|
||||
console.log('Department in form (FIXED):', fixedResult1.department)
|
||||
|
||||
console.log('\n2. Test with processed app 2 (creator was string):')
|
||||
const fixedResult2 = handleEditAppFixed(processedApp2)
|
||||
console.log('Department in form (FIXED):', fixedResult2.department)
|
||||
|
||||
// Verify the fix
|
||||
console.log('\n=== Verification ===')
|
||||
const expectedDepartment1 = "ITBU" // Should be from creator.department
|
||||
const expectedDepartment2 = "MBU1" // Should be from app.department
|
||||
|
||||
console.log('Scenario 1 - Expected:', expectedDepartment1, 'Got:', fixedResult1.department, '✅', fixedResult1.department === expectedDepartment1 ? 'PASS' : 'FAIL')
|
||||
console.log('Scenario 2 - Expected:', expectedDepartment2, 'Got:', fixedResult2.department, '✅', fixedResult2.department === expectedDepartment2 ? 'PASS' : 'FAIL')
|
||||
|
||||
if (fixedResult1.department === expectedDepartment1 && fixedResult2.department === expectedDepartment2) {
|
||||
console.log('\n🎉 All tests passed! The department pre-fill fix is working correctly.')
|
||||
} else {
|
||||
console.log('\n❌ Some tests failed. Check the handleEditApp function.')
|
||||
}
|
166
scripts/test-detail-view-edit.js
Normal file
166
scripts/test-detail-view-edit.js
Normal file
@@ -0,0 +1,166 @@
|
||||
const mysql = require('mysql2/promise');
|
||||
|
||||
// Simulate the detailed API response structure
|
||||
const simulateDetailedApiResponse = async () => {
|
||||
try {
|
||||
const connection = await mysql.createConnection({
|
||||
host: 'localhost',
|
||||
user: 'root',
|
||||
password: '123456',
|
||||
database: 'ai_showcase_platform'
|
||||
});
|
||||
|
||||
console.log('=== Testing Detail View Edit Flow ===\n');
|
||||
|
||||
// 1. First, get the actual detailed API response
|
||||
const [apps] = await connection.execute(`
|
||||
SELECT
|
||||
a.id, a.name, a.description, a.type, a.department as app_department,
|
||||
a.creator_name as app_creator_name, a.creator_email as app_creator_email,
|
||||
a.icon, a.icon_color, a.status, a.created_at,
|
||||
u.id as user_id, u.name as user_name, u.email as user_email, u.department as user_department
|
||||
FROM apps a LEFT JOIN users u ON a.creator_id = u.id
|
||||
ORDER BY a.created_at DESC LIMIT 1
|
||||
`);
|
||||
|
||||
if (apps.length === 0) {
|
||||
console.log('No apps found in database');
|
||||
return;
|
||||
}
|
||||
|
||||
const app = apps[0];
|
||||
console.log('Raw database data:');
|
||||
console.log('app_department:', app.app_department);
|
||||
console.log('app_creator_name:', app.app_creator_name);
|
||||
console.log('user_department:', app.user_department);
|
||||
console.log('user_name:', app.user_name);
|
||||
console.log('type:', app.type);
|
||||
console.log('icon:', app.icon);
|
||||
console.log('icon_color:', app.icon_color);
|
||||
console.log('');
|
||||
|
||||
// 2. Simulate the detailed API response structure (like /api/apps/[id])
|
||||
const detailedAppData = {
|
||||
id: app.id,
|
||||
name: app.name,
|
||||
description: app.description,
|
||||
type: app.type, // This is the API type (English)
|
||||
department: app.app_department, // This should be the app's department
|
||||
icon: app.icon,
|
||||
iconColor: app.icon_color,
|
||||
status: app.status,
|
||||
createdAt: app.created_at,
|
||||
creator: {
|
||||
id: app.user_id,
|
||||
name: app.app_creator_name || app.user_name, // Prioritize app.creator_name
|
||||
email: app.app_creator_email || app.user_email,
|
||||
department: app.app_department || app.user_department, // Prioritize app.department
|
||||
role: 'developer'
|
||||
}
|
||||
};
|
||||
|
||||
console.log('Simulated detailed API response:');
|
||||
console.log('detailedAppData:', JSON.stringify(detailedAppData, null, 2));
|
||||
console.log('');
|
||||
|
||||
// 3. Simulate the handleEditApp function processing
|
||||
const handleEditApp = (app) => {
|
||||
console.log('=== handleEditApp Debug ===');
|
||||
console.log('Input app:', app);
|
||||
console.log('app.type:', app.type);
|
||||
console.log('app.department:', app.department);
|
||||
console.log('app.creator:', app.creator);
|
||||
console.log('app.icon:', app.icon);
|
||||
console.log('app.iconColor:', app.iconColor);
|
||||
|
||||
// 處理類型轉換:如果類型是英文的,轉換為中文
|
||||
let displayType = app.type;
|
||||
if (app.type && !['文字處理', '圖像生成', '程式開發', '數據分析', '教育工具', '健康醫療', '金融科技', '物聯網', '區塊鏈', 'AR/VR', '機器學習', '電腦視覺', '自然語言處理', '機器人', '網路安全', '雲端服務', '其他'].includes(app.type)) {
|
||||
displayType = mapApiTypeToDisplayType(app.type);
|
||||
}
|
||||
|
||||
// 處理部門和創建者資料
|
||||
let department = app.department;
|
||||
let creator = app.creator;
|
||||
|
||||
// 如果 app.creator 是物件(來自詳細 API),提取名稱
|
||||
if (app.creator && typeof app.creator === 'object') {
|
||||
creator = app.creator.name || "";
|
||||
// 優先使用應用程式的部門,而不是創建者的部門
|
||||
department = app.department || app.creator.department || "";
|
||||
}
|
||||
|
||||
const newAppData = {
|
||||
name: app.name || "",
|
||||
type: displayType || "文字處理",
|
||||
department: department || "",
|
||||
creator: creator || "",
|
||||
description: app.description || "",
|
||||
appUrl: app.appUrl || app.demoUrl || "",
|
||||
icon: app.icon || "",
|
||||
iconColor: app.iconColor || "",
|
||||
}
|
||||
|
||||
console.log('newAppData:', newAppData);
|
||||
return newAppData;
|
||||
};
|
||||
|
||||
// 4. Test the type conversion function
|
||||
const mapApiTypeToDisplayType = (apiType) => {
|
||||
const typeMap = {
|
||||
'productivity': '文字處理',
|
||||
'ai_model': '圖像生成',
|
||||
'automation': '程式開發',
|
||||
'data_analysis': '數據分析',
|
||||
'educational': '教育工具',
|
||||
'healthcare': '健康醫療',
|
||||
'finance': '金融科技',
|
||||
'iot_device': '物聯網',
|
||||
'blockchain': '區塊鏈',
|
||||
'ar_vr': 'AR/VR',
|
||||
'machine_learning': '機器學習',
|
||||
'computer_vision': '電腦視覺',
|
||||
'nlp': '自然語言處理',
|
||||
'robotics': '機器人',
|
||||
'cybersecurity': '網路安全',
|
||||
'cloud_service': '雲端服務',
|
||||
'other': '其他',
|
||||
// Old English types
|
||||
'web_app': '文字處理',
|
||||
'mobile_app': '文字處理',
|
||||
'desktop_app': '文字處理',
|
||||
'api_service': '程式開發'
|
||||
};
|
||||
return typeMap[apiType] || '其他';
|
||||
};
|
||||
|
||||
// 5. Process the detailed app data
|
||||
const result = handleEditApp(detailedAppData);
|
||||
|
||||
console.log('\n=== Final Result ===');
|
||||
console.log('Expected creator name:', app.app_creator_name || app.user_name);
|
||||
console.log('Expected department:', app.app_department);
|
||||
console.log('Actual result creator:', result.creator);
|
||||
console.log('Actual result department:', result.department);
|
||||
console.log('Actual result type:', result.type);
|
||||
console.log('Actual result icon:', result.icon);
|
||||
console.log('Actual result iconColor:', result.iconColor);
|
||||
|
||||
// 6. Verify the results
|
||||
const expectedCreator = app.app_creator_name || app.user_name;
|
||||
const expectedDepartment = app.app_department;
|
||||
|
||||
console.log('\n=== Verification ===');
|
||||
console.log('Creator match:', result.creator === expectedCreator ? '✅ PASS' : '❌ FAIL');
|
||||
console.log('Department match:', result.department === expectedDepartment ? '✅ PASS' : '❌ FAIL');
|
||||
console.log('Type conversion:', result.type !== app.type ? '✅ PASS (converted)' : '⚠️ No conversion needed');
|
||||
console.log('Icon preserved:', result.icon === app.icon ? '✅ PASS' : '❌ FAIL');
|
||||
console.log('IconColor preserved:', result.iconColor === app.icon_color ? '✅ PASS' : '❌ FAIL');
|
||||
|
||||
await connection.end();
|
||||
} catch (error) {
|
||||
console.error('Test failed:', error);
|
||||
}
|
||||
};
|
||||
|
||||
simulateDetailedApiResponse();
|
92
scripts/test-detailed-api-data.js
Normal file
92
scripts/test-detailed-api-data.js
Normal file
@@ -0,0 +1,92 @@
|
||||
// 測試詳細 API 資料結構,檢查創建者資訊
|
||||
console.log('🧪 測試詳細 API 資料結構...');
|
||||
|
||||
// 模擬詳細 API 的資料結構(基於實際資料庫查詢結果)
|
||||
const detailedAppData = {
|
||||
id: "mdzncsmzelu6n5v6e5",
|
||||
name: "ITBU_佩庭_天氣查詢機器人",
|
||||
description: "SADSADSADASDASDASDAS",
|
||||
creatorId: "user-123",
|
||||
teamId: null,
|
||||
status: "draft",
|
||||
type: "ai_model",
|
||||
filePath: null,
|
||||
techStack: [],
|
||||
tags: [],
|
||||
screenshots: [],
|
||||
demoUrl: "https://dify.theaken.com/chat/xLqNfXDQleoKGROm",
|
||||
githubUrl: null,
|
||||
docsUrl: null,
|
||||
version: "1.0.0",
|
||||
likesCount: 0,
|
||||
viewsCount: 0,
|
||||
rating: 0,
|
||||
createdAt: "2025-08-06T07:28:50.000Z",
|
||||
updatedAt: "2025-08-06T07:28:50.000Z",
|
||||
lastUpdated: "2025-08-06T07:28:50.000Z",
|
||||
creator: {
|
||||
id: "user-123",
|
||||
name: "佩庭", // 實際資料庫中的創建者名稱
|
||||
email: "admin@example.com",
|
||||
department: "ITBU",
|
||||
role: "developer"
|
||||
},
|
||||
team: undefined
|
||||
};
|
||||
|
||||
console.log('📋 詳細 API 資料結構:');
|
||||
console.log('應用名稱:', detailedAppData.name);
|
||||
console.log('創建者物件:', detailedAppData.creator);
|
||||
console.log('創建者名稱:', detailedAppData.creator.name);
|
||||
console.log('創建者部門:', detailedAppData.creator.department);
|
||||
|
||||
// 模擬 handleEditApp 函數處理
|
||||
const handleEditApp = (app) => {
|
||||
console.log('\n=== handleEditApp Debug ===');
|
||||
console.log('Input app:', app);
|
||||
console.log('app.creator:', app.creator);
|
||||
console.log('app.creator.name:', app.creator?.name);
|
||||
console.log('app.creator.department:', app.creator?.department);
|
||||
|
||||
// 處理部門和創建者資料
|
||||
let department = app.department;
|
||||
let creator = app.creator;
|
||||
|
||||
// 如果 app.creator 是物件(來自詳細 API),提取名稱
|
||||
if (app.creator && typeof app.creator === 'object') {
|
||||
creator = app.creator.name || "";
|
||||
department = app.creator.department || app.department || "";
|
||||
}
|
||||
|
||||
const newAppData = {
|
||||
name: app.name || "",
|
||||
type: app.type || "",
|
||||
department: department || "",
|
||||
creator: creator || "",
|
||||
description: app.description || "",
|
||||
appUrl: app.demoUrl || "",
|
||||
icon: app.icon || "",
|
||||
iconColor: app.iconColor || "",
|
||||
};
|
||||
|
||||
console.log('newAppData:', newAppData);
|
||||
return newAppData;
|
||||
};
|
||||
|
||||
// 測試處理
|
||||
const result = handleEditApp(detailedAppData);
|
||||
|
||||
console.log('\n✅ 測試結果:');
|
||||
console.log('期望創建者名稱: 佩庭');
|
||||
console.log('實際創建者名稱:', result.creator);
|
||||
console.log('期望部門: ITBU');
|
||||
console.log('實際部門:', result.department);
|
||||
|
||||
const isCorrect = result.creator === "佩庭" && result.department === "ITBU";
|
||||
console.log('✅ 測試通過:', isCorrect);
|
||||
|
||||
if (isCorrect) {
|
||||
console.log('\n🎉 創建者資訊處理正確!');
|
||||
} else {
|
||||
console.log('\n❌ 創建者資訊處理有問題,需要檢查。');
|
||||
}
|
132
scripts/test-detailed-api-fix.js
Normal file
132
scripts/test-detailed-api-fix.js
Normal file
@@ -0,0 +1,132 @@
|
||||
const mysql = require('mysql2/promise');
|
||||
|
||||
// Test the detailed API fix
|
||||
const testDetailedApiFix = async () => {
|
||||
try {
|
||||
const connection = await mysql.createConnection({
|
||||
host: 'localhost',
|
||||
user: 'root',
|
||||
password: '123456',
|
||||
database: 'ai_showcase_platform'
|
||||
});
|
||||
|
||||
console.log('=== Testing Detailed API Fix ===\n');
|
||||
|
||||
// 1. Get the latest app data
|
||||
const [apps] = await connection.execute(`
|
||||
SELECT
|
||||
a.id, a.name, a.description, a.type, a.department as app_department,
|
||||
a.creator_name as app_creator_name, a.creator_email as app_creator_email,
|
||||
a.icon, a.icon_color, a.status, a.created_at,
|
||||
u.id as user_id, u.name as user_name, u.email as user_email, u.department as user_department
|
||||
FROM apps a LEFT JOIN users u ON a.creator_id = u.id
|
||||
ORDER BY a.created_at DESC LIMIT 1
|
||||
`);
|
||||
|
||||
if (apps.length === 0) {
|
||||
console.log('No apps found in database');
|
||||
return;
|
||||
}
|
||||
|
||||
const app = apps[0];
|
||||
console.log('Database values:');
|
||||
console.log('app_department:', app.app_department);
|
||||
console.log('app_creator_name:', app.app_creator_name);
|
||||
console.log('user_department:', app.user_department);
|
||||
console.log('user_name:', app.user_name);
|
||||
console.log('');
|
||||
|
||||
// 2. Simulate the updated detailed API response structure
|
||||
const detailedAppData = {
|
||||
id: app.id,
|
||||
name: app.name,
|
||||
description: app.description,
|
||||
type: app.type,
|
||||
department: app.app_department, // Now included in detailed API
|
||||
icon: app.icon,
|
||||
iconColor: app.icon_color,
|
||||
status: app.status,
|
||||
createdAt: app.created_at,
|
||||
creator: {
|
||||
id: app.user_id,
|
||||
name: app.app_creator_name || app.user_name, // Prioritize app.creator_name
|
||||
email: app.app_creator_email || app.user_email,
|
||||
department: app.app_department || app.user_department, // Prioritize app.department
|
||||
role: 'developer'
|
||||
}
|
||||
};
|
||||
|
||||
console.log('Simulated detailed API response:');
|
||||
console.log('department:', detailedAppData.department);
|
||||
console.log('creator.name:', detailedAppData.creator.name);
|
||||
console.log('creator.department:', detailedAppData.creator.department);
|
||||
console.log('');
|
||||
|
||||
// 3. Simulate handleEditApp processing
|
||||
const handleEditApp = (app) => {
|
||||
console.log('=== handleEditApp Processing ===');
|
||||
console.log('Input app.department:', app.department);
|
||||
console.log('Input app.creator:', app.creator);
|
||||
|
||||
// 處理部門和創建者資料
|
||||
let department = app.department;
|
||||
let creator = app.creator;
|
||||
|
||||
// 如果 app.creator 是物件(來自詳細 API),提取名稱
|
||||
if (app.creator && typeof app.creator === 'object') {
|
||||
creator = app.creator.name || "";
|
||||
// 優先使用應用程式的部門,而不是創建者的部門
|
||||
department = app.department || app.creator.department || "";
|
||||
}
|
||||
|
||||
const newAppData = {
|
||||
name: app.name || "",
|
||||
type: app.type || "文字處理",
|
||||
department: department || "",
|
||||
creator: creator || "",
|
||||
description: app.description || "",
|
||||
appUrl: app.appUrl || app.demoUrl || "",
|
||||
icon: app.icon || "",
|
||||
iconColor: app.iconColor || "",
|
||||
}
|
||||
|
||||
console.log('newAppData:', newAppData);
|
||||
return newAppData;
|
||||
};
|
||||
|
||||
// 4. Process the detailed app data
|
||||
const result = handleEditApp(detailedAppData);
|
||||
|
||||
console.log('\n=== Final Result ===');
|
||||
console.log('Expected creator name:', app.app_creator_name || app.user_name);
|
||||
console.log('Expected department:', app.app_department);
|
||||
console.log('Actual result creator:', result.creator);
|
||||
console.log('Actual result department:', result.department);
|
||||
|
||||
// 5. Verify the results
|
||||
const expectedCreator = app.app_creator_name || app.user_name;
|
||||
const expectedDepartment = app.app_department;
|
||||
|
||||
console.log('\n=== Verification ===');
|
||||
console.log('Creator match:', result.creator === expectedCreator ? '✅ PASS' : '❌ FAIL');
|
||||
console.log('Department match:', result.department === expectedDepartment ? '✅ PASS' : '❌ FAIL');
|
||||
|
||||
if (result.creator !== expectedCreator) {
|
||||
console.log('❌ Creator mismatch!');
|
||||
console.log('Expected:', expectedCreator);
|
||||
console.log('Actual:', result.creator);
|
||||
}
|
||||
|
||||
if (result.department !== expectedDepartment) {
|
||||
console.log('❌ Department mismatch!');
|
||||
console.log('Expected:', expectedDepartment);
|
||||
console.log('Actual:', result.department);
|
||||
}
|
||||
|
||||
await connection.end();
|
||||
} catch (error) {
|
||||
console.error('Test failed:', error);
|
||||
}
|
||||
};
|
||||
|
||||
testDetailedApiFix();
|
118
scripts/test-detailed-api-logic.js
Normal file
118
scripts/test-detailed-api-logic.js
Normal file
@@ -0,0 +1,118 @@
|
||||
// Test the detailed API logic without database connection
|
||||
const testDetailedApiLogic = () => {
|
||||
console.log('=== Testing Detailed API Logic ===\n');
|
||||
|
||||
// Simulate the database values we expect
|
||||
const mockAppData = {
|
||||
app_department: 'MBU1',
|
||||
app_creator_name: '佩庭',
|
||||
user_department: 'ITBU',
|
||||
user_name: '系統管理員'
|
||||
};
|
||||
|
||||
console.log('Mock database values:');
|
||||
console.log('app_department:', mockAppData.app_department);
|
||||
console.log('app_creator_name:', mockAppData.app_creator_name);
|
||||
console.log('user_department:', mockAppData.user_department);
|
||||
console.log('user_name:', mockAppData.user_name);
|
||||
console.log('');
|
||||
|
||||
// Simulate the updated detailed API response structure
|
||||
const detailedAppData = {
|
||||
id: 1,
|
||||
name: 'Test App',
|
||||
description: 'Test Description',
|
||||
type: 'productivity',
|
||||
department: mockAppData.app_department, // Now included in detailed API
|
||||
icon: 'Bot',
|
||||
iconColor: 'from-blue-500 to-purple-500',
|
||||
status: 'published',
|
||||
createdAt: '2024-01-01',
|
||||
creator: {
|
||||
id: 1,
|
||||
name: mockAppData.app_creator_name || mockAppData.user_name, // Prioritize app.creator_name
|
||||
email: 'test@example.com',
|
||||
department: mockAppData.app_department || mockAppData.user_department, // Prioritize app.department
|
||||
role: 'developer'
|
||||
}
|
||||
};
|
||||
|
||||
console.log('Simulated detailed API response:');
|
||||
console.log('department:', detailedAppData.department);
|
||||
console.log('creator.name:', detailedAppData.creator.name);
|
||||
console.log('creator.department:', detailedAppData.creator.department);
|
||||
console.log('');
|
||||
|
||||
// Simulate handleEditApp processing
|
||||
const handleEditApp = (app) => {
|
||||
console.log('=== handleEditApp Processing ===');
|
||||
console.log('Input app.department:', app.department);
|
||||
console.log('Input app.creator:', app.creator);
|
||||
|
||||
// 處理部門和創建者資料
|
||||
let department = app.department;
|
||||
let creator = app.creator;
|
||||
|
||||
// 如果 app.creator 是物件(來自詳細 API),提取名稱
|
||||
if (app.creator && typeof app.creator === 'object') {
|
||||
creator = app.creator.name || "";
|
||||
// 優先使用應用程式的部門,而不是創建者的部門
|
||||
department = app.department || app.creator.department || "";
|
||||
}
|
||||
|
||||
const newAppData = {
|
||||
name: app.name || "",
|
||||
type: app.type || "文字處理",
|
||||
department: department || "",
|
||||
creator: creator || "",
|
||||
description: app.description || "",
|
||||
appUrl: app.appUrl || app.demoUrl || "",
|
||||
icon: app.icon || "",
|
||||
iconColor: app.iconColor || "",
|
||||
}
|
||||
|
||||
console.log('newAppData:', newAppData);
|
||||
return newAppData;
|
||||
};
|
||||
|
||||
// Process the detailed app data
|
||||
const result = handleEditApp(detailedAppData);
|
||||
|
||||
console.log('\n=== Final Result ===');
|
||||
console.log('Expected creator name:', mockAppData.app_creator_name || mockAppData.user_name);
|
||||
console.log('Expected department:', mockAppData.app_department);
|
||||
console.log('Actual result creator:', result.creator);
|
||||
console.log('Actual result department:', result.department);
|
||||
|
||||
// Verify the results
|
||||
const expectedCreator = mockAppData.app_creator_name || mockAppData.user_name;
|
||||
const expectedDepartment = mockAppData.app_department;
|
||||
|
||||
console.log('\n=== Verification ===');
|
||||
console.log('Creator match:', result.creator === expectedCreator ? '✅ PASS' : '❌ FAIL');
|
||||
console.log('Department match:', result.department === expectedDepartment ? '✅ PASS' : '❌ FAIL');
|
||||
|
||||
if (result.creator !== expectedCreator) {
|
||||
console.log('❌ Creator mismatch!');
|
||||
console.log('Expected:', expectedCreator);
|
||||
console.log('Actual:', result.creator);
|
||||
}
|
||||
|
||||
if (result.department !== expectedDepartment) {
|
||||
console.log('❌ Department mismatch!');
|
||||
console.log('Expected:', expectedDepartment);
|
||||
console.log('Actual:', result.department);
|
||||
}
|
||||
|
||||
console.log('\n=== Summary ===');
|
||||
console.log('The detailed API should now return:');
|
||||
console.log('- department: app.department (MBU1)');
|
||||
console.log('- creator.name: app.creator_name (佩庭)');
|
||||
console.log('- creator.department: app.department (MBU1)');
|
||||
console.log('');
|
||||
console.log('The handleEditApp function should extract:');
|
||||
console.log('- department: app.department (MBU1)');
|
||||
console.log('- creator: app.creator.name (佩庭)');
|
||||
};
|
||||
|
||||
testDetailedApiLogic();
|
155
scripts/test-edit-app-consistency.js
Normal file
155
scripts/test-edit-app-consistency.js
Normal file
@@ -0,0 +1,155 @@
|
||||
// 模擬前端類型映射函數
|
||||
const mapApiTypeToDisplayType = (apiType) => {
|
||||
const typeMap = {
|
||||
'productivity': '文字處理',
|
||||
'ai_model': '圖像生成',
|
||||
'automation': '程式開發',
|
||||
'data_analysis': '數據分析',
|
||||
'educational': '教育工具',
|
||||
'healthcare': '健康醫療',
|
||||
'finance': '金融科技',
|
||||
'iot_device': '物聯網',
|
||||
'blockchain': '區塊鏈',
|
||||
'ar_vr': 'AR/VR',
|
||||
'machine_learning': '機器學習',
|
||||
'computer_vision': '電腦視覺',
|
||||
'nlp': '自然語言處理',
|
||||
'robotics': '機器人',
|
||||
'cybersecurity': '網路安全',
|
||||
'cloud_service': '雲端服務',
|
||||
'other': '其他'
|
||||
};
|
||||
return typeMap[apiType] || '其他';
|
||||
};
|
||||
|
||||
// 模擬 handleEditApp 函數(修正後)
|
||||
const handleEditApp = (app) => {
|
||||
console.log('=== handleEditApp Debug ===');
|
||||
console.log('Input app:', app);
|
||||
console.log('app.type:', app.type);
|
||||
console.log('app.department:', app.department);
|
||||
console.log('app.creator:', app.creator);
|
||||
|
||||
// 處理類型轉換:如果類型是英文的,轉換為中文
|
||||
let displayType = app.type;
|
||||
if (app.type && !['文字處理', '圖像生成', '程式開發', '數據分析', '教育工具', '健康醫療', '金融科技', '物聯網', '區塊鏈', 'AR/VR', '機器學習', '電腦視覺', '自然語言處理', '機器人', '網路安全', '雲端服務', '其他'].includes(app.type)) {
|
||||
displayType = mapApiTypeToDisplayType(app.type);
|
||||
}
|
||||
|
||||
// 處理部門和創建者資料
|
||||
let department = app.department;
|
||||
let creator = app.creator;
|
||||
|
||||
// 如果 app.creator 是物件(來自詳細 API),提取名稱
|
||||
if (app.creator && typeof app.creator === 'object') {
|
||||
creator = app.creator.name || "";
|
||||
department = app.creator.department || app.department || "HQBU";
|
||||
}
|
||||
|
||||
const newAppData = {
|
||||
name: app.name,
|
||||
type: displayType,
|
||||
department: department || "HQBU",
|
||||
creator: creator || "",
|
||||
description: app.description,
|
||||
appUrl: app.appUrl || app.demoUrl || "",
|
||||
icon: app.icon || "Bot",
|
||||
iconColor: app.iconColor || "from-blue-500 to-purple-500",
|
||||
};
|
||||
|
||||
console.log('newAppData:', newAppData);
|
||||
return newAppData;
|
||||
};
|
||||
|
||||
async function testEditAppConsistency() {
|
||||
console.log('🧪 測試編輯應用功能一致性...\n');
|
||||
|
||||
// 1. 模擬列表中的應用資料(來自 loadApps)
|
||||
const listApp = {
|
||||
id: 'test123',
|
||||
name: '測試應用程式',
|
||||
description: '這是一個測試應用程式',
|
||||
type: '文字處理', // 已經轉換為中文
|
||||
department: 'HQBU',
|
||||
creator: '測試創建者',
|
||||
appUrl: 'https://example.com',
|
||||
icon: 'Bot',
|
||||
iconColor: 'from-blue-500 to-purple-500'
|
||||
};
|
||||
|
||||
// 2. 模擬詳細 API 返回的應用資料
|
||||
const detailApp = {
|
||||
id: 'test123',
|
||||
name: '測試應用程式',
|
||||
description: '這是一個測試應用程式',
|
||||
type: 'productivity', // 英文類型
|
||||
department: 'HQBU',
|
||||
creator: {
|
||||
id: 'user123',
|
||||
name: '測試創建者',
|
||||
email: 'test@example.com',
|
||||
department: 'HQBU',
|
||||
role: 'developer'
|
||||
},
|
||||
demoUrl: 'https://example.com',
|
||||
icon: 'Bot',
|
||||
iconColor: 'from-blue-500 to-purple-500'
|
||||
};
|
||||
|
||||
console.log('📋 測試列表中的編輯功能:');
|
||||
console.log('輸入資料:', listApp);
|
||||
const listResult = handleEditApp(listApp);
|
||||
console.log('處理結果:', listResult);
|
||||
|
||||
console.log('\n📋 測試詳細對話框中的編輯功能:');
|
||||
console.log('輸入資料:', detailApp);
|
||||
const detailResult = handleEditApp(detailApp);
|
||||
console.log('處理結果:', detailResult);
|
||||
|
||||
// 3. 驗證一致性
|
||||
console.log('\n✅ 一致性檢查:');
|
||||
const fieldsToCheck = ['name', 'type', 'department', 'creator', 'description', 'appUrl', 'icon', 'iconColor'];
|
||||
|
||||
fieldsToCheck.forEach(field => {
|
||||
const listValue = listResult[field];
|
||||
const detailValue = detailResult[field];
|
||||
const isConsistent = listValue === detailValue;
|
||||
console.log(` ${field}: ${listValue} vs ${detailValue} ${isConsistent ? '✅' : '❌'}`);
|
||||
});
|
||||
|
||||
// 4. 測試不同類型的轉換
|
||||
console.log('\n🔍 測試類型轉換:');
|
||||
const testTypes = [
|
||||
{ apiType: 'productivity', expected: '文字處理' },
|
||||
{ apiType: 'ai_model', expected: '圖像生成' },
|
||||
{ apiType: 'automation', expected: '程式開發' },
|
||||
{ apiType: 'data_analysis', expected: '數據分析' },
|
||||
{ apiType: 'educational', expected: '教育工具' },
|
||||
{ apiType: 'healthcare', expected: '健康醫療' },
|
||||
{ apiType: 'finance', expected: '金融科技' },
|
||||
{ apiType: 'iot_device', expected: '物聯網' },
|
||||
{ apiType: 'blockchain', expected: '區塊鏈' },
|
||||
{ apiType: 'ar_vr', expected: 'AR/VR' },
|
||||
{ apiType: 'machine_learning', expected: '機器學習' },
|
||||
{ apiType: 'computer_vision', expected: '電腦視覺' },
|
||||
{ apiType: 'nlp', expected: '自然語言處理' },
|
||||
{ apiType: 'robotics', expected: '機器人' },
|
||||
{ apiType: 'cybersecurity', expected: '網路安全' },
|
||||
{ apiType: 'cloud_service', expected: '雲端服務' },
|
||||
{ apiType: 'other', expected: '其他' }
|
||||
];
|
||||
|
||||
testTypes.forEach(({ apiType, expected }) => {
|
||||
const testApp = {
|
||||
...detailApp,
|
||||
type: apiType
|
||||
};
|
||||
const result = handleEditApp(testApp);
|
||||
const isCorrect = result.type === expected;
|
||||
console.log(` ${apiType} -> ${result.type} ${isCorrect ? '✅' : '❌'}`);
|
||||
});
|
||||
|
||||
console.log('\n✅ 編輯應用功能一致性測試完成!');
|
||||
}
|
||||
|
||||
testEditAppConsistency().catch(console.error);
|
210
scripts/test-edit-app-database-values.js
Normal file
210
scripts/test-edit-app-database-values.js
Normal file
@@ -0,0 +1,210 @@
|
||||
// 測試編輯應用功能是否正確使用資料庫值而非預設值
|
||||
console.log('🧪 測試編輯應用功能資料庫值處理...');
|
||||
|
||||
// 模擬前端類型映射函數
|
||||
const mapApiTypeToDisplayType = (apiType) => {
|
||||
const typeMap = {
|
||||
'productivity': '文字處理',
|
||||
'ai_model': '圖像生成',
|
||||
'automation': '程式開發',
|
||||
'data_analysis': '數據分析',
|
||||
'educational': '教育工具',
|
||||
'healthcare': '健康醫療',
|
||||
'finance': '金融科技',
|
||||
'iot_device': '物聯網',
|
||||
'blockchain': '區塊鏈',
|
||||
'ar_vr': 'AR/VR',
|
||||
'machine_learning': '機器學習',
|
||||
'computer_vision': '電腦視覺',
|
||||
'nlp': '自然語言處理',
|
||||
'robotics': '機器人',
|
||||
'cybersecurity': '網路安全',
|
||||
'cloud_service': '雲端服務',
|
||||
'other': '其他',
|
||||
// 舊的英文類型映射
|
||||
'web_app': '文字處理',
|
||||
'mobile_app': '文字處理',
|
||||
'desktop_app': '文字處理',
|
||||
'api_service': '程式開發'
|
||||
};
|
||||
return typeMap[apiType] || '其他';
|
||||
};
|
||||
|
||||
// 模擬修正後的 handleEditApp 函數
|
||||
const handleEditApp = (app) => {
|
||||
console.log('=== handleEditApp Debug ===');
|
||||
console.log('Input app:', app);
|
||||
console.log('app.type:', app.type);
|
||||
console.log('app.department:', app.department);
|
||||
console.log('app.creator:', app.creator);
|
||||
console.log('app.icon:', app.icon);
|
||||
console.log('app.iconColor:', app.iconColor);
|
||||
|
||||
// 處理類型轉換:如果類型是英文的,轉換為中文
|
||||
let displayType = app.type;
|
||||
if (app.type && !['文字處理', '圖像生成', '程式開發', '數據分析', '教育工具', '健康醫療', '金融科技', '物聯網', '區塊鏈', 'AR/VR', '機器學習', '電腦視覺', '自然語言處理', '機器人', '網路安全', '雲端服務', '其他'].includes(app.type)) {
|
||||
displayType = mapApiTypeToDisplayType(app.type);
|
||||
}
|
||||
|
||||
// 處理部門和創建者資料
|
||||
let department = app.department;
|
||||
let creator = app.creator;
|
||||
|
||||
// 如果 app.creator 是物件(來自詳細 API),提取名稱
|
||||
if (app.creator && typeof app.creator === 'object') {
|
||||
creator = app.creator.name || "";
|
||||
department = app.creator.department || app.department || "";
|
||||
}
|
||||
|
||||
const newAppData = {
|
||||
name: app.name || "",
|
||||
type: displayType || "文字處理",
|
||||
department: department || "",
|
||||
creator: creator || "",
|
||||
description: app.description || "",
|
||||
appUrl: app.appUrl || app.demoUrl || "",
|
||||
icon: app.icon || "",
|
||||
iconColor: app.iconColor || "",
|
||||
};
|
||||
|
||||
console.log('newAppData:', newAppData);
|
||||
return newAppData;
|
||||
};
|
||||
|
||||
async function testEditAppDatabaseValues() {
|
||||
console.log('\n📋 測試案例 1: 資料庫有實際值的應用程式');
|
||||
|
||||
// 模擬來自詳細 API 的資料(有實際資料庫值)
|
||||
const appWithRealData = {
|
||||
id: "test-1",
|
||||
name: "真實 AI 應用",
|
||||
description: "這是一個真實的應用程式",
|
||||
type: "productivity", // 英文 API 類型
|
||||
department: "ITBU", // 實際部門
|
||||
creator: {
|
||||
id: "user-1",
|
||||
name: "張三", // 實際創建者名稱
|
||||
email: "zhang@example.com",
|
||||
department: "ITBU",
|
||||
role: "developer"
|
||||
},
|
||||
icon: "Zap", // 實際圖示
|
||||
iconColor: "from-yellow-500 to-orange-500", // 實際圖示顏色
|
||||
appUrl: "https://example.com/app",
|
||||
demoUrl: "https://demo.example.com"
|
||||
};
|
||||
|
||||
const result1 = handleEditApp(appWithRealData);
|
||||
|
||||
console.log('\n✅ 測試案例 1 結果:');
|
||||
console.log('期望: 使用資料庫的實際值');
|
||||
console.log('實際結果:', result1);
|
||||
|
||||
// 驗證結果
|
||||
const expected1 = {
|
||||
name: "真實 AI 應用",
|
||||
type: "文字處理", // 應該從 productivity 轉換
|
||||
department: "ITBU", // 應該使用實際部門
|
||||
creator: "張三", // 應該從物件提取名稱
|
||||
description: "這是一個真實的應用程式",
|
||||
appUrl: "https://example.com/app",
|
||||
icon: "Zap", // 應該使用實際圖示
|
||||
iconColor: "from-yellow-500 to-orange-500" // 應該使用實際顏色
|
||||
};
|
||||
|
||||
const isCorrect1 = JSON.stringify(result1) === JSON.stringify(expected1);
|
||||
console.log('✅ 測試案例 1 通過:', isCorrect1);
|
||||
|
||||
console.log('\n📋 測試案例 2: 資料庫值為空字串的應用程式');
|
||||
|
||||
// 模擬資料庫值為空字串的情況
|
||||
const appWithEmptyData = {
|
||||
id: "test-2",
|
||||
name: "空值測試應用",
|
||||
description: "測試空值處理",
|
||||
type: "other",
|
||||
department: "", // 空字串
|
||||
creator: {
|
||||
id: "user-2",
|
||||
name: "", // 空字串
|
||||
email: "test@example.com",
|
||||
department: "", // 空字串
|
||||
role: "user"
|
||||
},
|
||||
icon: "", // 空字串
|
||||
iconColor: "", // 空字串
|
||||
appUrl: "",
|
||||
demoUrl: ""
|
||||
};
|
||||
|
||||
const result2 = handleEditApp(appWithEmptyData);
|
||||
|
||||
console.log('\n✅ 測試案例 2 結果:');
|
||||
console.log('期望: 保持空字串,不使用預設值');
|
||||
console.log('實際結果:', result2);
|
||||
|
||||
// 驗證結果
|
||||
const expected2 = {
|
||||
name: "空值測試應用",
|
||||
type: "其他",
|
||||
department: "", // 應該保持空字串
|
||||
creator: "", // 應該保持空字串
|
||||
description: "測試空值處理",
|
||||
appUrl: "",
|
||||
icon: "", // 應該保持空字串
|
||||
iconColor: "" // 應該保持空字串
|
||||
};
|
||||
|
||||
const isCorrect2 = JSON.stringify(result2) === JSON.stringify(expected2);
|
||||
console.log('✅ 測試案例 2 通過:', isCorrect2);
|
||||
|
||||
console.log('\n📋 測試案例 3: 來自列表 API 的資料(字串格式)');
|
||||
|
||||
// 模擬來自列表 API 的資料(字串格式)
|
||||
const appFromList = {
|
||||
id: "test-3",
|
||||
name: "列表應用",
|
||||
description: "來自列表的應用",
|
||||
type: "文字處理", // 已經是中文
|
||||
department: "HQBU", // 字串格式
|
||||
creator: "李四", // 字串格式
|
||||
icon: "Bot", // 字串格式
|
||||
iconColor: "from-blue-500 to-purple-500", // 字串格式
|
||||
appUrl: "https://list.example.com"
|
||||
};
|
||||
|
||||
const result3 = handleEditApp(appFromList);
|
||||
|
||||
console.log('\n✅ 測試案例 3 結果:');
|
||||
console.log('期望: 直接使用字串值');
|
||||
console.log('實際結果:', result3);
|
||||
|
||||
// 驗證結果
|
||||
const expected3 = {
|
||||
name: "列表應用",
|
||||
type: "文字處理",
|
||||
department: "HQBU",
|
||||
creator: "李四",
|
||||
description: "來自列表的應用",
|
||||
appUrl: "https://list.example.com",
|
||||
icon: "Bot",
|
||||
iconColor: "from-blue-500 to-purple-500"
|
||||
};
|
||||
|
||||
const isCorrect3 = JSON.stringify(result3) === JSON.stringify(expected3);
|
||||
console.log('✅ 測試案例 3 通過:', isCorrect3);
|
||||
|
||||
console.log('\n📊 總結:');
|
||||
console.log(`✅ 測試案例 1 (實際資料庫值): ${isCorrect1 ? '通過' : '失敗'}`);
|
||||
console.log(`✅ 測試案例 2 (空字串處理): ${isCorrect2 ? '通過' : '失敗'}`);
|
||||
console.log(`✅ 測試案例 3 (列表資料格式): ${isCorrect3 ? '通過' : '失敗'}`);
|
||||
|
||||
if (isCorrect1 && isCorrect2 && isCorrect3) {
|
||||
console.log('\n🎉 所有測試案例通過!編輯功能現在正確使用資料庫值而非預設值。');
|
||||
} else {
|
||||
console.log('\n❌ 部分測試案例失敗,需要進一步檢查。');
|
||||
}
|
||||
}
|
||||
|
||||
// 執行測試
|
||||
testEditAppDatabaseValues().catch(console.error);
|
142
scripts/test-edit-app-department-fix.js
Normal file
142
scripts/test-edit-app-department-fix.js
Normal file
@@ -0,0 +1,142 @@
|
||||
// 測試編輯應用功能部門資訊修正
|
||||
console.log('🧪 測試編輯應用功能部門資訊修正...');
|
||||
|
||||
// 模擬前端類型映射函數
|
||||
const mapApiTypeToDisplayType = (apiType) => {
|
||||
const typeMap = {
|
||||
'productivity': '文字處理',
|
||||
'ai_model': '圖像生成',
|
||||
'automation': '程式開發',
|
||||
'data_analysis': '數據分析',
|
||||
'educational': '教育工具',
|
||||
'healthcare': '健康醫療',
|
||||
'finance': '金融科技',
|
||||
'iot_device': '物聯網',
|
||||
'blockchain': '區塊鏈',
|
||||
'ar_vr': 'AR/VR',
|
||||
'machine_learning': '機器學習',
|
||||
'computer_vision': '電腦視覺',
|
||||
'nlp': '自然語言處理',
|
||||
'robotics': '機器人',
|
||||
'cybersecurity': '網路安全',
|
||||
'cloud_service': '雲端服務',
|
||||
'other': '其他'
|
||||
};
|
||||
return typeMap[apiType] || '其他';
|
||||
};
|
||||
|
||||
// 模擬修正後的 handleEditApp 函數
|
||||
const handleEditApp = (app) => {
|
||||
console.log('=== handleEditApp Debug ===');
|
||||
console.log('Input app:', app);
|
||||
console.log('app.department:', app.department);
|
||||
console.log('app.creator:', app.creator);
|
||||
|
||||
// 處理類型轉換:如果類型是英文的,轉換為中文
|
||||
let displayType = app.type;
|
||||
if (app.type && !['文字處理', '圖像生成', '程式開發', '數據分析', '教育工具', '健康醫療', '金融科技', '物聯網', '區塊鏈', 'AR/VR', '機器學習', '電腦視覺', '自然語言處理', '機器人', '網路安全', '雲端服務', '其他'].includes(app.type)) {
|
||||
displayType = mapApiTypeToDisplayType(app.type);
|
||||
}
|
||||
|
||||
// 處理部門和創建者資料
|
||||
let department = app.department;
|
||||
let creator = app.creator;
|
||||
|
||||
// 如果 app.creator 是物件(來自詳細 API),提取名稱
|
||||
if (app.creator && typeof app.creator === 'object') {
|
||||
creator = app.creator.name || "";
|
||||
// 優先使用應用程式的部門,而不是創建者的部門
|
||||
department = app.department || app.creator.department || "";
|
||||
}
|
||||
|
||||
const newAppData = {
|
||||
name: app.name || "",
|
||||
type: displayType || "文字處理",
|
||||
department: department || "",
|
||||
creator: creator || "",
|
||||
description: app.description || "",
|
||||
appUrl: app.appUrl || app.demoUrl || "",
|
||||
icon: app.icon || "",
|
||||
iconColor: app.iconColor || "",
|
||||
};
|
||||
|
||||
console.log('newAppData:', newAppData);
|
||||
return newAppData;
|
||||
};
|
||||
|
||||
async function testEditAppDepartmentFix() {
|
||||
console.log('\n📋 測試案例 1: 來自列表 API 的資料');
|
||||
|
||||
// 模擬來自列表 API 的資料(基於實際資料庫資料)
|
||||
const listAppData = {
|
||||
id: "mdzotctmlayh9u9iogt",
|
||||
name: "Wu Petty",
|
||||
description: "ewqewqewqewqeqwewqewq",
|
||||
type: "automation",
|
||||
department: "MBU1", // 應用程式的部門
|
||||
creator: {
|
||||
id: "admin-1754374591679",
|
||||
name: "佩庭", // 創建者名稱
|
||||
email: "admin@example.com",
|
||||
department: "ITBU", // 創建者的部門
|
||||
role: "admin"
|
||||
},
|
||||
icon: "Zap",
|
||||
iconColor: "from-yellow-500 to-orange-500",
|
||||
appUrl: "https://example.com/app"
|
||||
};
|
||||
|
||||
const result1 = handleEditApp(listAppData);
|
||||
|
||||
console.log('\n✅ 測試案例 1 結果:');
|
||||
console.log('期望創建者名稱: 佩庭');
|
||||
console.log('實際創建者名稱:', result1.creator);
|
||||
console.log('期望部門: MBU1 (應用程式部門)');
|
||||
console.log('實際部門:', result1.department);
|
||||
|
||||
const isCorrect1 = result1.creator === "佩庭" && result1.department === "MBU1";
|
||||
console.log('✅ 測試案例 1 通過:', isCorrect1);
|
||||
|
||||
console.log('\n📋 測試案例 2: 來自詳細 API 的資料');
|
||||
|
||||
// 模擬來自詳細 API 的資料
|
||||
const detailAppData = {
|
||||
id: "mdzotctmlayh9u9iogt",
|
||||
name: "Wu Petty",
|
||||
description: "ewqewqewqewqeqwewqewq",
|
||||
type: "automation",
|
||||
department: "MBU1", // 應用程式的部門
|
||||
creator: {
|
||||
id: "admin-1754374591679",
|
||||
name: "佩庭",
|
||||
email: "admin@example.com",
|
||||
department: "ITBU", // 創建者的部門
|
||||
role: "admin"
|
||||
},
|
||||
demoUrl: "https://example.com/demo"
|
||||
};
|
||||
|
||||
const result2 = handleEditApp(detailAppData);
|
||||
|
||||
console.log('\n✅ 測試案例 2 結果:');
|
||||
console.log('期望創建者名稱: 佩庭');
|
||||
console.log('實際創建者名稱:', result2.creator);
|
||||
console.log('期望部門: MBU1 (應用程式部門)');
|
||||
console.log('實際部門:', result2.department);
|
||||
|
||||
const isCorrect2 = result2.creator === "佩庭" && result2.department === "MBU1";
|
||||
console.log('✅ 測試案例 2 通過:', isCorrect2);
|
||||
|
||||
console.log('\n📊 總結:');
|
||||
console.log(`✅ 測試案例 1 (列表資料): ${isCorrect1 ? '通過' : '失敗'}`);
|
||||
console.log(`✅ 測試案例 2 (詳細資料): ${isCorrect2 ? '通過' : '失敗'}`);
|
||||
|
||||
if (isCorrect1 && isCorrect2) {
|
||||
console.log('\n🎉 部門資訊修正成功!現在正確使用應用程式的部門而非創建者的部門。');
|
||||
} else {
|
||||
console.log('\n❌ 部分測試案例失敗,需要進一步檢查。');
|
||||
}
|
||||
}
|
||||
|
||||
// 執行測試
|
||||
testEditAppDepartmentFix().catch(console.error);
|
@@ -1,138 +0,0 @@
|
||||
const mysql = require('mysql2/promise');
|
||||
|
||||
const dbConfig = {
|
||||
host: process.env.DB_HOST || 'mysql.theaken.com',
|
||||
port: parseInt(process.env.DB_PORT || '33306'),
|
||||
user: process.env.DB_USER || 'AI_Platform',
|
||||
password: process.env.DB_PASSWORD || 'Aa123456',
|
||||
database: process.env.DB_NAME || 'db_AI_Platform',
|
||||
charset: 'utf8mb4',
|
||||
timezone: '+08:00'
|
||||
};
|
||||
|
||||
async function testFrontendAppCreation() {
|
||||
let connection;
|
||||
|
||||
try {
|
||||
console.log('🧪 測試前端應用程式創建流程...');
|
||||
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
console.log('✅ 資料庫連接成功');
|
||||
|
||||
// 1. 創建測試用戶
|
||||
const userId = Date.now().toString(36) + Math.random().toString(36).substr(2);
|
||||
const userData = {
|
||||
id: userId,
|
||||
name: '測試用戶',
|
||||
email: 'test@example.com',
|
||||
password: '$2b$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/LewdBPj4J/HS.i8eK', // 密碼: test123
|
||||
role: 'developer',
|
||||
department: 'IT',
|
||||
join_date: new Date(),
|
||||
created_at: new Date(),
|
||||
updated_at: new Date()
|
||||
};
|
||||
|
||||
await connection.execute(
|
||||
'INSERT INTO users (id, name, email, password_hash, role, department, join_date, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)',
|
||||
[userData.id, userData.name, userData.email, userData.password, userData.role, userData.department, userData.join_date, userData.created_at, userData.updated_at]
|
||||
);
|
||||
console.log('✅ 測試用戶創建成功');
|
||||
|
||||
// 2. 模擬前端提交的應用程式資料
|
||||
const frontendAppData = {
|
||||
name: '測試前端應用',
|
||||
description: '這是一個通過前端界面創建的測試應用程式',
|
||||
type: 'productivity', // 映射自 '文字處理'
|
||||
demoUrl: 'https://example.com/demo',
|
||||
githubUrl: 'https://github.com/example/app',
|
||||
docsUrl: 'https://docs.example.com',
|
||||
techStack: ['React', 'TypeScript', 'Tailwind CSS'],
|
||||
tags: ['生產力工具', '文字處理'],
|
||||
version: '1.0.0'
|
||||
};
|
||||
|
||||
console.log('📋 前端提交的資料:', frontendAppData);
|
||||
|
||||
// 3. 創建應用程式(模擬 API 調用)
|
||||
const appId = Date.now().toString(36) + Math.random().toString(36).substr(2);
|
||||
const appData = {
|
||||
id: appId,
|
||||
name: frontendAppData.name,
|
||||
description: frontendAppData.description,
|
||||
creator_id: userId,
|
||||
team_id: null,
|
||||
type: frontendAppData.type,
|
||||
tech_stack: JSON.stringify(frontendAppData.techStack),
|
||||
tags: JSON.stringify(frontendAppData.tags),
|
||||
demo_url: frontendAppData.demoUrl,
|
||||
github_url: frontendAppData.githubUrl,
|
||||
docs_url: frontendAppData.docsUrl,
|
||||
version: frontendAppData.version,
|
||||
status: 'draft'
|
||||
};
|
||||
|
||||
await connection.execute(
|
||||
`INSERT INTO apps (
|
||||
id, name, description, creator_id, team_id, type,
|
||||
tech_stack, tags, demo_url, github_url, docs_url,
|
||||
version, status, created_at, updated_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())`,
|
||||
[
|
||||
appData.id, appData.name, appData.description, appData.creator_id,
|
||||
appData.team_id, appData.type, appData.tech_stack, appData.tags,
|
||||
appData.demo_url, appData.github_url, appData.docs_url,
|
||||
appData.version, appData.status
|
||||
]
|
||||
);
|
||||
console.log('✅ 應用程式創建成功');
|
||||
|
||||
// 4. 驗證應用程式是否正確保存到資料庫
|
||||
const [apps] = await connection.execute(
|
||||
`SELECT a.*, u.name as creator_name
|
||||
FROM apps a
|
||||
LEFT JOIN users u ON a.creator_id = u.id
|
||||
WHERE a.id = ?`,
|
||||
[appId]
|
||||
);
|
||||
|
||||
if (apps.length > 0) {
|
||||
const app = apps[0];
|
||||
console.log('\n📋 資料庫中的應用程式資料:');
|
||||
console.log(` ID: ${app.id}`);
|
||||
console.log(` 名稱: ${app.name}`);
|
||||
console.log(` 描述: ${app.description}`);
|
||||
console.log(` 類型: ${app.type}`);
|
||||
console.log(` 狀態: ${app.status}`);
|
||||
console.log(` 創建者: ${app.creator_name}`);
|
||||
console.log(` 技術棧: ${app.tech_stack}`);
|
||||
console.log(` 標籤: ${app.tags}`);
|
||||
console.log(` 演示連結: ${app.demo_url}`);
|
||||
console.log(` GitHub: ${app.github_url}`);
|
||||
console.log(` 文檔: ${app.docs_url}`);
|
||||
console.log(` 版本: ${app.version}`);
|
||||
console.log(` 創建時間: ${app.created_at}`);
|
||||
|
||||
console.log('\n✅ 前端應用程式創建測試成功!');
|
||||
console.log('🎯 問題已解決:前端現在可以正確創建應用程式並保存到資料庫');
|
||||
} else {
|
||||
console.log('❌ 應用程式未在資料庫中找到');
|
||||
}
|
||||
|
||||
// 5. 清理測試資料
|
||||
await connection.execute('DELETE FROM apps WHERE id = ?', [appId]);
|
||||
await connection.execute('DELETE FROM users WHERE id = ?', [userId]);
|
||||
console.log('✅ 測試資料清理完成');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 測試失敗:', error);
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
console.log('🔌 資料庫連接已關閉');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 執行測試
|
||||
testFrontendAppCreation().catch(console.error);
|
@@ -1,101 +0,0 @@
|
||||
const http = require('http');
|
||||
const jwt = require('jsonwebtoken');
|
||||
|
||||
const JWT_SECRET = process.env.JWT_SECRET || 'good777';
|
||||
|
||||
// 生成測試 Token
|
||||
function generateTestToken() {
|
||||
return jwt.sign({
|
||||
userId: 'mdxxt1xt7slle4g8wz8',
|
||||
email: 'petty091901@gmail.com',
|
||||
role: 'admin'
|
||||
}, JWT_SECRET, { expiresIn: '1h' });
|
||||
}
|
||||
|
||||
// 模擬瀏覽器的 localStorage
|
||||
const mockLocalStorage = {
|
||||
token: generateTestToken()
|
||||
};
|
||||
|
||||
console.log('🧪 測試前端認證狀態...');
|
||||
console.log('Token 存在:', !!mockLocalStorage.token);
|
||||
console.log('Token 長度:', mockLocalStorage.token.length);
|
||||
|
||||
function makeRequest(url, method = 'GET', headers = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const urlObj = new URL(url);
|
||||
|
||||
const options = {
|
||||
hostname: urlObj.hostname,
|
||||
port: urlObj.port,
|
||||
path: urlObj.pathname,
|
||||
method: method,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...headers
|
||||
}
|
||||
};
|
||||
|
||||
const req = http.request(options, (res) => {
|
||||
let data = '';
|
||||
|
||||
res.on('data', (chunk) => {
|
||||
data += chunk;
|
||||
});
|
||||
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const jsonData = JSON.parse(data);
|
||||
resolve({
|
||||
status: res.statusCode,
|
||||
data: jsonData
|
||||
});
|
||||
} catch (error) {
|
||||
resolve({
|
||||
status: res.statusCode,
|
||||
data: data
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', (error) => {
|
||||
reject(error);
|
||||
});
|
||||
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
async function testFrontendAPI() {
|
||||
try {
|
||||
console.log('\n🧪 測試前端 API 調用...');
|
||||
|
||||
const response = await makeRequest('http://localhost:3000/api/apps', 'GET', {
|
||||
'Authorization': `Bearer ${mockLocalStorage.token}`
|
||||
});
|
||||
|
||||
if (response.status === 200) {
|
||||
console.log('✅ API 調用成功');
|
||||
console.log('應用程式數量:', response.data.apps?.length || 0);
|
||||
|
||||
if (response.data.apps && response.data.apps.length > 0) {
|
||||
const app = response.data.apps[0];
|
||||
console.log('第一個應用程式範例:');
|
||||
console.log('- ID:', app.id);
|
||||
console.log('- 名稱:', app.name);
|
||||
console.log('- 創建者:', app.creator?.name);
|
||||
console.log('- 部門:', app.creator?.department);
|
||||
console.log('- 狀態:', app.status);
|
||||
console.log('- 類型:', app.type);
|
||||
}
|
||||
} else {
|
||||
console.log('❌ API 調用失敗:', response.status);
|
||||
console.log('回應:', response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ 測試失敗:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
testFrontendAPI();
|
@@ -1,128 +0,0 @@
|
||||
const jwt = require('jsonwebtoken');
|
||||
|
||||
const JWT_SECRET = process.env.JWT_SECRET || 'good777';
|
||||
|
||||
async function testFrontendFixes() {
|
||||
try {
|
||||
// Generate a token for admin user
|
||||
const adminPayload = {
|
||||
userId: 'admin-001',
|
||||
email: 'admin@theaken.com',
|
||||
role: 'admin'
|
||||
};
|
||||
const token = jwt.sign(adminPayload, JWT_SECRET, { expiresIn: '1h' });
|
||||
|
||||
console.log('=== 測試前端修復 ===');
|
||||
|
||||
// Test 1: Get apps list with pagination
|
||||
console.log('\n1. 測試應用程式列表 (分頁)');
|
||||
const response1 = await fetch('http://localhost:3000/api/apps?page=1&limit=10', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
|
||||
if (response1.ok) {
|
||||
const data1 = await response1.json();
|
||||
console.log('✅ API 回應成功');
|
||||
console.log(`總應用數: ${data1.pagination?.total || 'N/A'}`);
|
||||
console.log(`總頁數: ${data1.pagination?.totalPages || 'N/A'}`);
|
||||
console.log(`當前頁應用數: ${data1.apps?.length || 0}`);
|
||||
console.log('統計資訊:', data1.stats);
|
||||
|
||||
// 模擬前端數據轉換
|
||||
const formattedApps = (data1.apps || []).map((app) => ({
|
||||
...app,
|
||||
creator: app.creator?.name || '未知',
|
||||
department: app.creator?.department || '未知',
|
||||
views: app.viewsCount || 0,
|
||||
likes: app.likesCount || 0,
|
||||
appUrl: app.demoUrl || '',
|
||||
type: mapApiTypeToDisplayType(app.type),
|
||||
icon: 'Bot',
|
||||
iconColor: 'from-blue-500 to-purple-500',
|
||||
reviews: 0,
|
||||
createdAt: app.createdAt ? new Date(app.createdAt).toLocaleDateString() : '未知'
|
||||
}));
|
||||
|
||||
console.log('\n模擬前端統計:');
|
||||
console.log(`總應用數 (totalApps): ${data1.pagination?.total}`);
|
||||
console.log(`已發布: ${data1.stats?.published || 0}`);
|
||||
console.log(`待審核: ${data1.stats?.pending || 0}`);
|
||||
console.log(`草稿: ${data1.stats?.draft || 0}`);
|
||||
console.log(`已拒絕: ${data1.stats?.rejected || 0}`);
|
||||
|
||||
// 檢查分頁是否應該顯示
|
||||
const shouldShowPagination = data1.pagination?.totalPages > 1;
|
||||
console.log(`\n分頁是否應該顯示: ${shouldShowPagination} (總頁數: ${data1.pagination?.totalPages})`);
|
||||
|
||||
} else {
|
||||
console.log('❌ API 回應失敗:', response1.status, response1.statusText);
|
||||
}
|
||||
|
||||
// Test 2: Create a new app as admin
|
||||
console.log('\n2. 測試管理員創建應用程式');
|
||||
const newAppData = {
|
||||
name: '測試應用程式_' + Date.now(),
|
||||
description: '這是一個測試應用程式',
|
||||
type: 'productivity',
|
||||
demoUrl: 'https://example.com',
|
||||
version: '1.0.0'
|
||||
};
|
||||
|
||||
const response2 = await fetch('http://localhost:3000/api/apps', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify(newAppData)
|
||||
});
|
||||
|
||||
if (response2.ok) {
|
||||
const result = await response2.json();
|
||||
console.log('✅ 創建應用程式成功');
|
||||
console.log('創建的應用程式狀態:', result.app?.status);
|
||||
console.log('應用程式ID:', result.appId);
|
||||
|
||||
// 檢查狀態是否正確 (應該是 draft)
|
||||
if (result.app?.status === 'draft') {
|
||||
console.log('✅ 狀態正確: 管理員創建的應用程式狀態為 draft');
|
||||
} else {
|
||||
console.log('❌ 狀態錯誤: 管理員創建的應用程式狀態應該為 draft,但實際為', result.app?.status);
|
||||
}
|
||||
} else {
|
||||
const errorData = await response2.json();
|
||||
console.log('❌ 創建應用程式失敗:', errorData);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('測試過程中發生錯誤:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 模擬前端的類型轉換函數
|
||||
function mapApiTypeToDisplayType(apiType) {
|
||||
const typeMap = {
|
||||
'productivity': '文字處理',
|
||||
'ai_model': '圖像生成',
|
||||
'automation': '程式開發',
|
||||
'data_analysis': '數據分析',
|
||||
'educational': '教育工具',
|
||||
'healthcare': '健康醫療',
|
||||
'finance': '金融科技',
|
||||
'iot_device': '物聯網',
|
||||
'blockchain': '區塊鏈',
|
||||
'ar_vr': 'AR/VR',
|
||||
'machine_learning': '機器學習',
|
||||
'computer_vision': '電腦視覺',
|
||||
'nlp': '自然語言處理',
|
||||
'robotics': '機器人',
|
||||
'cybersecurity': '網路安全',
|
||||
'cloud_service': '雲端服務',
|
||||
'other': '其他'
|
||||
};
|
||||
return typeMap[apiType] || '其他';
|
||||
}
|
||||
|
||||
testFrontendFixes();
|
106
scripts/test-list-api-fix.js
Normal file
106
scripts/test-list-api-fix.js
Normal file
@@ -0,0 +1,106 @@
|
||||
const mysql = require('mysql2/promise');
|
||||
|
||||
const dbConfig = {
|
||||
host: process.env.DB_HOST || 'mysql.theaken.com',
|
||||
port: parseInt(process.env.DB_PORT || '33306'),
|
||||
user: process.env.DB_USER || 'AI_Platform',
|
||||
password: process.env.DB_PASSWORD || 'Aa123456',
|
||||
database: process.env.DB_NAME || 'db_AI_Platform',
|
||||
charset: 'utf8mb4',
|
||||
timezone: '+08:00'
|
||||
};
|
||||
|
||||
async function testListApiFix() {
|
||||
let connection;
|
||||
|
||||
try {
|
||||
console.log('🔍 測試列表 API 創建者資訊修正...');
|
||||
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
console.log('✅ 資料庫連接成功');
|
||||
|
||||
// 模擬列表 API 的查詢
|
||||
const sql = `
|
||||
SELECT
|
||||
a.*,
|
||||
u.name as user_creator_name,
|
||||
u.email as user_creator_email,
|
||||
u.department as user_creator_department,
|
||||
u.role as creator_role
|
||||
FROM apps a
|
||||
LEFT JOIN users u ON a.creator_id = u.id
|
||||
ORDER BY a.created_at DESC
|
||||
LIMIT 3
|
||||
`;
|
||||
|
||||
const [apps] = await connection.execute(sql, []);
|
||||
|
||||
console.log('\n📊 原始資料庫查詢結果:');
|
||||
apps.forEach((app, index) => {
|
||||
console.log(`\n應用程式 ${index + 1}:`);
|
||||
console.log(` ID: ${app.id}`);
|
||||
console.log(` 名稱: ${app.name}`);
|
||||
console.log(` creator_id: ${app.creator_id}`);
|
||||
console.log(` user_creator_name: ${app.user_creator_name}`);
|
||||
console.log(` user_creator_email: ${app.user_creator_email}`);
|
||||
console.log(` user_creator_department: ${app.user_creator_department}`);
|
||||
console.log(` department: ${app.department}`);
|
||||
});
|
||||
|
||||
// 模擬修正後的格式化邏輯
|
||||
const formattedApps = apps.map((app) => ({
|
||||
id: app.id,
|
||||
name: app.name,
|
||||
description: app.description,
|
||||
creatorId: app.creator_id,
|
||||
status: app.status,
|
||||
type: app.type,
|
||||
icon: app.icon,
|
||||
iconColor: app.icon_color,
|
||||
department: app.department,
|
||||
creator: {
|
||||
id: app.creator_id,
|
||||
name: app.user_creator_name, // 修正:直接使用 user_creator_name
|
||||
email: app.user_creator_email, // 修正:直接使用 user_creator_email
|
||||
department: app.department || app.user_creator_department,
|
||||
role: app.creator_role
|
||||
}
|
||||
}));
|
||||
|
||||
console.log('\n📋 修正後的格式化結果:');
|
||||
formattedApps.forEach((app, index) => {
|
||||
console.log(`\n應用程式 ${index + 1}:`);
|
||||
console.log(` 名稱: ${app.name}`);
|
||||
console.log(` 創建者 ID: ${app.creator.id}`);
|
||||
console.log(` 創建者名稱: ${app.creator.name}`);
|
||||
console.log(` 創建者郵箱: ${app.creator.email}`);
|
||||
console.log(` 創建者部門: ${app.creator.department}`);
|
||||
console.log(` 應用部門: ${app.department}`);
|
||||
});
|
||||
|
||||
// 驗證修正是否有效
|
||||
const hasValidCreatorNames = formattedApps.every(app =>
|
||||
app.creator.name && app.creator.name.trim() !== ''
|
||||
);
|
||||
|
||||
console.log('\n✅ 驗證結果:');
|
||||
console.log(`所有應用程式都有有效的創建者名稱: ${hasValidCreatorNames}`);
|
||||
|
||||
if (hasValidCreatorNames) {
|
||||
console.log('🎉 列表 API 創建者資訊修正成功!');
|
||||
} else {
|
||||
console.log('❌ 仍有應用程式缺少創建者名稱,需要進一步檢查。');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 測試列表 API 修正失敗:', error);
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
console.log('🔌 資料庫連接已關閉');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 執行測試
|
||||
testListApiFix().catch(console.error);
|
@@ -1,87 +0,0 @@
|
||||
const http = require('http');
|
||||
const jwt = require('jsonwebtoken');
|
||||
|
||||
const JWT_SECRET = process.env.JWT_SECRET || 'good777';
|
||||
|
||||
// 測試登入
|
||||
function testLogin(email, password) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const postData = JSON.stringify({
|
||||
email: email,
|
||||
password: password
|
||||
});
|
||||
|
||||
const options = {
|
||||
hostname: 'localhost',
|
||||
port: 3000,
|
||||
path: '/api/auth/login',
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Content-Length': Buffer.byteLength(postData)
|
||||
}
|
||||
};
|
||||
|
||||
const req = http.request(options, (res) => {
|
||||
let data = '';
|
||||
|
||||
res.on('data', (chunk) => {
|
||||
data += chunk;
|
||||
});
|
||||
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const jsonData = JSON.parse(data);
|
||||
resolve({
|
||||
status: res.statusCode,
|
||||
data: jsonData
|
||||
});
|
||||
} catch (error) {
|
||||
resolve({
|
||||
status: res.statusCode,
|
||||
data: data
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', (error) => {
|
||||
reject(error);
|
||||
});
|
||||
|
||||
req.write(postData);
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
async function testLogins() {
|
||||
console.log('🧪 測試登入...');
|
||||
|
||||
const testUsers = [
|
||||
{ email: 'admin@theaken.com', password: 'Admin123' },
|
||||
{ email: 'admin@example.com', password: 'Admin123' },
|
||||
{ email: 'petty091901@gmail.com', password: 'Admin123' },
|
||||
{ email: 'test@theaken.com', password: 'Test123' },
|
||||
{ email: 'test@example.com', password: 'Test123' }
|
||||
];
|
||||
|
||||
for (const user of testUsers) {
|
||||
try {
|
||||
console.log(`\n測試用戶: ${user.email}`);
|
||||
const response = await testLogin(user.email, user.password);
|
||||
|
||||
if (response.status === 200) {
|
||||
console.log('✅ 登入成功');
|
||||
console.log('用戶資訊:', response.data.user);
|
||||
console.log('Token 長度:', response.data.token?.length || 0);
|
||||
} else {
|
||||
console.log('❌ 登入失敗');
|
||||
console.log('錯誤:', response.data.error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ 測試失敗:', error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testLogins();
|
137
scripts/test-modal-reset-fix.js
Normal file
137
scripts/test-modal-reset-fix.js
Normal file
@@ -0,0 +1,137 @@
|
||||
// Test script to verify modal reset fix
|
||||
console.log('Testing modal reset fix...')
|
||||
|
||||
// Simulate the newApp state
|
||||
let newApp = {
|
||||
name: "",
|
||||
type: "文字處理",
|
||||
department: "HQBU",
|
||||
creator: "",
|
||||
description: "",
|
||||
appUrl: "",
|
||||
icon: "Bot",
|
||||
iconColor: "from-blue-500 to-purple-500",
|
||||
}
|
||||
|
||||
// Simulate the resetNewApp function
|
||||
function resetNewApp() {
|
||||
newApp = {
|
||||
name: "",
|
||||
type: "文字處理",
|
||||
department: "HQBU",
|
||||
creator: "",
|
||||
description: "",
|
||||
appUrl: "",
|
||||
icon: "Bot",
|
||||
iconColor: "from-blue-500 to-purple-500",
|
||||
}
|
||||
console.log('✅ Form reset to initial values')
|
||||
}
|
||||
|
||||
// Simulate the handleEditApp function
|
||||
function handleEditApp(app) {
|
||||
console.log('📝 Editing app:', app.name)
|
||||
newApp = {
|
||||
name: app.name,
|
||||
type: app.type,
|
||||
department: app.creator?.department || app.department || "HQBU",
|
||||
creator: app.creator?.name || app.creator || "",
|
||||
description: app.description,
|
||||
appUrl: app.appUrl || app.demoUrl || "",
|
||||
icon: app.icon || "Bot",
|
||||
iconColor: app.iconColor || "from-blue-500 to-purple-500",
|
||||
}
|
||||
console.log('📝 Form populated with app data:', newApp)
|
||||
}
|
||||
|
||||
// Simulate the "Add New App" button click
|
||||
function handleAddNewAppClick() {
|
||||
console.log('➕ Add New App button clicked')
|
||||
console.log('📋 Form state before reset:', newApp)
|
||||
resetNewApp()
|
||||
console.log('📋 Form state after reset:', newApp)
|
||||
}
|
||||
|
||||
// Test scenario 1: Edit an app, then click "Add New App"
|
||||
console.log('\n=== Test Scenario 1: Edit then Add New ===')
|
||||
const testApp = {
|
||||
name: "Test AI App",
|
||||
type: "圖像生成",
|
||||
department: "ITBU",
|
||||
creator: "John Doe",
|
||||
description: "A test AI application",
|
||||
appUrl: "https://example.com",
|
||||
icon: "Brain",
|
||||
iconColor: "from-purple-500 to-pink-500",
|
||||
}
|
||||
|
||||
console.log('1. Initial form state:')
|
||||
console.log(newApp)
|
||||
|
||||
console.log('\n2. Edit an app:')
|
||||
handleEditApp(testApp)
|
||||
|
||||
console.log('\n3. Click "Add New App" button:')
|
||||
handleAddNewAppClick()
|
||||
|
||||
// Test scenario 2: Multiple edits without reset
|
||||
console.log('\n=== Test Scenario 2: Multiple Edits ===')
|
||||
const testApp2 = {
|
||||
name: "Another Test App",
|
||||
type: "語音辨識",
|
||||
department: "MBU1",
|
||||
creator: "Jane Smith",
|
||||
description: "Another test application",
|
||||
appUrl: "https://test2.com",
|
||||
icon: "Mic",
|
||||
iconColor: "from-green-500 to-teal-500",
|
||||
}
|
||||
|
||||
console.log('1. Edit first app:')
|
||||
handleEditApp(testApp)
|
||||
|
||||
console.log('2. Edit second app (without reset):')
|
||||
handleEditApp(testApp2)
|
||||
|
||||
console.log('3. Click "Add New App" button:')
|
||||
handleAddNewAppClick()
|
||||
|
||||
// Test scenario 3: Verify reset function works correctly
|
||||
console.log('\n=== Test Scenario 3: Reset Verification ===')
|
||||
console.log('1. Populate form with data:')
|
||||
newApp = {
|
||||
name: "Some App",
|
||||
type: "其他",
|
||||
department: "SBU",
|
||||
creator: "Test User",
|
||||
description: "Test description",
|
||||
appUrl: "https://test.com",
|
||||
icon: "Settings",
|
||||
iconColor: "from-gray-500 to-zinc-500",
|
||||
}
|
||||
console.log('Form populated:', newApp)
|
||||
|
||||
console.log('\n2. Reset form:')
|
||||
resetNewApp()
|
||||
console.log('Form after reset:', newApp)
|
||||
|
||||
// Verify all fields are reset to initial values
|
||||
const expectedInitialState = {
|
||||
name: "",
|
||||
type: "文字處理",
|
||||
department: "HQBU",
|
||||
creator: "",
|
||||
description: "",
|
||||
appUrl: "",
|
||||
icon: "Bot",
|
||||
iconColor: "from-blue-500 to-purple-500",
|
||||
}
|
||||
|
||||
const isResetCorrect = JSON.stringify(newApp) === JSON.stringify(expectedInitialState)
|
||||
console.log('\n✅ Reset verification:', isResetCorrect ? 'PASSED' : 'FAILED')
|
||||
|
||||
if (isResetCorrect) {
|
||||
console.log('🎉 All tests passed! The modal reset fix is working correctly.')
|
||||
} else {
|
||||
console.log('❌ Reset verification failed. Check the resetNewApp function.')
|
||||
}
|
@@ -1,54 +0,0 @@
|
||||
const jwt = require('jsonwebtoken');
|
||||
|
||||
async function testPagination() {
|
||||
console.log('🧪 測試分頁功能...');
|
||||
|
||||
// 生成測試 token
|
||||
const token = jwt.sign(
|
||||
{ userId: 'admin-001', role: 'admin' },
|
||||
process.env.JWT_SECRET || 'good777',
|
||||
{ expiresIn: '1h' }
|
||||
);
|
||||
|
||||
console.log('✅ Token 生成成功\n');
|
||||
|
||||
// 測試不同的分頁參數
|
||||
const testCases = [
|
||||
{ page: 1, limit: 3, description: '第1頁,每頁3筆' },
|
||||
{ page: 2, limit: 3, description: '第2頁,每頁3筆' },
|
||||
{ page: 1, limit: 5, description: '第1頁,每頁5筆' },
|
||||
{ page: 1, limit: 10, description: '第1頁,每頁10筆' }
|
||||
];
|
||||
|
||||
for (const testCase of testCases) {
|
||||
console.log(`\n${testCase.description}:`);
|
||||
|
||||
try {
|
||||
const params = new URLSearchParams({
|
||||
page: testCase.page.toString(),
|
||||
limit: testCase.limit.toString()
|
||||
});
|
||||
|
||||
const response = await fetch(`http://localhost:3000/api/apps?${params}`, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
console.log(` 狀態碼: ${response.status}`);
|
||||
console.log(` 應用程式數量: ${data.apps.length}`);
|
||||
console.log(` 分頁資訊:`, data.pagination);
|
||||
} else {
|
||||
console.log(` 錯誤: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(` 請求失敗: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n✅ 分頁測試完成');
|
||||
}
|
||||
|
||||
testPagination();
|
@@ -1,60 +0,0 @@
|
||||
const mysql = require('mysql2/promise');
|
||||
const bcrypt = require('bcrypt');
|
||||
|
||||
async function testPasswordVerification() {
|
||||
console.log('=== 測試密碼驗證 ===');
|
||||
|
||||
try {
|
||||
const connection = await mysql.createConnection({
|
||||
host: 'mysql.theaken.com',
|
||||
port: 33306,
|
||||
user: 'AI_Platform',
|
||||
password: 'Aa123456',
|
||||
database: 'db_AI_Platform'
|
||||
});
|
||||
|
||||
console.log('✅ 資料庫連接成功');
|
||||
|
||||
// 測試密碼
|
||||
const testPasswords = [
|
||||
'Admin123!',
|
||||
'Admin@2024',
|
||||
'admin123',
|
||||
'password',
|
||||
'123456'
|
||||
];
|
||||
|
||||
// 查詢管理員用戶
|
||||
const [rows] = await connection.execute(`
|
||||
SELECT id, name, email, role, password_hash
|
||||
FROM users
|
||||
WHERE role = 'admin'
|
||||
ORDER BY created_at DESC
|
||||
`);
|
||||
|
||||
console.log(`\n找到 ${rows.length} 個管理員用戶:`);
|
||||
|
||||
for (const user of rows) {
|
||||
console.log(`\n用戶: ${user.name} (${user.email})`);
|
||||
console.log(`密碼雜湊: ${user.password_hash}`);
|
||||
|
||||
// 測試每個密碼
|
||||
for (const password of testPasswords) {
|
||||
try {
|
||||
const isValid = await bcrypt.compare(password, user.password_hash);
|
||||
if (isValid) {
|
||||
console.log(`✅ 密碼匹配: "${password}"`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`❌ 密碼驗證錯誤: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await connection.end();
|
||||
} catch (error) {
|
||||
console.error('❌ 資料庫連接失敗:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
testPasswordVerification().catch(console.error);
|
@@ -1,63 +0,0 @@
|
||||
const mysql = require('mysql2/promise');
|
||||
|
||||
const dbConfig = {
|
||||
host: process.env.DB_HOST || 'mysql.theaken.com',
|
||||
port: parseInt(process.env.DB_PORT || '33306'),
|
||||
user: process.env.DB_USER || 'AI_Platform',
|
||||
password: process.env.DB_PASSWORD || 'Aa123456',
|
||||
database: process.env.DB_NAME || 'db_AI_Platform',
|
||||
charset: 'utf8mb4',
|
||||
timezone: '+08:00'
|
||||
};
|
||||
|
||||
async function testSimpleQuery() {
|
||||
let connection;
|
||||
|
||||
try {
|
||||
console.log('🧪 測試簡單查詢...');
|
||||
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
console.log('✅ 資料庫連接成功');
|
||||
|
||||
// 測試 1: 簡單的 apps 查詢
|
||||
console.log('\n1. 測試簡單的 apps 查詢...');
|
||||
const [apps1] = await connection.execute('SELECT * FROM apps LIMIT 5');
|
||||
console.log('結果:', apps1.length, '個應用程式');
|
||||
|
||||
// 測試 2: 帶 JOIN 的查詢
|
||||
console.log('\n2. 測試帶 JOIN 的查詢...');
|
||||
const [apps2] = await connection.execute(`
|
||||
SELECT
|
||||
a.*,
|
||||
u.name as creator_name,
|
||||
u.email as creator_email
|
||||
FROM apps a
|
||||
LEFT JOIN users u ON a.creator_id = u.id
|
||||
LIMIT 5
|
||||
`);
|
||||
console.log('結果:', apps2.length, '個應用程式');
|
||||
|
||||
// 測試 3: 帶參數的查詢
|
||||
console.log('\n3. 測試帶參數的查詢...');
|
||||
const [apps3] = await connection.execute(`
|
||||
SELECT * FROM apps
|
||||
WHERE creator_id = ?
|
||||
LIMIT ?
|
||||
`, ['mdxxt1xt7slle4g8wz8', 5]);
|
||||
console.log('結果:', apps3.length, '個應用程式');
|
||||
|
||||
// 測試 4: 計數查詢
|
||||
console.log('\n4. 測試計數查詢...');
|
||||
const [countResult] = await connection.execute('SELECT COUNT(*) as total FROM apps');
|
||||
console.log('總數:', countResult[0].total);
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 測試失敗:', error);
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testSimpleQuery();
|
@@ -1,4 +1,38 @@
|
||||
// 測試類型轉換函數
|
||||
// Test script to check type conversion and identify English types
|
||||
console.log('Testing type conversion functions...')
|
||||
|
||||
// Simulate the type mapping functions from app-management.tsx
|
||||
const mapTypeToApiType = (frontendType) => {
|
||||
const typeMap = {
|
||||
'文字處理': 'productivity',
|
||||
'圖像生成': 'ai_model',
|
||||
'圖像處理': 'ai_model',
|
||||
'語音辨識': 'ai_model',
|
||||
'推薦系統': 'ai_model',
|
||||
'音樂生成': 'ai_model',
|
||||
'程式開發': 'automation',
|
||||
'影像處理': 'ai_model',
|
||||
'對話系統': 'ai_model',
|
||||
'數據分析': 'data_analysis',
|
||||
'設計工具': 'productivity',
|
||||
'語音技術': 'ai_model',
|
||||
'教育工具': 'educational',
|
||||
'健康醫療': 'healthcare',
|
||||
'金融科技': 'finance',
|
||||
'物聯網': 'iot_device',
|
||||
'區塊鏈': 'blockchain',
|
||||
'AR/VR': 'ar_vr',
|
||||
'機器學習': 'machine_learning',
|
||||
'電腦視覺': 'computer_vision',
|
||||
'自然語言處理': 'nlp',
|
||||
'機器人': 'robotics',
|
||||
'網路安全': 'cybersecurity',
|
||||
'雲端服務': 'cloud_service',
|
||||
'其他': 'other'
|
||||
}
|
||||
return typeMap[frontendType] || 'other'
|
||||
}
|
||||
|
||||
const mapApiTypeToDisplayType = (apiType) => {
|
||||
const typeMap = {
|
||||
'productivity': '文字處理',
|
||||
@@ -17,15 +51,115 @@ const mapApiTypeToDisplayType = (apiType) => {
|
||||
'robotics': '機器人',
|
||||
'cybersecurity': '網路安全',
|
||||
'cloud_service': '雲端服務',
|
||||
// 處理舊的英文類型,確保它們都轉換為中文
|
||||
'web_app': '文字處理',
|
||||
'mobile_app': '文字處理',
|
||||
'desktop_app': '文字處理',
|
||||
'api_service': '程式開發',
|
||||
'other': '其他'
|
||||
}
|
||||
return typeMap[apiType] || '其他'
|
||||
}
|
||||
|
||||
// 測試轉換
|
||||
console.log('🧪 測試類型轉換...')
|
||||
console.log('productivity ->', mapApiTypeToDisplayType('productivity'))
|
||||
console.log('ai_model ->', mapApiTypeToDisplayType('ai_model'))
|
||||
console.log('automation ->', mapApiTypeToDisplayType('automation'))
|
||||
console.log('unknown ->', mapApiTypeToDisplayType('unknown'))
|
||||
console.log('✅ 類型轉換測試完成')
|
||||
// Test different scenarios
|
||||
console.log('\n=== Testing Type Conversion ===')
|
||||
|
||||
// Test 1: Check if there are any English types that might slip through
|
||||
const possibleEnglishTypes = [
|
||||
'web_app', 'mobile_app', 'desktop_app', 'api_service', 'ai_model',
|
||||
'data_analysis', 'automation', 'other', 'productivity', 'educational',
|
||||
'healthcare', 'finance', 'iot_device', 'blockchain', 'ar_vr',
|
||||
'machine_learning', 'computer_vision', 'nlp', 'robotics', 'cybersecurity',
|
||||
'cloud_service'
|
||||
]
|
||||
|
||||
console.log('\n1. Testing English API types:')
|
||||
possibleEnglishTypes.forEach(englishType => {
|
||||
const chineseType = mapApiTypeToDisplayType(englishType)
|
||||
console.log(` ${englishType} -> ${chineseType}`)
|
||||
})
|
||||
|
||||
// Test 2: Check if all Chinese types map back correctly
|
||||
const chineseTypes = [
|
||||
'文字處理', '圖像生成', '圖像處理', '語音辨識', '推薦系統', '音樂生成',
|
||||
'程式開發', '影像處理', '對話系統', '數據分析', '設計工具', '語音技術',
|
||||
'教育工具', '健康醫療', '金融科技', '物聯網', '區塊鏈', 'AR/VR',
|
||||
'機器學習', '電腦視覺', '自然語言處理', '機器人', '網路安全', '雲端服務', '其他'
|
||||
]
|
||||
|
||||
console.log('\n2. Testing Chinese display types:')
|
||||
chineseTypes.forEach(chineseType => {
|
||||
const apiType = mapTypeToApiType(chineseType)
|
||||
const backToChinese = mapApiTypeToDisplayType(apiType)
|
||||
const isConsistent = chineseType === backToChinese
|
||||
console.log(` ${chineseType} -> ${apiType} -> ${backToChinese} ${isConsistent ? '✅' : '❌'}`)
|
||||
})
|
||||
|
||||
// Test 3: Check for any unmapped types
|
||||
console.log('\n3. Checking for unmapped types:')
|
||||
const allApiTypes = new Set(possibleEnglishTypes)
|
||||
const mappedApiTypes = new Set(Object.values({
|
||||
'文字處理': 'productivity',
|
||||
'圖像生成': 'ai_model',
|
||||
'圖像處理': 'ai_model',
|
||||
'語音辨識': 'ai_model',
|
||||
'推薦系統': 'ai_model',
|
||||
'音樂生成': 'ai_model',
|
||||
'程式開發': 'automation',
|
||||
'影像處理': 'ai_model',
|
||||
'對話系統': 'ai_model',
|
||||
'數據分析': 'data_analysis',
|
||||
'設計工具': 'productivity',
|
||||
'語音技術': 'ai_model',
|
||||
'教育工具': 'educational',
|
||||
'健康醫療': 'healthcare',
|
||||
'金融科技': 'finance',
|
||||
'物聯網': 'iot_device',
|
||||
'區塊鏈': 'blockchain',
|
||||
'AR/VR': 'ar_vr',
|
||||
'機器學習': 'machine_learning',
|
||||
'電腦視覺': 'computer_vision',
|
||||
'自然語言處理': 'nlp',
|
||||
'機器人': 'robotics',
|
||||
'網路安全': 'cybersecurity',
|
||||
'雲端服務': 'cloud_service',
|
||||
'其他': 'other'
|
||||
}))
|
||||
|
||||
const unmappedApiTypes = [...allApiTypes].filter(type => !mappedApiTypes.has(type))
|
||||
console.log(' Unmapped API types:', unmappedApiTypes)
|
||||
|
||||
// Test 4: Simulate what happens when editing an app
|
||||
console.log('\n4. Testing edit scenario:')
|
||||
const mockApiResponse = {
|
||||
apps: [
|
||||
{ id: '1', name: 'Test App 1', type: 'productivity' },
|
||||
{ id: '2', name: 'Test App 2', type: 'ai_model' },
|
||||
{ id: '3', name: 'Test App 3', type: 'web_app' }, // This should now be handled
|
||||
{ id: '4', name: 'Test App 4', type: 'mobile_app' }, // This should now be handled
|
||||
{ id: '5', name: 'Test App 5', type: 'other' }
|
||||
]
|
||||
}
|
||||
|
||||
console.log(' Simulating loadApps processing:')
|
||||
mockApiResponse.apps.forEach(app => {
|
||||
const displayType = mapApiTypeToDisplayType(app.type)
|
||||
console.log(` ${app.name}: ${app.type} -> ${displayType}`)
|
||||
})
|
||||
|
||||
// Test 5: Test the actual database types from the update
|
||||
console.log('\n5. Testing database types after update:')
|
||||
const databaseTypes = [
|
||||
'productivity', 'ai_model', 'automation', 'data_analysis',
|
||||
'educational', 'healthcare', 'finance', 'iot_device',
|
||||
'blockchain', 'ar_vr', 'machine_learning', 'computer_vision',
|
||||
'nlp', 'robotics', 'cybersecurity', 'cloud_service', 'other'
|
||||
]
|
||||
|
||||
console.log(' Database types conversion:')
|
||||
databaseTypes.forEach(dbType => {
|
||||
const displayType = mapApiTypeToDisplayType(dbType)
|
||||
console.log(` ${dbType} -> ${displayType}`)
|
||||
})
|
||||
|
||||
console.log('\n✅ Type conversion test completed!')
|
139
scripts/test-type-handling.js
Normal file
139
scripts/test-type-handling.js
Normal file
@@ -0,0 +1,139 @@
|
||||
// Test script to verify type handling in app management
|
||||
console.log('Testing type handling in app management...')
|
||||
|
||||
// Simulate the type mapping functions
|
||||
const mapApiTypeToDisplayType = (apiType) => {
|
||||
const typeMap = {
|
||||
'productivity': '文字處理',
|
||||
'ai_model': '圖像生成',
|
||||
'automation': '程式開發',
|
||||
'data_analysis': '數據分析',
|
||||
'educational': '教育工具',
|
||||
'healthcare': '健康醫療',
|
||||
'finance': '金融科技',
|
||||
'iot_device': '物聯網',
|
||||
'blockchain': '區塊鏈',
|
||||
'ar_vr': 'AR/VR',
|
||||
'machine_learning': '機器學習',
|
||||
'computer_vision': '電腦視覺',
|
||||
'nlp': '自然語言處理',
|
||||
'robotics': '機器人',
|
||||
'cybersecurity': '網路安全',
|
||||
'cloud_service': '雲端服務',
|
||||
'other': '其他'
|
||||
}
|
||||
return typeMap[apiType] || '其他'
|
||||
}
|
||||
|
||||
const mapTypeToApiType = (frontendType) => {
|
||||
const typeMap = {
|
||||
'文字處理': 'productivity',
|
||||
'圖像生成': 'ai_model',
|
||||
'圖像處理': 'ai_model',
|
||||
'語音辨識': 'ai_model',
|
||||
'推薦系統': 'ai_model',
|
||||
'音樂生成': 'ai_model',
|
||||
'程式開發': 'automation',
|
||||
'影像處理': 'ai_model',
|
||||
'對話系統': 'ai_model',
|
||||
'數據分析': 'data_analysis',
|
||||
'設計工具': 'productivity',
|
||||
'語音技術': 'ai_model',
|
||||
'教育工具': 'educational',
|
||||
'健康醫療': 'healthcare',
|
||||
'金融科技': 'finance',
|
||||
'物聯網': 'iot_device',
|
||||
'區塊鏈': 'blockchain',
|
||||
'AR/VR': 'ar_vr',
|
||||
'機器學習': 'machine_learning',
|
||||
'電腦視覺': 'computer_vision',
|
||||
'自然語言處理': 'nlp',
|
||||
'機器人': 'robotics',
|
||||
'網路安全': 'cybersecurity',
|
||||
'雲端服務': 'cloud_service',
|
||||
'其他': 'other'
|
||||
}
|
||||
return typeMap[frontendType] || 'other'
|
||||
}
|
||||
|
||||
// Simulate API response
|
||||
const mockApiResponse = {
|
||||
apps: [
|
||||
{
|
||||
id: '1',
|
||||
name: 'Test App',
|
||||
type: 'productivity', // API type (English)
|
||||
description: 'Test description',
|
||||
creator: {
|
||||
name: 'John Doe',
|
||||
department: 'HQBU'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
name: 'AI App',
|
||||
type: 'ai_model', // API type (English)
|
||||
description: 'AI description',
|
||||
creator: {
|
||||
name: 'Jane Smith',
|
||||
department: 'ITBU'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// Simulate loadApps processing
|
||||
console.log('=== API Response ===')
|
||||
console.log('Original API data:', mockApiResponse.apps)
|
||||
|
||||
const formattedApps = mockApiResponse.apps.map(app => ({
|
||||
...app,
|
||||
type: mapApiTypeToDisplayType(app.type), // Convert to Chinese display type
|
||||
creator: typeof app.creator === 'object' ? app.creator.name : app.creator,
|
||||
department: typeof app.creator === 'object' ? app.creator.department : app.department
|
||||
}))
|
||||
|
||||
console.log('=== After loadApps processing ===')
|
||||
console.log('Formatted apps:', formattedApps)
|
||||
|
||||
// Simulate handleEditApp
|
||||
const simulateHandleEditApp = (app) => {
|
||||
console.log('=== handleEditApp simulation ===')
|
||||
console.log('Input app:', app)
|
||||
|
||||
const newApp = {
|
||||
name: app.name,
|
||||
type: app.type, // This should be the Chinese display type
|
||||
department: app.department || "HQBU",
|
||||
creator: app.creator || "",
|
||||
description: app.description,
|
||||
appUrl: app.appUrl || app.demoUrl || "",
|
||||
icon: app.icon || "Bot",
|
||||
iconColor: app.iconColor || "from-blue-500 to-purple-500",
|
||||
}
|
||||
|
||||
console.log('newApp after handleEditApp:', newApp)
|
||||
return newApp
|
||||
}
|
||||
|
||||
// Test both apps
|
||||
console.log('\n=== Testing handleEditApp for both apps ===')
|
||||
formattedApps.forEach((app, index) => {
|
||||
console.log(`\n--- App ${index + 1} ---`)
|
||||
const newApp = simulateHandleEditApp(app)
|
||||
console.log('Final newApp.type:', newApp.type)
|
||||
console.log('Is this a valid Select value?', ['文字處理', '圖像生成', '程式開發', '數據分析', '教育工具', '健康醫療', '金融科技', '物聯網', '區塊鏈', 'AR/VR', '機器學習', '電腦視覺', '自然語言處理', '機器人', '網路安全', '雲端服務', '其他'].includes(newApp.type))
|
||||
})
|
||||
|
||||
// Test the reverse mapping for update
|
||||
console.log('\n=== Testing update mapping ===')
|
||||
formattedApps.forEach((app, index) => {
|
||||
console.log(`\n--- App ${index + 1} update test ---`)
|
||||
const displayType = app.type
|
||||
const apiType = mapTypeToApiType(displayType)
|
||||
console.log('Display type:', displayType)
|
||||
console.log('Mapped to API type:', apiType)
|
||||
console.log('Round trip test:', mapApiTypeToDisplayType(apiType) === displayType)
|
||||
})
|
||||
|
||||
console.log('\n=== Test completed ===')
|
@@ -1,77 +0,0 @@
|
||||
const mysql = require('mysql2/promise');
|
||||
|
||||
const dbConfig = {
|
||||
host: process.env.DB_HOST || 'mysql.theaken.com',
|
||||
port: parseInt(process.env.DB_PORT || '33306'),
|
||||
user: process.env.DB_USER || 'AI_Platform',
|
||||
password: process.env.DB_PASSWORD || 'Aa123456',
|
||||
database: process.env.DB_NAME || 'db_AI_Platform',
|
||||
charset: 'utf8mb4',
|
||||
timezone: '+08:00'
|
||||
};
|
||||
|
||||
async function testUserPermissions() {
|
||||
let connection;
|
||||
try {
|
||||
console.log('🧪 測試用戶權限和認證狀態...');
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
console.log('✅ 資料庫連接成功');
|
||||
|
||||
// 檢查現有用戶
|
||||
const [users] = await connection.execute('SELECT id, name, email, role, department FROM users ORDER BY created_at DESC LIMIT 5');
|
||||
|
||||
console.log('\n📋 資料庫中的用戶列表:');
|
||||
users.forEach((user, index) => {
|
||||
console.log(` ${index + 1}. ${user.name} (${user.email}) - 角色: ${user.role} - 部門: ${user.department}`);
|
||||
});
|
||||
|
||||
// 檢查應用程式
|
||||
const [apps] = await connection.execute('SELECT id, name, creator_id, type, status FROM apps ORDER BY created_at DESC LIMIT 5');
|
||||
|
||||
console.log('\n📋 資料庫中的應用程式列表:');
|
||||
apps.forEach((app, index) => {
|
||||
console.log(` ${index + 1}. ${app.name} - 創建者: ${app.creator_id} - 類型: ${app.type} - 狀態: ${app.status}`);
|
||||
});
|
||||
|
||||
// 創建一個管理員用戶用於測試
|
||||
const adminUserData = {
|
||||
id: 'admin-test-' + Date.now(),
|
||||
name: '測試管理員',
|
||||
email: 'admin-test@example.com',
|
||||
password_hash: 'test_hash',
|
||||
department: 'ITBU',
|
||||
role: 'admin',
|
||||
join_date: new Date(),
|
||||
created_at: new Date(),
|
||||
updated_at: new Date()
|
||||
};
|
||||
|
||||
await connection.execute(
|
||||
'INSERT INTO users (id, name, email, password_hash, department, role, join_date, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)',
|
||||
[adminUserData.id, adminUserData.name, adminUserData.email, adminUserData.password_hash, adminUserData.department, adminUserData.role, adminUserData.join_date, adminUserData.created_at, adminUserData.updated_at]
|
||||
);
|
||||
console.log('\n✅ 測試管理員用戶創建成功');
|
||||
|
||||
// 模擬 API 調用
|
||||
console.log('\n🧪 模擬 API 調用測試...');
|
||||
|
||||
// 這裡我們需要模擬一個有效的 JWT token
|
||||
// 在實際環境中,這個 token 應該通過登入 API 獲得
|
||||
console.log('💡 提示:要測試 API 調用,需要先通過登入 API 獲得有效的 JWT token');
|
||||
console.log('💡 建議:在瀏覽器中登入管理後台,然後檢查 localStorage 中的 token');
|
||||
|
||||
// 清理測試資料
|
||||
await connection.execute('DELETE FROM users WHERE id = ?', [adminUserData.id]);
|
||||
console.log('✅ 測試資料清理完成');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 測試失敗:', error.message);
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
console.log('🔌 資料庫連接已關閉');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testUserPermissions().catch(console.error);
|
@@ -1,55 +0,0 @@
|
||||
const mysql = require('mysql2/promise');
|
||||
const bcrypt = require('bcrypt');
|
||||
|
||||
async function updateAllAdminPasswords() {
|
||||
console.log('=== 更新所有管理員密碼 ===');
|
||||
|
||||
try {
|
||||
const connection = await mysql.createConnection({
|
||||
host: 'mysql.theaken.com',
|
||||
port: 33306,
|
||||
user: 'AI_Platform',
|
||||
password: 'Aa123456',
|
||||
database: 'db_AI_Platform'
|
||||
});
|
||||
|
||||
console.log('✅ 資料庫連接成功');
|
||||
|
||||
// 新密碼
|
||||
const newPassword = 'Admin123!';
|
||||
const passwordHash = await bcrypt.hash(newPassword, 12);
|
||||
|
||||
console.log(`\n更新所有管理員密碼為: ${newPassword}`);
|
||||
|
||||
// 更新所有管理員用戶的密碼
|
||||
const [result] = await connection.execute(`
|
||||
UPDATE users
|
||||
SET password_hash = ?, updated_at = NOW()
|
||||
WHERE role = 'admin'
|
||||
`, [passwordHash]);
|
||||
|
||||
console.log(`✅ 已更新 ${result.affectedRows} 個管理員用戶的密碼`);
|
||||
|
||||
// 驗證更新結果
|
||||
const [users] = await connection.execute(`
|
||||
SELECT id, name, email, role, updated_at
|
||||
FROM users
|
||||
WHERE role = 'admin'
|
||||
ORDER BY created_at DESC
|
||||
`);
|
||||
|
||||
console.log('\n📋 更新後的管理員用戶:');
|
||||
for (const user of users) {
|
||||
console.log(` - ${user.name} (${user.email}) - 更新時間: ${user.updated_at}`);
|
||||
}
|
||||
|
||||
console.log('\n🎉 所有管理員密碼已統一為: Admin123!');
|
||||
console.log('💡 現在所有管理員用戶都可以使用相同的密碼登入');
|
||||
|
||||
await connection.end();
|
||||
} catch (error) {
|
||||
console.error('❌ 更新失敗:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
updateAllAdminPasswords().catch(console.error);
|
@@ -10,55 +10,105 @@ const dbConfig = {
|
||||
timezone: '+08:00'
|
||||
};
|
||||
|
||||
// Type mapping for converting old types to new types
|
||||
const typeMapping = {
|
||||
'web_app': 'productivity',
|
||||
'mobile_app': 'productivity',
|
||||
'desktop_app': 'productivity',
|
||||
'api_service': 'automation',
|
||||
'ai_model': 'ai_model',
|
||||
'data_analysis': 'data_analysis',
|
||||
'automation': 'automation',
|
||||
'other': 'other'
|
||||
};
|
||||
|
||||
async function updateAppTypes() {
|
||||
let connection;
|
||||
|
||||
try {
|
||||
console.log('🔄 開始更新應用程式類型...');
|
||||
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
console.log('✅ 資料庫連接成功');
|
||||
|
||||
// 更新 ENUM 類型,移除不適合企業平台的類型
|
||||
const updateTypeEnum = `
|
||||
ALTER TABLE apps MODIFY COLUMN type ENUM(
|
||||
'web_app', 'mobile_app', 'desktop_app', 'api_service', 'ai_model',
|
||||
'data_analysis', 'automation', 'productivity', 'educational', 'healthcare',
|
||||
'finance', 'iot_device', 'blockchain', 'ar_vr', 'machine_learning',
|
||||
'computer_vision', 'nlp', 'robotics', 'cybersecurity', 'cloud_service', 'other'
|
||||
) DEFAULT 'other'
|
||||
`;
|
||||
|
||||
await connection.execute(updateTypeEnum);
|
||||
console.log('✅ 應用程式類型 ENUM 更新成功');
|
||||
|
||||
// 查看更新後的結構
|
||||
const [describeResult] = await connection.execute('DESCRIBE apps');
|
||||
console.log('\n📋 更新後的 apps 表結構:');
|
||||
describeResult.forEach(row => {
|
||||
if (row.Field === 'type') {
|
||||
console.log(` ${row.Field}: ${row.Type}`);
|
||||
// 1. 檢查現有的類型分佈
|
||||
console.log('\n📊 檢查現有類型分佈:');
|
||||
const [typeStats] = await connection.execute(`
|
||||
SELECT type, COUNT(*) as count
|
||||
FROM apps
|
||||
WHERE type IS NOT NULL
|
||||
GROUP BY type
|
||||
`);
|
||||
|
||||
typeStats.forEach(row => {
|
||||
console.log(` ${row.type}: ${row.count} 個應用程式`);
|
||||
});
|
||||
|
||||
// 2. 更新現有數據的類型
|
||||
console.log('\n🔄 更新現有應用程式的類型...');
|
||||
for (const [oldType, newType] of Object.entries(typeMapping)) {
|
||||
if (oldType !== newType) {
|
||||
const [result] = await connection.execute(
|
||||
'UPDATE apps SET type = ? WHERE type = ?',
|
||||
[newType, oldType]
|
||||
);
|
||||
if (result.affectedRows > 0) {
|
||||
console.log(` ✅ 將 ${oldType} 更新為 ${newType}: ${result.affectedRows} 個應用程式`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 修改 type 欄位的 ENUM 定義
|
||||
console.log('\n🔧 更新 type 欄位的 ENUM 定義...');
|
||||
try {
|
||||
// 先刪除舊的 ENUM 約束
|
||||
await connection.execute(`
|
||||
ALTER TABLE apps
|
||||
MODIFY COLUMN type VARCHAR(50) DEFAULT 'other'
|
||||
`);
|
||||
console.log(' ✅ 移除舊的 ENUM 約束');
|
||||
|
||||
// 添加新的 ENUM 約束
|
||||
await connection.execute(`
|
||||
ALTER TABLE apps
|
||||
MODIFY COLUMN type ENUM(
|
||||
'productivity', 'ai_model', 'automation', 'data_analysis',
|
||||
'educational', 'healthcare', 'finance', 'iot_device',
|
||||
'blockchain', 'ar_vr', 'machine_learning', 'computer_vision',
|
||||
'nlp', 'robotics', 'cybersecurity', 'cloud_service', 'other'
|
||||
) DEFAULT 'other'
|
||||
`);
|
||||
console.log(' ✅ 添加新的 ENUM 約束');
|
||||
} catch (error) {
|
||||
console.error(' ❌ 更新 ENUM 約束失敗:', error.message);
|
||||
}
|
||||
|
||||
// 4. 檢查更新後的類型分佈
|
||||
console.log('\n📊 更新後的類型分佈:');
|
||||
const [newTypeStats] = await connection.execute(`
|
||||
SELECT type, COUNT(*) as count
|
||||
FROM apps
|
||||
WHERE type IS NOT NULL
|
||||
GROUP BY type
|
||||
`);
|
||||
|
||||
newTypeStats.forEach(row => {
|
||||
console.log(` ${row.type}: ${row.count} 個應用程式`);
|
||||
});
|
||||
|
||||
// 5. 檢查表格結構
|
||||
console.log('\n📋 apps 表格結構:');
|
||||
const [columns] = await connection.execute('DESCRIBE apps');
|
||||
columns.forEach(col => {
|
||||
if (col.Field === 'type') {
|
||||
console.log(` ${col.Field}: ${col.Type} ${col.Null === 'YES' ? 'NULL' : 'NOT NULL'} ${col.Default ? `DEFAULT ${col.Default}` : ''}`);
|
||||
}
|
||||
});
|
||||
|
||||
// 列出所有有效的類型
|
||||
console.log('\n🎯 有效的應用程式類型:');
|
||||
const validTypes = [
|
||||
'web_app', 'mobile_app', 'desktop_app', 'api_service', 'ai_model',
|
||||
'data_analysis', 'automation', 'productivity', 'educational', 'healthcare',
|
||||
'finance', 'iot_device', 'blockchain', 'ar_vr', 'machine_learning',
|
||||
'computer_vision', 'nlp', 'robotics', 'cybersecurity', 'cloud_service', 'other'
|
||||
];
|
||||
validTypes.forEach((type, index) => {
|
||||
console.log(` ${index + 1}. ${type}`);
|
||||
});
|
||||
|
||||
console.log('\n✅ 企業 AI 平台應用類型更新完成!');
|
||||
console.log('🎯 已移除遊戲、娛樂、社交媒體等不適合企業平台的類型');
|
||||
console.log('📈 新增了更多適合企業 AI 應用的類型');
|
||||
|
||||
|
||||
console.log('\n✅ 應用程式類型更新完成!');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 更新失敗:', error.message);
|
||||
if (error.code === 'ER_DUP_FIELDNAME') {
|
||||
console.log('💡 提示:某些欄位可能已存在,這是正常的');
|
||||
}
|
||||
console.error('❌ 更新應用程式類型失敗:', error);
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
|
Reference in New Issue
Block a user