add first files
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user