ENTERPRISE: Implement zero-config SSL/TLS with dual HTTP/HTTPS architecture

Major architectural improvements:
- Auto-generate SSL certificates on first startup with OpenSSL
- Dual HTTP (8081) + HTTPS (8443) backend API servers
- Frontend auto-detects protocol and uses appropriate API endpoint
- Fix database ORM initialization race condition with getDb() pattern
- WebSocket authentication with JWT verification during handshake
- Zero-config .env file generation for production deployment
- Docker and nginx configurations for container deployment

Technical fixes:
- Eliminate module initialization race conditions in database access
- Replace direct db imports with safer getDb() function calls
- Automatic HTTPS frontend development server (npm run dev:https)
- SSL certificate generation with termix.crt/termix.key
- Cross-platform environment variable support with cross-env

This enables seamless HTTP→HTTPS upgrade with zero manual configuration.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
ZacharyZcR
2025-09-22 11:12:58 +08:00
parent dfc92428e0
commit 7763e6a904
28 changed files with 1122 additions and 113 deletions

View File

@@ -135,6 +135,14 @@ async function initializeCompleteDatabase(): Promise<void> {
// Create module-level sqlite instance after database is initialized
sqlite = memoryDatabase;
// Initialize drizzle ORM with the configured database
db = drizzle(sqlite, { schema });
databaseLogger.info("Database ORM initialized", {
operation: "drizzle_init",
tablesConfigured: Object.keys(schema).length
});
sqlite.exec(`
CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY,
@@ -510,8 +518,19 @@ async function handlePostInitFileEncryption() {
}
}
initializeCompleteDatabase()
.then(() => handlePostInitFileEncryption())
// Export a promise that resolves when database is fully initialized
export const databaseReady = initializeCompleteDatabase()
.then(async () => {
await handlePostInitFileEncryption();
databaseLogger.success("Database connection established", {
operation: "db_init",
path: actualDbPath,
hasEncryptedBackup:
enableFileEncryption &&
DatabaseFileEncryption.isEncryptedDatabaseFile(encryptedDbPath),
});
})
.catch((error) => {
databaseLogger.error("Failed to initialize database", error, {
operation: "db_init",
@@ -519,14 +538,6 @@ initializeCompleteDatabase()
process.exit(1);
});
databaseLogger.success("Database connection established", {
operation: "db_init",
path: actualDbPath,
hasEncryptedBackup:
enableFileEncryption &&
DatabaseFileEncryption.isEncryptedDatabaseFile(encryptedDbPath),
});
// Cleanup function for database and temporary files
async function cleanupDatabase() {
// Save in-memory database before closing
@@ -612,9 +623,19 @@ process.on("SIGTERM", async () => {
process.exit(0);
});
// Export database connection and file encryption utilities
export const db = drizzle(sqlite, { schema });
export const sqliteInstance = sqlite; // Export underlying SQLite instance for schema queries
// Database connection - will be initialized after database setup
let db: ReturnType<typeof drizzle<typeof schema>>;
// Export database connection getter function to avoid undefined access
export function getDb(): ReturnType<typeof drizzle<typeof schema>> {
if (!db) {
throw new Error("Database not initialized. Ensure databaseReady promise is awaited before accessing db.");
}
return db;
}
// Legacy export for compatibility - will throw if accessed before initialization
export { db };
export { DatabaseFileEncryption };
export const databasePaths = {
main: actualDbPath,