Snapshot: full project state
This commit is contained in:
113
pettalk-auth-db.ts
Normal file
113
pettalk-auth-db.ts
Normal 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,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user