mike-backend / backend /scripts /createLocalAuthUser.mjs
bardd's picture
Deploy Mike backend-only Space
06671aa verified
Raw History Blame Contribute Delete
2.04 kB
#!/usr/bin/env node
import crypto from "crypto";
import pg from "pg";
const { Pool } = pg;
const SCRYPT_PARAMS = { N: 16384, r: 8, p: 1, maxmem: 64 * 1024 * 1024 };
const SCRYPT_KEYLEN = 32;
const PASSWORD_PREFIX = "scrypt:v1:";
function hashPassword(password) {
const salt = crypto.randomBytes(16);
return new Promise((resolve, reject) => {
crypto.scrypt(password, salt, SCRYPT_KEYLEN, SCRYPT_PARAMS, (err, derived) => {
if (err) {
reject(err);
return;
}
resolve(
`${PASSWORD_PREFIX}${salt.toString("base64url")}:${derived.toString("base64url")}`,
);
});
});
}
async function main() {
const email = process.env.LOCAL_AUTH_EMAIL?.trim().toLowerCase();
const password = process.env.LOCAL_AUTH_PASSWORD;
const isOperator = process.env.LOCAL_AUTH_OPERATOR === "1";
const databaseUrl = process.env.DATABASE_URL;
if (!databaseUrl) {
throw new Error("DATABASE_URL is required");
}
if (!email) {
throw new Error("LOCAL_AUTH_EMAIL is required");
}
if (!password || password.length < 8) {
throw new Error("LOCAL_AUTH_PASSWORD must be at least 8 characters");
}
const pool = new Pool({ connectionString: databaseUrl });
const client = await pool.connect();
try {
const passwordHash = await hashPassword(password);
const userId = crypto.randomUUID();
const existing = await client.query(
`SELECT id FROM auth.users WHERE lower(email) = lower($1) LIMIT 1`,
[email],
);
if (existing.rows.length > 0) {
throw new Error(`User already exists for ${email}`);
}
await client.query(
`INSERT INTO auth.users (id, email, password_hash, is_operator)
VALUES ($1, $2, $3, $4)`,
[userId, email, passwordHash, isOperator],
);
console.log(
`Created local auth user ${email} (operator=${isOperator ? "yes" : "no"})`,
);
} finally {
client.release();
await pool.end();
}
}
main().catch((err) => {
console.error(err.message || err);
process.exit(1);
});