phase-10.5: market screener ui enhancements
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* UserStore — persistence layer for the users table.
|
||||
* All queries go through DatabaseConnection for audit + safety.
|
||||
*/
|
||||
|
||||
import { randomUUID } from 'crypto';
|
||||
import type { DatabaseConnection } from '../shared/db/DatabaseConnection.js';
|
||||
import { USER_QUERIES, RESET_TOKEN_QUERIES } from '../shared/db/queries.constant.js';
|
||||
import type { Role, User, UserRow } from './auth.model.js';
|
||||
|
||||
export class UserStore {
|
||||
constructor(private readonly db: DatabaseConnection) {}
|
||||
|
||||
findByEmail(email: string): UserRow | undefined {
|
||||
return this.db.rawGet<UserRow>(USER_QUERIES.SELECT_BY_EMAIL, [email]);
|
||||
}
|
||||
|
||||
findById(id: string): User | undefined {
|
||||
const row = this.db.rawGet<UserRow>(USER_QUERIES.SELECT_BY_ID, [id]);
|
||||
if (!row) return undefined;
|
||||
return this.#toUser(row);
|
||||
}
|
||||
|
||||
create(email: string, passwordHash: string, role: Role = 'viewer'): User {
|
||||
const id = randomUUID();
|
||||
const createdAt = new Date().toISOString();
|
||||
this.db.rawRun(USER_QUERIES.INSERT, [id, email, passwordHash, role, createdAt]);
|
||||
return { id, email, role, createdAt, lastLogin: null };
|
||||
}
|
||||
|
||||
touchLogin(id: string): void {
|
||||
this.db.rawRun(USER_QUERIES.UPDATE_LAST_LOGIN, [new Date().toISOString(), id]);
|
||||
}
|
||||
|
||||
updatePassword(id: string, passwordHash: string): void {
|
||||
this.db.rawRun('UPDATE users SET password_hash = ? WHERE id = ?', [passwordHash, id]);
|
||||
}
|
||||
|
||||
// ── Password reset tokens ──────────────────────────────────────────────────
|
||||
|
||||
createResetToken(userId: string, token: string, expiresAt: string): void {
|
||||
this.db.rawRun(RESET_TOKEN_QUERIES.INSERT, [token, userId, expiresAt]);
|
||||
}
|
||||
|
||||
findResetToken(
|
||||
token: string,
|
||||
): { token: string; user_id: string; expires_at: string; used: number } | undefined {
|
||||
return this.db.rawGet(RESET_TOKEN_QUERIES.FIND, [token]);
|
||||
}
|
||||
|
||||
markTokenUsed(token: string): void {
|
||||
this.db.rawRun(RESET_TOKEN_QUERIES.MARK_USED, [token]);
|
||||
}
|
||||
|
||||
purgeExpiredTokens(): void {
|
||||
this.db.rawRun(RESET_TOKEN_QUERIES.PURGE, [new Date().toISOString()]);
|
||||
}
|
||||
|
||||
#toUser(row: UserRow): User {
|
||||
return {
|
||||
id: row.id,
|
||||
email: row.email,
|
||||
role: row.role,
|
||||
createdAt: row.created_at,
|
||||
lastLogin: row.last_login,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user