diff --git a/logrotate.conf b/logrotate.conf new file mode 100644 index 0000000..e989b0b --- /dev/null +++ b/logrotate.conf @@ -0,0 +1,11 @@ +/home/exedev/stdeditor/data/app.log /home/exedev/stdeditor/data/cleanup.log { + daily + rotate 14 + compress + delaycompress + missingok + notifempty + create 640 exedev exedev + dateext + dateformat -%Y%m%d +} diff --git a/scripts/cleanup.ts b/scripts/cleanup.ts new file mode 100755 index 0000000..105b2d6 --- /dev/null +++ b/scripts/cleanup.ts @@ -0,0 +1,37 @@ +#!/usr/bin/env bun +/** + * Database cleanup script + * Removes expired OAuth states and optionally old sessions + * Run via cron: 0 * * * * /home/exedev/.bun/bin/bun /home/exedev/stdeditor/scripts/cleanup.ts + */ + +import { Database } from 'bun:sqlite'; +import * as path from 'path'; + +const DATA_DIR = process.env.DATA_DIR || './data'; +const DB_PATH = path.join(DATA_DIR, 'oauth.db'); + +try { + const db = new Database(DB_PATH); + + // Clean up OAuth states older than 1 hour + const statesResult = db.run( + `DELETE FROM oauth_states WHERE created_at < strftime('%s', 'now') - 3600` + ); + + // Clean up sessions older than 30 days (optional - sessions may still be valid) + const sessionsResult = db.run( + `DELETE FROM oauth_sessions WHERE updated_at < strftime('%s', 'now') - 2592000` + ); + + // Vacuum the database to reclaim space + db.run('VACUUM'); + + const timestamp = new Date().toISOString(); + console.log(`[${timestamp}] Cleanup complete: removed old states and sessions, vacuumed database`); + + db.close(); +} catch (error) { + console.error('Cleanup failed:', error); + process.exit(1); +} diff --git a/src/lib/logger.ts b/src/lib/logger.ts new file mode 100644 index 0000000..6b9cd34 --- /dev/null +++ b/src/lib/logger.ts @@ -0,0 +1,43 @@ +import * as fs from 'fs'; +import * as path from 'path'; + +const DATA_DIR = process.env.DATA_DIR || './data'; +const LOG_PATH = path.join(DATA_DIR, 'app.log'); + +type LogLevel = 'INFO' | 'WARN' | 'ERROR' | 'DEBUG'; + +function formatDate(date: Date): string { + return date.toISOString(); +} + +function writeLog(level: LogLevel, message: string, meta?: Record) { + const timestamp = formatDate(new Date()); + const metaStr = meta ? ` ${JSON.stringify(meta)}` : ''; + const logLine = `[${timestamp}] ${level}: ${message}${metaStr}\n`; + + // Write to file + try { + fs.appendFileSync(LOG_PATH, logLine); + } catch (err) { + // Fall back to console if file write fails + console.error('Failed to write to log file:', err); + } + + // Also write to stdout/stderr for systemd journal + if (level === 'ERROR') { + process.stderr.write(logLine); + } else { + process.stdout.write(logLine); + } +} + +export const logger = { + info: (message: string, meta?: Record) => writeLog('INFO', message, meta), + warn: (message: string, meta?: Record) => writeLog('WARN', message, meta), + error: (message: string, meta?: Record) => writeLog('ERROR', message, meta), + debug: (message: string, meta?: Record) => { + if (process.env.DEBUG) { + writeLog('DEBUG', message, meta); + } + }, +};