feat(backend): add fastify api, auth, prisma schema and jobs
This commit is contained in:
49
apps/backend/src/utils/errors.ts
Normal file
49
apps/backend/src/utils/errors.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
export class HttpError extends Error {
|
||||
readonly statusCode: number;
|
||||
readonly code: string;
|
||||
readonly details?: unknown;
|
||||
|
||||
constructor(statusCode: number, code: string, message: string, details?: unknown) {
|
||||
super(message);
|
||||
this.name = 'HttpError';
|
||||
this.statusCode = statusCode;
|
||||
this.code = code;
|
||||
this.details = details;
|
||||
}
|
||||
}
|
||||
|
||||
export class InvalidCredentialsError extends HttpError {
|
||||
constructor() {
|
||||
super(401, 'INVALID_CREDENTIALS', 'Invalid username or password');
|
||||
}
|
||||
}
|
||||
|
||||
export class UnauthorizedError extends HttpError {
|
||||
constructor(message = 'Not authenticated') {
|
||||
super(401, 'UNAUTHORIZED', message);
|
||||
}
|
||||
}
|
||||
|
||||
export class NotFoundError extends HttpError {
|
||||
constructor(what = 'Resource') {
|
||||
super(404, 'NOT_FOUND', `${what} not found`);
|
||||
}
|
||||
}
|
||||
|
||||
export class ConflictError extends HttpError {
|
||||
constructor(message: string) {
|
||||
super(409, 'CONFLICT', message);
|
||||
}
|
||||
}
|
||||
|
||||
export class ValidationError extends HttpError {
|
||||
constructor(message: string, details?: unknown) {
|
||||
super(400, 'VALIDATION', message, details);
|
||||
}
|
||||
}
|
||||
|
||||
export class ForbiddenError extends HttpError {
|
||||
constructor(message = 'Forbidden') {
|
||||
super(403, 'FORBIDDEN', message);
|
||||
}
|
||||
}
|
||||
11
apps/backend/src/utils/password.ts
Normal file
11
apps/backend/src/utils/password.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import bcrypt from 'bcryptjs';
|
||||
|
||||
export const BCRYPT_ROUNDS = 12;
|
||||
|
||||
export async function hashPassword(plain: string): Promise<string> {
|
||||
return bcrypt.hash(plain, BCRYPT_ROUNDS);
|
||||
}
|
||||
|
||||
export async function verifyPassword(plain: string, hash: string): Promise<boolean> {
|
||||
return bcrypt.compare(plain, hash);
|
||||
}
|
||||
31
apps/backend/src/utils/version.ts
Normal file
31
apps/backend/src/utils/version.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { dirname, resolve } from 'node:path';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
let cached: string | null = null;
|
||||
|
||||
export function getBackendVersion(): string {
|
||||
if (cached) return cached;
|
||||
// Walk up until we find apps/backend/package.json.
|
||||
const candidates = [
|
||||
resolve(__dirname, '../../package.json'),
|
||||
resolve(__dirname, '../../../package.json'),
|
||||
resolve(process.cwd(), 'package.json'),
|
||||
];
|
||||
for (const p of candidates) {
|
||||
try {
|
||||
const raw = readFileSync(p, 'utf-8');
|
||||
const pkg = JSON.parse(raw) as { name?: string; version?: string };
|
||||
if (pkg.name === '@family-wishlist/backend' && pkg.version) {
|
||||
cached = pkg.version;
|
||||
return cached;
|
||||
}
|
||||
} catch {
|
||||
// try next
|
||||
}
|
||||
}
|
||||
cached = '0.0.0';
|
||||
return cached;
|
||||
}
|
||||
Reference in New Issue
Block a user