優化認證流程和頁面導航邏輯
Browse files主要變更:
- 擴充 auth.js 新增統一認證方法 (checkPageAuth, initProtectedPage)
- 統一所有頁面使用 authManager 處理認證
- 修正導航流程:登入 → dashboard → 各功能頁面
- 統一 Home 按鈕導向 dashboard.html
- 移除各頁面重複的認證代碼
- 保持 sessionStorage 和 15分鐘自動登出機制
技術改進:
- 減少代碼重複,提升可維護性
- 統一認證邏輯,避免各頁面獨立實作
- 保持開發模式支援 (localhost 自動跳過認證)
Co-Authored-By: Claude <noreply@anthropic.com>
- frontend/dashboard.html +36 -95
- frontend/index.html +27 -67
- frontend/js/auth.js +90 -4
- frontend/versions.html +26 -68
frontend/dashboard.html
CHANGED
|
@@ -316,78 +316,36 @@
|
|
| 316 |
<script src="js/utils.js"></script>
|
| 317 |
<script src="js/auth.js"></script>
|
| 318 |
<script>
|
| 319 |
-
//
|
| 320 |
let supabaseLicense = null;
|
| 321 |
let supabaseVersion = null;
|
| 322 |
-
let isDevMode = false;
|
| 323 |
|
| 324 |
-
// 初始化
|
| 325 |
-
async function
|
| 326 |
try {
|
| 327 |
-
//
|
| 328 |
-
|
| 329 |
-
|
| 330 |
-
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
|
| 334 |
-
|
| 335 |
-
|
| 336 |
-
// 使用統一的用戶資訊更新方式
|
| 337 |
-
updateUserInfo('admin@kstools.dev');
|
| 338 |
-
|
| 339 |
-
|
| 340 |
-
// 隱藏載入畫面
|
| 341 |
-
document.getElementById('loading-overlay').style.display = 'none';
|
| 342 |
-
return;
|
| 343 |
-
}
|
| 344 |
-
|
| 345 |
-
// 生產模式 - 等待配置載入
|
| 346 |
-
if (!window.APP_CONFIG.CONFIG_LOADED) {
|
| 347 |
-
await new Promise(resolve => {
|
| 348 |
-
window.addEventListener('configLoaded', resolve, { once: true });
|
| 349 |
-
});
|
| 350 |
-
}
|
| 351 |
-
|
| 352 |
-
// 初始化雙 Supabase 客戶端
|
| 353 |
-
if (window.APP_CONFIG.SUPABASE_LICENSE_URL && window.APP_CONFIG.SUPABASE_LICENSE_ANON_KEY) {
|
| 354 |
-
supabaseLicense = supabase.createClient(
|
| 355 |
-
window.APP_CONFIG.SUPABASE_LICENSE_URL,
|
| 356 |
-
window.APP_CONFIG.SUPABASE_LICENSE_ANON_KEY
|
| 357 |
-
);
|
| 358 |
-
}
|
| 359 |
-
|
| 360 |
-
if (window.APP_CONFIG.SUPABASE_VERSION_URL && window.APP_CONFIG.SUPABASE_VERSION_ANON_KEY) {
|
| 361 |
-
supabaseVersion = supabase.createClient(
|
| 362 |
-
window.APP_CONFIG.SUPABASE_VERSION_URL,
|
| 363 |
-
window.APP_CONFIG.SUPABASE_VERSION_ANON_KEY
|
| 364 |
-
);
|
| 365 |
-
}
|
| 366 |
-
|
| 367 |
-
// 檢查登入狀態
|
| 368 |
-
if (!supabaseLicense) {
|
| 369 |
-
window.location.href = '/login.html';
|
| 370 |
-
return;
|
| 371 |
-
}
|
| 372 |
|
| 373 |
-
|
| 374 |
-
|
| 375 |
-
|
| 376 |
-
|
|
|
|
|
|
|
| 377 |
}
|
| 378 |
|
| 379 |
-
// 使用統一的用戶資訊更新方式
|
| 380 |
-
updateUserInfo(session.user.email);
|
| 381 |
-
|
| 382 |
// 載入統計資料
|
| 383 |
await loadDashboardStats();
|
| 384 |
|
| 385 |
-
// 隱藏載入畫面
|
| 386 |
-
document.getElementById('loading-overlay').style.display = 'none';
|
| 387 |
-
|
| 388 |
} catch (error) {
|
| 389 |
-
console.error('初始化失敗:', error);
|
| 390 |
-
Utils.showError('
|
| 391 |
}
|
| 392 |
}
|
| 393 |
|
|
@@ -395,7 +353,7 @@
|
|
| 395 |
async function loadDashboardStats() {
|
| 396 |
try {
|
| 397 |
// 開發模式使用模擬資料
|
| 398 |
-
if (isDevMode) {
|
| 399 |
const mockLicenseStats = {
|
| 400 |
total: 25,
|
| 401 |
active: 18,
|
|
@@ -553,43 +511,26 @@
|
|
| 553 |
}
|
| 554 |
}
|
| 555 |
|
| 556 |
-
//
|
| 557 |
-
function updateUserInfo(email) {
|
| 558 |
-
const userInfo = document.getElementById('userInfo');
|
| 559 |
-
if (userInfo) {
|
| 560 |
-
userInfo.innerHTML = `
|
| 561 |
-
<div class="user-profile">
|
| 562 |
-
<span class="user-email">${email}</span>
|
| 563 |
-
<button class="btn btn-sm btn-secondary" onclick="logout()">
|
| 564 |
-
<i class="fas fa-sign-out-alt"></i>
|
| 565 |
-
登出
|
| 566 |
-
</button>
|
| 567 |
-
</div>
|
| 568 |
-
`;
|
| 569 |
-
}
|
| 570 |
-
}
|
| 571 |
-
|
| 572 |
-
// 登出
|
| 573 |
async function logout() {
|
| 574 |
-
|
| 575 |
-
|
| 576 |
-
console.log('🔧 開發模式 - 模擬登出');
|
| 577 |
-
window.location.href = '/login.html';
|
| 578 |
-
return;
|
| 579 |
-
}
|
| 580 |
-
|
| 581 |
-
if (!supabaseLicense) return;
|
| 582 |
-
|
| 583 |
-
try {
|
| 584 |
-
await supabaseLicense.auth.signOut();
|
| 585 |
-
window.location.href = '/login.html';
|
| 586 |
-
} catch (error) {
|
| 587 |
-
console.error('登出失敗:', error);
|
| 588 |
}
|
| 589 |
}
|
| 590 |
|
| 591 |
-
// 頁面載入時初始化
|
| 592 |
-
document.addEventListener('DOMContentLoaded',
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 593 |
</script>
|
| 594 |
</body>
|
| 595 |
</html>
|
|
|
|
| 316 |
<script src="js/utils.js"></script>
|
| 317 |
<script src="js/auth.js"></script>
|
| 318 |
<script>
|
| 319 |
+
// 使用統一的 AuthManager
|
| 320 |
let supabaseLicense = null;
|
| 321 |
let supabaseVersion = null;
|
|
|
|
| 322 |
|
| 323 |
+
// 初始化儀表板
|
| 324 |
+
async function initDashboard() {
|
| 325 |
try {
|
| 326 |
+
// 初始化雙 Supabase 客戶端(如果需要)
|
| 327 |
+
if (window.APP_CONFIG?.CONFIG_LOADED) {
|
| 328 |
+
if (window.APP_CONFIG.SUPABASE_LICENSE_URL && window.APP_CONFIG.SUPABASE_LICENSE_ANON_KEY) {
|
| 329 |
+
supabaseLicense = window.supabase?.createClient(
|
| 330 |
+
window.APP_CONFIG.SUPABASE_LICENSE_URL,
|
| 331 |
+
window.APP_CONFIG.SUPABASE_LICENSE_ANON_KEY
|
| 332 |
+
);
|
| 333 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 334 |
|
| 335 |
+
if (window.APP_CONFIG.SUPABASE_VERSION_URL && window.APP_CONFIG.SUPABASE_VERSION_ANON_KEY) {
|
| 336 |
+
supabaseVersion = window.supabase?.createClient(
|
| 337 |
+
window.APP_CONFIG.SUPABASE_VERSION_URL,
|
| 338 |
+
window.APP_CONFIG.SUPABASE_VERSION_ANON_KEY
|
| 339 |
+
);
|
| 340 |
+
}
|
| 341 |
}
|
| 342 |
|
|
|
|
|
|
|
|
|
|
| 343 |
// 載入統計資料
|
| 344 |
await loadDashboardStats();
|
| 345 |
|
|
|
|
|
|
|
|
|
|
| 346 |
} catch (error) {
|
| 347 |
+
console.error('儀表板初始化失敗:', error);
|
| 348 |
+
Utils.showError('載入統計資料失敗');
|
| 349 |
}
|
| 350 |
}
|
| 351 |
|
|
|
|
| 353 |
async function loadDashboardStats() {
|
| 354 |
try {
|
| 355 |
// 開發模式使用模擬資料
|
| 356 |
+
if (window.authManager?.isDevMode) {
|
| 357 |
const mockLicenseStats = {
|
| 358 |
total: 25,
|
| 359 |
active: 18,
|
|
|
|
| 511 |
}
|
| 512 |
}
|
| 513 |
|
| 514 |
+
// 使用 authManager 的登出方法
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 515 |
async function logout() {
|
| 516 |
+
if (window.authManager) {
|
| 517 |
+
await window.authManager.handleLogout();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 518 |
}
|
| 519 |
}
|
| 520 |
|
| 521 |
+
// 頁面載入時使用統一認證初始化
|
| 522 |
+
document.addEventListener('DOMContentLoaded', async () => {
|
| 523 |
+
if (window.authManager) {
|
| 524 |
+
await window.authManager.initProtectedPage('dashboard', initDashboard);
|
| 525 |
+
} else {
|
| 526 |
+
// 等待 authManager 載入
|
| 527 |
+
window.addEventListener('supabaseReady', async () => {
|
| 528 |
+
if (window.authManager) {
|
| 529 |
+
await window.authManager.initProtectedPage('dashboard', initDashboard);
|
| 530 |
+
}
|
| 531 |
+
});
|
| 532 |
+
}
|
| 533 |
+
});
|
| 534 |
</script>
|
| 535 |
</body>
|
| 536 |
</html>
|
frontend/index.html
CHANGED
|
@@ -89,6 +89,12 @@
|
|
| 89 |
</div>
|
| 90 |
|
| 91 |
<ul class="nav-menu">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 92 |
<li class="nav-item active" data-page="dashboard">
|
| 93 |
<a href="#" class="nav-link">
|
| 94 |
<i class="fas fa-chart-line"></i>
|
|
@@ -148,9 +154,6 @@
|
|
| 148 |
<button class="mobile-menu-btn" id="mobileMenuBtn">
|
| 149 |
<i class="fas fa-bars"></i>
|
| 150 |
</button>
|
| 151 |
-
<button class="btn btn-sm btn-outline" onclick="window.location.href='/dashboard.html'" title="回到主控台">
|
| 152 |
-
<i class="fas fa-home"></i>
|
| 153 |
-
</button>
|
| 154 |
<h1 id="pageTitle">系統儀表板</h1>
|
| 155 |
</div>
|
| 156 |
<div class="header-right">
|
|
@@ -210,72 +213,29 @@
|
|
| 210 |
<script>
|
| 211 |
// 手機選單事件由 app.js 統一處理
|
| 212 |
|
| 213 |
-
//
|
| 214 |
-
document.addEventListener('DOMContentLoaded', () => {
|
| 215 |
-
console.log('🚀
|
| 216 |
-
|
| 217 |
-
//
|
| 218 |
-
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
// 開發模式直接啟動
|
| 224 |
-
setTimeout(() => {
|
| 225 |
-
const loadingOverlay = document.getElementById('loading-overlay');
|
| 226 |
-
if (loadingOverlay) {
|
| 227 |
-
loadingOverlay.classList.add('hidden');
|
| 228 |
-
}
|
| 229 |
-
|
| 230 |
-
if (window.app) {
|
| 231 |
-
window.app.init().catch(console.error);
|
| 232 |
-
}
|
| 233 |
-
}, 1000);
|
| 234 |
-
return;
|
| 235 |
}
|
| 236 |
-
|
| 237 |
-
//
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
window.app.init().then(() => {
|
| 246 |
-
// 應用啟動成功後隱藏載入動畫
|
| 247 |
-
if (loadingOverlay) {
|
| 248 |
-
loadingOverlay.classList.add('hidden');
|
| 249 |
-
}
|
| 250 |
-
window.app.restoreSidebarState();
|
| 251 |
-
}).catch(error => {
|
| 252 |
-
console.error('❌ 應用啟動失敗:', error);
|
| 253 |
-
// 即使失敗也要隱藏載入動畫
|
| 254 |
-
if (loadingOverlay) {
|
| 255 |
-
loadingOverlay.classList.add('hidden');
|
| 256 |
-
}
|
| 257 |
-
});
|
| 258 |
-
} else {
|
| 259 |
-
// 沒有 app 對象也要隱藏載入動畫
|
| 260 |
-
if (loadingOverlay) {
|
| 261 |
-
loadingOverlay.classList.add('hidden');
|
| 262 |
-
}
|
| 263 |
-
}
|
| 264 |
-
} else {
|
| 265 |
-
console.log('⚠️ 等待認證或重定向');
|
| 266 |
-
// 如果未認證,也要隱藏載入動畫(因為會重定向)
|
| 267 |
-
if (loadingOverlay) {
|
| 268 |
-
loadingOverlay.classList.add('hidden');
|
| 269 |
-
}
|
| 270 |
-
}
|
| 271 |
-
} catch (error) {
|
| 272 |
-
console.error('❌ 初始化過程出錯:', error);
|
| 273 |
-
// 出錯時也要隱藏載入動畫
|
| 274 |
-
if (loadingOverlay) {
|
| 275 |
-
loadingOverlay.classList.add('hidden');
|
| 276 |
}
|
| 277 |
-
}
|
| 278 |
-
}
|
| 279 |
});
|
| 280 |
</script>
|
| 281 |
</body>
|
|
|
|
| 89 |
</div>
|
| 90 |
|
| 91 |
<ul class="nav-menu">
|
| 92 |
+
<li class="nav-item">
|
| 93 |
+
<a href="/dashboard.html" class="nav-link">
|
| 94 |
+
<i class="fas fa-home"></i>
|
| 95 |
+
<span>返回主控台</span>
|
| 96 |
+
</a>
|
| 97 |
+
</li>
|
| 98 |
<li class="nav-item active" data-page="dashboard">
|
| 99 |
<a href="#" class="nav-link">
|
| 100 |
<i class="fas fa-chart-line"></i>
|
|
|
|
| 154 |
<button class="mobile-menu-btn" id="mobileMenuBtn">
|
| 155 |
<i class="fas fa-bars"></i>
|
| 156 |
</button>
|
|
|
|
|
|
|
|
|
|
| 157 |
<h1 id="pageTitle">系統儀表板</h1>
|
| 158 |
</div>
|
| 159 |
<div class="header-right">
|
|
|
|
| 213 |
<script>
|
| 214 |
// 手機選單事件由 app.js 統一處理
|
| 215 |
|
| 216 |
+
// 統一使用 authManager 初始化
|
| 217 |
+
document.addEventListener('DOMContentLoaded', async () => {
|
| 218 |
+
console.log('🚀 授權管理頁面初始化');
|
| 219 |
+
|
| 220 |
+
// 初始化應用的函數
|
| 221 |
+
async function initApp() {
|
| 222 |
+
if (window.app) {
|
| 223 |
+
await window.app.init();
|
| 224 |
+
window.app.restoreSidebarState();
|
| 225 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 226 |
}
|
| 227 |
+
|
| 228 |
+
// 使用 authManager 的統一認證
|
| 229 |
+
if (window.authManager) {
|
| 230 |
+
await window.authManager.initProtectedPage('index', initApp);
|
| 231 |
+
} else {
|
| 232 |
+
// 等待 authManager 載入
|
| 233 |
+
window.addEventListener('supabaseReady', async () => {
|
| 234 |
+
if (window.authManager) {
|
| 235 |
+
await window.authManager.initProtectedPage('index', initApp);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 236 |
}
|
| 237 |
+
});
|
| 238 |
+
}
|
| 239 |
});
|
| 240 |
</script>
|
| 241 |
</body>
|
frontend/js/auth.js
CHANGED
|
@@ -339,10 +339,9 @@ class AuthManager {
|
|
| 339 |
}
|
| 340 |
|
| 341 |
redirectToDashboard() {
|
| 342 |
-
console.log('🔄 重定向到
|
| 343 |
-
|
| 344 |
-
|
| 345 |
-
}
|
| 346 |
}
|
| 347 |
|
| 348 |
updateAuthUI() {
|
|
@@ -428,6 +427,93 @@ class AuthManager {
|
|
| 428 |
});
|
| 429 |
}
|
| 430 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 431 |
// Development helper
|
| 432 |
setSupabaseConfig(url, anonKey) {
|
| 433 |
Utils.setStorage('supabaseConfig', { url, anonKey });
|
|
|
|
| 339 |
}
|
| 340 |
|
| 341 |
redirectToDashboard() {
|
| 342 |
+
console.log('🔄 重定向到儀表板頁面');
|
| 343 |
+
// 統一導向到 dashboard.html
|
| 344 |
+
window.location.href = '/dashboard.html';
|
|
|
|
| 345 |
}
|
| 346 |
|
| 347 |
updateAuthUI() {
|
|
|
|
| 427 |
});
|
| 428 |
}
|
| 429 |
|
| 430 |
+
// 統一的頁面認證檢查方法
|
| 431 |
+
async checkPageAuth(pageName = 'protected') {
|
| 432 |
+
console.log(`🔐 檢查頁面認證: ${pageName}`);
|
| 433 |
+
|
| 434 |
+
// 開發模式直接通過
|
| 435 |
+
if (this.isDevMode) {
|
| 436 |
+
console.log('🔧 開發模式 - 跳過認證檢查');
|
| 437 |
+
this.updateAuthUI();
|
| 438 |
+
return true;
|
| 439 |
+
}
|
| 440 |
+
|
| 441 |
+
// 等待初始化完成
|
| 442 |
+
if (!this.initialized) {
|
| 443 |
+
console.log('⏳ 等待 AuthManager 初始化...');
|
| 444 |
+
await new Promise(resolve => {
|
| 445 |
+
const checkInit = setInterval(() => {
|
| 446 |
+
if (this.initialized) {
|
| 447 |
+
clearInterval(checkInit);
|
| 448 |
+
resolve();
|
| 449 |
+
}
|
| 450 |
+
}, 100);
|
| 451 |
+
// 最多等待 5 秒
|
| 452 |
+
setTimeout(() => {
|
| 453 |
+
clearInterval(checkInit);
|
| 454 |
+
resolve();
|
| 455 |
+
}, 5000);
|
| 456 |
+
});
|
| 457 |
+
}
|
| 458 |
+
|
| 459 |
+
// 檢查認證狀態
|
| 460 |
+
if (!this.isAuthenticated()) {
|
| 461 |
+
console.log('❌ 用戶未登入,重定向到登入頁');
|
| 462 |
+
this.redirectToLogin();
|
| 463 |
+
return false;
|
| 464 |
+
}
|
| 465 |
+
|
| 466 |
+
console.log('✅ 認證檢查通過');
|
| 467 |
+
this.updateAuthUI();
|
| 468 |
+
this.startActivityMonitoring();
|
| 469 |
+
return true;
|
| 470 |
+
}
|
| 471 |
+
|
| 472 |
+
// 初始化受保護頁面
|
| 473 |
+
async initProtectedPage(pageName, callback) {
|
| 474 |
+
console.log(`🚀 初始化受保護頁面: ${pageName}`);
|
| 475 |
+
|
| 476 |
+
// 先隱藏載入畫面
|
| 477 |
+
const loadingOverlay = document.getElementById('loading-overlay');
|
| 478 |
+
|
| 479 |
+
try {
|
| 480 |
+
// 檢查認證
|
| 481 |
+
const isAuth = await this.checkPageAuth(pageName);
|
| 482 |
+
|
| 483 |
+
if (isAuth) {
|
| 484 |
+
// 認證通過,執行頁面特定的初始化
|
| 485 |
+
if (callback && typeof callback === 'function') {
|
| 486 |
+
await callback();
|
| 487 |
+
}
|
| 488 |
+
}
|
| 489 |
+
} catch (error) {
|
| 490 |
+
console.error('❌ 頁面初始化失敗:', error);
|
| 491 |
+
Utils.showError('頁面載入失敗,請重新整理');
|
| 492 |
+
} finally {
|
| 493 |
+
// 無論成功或失敗都隱藏載入畫面
|
| 494 |
+
if (loadingOverlay) {
|
| 495 |
+
loadingOverlay.style.display = 'none';
|
| 496 |
+
}
|
| 497 |
+
}
|
| 498 |
+
}
|
| 499 |
+
|
| 500 |
+
// 統一的登出方法(增加清理和重定向)
|
| 501 |
+
async handleLogout() {
|
| 502 |
+
try {
|
| 503 |
+
const result = await this.logout();
|
| 504 |
+
if (result.success) {
|
| 505 |
+
Utils.showSuccess('登出成功');
|
| 506 |
+
this.redirectToLogin();
|
| 507 |
+
} else {
|
| 508 |
+
Utils.showError('登出失敗');
|
| 509 |
+
}
|
| 510 |
+
} catch (error) {
|
| 511 |
+
console.error('登出錯誤:', error);
|
| 512 |
+
// 即使出錯也重定向到登入頁
|
| 513 |
+
this.redirectToLogin();
|
| 514 |
+
}
|
| 515 |
+
}
|
| 516 |
+
|
| 517 |
// Development helper
|
| 518 |
setSupabaseConfig(url, anonKey) {
|
| 519 |
Utils.setStorage('supabaseConfig', { url, anonKey });
|
frontend/versions.html
CHANGED
|
@@ -773,7 +773,7 @@
|
|
| 773 |
<button class="mobile-menu-btn" id="mobileMenuBtn">
|
| 774 |
<i class="fas fa-bars"></i>
|
| 775 |
</button>
|
| 776 |
-
<button class="btn btn-sm btn-outline" onclick="window.location.href='/dashboard.html'" title="回到
|
| 777 |
<i class="fas fa-home"></i>
|
| 778 |
</button>
|
| 779 |
<h1 id="pageTitle">版本列表</h1>
|
|
@@ -835,30 +835,19 @@
|
|
| 835 |
|
| 836 |
async init() {
|
| 837 |
console.log('🚀 初始化版本管理應用...');
|
| 838 |
-
|
| 839 |
-
// 檢查開發模式
|
| 840 |
-
this.isDevMode = window.location.hostname === 'localhost' ||
|
| 841 |
-
window.location.hostname === '127.0.0.1';
|
| 842 |
|
| 843 |
-
|
| 844 |
-
|
| 845 |
-
|
| 846 |
-
|
| 847 |
-
|
| 848 |
-
} else {
|
| 849 |
-
// 生產模式初始化
|
| 850 |
-
await this.initSupabase();
|
| 851 |
-
await this.checkAuth();
|
| 852 |
-
}
|
| 853 |
|
| 854 |
-
// 設定頁面
|
|
|
|
| 855 |
this.setupPageNavigation();
|
| 856 |
-
|
| 857 |
-
//
|
| 858 |
-
|
| 859 |
-
if (loadingOverlay) {
|
| 860 |
-
loadingOverlay.style.display = 'none';
|
| 861 |
-
}
|
| 862 |
}
|
| 863 |
|
| 864 |
async initSupabase() {
|
|
@@ -883,37 +872,6 @@
|
|
| 883 |
}
|
| 884 |
}
|
| 885 |
|
| 886 |
-
async checkAuth() {
|
| 887 |
-
if (!this.supabaseLicense) {
|
| 888 |
-
window.location.href = '/login.html';
|
| 889 |
-
return;
|
| 890 |
-
}
|
| 891 |
-
|
| 892 |
-
const { data: { session } } = await this.supabaseLicense.auth.getSession();
|
| 893 |
-
if (!session) {
|
| 894 |
-
window.location.href = '/login.html';
|
| 895 |
-
return;
|
| 896 |
-
}
|
| 897 |
-
|
| 898 |
-
this.updateUserInfo(session.user.email);
|
| 899 |
-
this.setupEventListeners();
|
| 900 |
-
this.loadPageContent('versions');
|
| 901 |
-
}
|
| 902 |
-
|
| 903 |
-
updateUserInfo(email) {
|
| 904 |
-
const userInfo = document.getElementById('userInfo');
|
| 905 |
-
if (userInfo) {
|
| 906 |
-
userInfo.innerHTML = `
|
| 907 |
-
<div class="user-profile">
|
| 908 |
-
<span class="user-email">${email}</span>
|
| 909 |
-
<button class="btn btn-sm btn-secondary" onclick="versionApp.logout()">
|
| 910 |
-
<i class="fas fa-sign-out-alt"></i>
|
| 911 |
-
登出
|
| 912 |
-
</button>
|
| 913 |
-
</div>
|
| 914 |
-
`;
|
| 915 |
-
}
|
| 916 |
-
}
|
| 917 |
|
| 918 |
setupEventListeners() {
|
| 919 |
// 手機選單功能
|
|
@@ -1642,19 +1600,8 @@
|
|
| 1642 |
}
|
| 1643 |
|
| 1644 |
async logout() {
|
| 1645 |
-
if (
|
| 1646 |
-
|
| 1647 |
-
window.location.href = '/login.html';
|
| 1648 |
-
return;
|
| 1649 |
-
}
|
| 1650 |
-
|
| 1651 |
-
if (this.supabaseLicense) {
|
| 1652 |
-
try {
|
| 1653 |
-
await this.supabaseLicense.auth.signOut();
|
| 1654 |
-
window.location.href = '/login.html';
|
| 1655 |
-
} catch (error) {
|
| 1656 |
-
console.error('登出失敗:', error);
|
| 1657 |
-
}
|
| 1658 |
}
|
| 1659 |
}
|
| 1660 |
|
|
@@ -1768,11 +1715,22 @@
|
|
| 1768 |
// 全域實例
|
| 1769 |
let versionApp;
|
| 1770 |
|
| 1771 |
-
// 頁面載入時初始化
|
| 1772 |
document.addEventListener('DOMContentLoaded', async () => {
|
| 1773 |
console.log('🚀 版本管理系統初始化');
|
| 1774 |
versionApp = new VersionApp();
|
| 1775 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1776 |
});
|
| 1777 |
|
| 1778 |
// 手機選單功能由 VersionApp 統一處理
|
|
|
|
| 773 |
<button class="mobile-menu-btn" id="mobileMenuBtn">
|
| 774 |
<i class="fas fa-bars"></i>
|
| 775 |
</button>
|
| 776 |
+
<button class="btn btn-sm btn-outline" onclick="window.location.href='/dashboard.html'" title="回到儀表板">
|
| 777 |
<i class="fas fa-home"></i>
|
| 778 |
</button>
|
| 779 |
<h1 id="pageTitle">版本列表</h1>
|
|
|
|
| 835 |
|
| 836 |
async init() {
|
| 837 |
console.log('🚀 初始化版本管理應用...');
|
|
|
|
|
|
|
|
|
|
|
|
|
| 838 |
|
| 839 |
+
// 使用 authManager 的開發模式狀態
|
| 840 |
+
this.isDevMode = window.authManager?.isDevMode || false;
|
| 841 |
+
|
| 842 |
+
// 初始化 Supabase
|
| 843 |
+
await this.initSupabase();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 844 |
|
| 845 |
+
// 設定事件監聽和頁面導航
|
| 846 |
+
this.setupEventListeners();
|
| 847 |
this.setupPageNavigation();
|
| 848 |
+
|
| 849 |
+
// 載入預設頁面
|
| 850 |
+
this.loadPageContent('versions');
|
|
|
|
|
|
|
|
|
|
| 851 |
}
|
| 852 |
|
| 853 |
async initSupabase() {
|
|
|
|
| 872 |
}
|
| 873 |
}
|
| 874 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 875 |
|
| 876 |
setupEventListeners() {
|
| 877 |
// 手機選單功能
|
|
|
|
| 1600 |
}
|
| 1601 |
|
| 1602 |
async logout() {
|
| 1603 |
+
if (window.authManager) {
|
| 1604 |
+
await window.authManager.handleLogout();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1605 |
}
|
| 1606 |
}
|
| 1607 |
|
|
|
|
| 1715 |
// 全域實例
|
| 1716 |
let versionApp;
|
| 1717 |
|
| 1718 |
+
// 頁面載入時使用統一認證初始化
|
| 1719 |
document.addEventListener('DOMContentLoaded', async () => {
|
| 1720 |
console.log('🚀 版本管理系統初始化');
|
| 1721 |
versionApp = new VersionApp();
|
| 1722 |
+
|
| 1723 |
+
// 使用 authManager 的統一認證
|
| 1724 |
+
if (window.authManager) {
|
| 1725 |
+
await window.authManager.initProtectedPage('versions', () => versionApp.init());
|
| 1726 |
+
} else {
|
| 1727 |
+
// 等待 authManager 載入
|
| 1728 |
+
window.addEventListener('supabaseReady', async () => {
|
| 1729 |
+
if (window.authManager) {
|
| 1730 |
+
await window.authManager.initProtectedPage('versions', () => versionApp.init());
|
| 1731 |
+
}
|
| 1732 |
+
});
|
| 1733 |
+
}
|
| 1734 |
});
|
| 1735 |
|
| 1736 |
// 手機選單功能由 VersionApp 統一處理
|