75 lines
2 KiB
TypeScript
75 lines
2 KiB
TypeScript
import { randomBytes } from 'crypto';
|
|
import { hashPassword } from './accounts';
|
|
|
|
/**
|
|
* Admin-console accounts. Replaces the original static two-account env map
|
|
* with a persisted table; the env values remain the seed for empty databases.
|
|
*/
|
|
|
|
export interface AdminUser {
|
|
id: string; // usr_<hex>
|
|
username: string;
|
|
passwordHash: string;
|
|
active: boolean;
|
|
createdMs: number;
|
|
}
|
|
|
|
export interface AdminUserRepo {
|
|
list(): AdminUser[];
|
|
findByUsername(username: string): AdminUser | undefined;
|
|
save(user: AdminUser): void;
|
|
}
|
|
|
|
export function newAdminUserId(): string {
|
|
return `usr_${randomBytes(6).toString('hex')}`;
|
|
}
|
|
|
|
/** The env-backed seed accounts (preserved from the pre-table behavior). */
|
|
export function seedAdminUsersFromEnv(): { username: string; password: string }[] {
|
|
return [
|
|
{
|
|
username: process.env.ADMIN_USER ?? 'admin',
|
|
password: process.env.ADMIN_KEY ?? 'admin-dev-key',
|
|
},
|
|
{
|
|
username: process.env.DEMO_ADMIN_USER ?? 'demo',
|
|
password: process.env.DEMO_ADMIN_PASSWORD ?? '$$$Adm1n###',
|
|
},
|
|
];
|
|
}
|
|
|
|
export function makeAdminUser(username: string, password: string): AdminUser {
|
|
return {
|
|
id: newAdminUserId(),
|
|
username,
|
|
passwordHash: hashPassword(password),
|
|
active: true,
|
|
createdMs: Date.now(),
|
|
};
|
|
}
|
|
|
|
export class InMemoryAdminUserRepo implements AdminUserRepo {
|
|
private users: AdminUser[];
|
|
|
|
private constructor(users: AdminUser[]) {
|
|
this.users = users.map((u) => ({ ...u }));
|
|
}
|
|
|
|
static seeded(seed: { username: string; password: string }[]): InMemoryAdminUserRepo {
|
|
return new InMemoryAdminUserRepo(seed.map((s) => makeAdminUser(s.username, s.password)));
|
|
}
|
|
|
|
list(): AdminUser[] {
|
|
return [...this.users];
|
|
}
|
|
|
|
findByUsername(username: string): AdminUser | undefined {
|
|
return this.users.find((u) => u.username === username);
|
|
}
|
|
|
|
save(user: AdminUser): void {
|
|
const i = this.users.findIndex((u) => u.id === user.id || u.username === user.username);
|
|
if (i >= 0) this.users[i] = user;
|
|
else this.users.push(user);
|
|
}
|
|
}
|