Snapshot: full project state

This commit is contained in:
2026-10-06 23:43:43 -07:00
commit fc6d00988c
13 changed files with 1283 additions and 0 deletions

113
pettalk-auth-db.ts Normal file
View File

@@ -0,0 +1,113 @@
import fs from "fs";
import path from "path";
import bcrypt from "bcryptjs";
const DATA_DIR = path.join(process.cwd(), "data");
const USERS_FILE = path.join(DATA_DIR, "users.json");
export interface UserRow {
id: number;
username: string;
email: string;
password_hash: string;
created_at: string;
}
export interface UserPublic {
id: number;
username: string;
email: string;
created_at: string;
}
function ensureDataDir() {
if (!fs.existsSync(DATA_DIR)) {
fs.mkdirSync(DATA_DIR, { recursive: true });
}
}
function readUsers(): UserRow[] {
ensureDataDir();
if (!fs.existsSync(USERS_FILE)) {
return [];
}
try {
const raw = fs.readFileSync(USERS_FILE, "utf-8");
return JSON.parse(raw);
} catch {
return [];
}
}
function writeUsers(users: UserRow[]) {
ensureDataDir();
fs.writeFileSync(USERS_FILE, JSON.stringify(users, null, 2), "utf-8");
}
let nextId = 0;
function getNextId(): number {
if (nextId === 0) {
const users = readUsers();
nextId = users.reduce((max, u) => Math.max(max, u.id), 0) + 1;
}
return nextId++;
}
export function createUser(username: string, email: string, password: string): UserPublic {
const users = readUsers();
if (users.find((u) => u.email === email || u.username === username)) {
throw new Error("A user with that email or username already exists.");
}
const password_hash = bcrypt.hashSync(password, 10);
const newUser: UserRow = {
id: getNextId(),
username,
email,
password_hash,
created_at: new Date().toISOString(),
};
users.push(newUser);
writeUsers(users);
return {
id: newUser.id,
username: newUser.username,
email: newUser.email,
created_at: newUser.created_at,
};
}
export function authenticateUser(email: string, password: string): UserPublic {
const users = readUsers();
const user = users.find((u) => u.email === email);
if (!user) {
throw new Error("Invalid email or password.");
}
if (!bcrypt.compareSync(password, user.password_hash)) {
throw new Error("Invalid email or password.");
}
return {
id: user.id,
username: user.username,
email: user.email,
created_at: user.created_at,
};
}
export function getUserById(id: number): UserPublic | null {
const users = readUsers();
const user = users.find((u) => u.id === id);
if (!user) return null;
return {
id: user.id,
username: user.username,
email: user.email,
created_at: user.created_at,
};
}