/opt/canhelp/apps/api/src/routes
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