Includes: API (Express/TypeScript), Astro site, Python workers, document generators, FCC compliance tools, Canada CRTC formation, Ansible infrastructure, and deployment scripts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
56 lines
1.6 KiB
TypeScript
56 lines
1.6 KiB
TypeScript
/**
|
|
* Create an admin user for the dashboard.
|
|
*
|
|
* Usage:
|
|
* npx tsx src/create-admin.ts <username> <password> [display_name] [email]
|
|
*
|
|
* Example:
|
|
* npx tsx src/create-admin.ts justin MySecurePass123 "Justin" "justin@performancewest.net"
|
|
*
|
|
* Requires DATABASE_URL environment variable.
|
|
*/
|
|
|
|
import bcrypt from "bcryptjs";
|
|
import pg from "pg";
|
|
|
|
async function main() {
|
|
const [, , username, password, displayName, email] = process.argv;
|
|
|
|
if (!username || !password) {
|
|
console.error("Usage: npx tsx src/create-admin.ts <username> <password> [display_name] [email]");
|
|
process.exit(1);
|
|
}
|
|
|
|
const dbUrl = process.env.DATABASE_URL;
|
|
if (!dbUrl) {
|
|
console.error("DATABASE_URL environment variable is required.");
|
|
process.exit(1);
|
|
}
|
|
|
|
const pool = new pg.Pool({ connectionString: dbUrl });
|
|
|
|
try {
|
|
// Hash password with bcrypt (12 rounds)
|
|
const hash = await bcrypt.hash(password, 12);
|
|
|
|
const result = await pool.query(
|
|
`INSERT INTO admin_users (username, password_hash, display_name, email)
|
|
VALUES ($1, $2, $3, $4)
|
|
ON CONFLICT (username) DO UPDATE SET
|
|
password_hash = $2, display_name = $3, email = $4, active = TRUE
|
|
RETURNING id, username`,
|
|
[username.toLowerCase().trim(), hash, displayName || username, email || null],
|
|
);
|
|
|
|
const user = result.rows[0];
|
|
console.log(`Admin user created/updated: ${user.username} (id: ${user.id})`);
|
|
console.log(`Login at: https://performancewest.net/admin`);
|
|
} catch (err) {
|
|
console.error("Failed to create admin user:", err);
|
|
process.exit(1);
|
|
} finally {
|
|
await pool.end();
|
|
}
|
|
}
|
|
|
|
main();
|