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:
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user