/opt/canhelp/apps/api/src/routes
NameSizeModeActions
admin.ts950570644editdlrm
auto-match.ts74060644editdlrm
auto-response.ts81110644editdlrm
availability.ts72940644editdlrm
categories.ts18670644editdlrm
chat.ts93770644editdlrm
dashboard.ts51420644editdlrm
favorites.ts44410644editdlrm
fomo.ts68440644editdlrm
google.ts48720644editdlrm
health.ts12140644editdlrm
i18n.ts1670860644editdlrm
locations.ts17810644editdlrm
notifications.ts28620644editdlrm
offers.ts100470644editdlrm
payments.ts211670644editdlrm
plans.ts13520644editdlrm
portfolio.ts66580644editdlrm
price-list.ts144170644editdlrm
reports.ts20070644editdlrm
reviews.ts27830644editdlrm
skill-suggestions.ts10350644editdlrm
specialist-cards.ts186410644editdlrm
stats.ts10930644editdlrm
statuses.ts37310644editdlrm
support.ts84520644editdlrm
tasks.ts300180644editdlrm
uploads.ts15260644editdlrm
users.ts371570644editdlrm
Edit: /opt/canhelp/apps/api/src/routes/reviews.ts (2783B)
import { Hono } from 'hono' import { zValidator } from '@hono/zod-validator' import { z } from 'zod' import { eq, avg, desc } from 'drizzle-orm' import { db } from '../db.js' import { reviews, tasks, users } from '@canhelp/db' import { notifyNewReview } from '../lib/notif.js' import { requireAuth, type AuthVariables } from '../middleware/auth.js' const app = new Hono<{ Variables: AuthVariables }>() // POST /reviews app.post( '/', requireAuth, zValidator( 'json', z.object({ taskId: z.string().uuid(), targetId: z.string(), rating: z.number().int().min(1).max(5), comment: z.string().max(2000).optional(), }), ), async (c) => { const user = c.get('user') const body = c.req.valid('json') const [task] = await db.select().from(tasks).where(eq(tasks.id, body.taskId)) if (!task) return c.json({ error: 'Task not found' }, 404) if (task.status !== 'completed') return c.json({ error: 'Task is not completed' }, 400) const [review] = await db .insert(reviews) .values({ taskId: body.taskId, authorId: user.id, targetId: body.targetId, rating: body.rating, comment: body.comment, }) .returning() await notifyNewReview(body.targetId, body.rating, body.taskId) return c.json(review, 201) }, ) // GET /reviews/user/:userId app.get('/user/:userId', async (c) => { const rows = await db .select({ review: reviews, author: { id: users.id, name: users.name, image: users.image, }, }) .from(reviews) .leftJoin(users, eq(reviews.authorId, users.id)) .where(eq(reviews.targetId, c.req.param('userId'))) .orderBy(desc(reviews.createdAt)) return c.json(rows) }) // GET /reviews/user/:userId/rating app.get('/user/:userId/rating', async (c) => { const [result] = await db .select({ avg: avg(reviews.rating) }) .from(reviews) .where(eq(reviews.targetId, c.req.param('userId'))) return c.json({ avg: result?.avg ? Number(result.avg).toFixed(2) : null }) }) // GET /reviews/my — reviews received by current user app.get('/my', requireAuth, async (c) => { const user = c.get('user') const rows = await db .select({ review: reviews, author: { id: users.id, name: users.name, image: users.image, }, }) .from(reviews) .leftJoin(users, eq(reviews.authorId, users.id)) .where(eq(reviews.targetId, user.id)) .orderBy(desc(reviews.createdAt)) return c.json(rows) }) // GET /reviews/task/:taskId app.get('/task/:taskId', async (c) => { const rows = await db .select() .from(reviews) .where(eq(reviews.taskId, c.req.param('taskId'))) return c.json(rows) }) export default app