mirror of
https://github.com/shuaiplus/nodewarden.git
synced 2026-06-21 05:10:41 +00:00
feat: Implement TOTP-based two-factor authentication
- Added TOTP support for two-factor authentication in user profiles and login flows. - Introduced device management endpoints to handle known devices and their registration. - Enhanced database schema to include devices and trusted two-factor tokens. - Updated response handling to include two-factor token in successful login responses. - Modified registration and login pages to guide users through enabling TOTP. - Improved device identification and management utilities for better user experience.
This commit is contained in:
+55
-26
@@ -3,39 +3,68 @@ import { handleRequest } from './router';
|
||||
import { StorageService } from './services/storage';
|
||||
import { applyCors, jsonResponse } from './utils/response';
|
||||
|
||||
// Per-isolate flags. Each Worker isolate may have its own copy of these flags.
|
||||
// initializeDatabase() only validates schema presence, so retries are cheap.
|
||||
let dbInitialized = false;
|
||||
let dbInitError: string | null = null;
|
||||
let dbInitPromise: Promise<void> | null = null;
|
||||
|
||||
function shouldSkipDatabaseInit(request: Request): boolean {
|
||||
const url = new URL(request.url);
|
||||
const path = url.pathname;
|
||||
const method = request.method;
|
||||
|
||||
if (method === 'OPTIONS') return true;
|
||||
if (method === 'GET' && (path === '/favicon.ico' || path === '/favicon.svg')) return true;
|
||||
if (method === 'GET' && path === '/.well-known/appspecific/com.chrome.devtools.json') return true;
|
||||
if (method === 'GET' && path.startsWith('/icons/')) return true;
|
||||
if (path.startsWith('/notifications/')) return true;
|
||||
if (method === 'GET' && (path === '/config' || path === '/api/config' || path === '/api/version')) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async function ensureDatabaseInitialized(env: Env): Promise<void> {
|
||||
if (dbInitialized) return;
|
||||
|
||||
if (!dbInitPromise) {
|
||||
dbInitPromise = (async () => {
|
||||
const storage = new StorageService(env.DB);
|
||||
await storage.initializeDatabase();
|
||||
dbInitialized = true;
|
||||
dbInitError = null;
|
||||
})()
|
||||
.catch((error: unknown) => {
|
||||
console.error('Failed to initialize database:', error);
|
||||
dbInitError = error instanceof Error ? error.message : 'Unknown database initialization error';
|
||||
})
|
||||
.finally(() => {
|
||||
dbInitPromise = null;
|
||||
});
|
||||
}
|
||||
|
||||
await dbInitPromise;
|
||||
}
|
||||
|
||||
export default {
|
||||
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
|
||||
// Auto-initialize database on first request
|
||||
if (!dbInitialized) {
|
||||
try {
|
||||
const storage = new StorageService(env.DB);
|
||||
await storage.initializeDatabase();
|
||||
dbInitialized = true;
|
||||
dbInitError = null;
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize database:', error);
|
||||
dbInitError = error instanceof Error ? error.message : 'Unknown database initialization error';
|
||||
}
|
||||
}
|
||||
void ctx;
|
||||
const requiresDatabase = !shouldSkipDatabaseInit(request);
|
||||
|
||||
if (dbInitError) {
|
||||
const resp = jsonResponse(
|
||||
{
|
||||
error: 'Database not initialized',
|
||||
error_description: dbInitError,
|
||||
ErrorModel: {
|
||||
Message: dbInitError,
|
||||
Object: 'error',
|
||||
if (requiresDatabase) {
|
||||
await ensureDatabaseInitialized(env);
|
||||
if (dbInitError) {
|
||||
const resp = jsonResponse(
|
||||
{
|
||||
error: 'Database not initialized',
|
||||
error_description: dbInitError,
|
||||
ErrorModel: {
|
||||
Message: dbInitError,
|
||||
Object: 'error',
|
||||
},
|
||||
},
|
||||
},
|
||||
500
|
||||
);
|
||||
return applyCors(request, resp);
|
||||
500
|
||||
);
|
||||
return applyCors(request, resp);
|
||||
}
|
||||
}
|
||||
|
||||
const resp = await handleRequest(request, env);
|
||||
|
||||
Reference in New Issue
Block a user