Spaces:
Paused
Paused
| /** | |
| * Password Reset CLI — T-38 | |
| * | |
| * Usage: | |
| * node bin/reset-password.mjs | |
| * npx omniroute reset-password | |
| * | |
| * Resets the admin password for OmniRoute. | |
| * Prompts for a new password and updates the database directly. | |
| * | |
| * @module bin/reset-password | |
| */ | |
| import { createInterface } from "node:readline"; | |
| import { resolve, dirname } from "node:path"; | |
| import { fileURLToPath } from "node:url"; | |
| import { existsSync } from "node:fs"; | |
| import bcrypt from "bcryptjs"; | |
| const __dirname = dirname(fileURLToPath(import.meta.url)); | |
| // Resolve data directory — same logic as the server | |
| const DATA_DIR = process.env.DATA_DIR || resolve(__dirname, "..", "data"); | |
| const DB_PATH = resolve(DATA_DIR, "settings.db"); | |
| const rl = createInterface({ | |
| input: process.stdin, | |
| output: process.stdout, | |
| }); | |
| function ask(question) { | |
| return new Promise((resolve) => rl.question(question, resolve)); | |
| } | |
| function generateSecretDigest(input) { | |
| // Use bcrypt with a salt round of 10 to match login/route.ts expectations | |
| // and resolve CodeQL js/insufficient-password-hash warning. | |
| return bcrypt.hashSync(input, 10); | |
| } | |
| console.log("\n🔑 OmniRoute — Password Reset\n"); | |
| async function main() { | |
| // Check if database exists | |
| if (!existsSync(DB_PATH)) { | |
| console.error(`❌ Database not found at: ${DB_PATH}`); | |
| console.error(` Make sure OmniRoute has been started at least once.`); | |
| console.error(` Or set DATA_DIR env var to your data directory.\n`); | |
| process.exit(1); | |
| } | |
| let Database; | |
| try { | |
| Database = (await import("better-sqlite3")).default; | |
| } catch { | |
| console.error("❌ better-sqlite3 not installed. Run: npm install"); | |
| process.exit(1); | |
| } | |
| const db = new Database(DB_PATH); | |
| // Check current settings | |
| const row = db.prepare("SELECT value FROM settings WHERE key = 'password'").get(); | |
| if (row) { | |
| console.log("ℹ️ A password is currently set."); | |
| } else { | |
| console.log("ℹ️ No password is currently set."); | |
| } | |
| const password = await ask("Enter new password (min 8 chars): "); | |
| if (!password || password.length < 8) { | |
| console.error("\n❌ Password must be at least 8 characters.\n"); | |
| db.close(); | |
| rl.close(); | |
| process.exit(1); | |
| } | |
| const confirm = await ask("Confirm new password: "); | |
| if (password !== confirm) { | |
| console.error("\n❌ Passwords do not match.\n"); | |
| db.close(); | |
| rl.close(); | |
| process.exit(1); | |
| } | |
| const hashed = generateSecretDigest(password); | |
| // Upsert the password | |
| const stmt = db.prepare(` | |
| INSERT INTO settings (key, value) VALUES ('password', ?) | |
| ON CONFLICT(key) DO UPDATE SET value = excluded.value | |
| `); | |
| stmt.run(hashed); | |
| // Also ensure requireLogin is true | |
| const loginStmt = db.prepare(` | |
| INSERT INTO settings (key, value) VALUES ('requireLogin', 'true') | |
| ON CONFLICT(key) DO UPDATE SET value = excluded.value | |
| `); | |
| loginStmt.run(); | |
| db.close(); | |
| rl.close(); | |
| console.log("\n✅ Password reset successfully!"); | |
| console.log(" Restart OmniRoute for changes to take effect.\n"); | |
| } | |
| main().catch((err) => { | |
| console.error(`\n❌ Error: ${err.message}\n`); | |
| rl.close(); | |
| process.exit(1); | |
| }); | |