diff --git a/src/db/actions.ts b/src/db/actions.ts index 2f82ebb3..3b6f6b2d 100644 --- a/src/db/actions.ts +++ b/src/db/actions.ts @@ -266,6 +266,33 @@ export const getComments = ( (_prismaError) => Errors.other('get comments') ) +/** + * Gets the latest comments on the site + */ +export const getLatestSiteComments = ( + siteId: string, +): ResultAsync => + ResultAsync.fromPromise( + prisma.comment.findMany({ + distinct: 'id', + include: { + author: true, + post: true + }, + where: { + post: { + site: { + is: { + id: siteId, + }, + }, + }, + }, + orderBy: { created_at: 'desc' } + }), + (_prismaError) => Errors.other('get latest site comments') + ) + export const createComment = ( postId: string, { body, parentCommentId, authorId, anonAuthorName }: Comment.Raw diff --git a/src/db/types.ts b/src/db/types.ts index 2cd3c623..375058f1 100644 --- a/src/db/types.ts +++ b/src/db/types.ts @@ -50,6 +50,11 @@ export namespace Comment { author: Nullable } + export type WithAuthorAndPost = Comment & { + post: Post, + author: Nullable + } + // This is the "raw" query response from prisma // Recursive comment tree export type WithRepliesAndAuthor = WithAuthor & { diff --git a/src/routes/admins/sites/get-site-comments.ts b/src/routes/admins/sites/get-site-comments.ts new file mode 100644 index 00000000..55a9ec5c --- /dev/null +++ b/src/routes/admins/sites/get-site-comments.ts @@ -0,0 +1,34 @@ +import { decode } from 'routes/parser' +import * as rt from 'runtypes' +import validator from 'validator' +import { err, ResultAsync } from 'neverthrow' + +import { protectedRoute, AppData } from 'router' +import * as Errors from 'errors' +import { getLatestSiteComments, getSingleSite } from 'db/actions' +import { User, Comment, Site } from 'db/types' + +type RouteError = Errors.RouteError + +const siteIdDecoder = rt.String.withConstraint( + (s) => s.startsWith('c') && validator.isAlphanumeric(s) +) + +const errorMsg = 'Request path requires a cuid' + +const getAdminSiteComments = ( + siteId: Site['id'], + adminId: User['id'] +): ResultAsync => + getSingleSite({ type_: 'Cuid', val: siteId }) + .andThen((site) => + site.owner_id === adminId + ? getLatestSiteComments(siteId) + : err(Errors.notFound()) + ) + +export const handler = protectedRoute((req, admin) => + decode(siteIdDecoder, req.params.id, errorMsg).map((siteId) => + getAdminSiteComments(siteId, admin.id).map(AppData.init) + ) +) diff --git a/src/routes/admins/sites/index.ts b/src/routes/admins/sites/index.ts index 14c45c3f..205dccb9 100644 --- a/src/routes/admins/sites/index.ts +++ b/src/routes/admins/sites/index.ts @@ -3,11 +3,13 @@ import { Router } from 'express' import { handler as getAllSites } from './get-all' import { handler as getSingle } from './get-single' import { handler as registerSite } from './register-site' +import { handler as getSingleSiteComments } from './get-site-comments' const router = Router() router.get('/', getAllSites) router.post('/register', registerSite) router.get('/:id', getSingle) +router.get('/:id/comments', getSingleSiteComments) export default router