From 589eb3e9438fc692779035948d651dbb69e08ac2 Mon Sep 17 00:00:00 2001 From: Yachika Sharma Date: Sun, 21 Jun 2026 17:10:29 +0530 Subject: [PATCH 1/5] refactor(auth): replace any-cast authenticate fallback with typed app.authenticate decorator in cards, event, and nfc routes (closes #594) --- apps/backend/src/routes/cards.ts | 18 +++++-------- apps/backend/src/routes/event.ts | 43 +++++++++++--------------------- apps/backend/src/routes/nfc.ts | 32 ++++++------------------ 3 files changed, 29 insertions(+), 64 deletions(-) diff --git a/apps/backend/src/routes/cards.ts b/apps/backend/src/routes/cards.ts index 48132e0e..f26cd084 100644 --- a/apps/backend/src/routes/cards.ts +++ b/apps/backend/src/routes/cards.ts @@ -62,15 +62,9 @@ function hasErrorCode( } export async function cardRoutes(app: FastifyInstance): Promise { - app.addHook('preHandler', async (request, reply) => { - const server = request.server; - if (typeof server?.authenticate === 'function') { await server.authenticate(request, reply); return } - if (typeof app.authenticate === 'function') { await app.authenticate(request, reply); return } - try { await request.jwtVerify() } catch (_e) { reply.status(401).send({ error: 'Unauthorized' }) } - }); - + // ─── List Cards ─── - app.get('/', async (request: FastifyRequest, reply: FastifyReply): Promise => { + app.get('/', {preHandler: [(req, reply) => app.authenticate(req, reply)] },async (request: FastifyRequest, reply: FastifyReply): Promise => { const userId = request.user.id; try { return await cardService.listCards(app, userId) @@ -80,7 +74,7 @@ export async function cardRoutes(app: FastifyInstance): Promise { }); // ─── Creates Card ─── - app.post('/', async (request: FastifyRequest<{ Body: CreateCardBody }>, reply: FastifyReply): Promise => { + app.post<{ Body: CreateCardBody }>('/', { preHandler: [(req, reply) => app.authenticate(req, reply)]}, async (request, reply): Promise => { const userId = request.user.id; const parsed = createCardSchema.safeParse(request.body); @@ -99,7 +93,7 @@ export async function cardRoutes(app: FastifyInstance): Promise { // ─── Update Card ─── - app.put('/:id', async (request: FastifyRequest<{ Params: CardParams; Body: UpdateCardBody }>, reply: FastifyReply): Promise => { + app.put<{ Params: CardParams; Body: UpdateCardBody }>('/:id', {preHandler: [(req, reply) => app.authenticate(req, reply)] }, async (request, reply): Promise => { const userId = request.user.id; const { id } = request.params; @@ -117,7 +111,7 @@ export async function cardRoutes(app: FastifyInstance): Promise { // ─── Delete Card ─── - app.delete('/:id', async (request: FastifyRequest<{ Params: CardParams }>, reply: FastifyReply): Promise => { + app.delete<{ Params: CardParams }>('/:id', { preHandler: [(req, reply) => app.authenticate(req, reply)]}, async (request, reply): Promise => { const userId = request.user.id; const { id } = request.params; @@ -139,7 +133,7 @@ export async function cardRoutes(app: FastifyInstance): Promise { }); // ─── Set Default Card ─── - app.put('/:id/default', async (request: FastifyRequest<{ Params: CardParams }>, reply: FastifyReply): Promise => { + app.put<{ Params: CardParams }>('/:id/default', {preHandler: [(req, reply) => app.authenticate(req, reply)]}, async (request, reply): Promise => { const userId = request.user.id; const { id } = request.params; diff --git a/apps/backend/src/routes/event.ts b/apps/backend/src/routes/event.ts index 8d7bc566..38c02d72 100644 --- a/apps/backend/src/routes/event.ts +++ b/apps/backend/src/routes/event.ts @@ -1,7 +1,7 @@ -import type { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify'; -import { createEventSchema, joinEventSchema} from '../validations/event.validation.js'; - import {generateUniqueSlug} from '../utils/slug.js' +import { createEventSchema} from '../validations/event.validation.js'; + +import type { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify'; type EventDetails = { @@ -58,21 +58,8 @@ type EventWithAttendees = { } export async function eventRoutes(app:FastifyInstance) { - app.post('/', { preHandler: [async (request, reply) => { - const server = request.server as any; - if (typeof server?.authenticate === 'function') { await server.authenticate(request, reply); return } - if (typeof (app as any).authenticate === 'function') { await (app as any).authenticate(request, reply); return } - try { await request.jwtVerify() } catch (e) { reply.status(401).send({ error: 'Unauthorized' }) } - }] }, async (request: FastifyRequest<{ - Body: { - name: string, - description?: string, - startDate: string, - location: string, - endDate: string, - isPublic?: boolean - }}>, reply: FastifyReply) => { - const userId = (request.user as any).id; + app.post<{Body: { name: string; description?: string; startDate: string; location: string; endDate: string; isPublic?: boolean; }}>('/', { preHandler: [(req, reply) => app.authenticate(req, reply)] }, async (request, reply) => { + const userId = request.user.id; const parsed = createEventSchema.safeParse(request.body); if(!parsed.success){ return reply.status(400).send({error: 'Bad request'}) @@ -80,8 +67,8 @@ export async function eventRoutes(app:FastifyInstance) { const {name, description, startDate, endDate, isPublic ,location} = parsed.data - let finalSlug = await generateUniqueSlug(name, async(slug) => { - const existing = await app.prisma.event.findUnique({where: {slug : slug}}) + const finalSlug = await generateUniqueSlug(name, async(slug) => { + const existing = await app.prisma.event.findUnique({where: {slug}}) return !!existing }) @@ -95,7 +82,7 @@ export async function eventRoutes(app:FastifyInstance) { name, description, slug: finalSlug, - location: location, + location, startDate: startDateObj, endDate: endDateObj, isPublic: isPublic ?? true, @@ -104,7 +91,7 @@ export async function eventRoutes(app:FastifyInstance) { }) return reply.status(201).send(newEvent); - } catch (error) { + } catch (_error) { app.log.error('Failed to create event'); return reply.status(500).send({error: 'Failed to create event'}) } @@ -153,8 +140,8 @@ export async function eventRoutes(app:FastifyInstance) { return response; }) - app.post('/:slug/join', { preHandler: [async (request, reply) => { const server = request.server as any; if (typeof server?.authenticate === 'function') { await server.authenticate(request, reply); return } if (typeof (app as any).authenticate === 'function') { await (app as any).authenticate(request, reply); return } try { await request.jwtVerify() } catch (e) { reply.status(401).send({ error: 'Unauthorized' }) } }] }, async(request: FastifyRequest<{Params: {slug: string}}>, reply: FastifyReply) => { - const userId = (request.user as any).id; + app.post<{ Params: { slug: string } }>('/:slug/join', {preHandler: [(req, reply) => app.authenticate(req, reply)]}, async(request, reply) => { + const userId = request.user.id; const paramsSlug = request.params.slug; const event = await app.prisma.event.findUnique({ @@ -171,7 +158,7 @@ export async function eventRoutes(app:FastifyInstance) { await app.prisma.eventAttendee.create({ data: { eventId: event.id, - userId: userId, + userId, joinedAt: new Date() } }) @@ -186,9 +173,9 @@ export async function eventRoutes(app:FastifyInstance) { } }) + app.delete<{Params: {slug: string}}>('/:slug/leave',{preHandler: [(req, reply) => app.authenticate(req, reply)]}, async(request, reply) => { - app.delete('/:slug/leave', { preHandler: [async (request, reply) => { const server = request.server as any; if (typeof server?.authenticate === 'function') { await server.authenticate(request, reply); return } if (typeof (app as any).authenticate === 'function') { await (app as any).authenticate(request, reply); return } try { await request.jwtVerify() } catch (e) { reply.status(401).send({ error: 'Unauthorized' }) } }] }, async(request: FastifyRequest<{Params: {slug: string}}>, reply: FastifyReply) => { - const userId = (request.user as any).id; + const userId = request.user.id; const paramsSlug = request.params.slug; const event = await app.prisma.event.findUnique({ @@ -205,7 +192,7 @@ export async function eventRoutes(app:FastifyInstance) { await app.prisma.eventAttendee.delete({ where: { userId_eventId: { - userId: userId, + userId, eventId: event.id } } diff --git a/apps/backend/src/routes/nfc.ts b/apps/backend/src/routes/nfc.ts index 5cf13f0c..647f8549 100644 --- a/apps/backend/src/routes/nfc.ts +++ b/apps/backend/src/routes/nfc.ts @@ -1,6 +1,7 @@ -import type { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify'; import { z } from 'zod'; +import type { FastifyInstance} from 'fastify'; + type NfcPayloadResponse = { type: 'URI'; payload: string; @@ -11,32 +12,15 @@ const nfcQuerySchema = z.object({ }); export async function nfcRoutes(app: FastifyInstance) { - app.addHook('preHandler', async (request, reply) => { - const server = request.server as any; - if (typeof server?.authenticate === 'function') { - await server.authenticate(request, reply); - return; - } - if (typeof (app as any).authenticate === 'function') { - await (app as any).authenticate(request, reply); - return; - } - try { - await request.jwtVerify(); - } catch (e) { - reply.status(401).send({ error: 'Unauthorized' }); - } - }); + // GET /api/nfc/payload — returns NDEF URI payload for user's default DevCard URL // GET /api/nfc/payload?card= — returns payload for a specific card - app.get( - '/payload', - async ( - request: FastifyRequest<{ Querystring: { card?: string } }>, - reply: FastifyReply - ) => { - const userId = (request.user as any).id; + app.get<{ Querystring: { card?: string } }>( + '/payload', + { preHandler: [(req, reply) => app.authenticate(req, reply)] }, + async (request, reply) => { + const userId = request.user.id; // Validate query params with Zod const parseResult = nfcQuerySchema.safeParse(request.query); From 6e19d2280e4feb13cbeac6bbd06f4bbc522b094f Mon Sep 17 00:00:00 2001 From: Yachika Sharma Date: Sun, 21 Jun 2026 20:45:18 +0530 Subject: [PATCH 2/5] Update cards.ts Signed-off-by: Yachika Sharma --- apps/backend/src/routes/cards.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/backend/src/routes/cards.ts b/apps/backend/src/routes/cards.ts index f7030ecc..f26cd084 100644 --- a/apps/backend/src/routes/cards.ts +++ b/apps/backend/src/routes/cards.ts @@ -3,7 +3,7 @@ import { handleDbError } from '../utils/error.util.js'; import { hashIp } from '../utils/refreshToken'; import { createCardSchema ,updateCardSchema, addPlatformLinkSchema} from '../validations/card.validation'; -import type { CardResponse, UpdateCardBody, UpdatedCardResponse } from '../services/cardService'; +import type { CardResponse, UpdateCardBody } from '../services/cardService'; import type { Card } from '@devcard/shared/src/types.js'; import type { CardVisibility } from '@prisma/client'; import type { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify'; From 7bd67838a3ff0696a36add698ada64878ea879b3 Mon Sep 17 00:00:00 2001 From: Yachika Sharma Date: Sun, 21 Jun 2026 20:55:48 +0530 Subject: [PATCH 3/5] Update event.ts Signed-off-by: Yachika Sharma --- apps/backend/src/routes/event.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/backend/src/routes/event.ts b/apps/backend/src/routes/event.ts index 38c02d72..34a12439 100644 --- a/apps/backend/src/routes/event.ts +++ b/apps/backend/src/routes/event.ts @@ -57,7 +57,7 @@ type EventWithAttendees = { }[]; } -export async function eventRoutes(app:FastifyInstance) { +export async function eventRoutes(app:FastifyInstance): Promise { app.post<{Body: { name: string; description?: string; startDate: string; location: string; endDate: string; isPublic?: boolean; }}>('/', { preHandler: [(req, reply) => app.authenticate(req, reply)] }, async (request, reply) => { const userId = request.user.id; const parsed = createEventSchema.safeParse(request.body); @@ -197,7 +197,7 @@ export async function eventRoutes(app:FastifyInstance) { } } }) - return reply.status(204).send({message: 'User left'}) + return reply.status(204).send() } catch (error:any) { if(error.code === 'P2025'){ return reply.status(404).send({error: 'User not found'}) @@ -269,4 +269,4 @@ export async function eventRoutes(app:FastifyInstance) { return response; }) -} \ No newline at end of file +} From 851def891c55b24d5c8fe67d9880d1eb0ad9a609 Mon Sep 17 00:00:00 2001 From: Yachika Sharma Date: Sun, 21 Jun 2026 20:56:10 +0530 Subject: [PATCH 4/5] Update nfc.ts Signed-off-by: Yachika Sharma --- apps/backend/src/routes/nfc.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/backend/src/routes/nfc.ts b/apps/backend/src/routes/nfc.ts index 647f8549..9dcb8088 100644 --- a/apps/backend/src/routes/nfc.ts +++ b/apps/backend/src/routes/nfc.ts @@ -11,7 +11,7 @@ const nfcQuerySchema = z.object({ card: z.string().uuid('Invalid card ID format').optional(), }); -export async function nfcRoutes(app: FastifyInstance) { +export async function nfcRoutes(app: FastifyInstance): Promise { // GET /api/nfc/payload — returns NDEF URI payload for user's default DevCard URL @@ -95,4 +95,4 @@ const payloadUrl = `${process.env.PUBLIC_APP_URL}/${safeUsername}${ return reply.send(response); } ); -} \ No newline at end of file +} From e29efc458c72bd8be01080a96c772757df64c032 Mon Sep 17 00:00:00 2001 From: Yachika Sharma Date: Mon, 22 Jun 2026 16:11:22 +0530 Subject: [PATCH 5/5] Update cards.ts Signed-off-by: Yachika Sharma --- apps/backend/src/routes/cards.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/backend/src/routes/cards.ts b/apps/backend/src/routes/cards.ts index f26cd084..a20b4601 100644 --- a/apps/backend/src/routes/cards.ts +++ b/apps/backend/src/routes/cards.ts @@ -3,7 +3,7 @@ import { handleDbError } from '../utils/error.util.js'; import { hashIp } from '../utils/refreshToken'; import { createCardSchema ,updateCardSchema, addPlatformLinkSchema} from '../validations/card.validation'; -import type { CardResponse, UpdateCardBody } from '../services/cardService'; +import type { CardResponse, UpdateCardBody,UpdatedCardResponse } from '../services/cardService'; import type { Card } from '@devcard/shared/src/types.js'; import type { CardVisibility } from '@prisma/client'; import type { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify'; @@ -93,7 +93,7 @@ export async function cardRoutes(app: FastifyInstance): Promise { // ─── Update Card ─── - app.put<{ Params: CardParams; Body: UpdateCardBody }>('/:id', {preHandler: [(req, reply) => app.authenticate(req, reply)] }, async (request, reply): Promise => { + app.put<{ Params: CardParams; Body: UpdateCardBody }>('/:id', {preHandler: [(req, reply) => app.authenticate(req, reply)] }, async (request, reply): Promise => { const userId = request.user.id; const { id } = request.params;