Add PostgreSQL support with SQLite/Postgres database compatibility layer

- Add dbGet/dbAll helper functions for database-agnostic queries
- Add toDbBool/convertBooleansForDb for boolean type conversion
- Add toDbDate/getNow for timestamp type handling
- Add generateId that returns UUID for Postgres, nanoid for SQLite
- Update all routes to use compatibility helpers
- Add normalizeEvent to return clean number types from Postgres decimal
- Add formatPrice utility for consistent price display
- Add legal pages admin interface with RichTextEditor
- Update carousel images
- Add drizzle migration files for PostgreSQL
This commit is contained in:
Michilis
2026-02-02 03:46:35 +00:00
parent 9410e83b89
commit bafd1425c4
61 changed files with 5015 additions and 881 deletions

View File

@@ -1,7 +1,7 @@
import { Hono } from 'hono';
import { zValidator } from '@hono/zod-validator';
import { z } from 'zod';
import { db, users, magicLinkTokens, User } from '../db/index.js';
import { db, dbGet, users, magicLinkTokens, User } from '../db/index.js';
import { eq } from 'drizzle-orm';
import {
hashPassword,
@@ -16,7 +16,7 @@ import {
invalidateAllUserSessions,
requireAuth,
} from '../lib/auth.js';
import { generateId, getNow } from '../lib/utils.js';
import { generateId, getNow, toDbBool } from '../lib/utils.js';
import { sendEmail } from '../lib/email.js';
// User type that includes all fields (some added in schema updates)
@@ -121,7 +121,9 @@ auth.post('/register', zValidator('json', registerSchema), async (c) => {
}
// Check if email exists
const existing = await (db as any).select().from(users).where(eq((users as any).email, data.email)).get();
const existing = await dbGet<any>(
(db as any).select().from(users).where(eq((users as any).email, data.email))
);
if (existing) {
// If user exists but is unclaimed, allow claiming
if (!existing.isClaimed || existing.accountStatus === 'unclaimed') {
@@ -149,7 +151,7 @@ auth.post('/register', zValidator('json', registerSchema), async (c) => {
phone: data.phone || null,
role: firstUser ? 'admin' : 'user',
languagePreference: data.languagePreference || null,
isClaimed: true,
isClaimed: toDbBool(true),
googleId: null,
rucNumber: null,
accountStatus: 'active',
@@ -189,7 +191,9 @@ auth.post('/login', zValidator('json', loginSchema), async (c) => {
}, 429);
}
const user = await (db as any).select().from(users).where(eq((users as any).email, data.email)).get();
const user = await dbGet<any>(
(db as any).select().from(users).where(eq((users as any).email, data.email))
);
if (!user) {
recordFailedAttempt(data.email);
return c.json({ error: 'Invalid credentials' }, 401);
@@ -243,7 +247,9 @@ auth.post('/login', zValidator('json', loginSchema), async (c) => {
auth.post('/magic-link/request', zValidator('json', magicLinkRequestSchema), async (c) => {
const { email } = c.req.valid('json');
const user = await (db as any).select().from(users).where(eq((users as any).email, email)).get();
const user = await dbGet<any>(
(db as any).select().from(users).where(eq((users as any).email, email))
);
if (!user) {
// Don't reveal if email exists
@@ -288,7 +294,9 @@ auth.post('/magic-link/verify', zValidator('json', magicLinkVerifySchema), async
return c.json({ error: verification.error }, 400);
}
const user = await (db as any).select().from(users).where(eq((users as any).id, verification.userId)).get();
const user = await dbGet<any>(
(db as any).select().from(users).where(eq((users as any).id, verification.userId))
);
if (!user || user.accountStatus === 'suspended') {
return c.json({ error: 'Invalid token' }, 400);
@@ -317,7 +325,9 @@ auth.post('/magic-link/verify', zValidator('json', magicLinkVerifySchema), async
auth.post('/password-reset/request', zValidator('json', passwordResetRequestSchema), async (c) => {
const { email } = c.req.valid('json');
const user = await (db as any).select().from(users).where(eq((users as any).email, email)).get();
const user = await dbGet<any>(
(db as any).select().from(users).where(eq((users as any).email, email))
);
if (!user) {
// Don't reveal if email exists
@@ -389,7 +399,9 @@ auth.post('/password-reset/confirm', zValidator('json', passwordResetSchema), as
auth.post('/claim-account/request', zValidator('json', magicLinkRequestSchema), async (c) => {
const { email } = c.req.valid('json');
const user = await (db as any).select().from(users).where(eq((users as any).email, email)).get();
const user = await dbGet<any>(
(db as any).select().from(users).where(eq((users as any).email, email))
);
if (!user) {
return c.json({ message: 'If an unclaimed account exists with this email, a claim link has been sent.' });
@@ -439,7 +451,7 @@ auth.post('/claim-account/confirm', zValidator('json', claimAccountSchema), asyn
const now = getNow();
const updates: Record<string, any> = {
isClaimed: true,
isClaimed: toDbBool(true),
accountStatus: 'active',
updatedAt: now,
};
@@ -461,7 +473,9 @@ auth.post('/claim-account/confirm', zValidator('json', claimAccountSchema), asyn
.set(updates)
.where(eq((users as any).id, verification.userId));
const user = await (db as any).select().from(users).where(eq((users as any).id, verification.userId)).get();
const user = await dbGet<any>(
(db as any).select().from(users).where(eq((users as any).id, verification.userId))
);
const authToken = await createToken(user.id, user.email, user.role);
const refreshToken = await createRefreshToken(user.id);
@@ -510,11 +524,15 @@ auth.post('/google', zValidator('json', googleAuthSchema), async (c) => {
const { sub: googleId, email, name } = googleData;
// Check if user exists by email or google_id
let user = await (db as any).select().from(users).where(eq((users as any).email, email)).get();
let user = await dbGet<any>(
(db as any).select().from(users).where(eq((users as any).email, email))
);
if (!user) {
// Check by google_id
user = await (db as any).select().from(users).where(eq((users as any).googleId, googleId)).get();
user = await dbGet<any>(
(db as any).select().from(users).where(eq((users as any).googleId, googleId))
);
}
const now = getNow();
@@ -530,7 +548,7 @@ auth.post('/google', zValidator('json', googleAuthSchema), async (c) => {
.update(users)
.set({
googleId,
isClaimed: true,
isClaimed: toDbBool(true),
accountStatus: 'active',
updatedAt: now,
})
@@ -538,7 +556,9 @@ auth.post('/google', zValidator('json', googleAuthSchema), async (c) => {
}
// Refresh user data
user = await (db as any).select().from(users).where(eq((users as any).id, user.id)).get();
user = await dbGet<any>(
(db as any).select().from(users).where(eq((users as any).id, user.id))
);
} else {
// Create new user
const firstUser = await isFirstUser();
@@ -552,7 +572,7 @@ auth.post('/google', zValidator('json', googleAuthSchema), async (c) => {
phone: null,
role: firstUser ? 'admin' : 'user',
languagePreference: null,
isClaimed: true,
isClaimed: toDbBool(true),
googleId,
rucNumber: null,
accountStatus: 'active',