/** * Seed (or update) a member's encrypted Fastmail app-password credential. * * There is no onboarding UI — member_credentials is seeded out-of-band by the * operator. This script encrypts the app password with the same AES-256-GCM * helper the app uses (APP_PASSWORD_ENCRYPTION_KEY) and upserts one row. * * Run INSIDE the api container (where APP_PASSWORD_ENCRYPTION_KEY + DB_* are set): * * docker compose cp apps/api/scripts/seed-credential.mjs api:/app/apps/api/scripts/seed-credential.mjs * docker compose exec \ * -e FASTMAIL_APP_PASSWORD='xxxx-xxxx-xxxx-xxxx' \ * -e FASTMAIL_EMAIL='me@lucasberger.ca' \ * -e SEED_USER_ID=2 \ * api node scripts/seed-credential.mjs * * The app password never leaves the operator's shell — it is passed as an env * var to `docker compose exec` and encrypted at rest immediately. */ import { encryptPassword } from '../dist/broker/crypto.js' import mysql from 'mysql2/promise' const email = process.env.FASTMAIL_EMAIL || 'me@lucasberger.ca' const password = process.env.FASTMAIL_APP_PASSWORD const userId = Number.parseInt(process.env.SEED_USER_ID || '', 10) if (!password) { console.error('ERROR: set FASTMAIL_APP_PASSWORD') process.exit(1) } if (!Number.isInteger(userId) || userId <= 0) { console.error('ERROR: set SEED_USER_ID to the target users.id (integer)') process.exit(1) } // Encrypt with the app's key (read from APP_PASSWORD_ENCRYPTION_KEY at call time). const encrypted = encryptPassword(password) const conn = await mysql.createConnection({ host: process.env.DB_HOST || 'mariadb', port: Number.parseInt(process.env.DB_PORT || '3306', 10), user: process.env.DB_USER || 'familysync', password: process.env.DB_PASSWORD, database: process.env.DB_NAME || 'familysync', }) try { // Confirm the user exists before linking a credential to it. const [users] = await conn.execute('SELECT id FROM users WHERE id = ?', [userId]) if (users.length === 0) { console.error(`ERROR: no users row with id=${userId}. Log in first to create it.`) process.exit(1) } const [existing] = await conn.execute( 'SELECT id FROM member_credentials WHERE user_id = ?', [userId], ) if (existing.length > 0) { await conn.execute( 'UPDATE member_credentials SET encrypted_password = ?, fastmail_email = ? WHERE user_id = ?', [encrypted, email, userId], ) console.log(`Updated member_credentials for user_id=${userId} (${email})`) } else { await conn.execute( 'INSERT INTO member_credentials (user_id, encrypted_password, fastmail_email) VALUES (?, ?, ?)', [userId, encrypted, email], ) console.log(`Inserted member_credentials for user_id=${userId} (${email})`) } } finally { await conn.end() }