add first files

This commit is contained in:
root
2026-04-30 14:59:57 -04:00
parent 2b97c1a04a
commit 695a7f7cc7
92 changed files with 4873 additions and 0 deletions
+83
View File
@@ -0,0 +1,83 @@
import { Router } from 'express'
import { z } from 'zod'
import bcrypt from 'bcryptjs'
import jwt from 'jsonwebtoken'
import { prisma } from '../lib/prisma'
import { requireRenterAuth } from '../middleware/requireRenterAuth'
const router = Router()
function signRenterToken(renterId: string) {
return jwt.sign({ sub: renterId, type: 'renter' }, process.env.JWT_SECRET!, {
expiresIn: process.env.RENTER_JWT_EXPIRY ?? '7d',
})
}
router.post('/signup', async (req, res, next) => {
try {
const body = z.object({
firstName: z.string().min(1),
lastName: z.string().min(1),
email: z.string().email(),
password: z.string().min(8),
phone: z.string().optional(),
}).parse(req.body)
const existing = await prisma.renter.findUnique({ where: { email: body.email } })
if (existing) return res.status(409).json({ error: 'email_taken', message: 'Email already in use', statusCode: 409 })
const passwordHash = await bcrypt.hash(body.password, 12)
const renter = await prisma.renter.create({
data: { firstName: body.firstName, lastName: body.lastName, email: body.email, passwordHash, phone: body.phone ?? null },
})
const token = signRenterToken(renter.id)
res.status(201).json({ data: { token, renter: { id: renter.id, email: renter.email, firstName: renter.firstName, lastName: renter.lastName } } })
} catch (err) { next(err) }
})
router.post('/login', async (req, res, next) => {
try {
const body = z.object({ email: z.string().email(), password: z.string() }).parse(req.body)
const renter = await prisma.renter.findUnique({ where: { email: body.email } })
if (!renter || !renter.isActive) return res.status(401).json({ error: 'invalid_credentials', message: 'Invalid email or password', statusCode: 401 })
const valid = await bcrypt.compare(body.password, renter.passwordHash)
if (!valid) return res.status(401).json({ error: 'invalid_credentials', message: 'Invalid email or password', statusCode: 401 })
const token = signRenterToken(renter.id)
res.json({ data: { token, renter: { id: renter.id, email: renter.email, firstName: renter.firstName, lastName: renter.lastName } } })
} catch (err) { next(err) }
})
router.get('/me', requireRenterAuth, async (req, res, next) => {
try {
const renter = await prisma.renter.findUniqueOrThrow({ where: { id: req.renterId }, select: { id: true, firstName: true, lastName: true, email: true, phone: true, preferredLocale: true, preferredCurrency: true, emailVerified: true } })
res.json({ data: renter })
} catch (err) { next(err) }
})
router.patch('/me', requireRenterAuth, async (req, res, next) => {
try {
const body = z.object({
firstName: z.string().min(1).optional(),
lastName: z.string().min(1).optional(),
phone: z.string().optional(),
preferredLocale: z.enum(['en', 'fr', 'ar']).optional(),
preferredCurrency: z.enum(['MAD', 'USD', 'EUR']).optional(),
}).parse(req.body)
const renter = await prisma.renter.update({ where: { id: req.renterId }, data: body })
res.json({ data: renter })
} catch (err) { next(err) }
})
router.post('/me/fcm-token', requireRenterAuth, async (req, res, next) => {
try {
const { fcmToken } = z.object({ fcmToken: z.string() }).parse(req.body)
await prisma.renter.update({ where: { id: req.renterId }, data: { fcmToken } })
res.json({ data: { success: true } })
} catch (err) { next(err) }
})
export default router