Skip to content

Commit

Permalink
refacto: teams/[teamId]/digests/digestid/route
Browse files Browse the repository at this point in the history
  • Loading branch information
quentingrchr committed Oct 4, 2024
1 parent 9471c58 commit 76efd71
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 129 deletions.
157 changes: 157 additions & 0 deletions src/app/api/teams/[teamId]/digests/[digestId]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import client from '@/lib/db';
import { checkDigestAppRouter, checkTeamAppRouter } from '@/lib/middleware';
import { HandlerApiError, HandlerApiResponse } from '@/utils/handlerResponse';
import { openAiCompletion } from '@/utils/openai';
import { Digest } from '@prisma/client';
import { createEdgeRouter } from 'next-connect';
import { NextRequest } from 'next/server';
import urlSlug from 'url-slug';

export type ApiDigestResponseSuccess = Digest;

export interface TeamsDigestsRequestContext {
params: {
teamId: string;
digestId: string;
};

// Not the best way to do this but it works for now
// We need the middleware to set these values
// Right now next-connect doesn't support generics to enrich the request (https://github.com/hoangvvo/next-connect/issues/230)
membershipId: string;
teamId: string;
user: { id: string; email: string };
}

const router = createEdgeRouter<NextRequest, TeamsDigestsRequestContext>();

router
.use(checkTeamAppRouter)
.use(checkDigestAppRouter)
.patch(async (req, event, next) => {
const digestId = event.params.digestId as string;
try {
const body = await req.json();
let digest = await client.digest.findUnique({
select: { publishedAt: true, teamId: true },
where: { id: digestId?.toString() },
});
if (body.title !== undefined && !body.title.trim()) {
/* Ensure title is not empty */
return HandlerApiError.customError('Title cannot be empty', 400);
}
const isFirstPublication = !digest?.publishedAt && !!body.publishedAt;
if (isFirstPublication && process.env.OPENAI_API_KEY) {
const lastDigests = await client.digest.findMany({
select: { title: true },
where: { teamId: digest?.teamId },
take: 5,
orderBy: { publishedAt: 'desc' },
});
if (!digest?.teamId) throw new Error('Missing teamId');

const lastDigestTitles = [
body.title,
...lastDigests?.map((digest) => digest?.title),
].filter((title) => !!title);

updateSuggestedDigestTitle(lastDigestTitles.reverse(), digest?.teamId!);
}

digest = await client.digest.update({
where: {
id: digestId?.toString(),
},
data: {
...body,
// Do not update slug if digest has been published
...(body.title &&
!digest?.publishedAt && {
slug: urlSlug(body.title),
}),
},
});
return HandlerApiResponse.success(digest);
} catch (error: unknown) {
// eslint-disable-next-line no-console
console.log(error);
return HandlerApiError.internalServerError();
}
})
.delete(async (req, event, next) => {
const digestId = event.params.digestId;
try {
const digest = await client.digest.delete({
where: {
id: digestId?.toString(),
},
});

if (!digest) {
return HandlerApiError.notFound();
}

const wasAPublishedDigest = Boolean(digest?.publishedAt);
if (wasAPublishedDigest) {
await client.team.update({
where: { id: digest?.teamId },
data: {
nextSuggestedDigestTitle: null,
},
});
}

return HandlerApiResponse.success(digest);
} catch (error: unknown) {
// eslint-disable-next-line no-console
console.log(error);
return HandlerApiError.internalServerError();
}
});

async function updateSuggestedDigestTitle(
lastDigestTitles: {
title: string;
}[],
teamId: string
) {
if (Boolean(lastDigestTitles?.length)) {
const prompt = `
Here is a list of document titles sorted from most recent to oldest, separared by ; signs : ${lastDigestTitles.join(
';'
)}
Just guess the next document title. Don't add any other sentence in your response. If you can't guess a logical title, just write idk.
`;

try {
const response = await openAiCompletion({ prompt });
const guessedTitle = response[0]?.message?.content;
const canPredict = guessedTitle !== 'idk';
if (canPredict) {
await client.team.update({
where: { id: teamId },
data: {
nextSuggestedDigestTitle: guessedTitle,
},
});
}
} catch (e) {
// eslint-disable-next-line no-console
console.log(e);
}
}
}

export async function PATCH(
request: NextRequest,
ctx: TeamsDigestsRequestContext
) {
return router.run(request, ctx) as Promise<Response>;
}

export async function DELETE(
request: NextRequest,
ctx: TeamsDigestsRequestContext
) {
return router.run(request, ctx) as Promise<Response>;
}
3 changes: 2 additions & 1 deletion src/components/digests/templates/TemplateEdit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import useTransitionRefresh from '@/hooks/useTransitionRefresh';
import api from '@/lib/api';
import { useRouter } from 'next/navigation';

import { ResponseSuccess } from '@/app/api/teams/[teamId]/digests/route';
import { formatTemplateTitle } from '@/components/digests/templates/TemplateItem';
import useAddAndRemoveBlockOnDigest from '@/hooks/useAddAndRemoveBlockOnDigest';
import { getDigest } from '@/services/database/digest';
Expand Down Expand Up @@ -126,7 +127,7 @@ export const TemplateEdit = ({ template, team }: Props) => {
);

const { mutate: deleteDigest, isLoading: isDeleting } = useMutation<
AxiosResponse<ApiDigestResponseSuccess>,
AxiosResponse<ResponseSuccess>,
AxiosError<ErrorResponse>
>(
'delete-digest',
Expand Down
4 changes: 3 additions & 1 deletion src/components/pages/DigestEditPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import useTransitionRefresh from '@/hooks/useTransitionRefresh';
import api from '@/lib/api';
import { useRouter } from 'next/navigation';

import { ResponseSuccess } from '@/app/api/teams/[teamId]/bookmark/[bookmarkId]/route';
import useAddAndRemoveBlockOnDigest from '@/hooks/useAddAndRemoveBlockOnDigest';
import { getDigest } from '@/services/database/digest';
import { TeamLinksData } from '@/services/database/link';
Expand Down Expand Up @@ -42,6 +43,7 @@ import { Breadcrumb } from '../teams/Breadcrumb';
import DigestEditSendNewsletter from './DigestEditSendNewsletter';
import DigestEditTypefully from './DigestEditTypefully';
import DigestEditVisit from './DigestEditVisit';
import { ApiDigestResponseSuccess } from '@/pages/api/teams/[teamId]/template';

type Props = {
teamLinksData: TeamLinksData;
Expand Down Expand Up @@ -169,7 +171,7 @@ export const DigestEditPage = ({
);

const { mutate: deleteDigest, isLoading: isDeleting } = useMutation<
AxiosResponse<ApiDigestResponseSuccess>,
AxiosResponse<ResponseSuccess>,
AxiosError<ErrorResponse>
>(
'delete-digest',
Expand Down
127 changes: 0 additions & 127 deletions src/pages/api/teams/[teamId]/digests/[digestId]/index.ts

This file was deleted.

5 changes: 5 additions & 0 deletions src/utils/handlerResponse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export const ApiErrorMessages = {
RATE_LIMIT_EXCEEDED: 'Rate limit exceeded',
MISSING_PARAMETERS: 'Missing parameters',
BAD_REQUEST: 'Bad request',
NOT_FOUND: 'Not found',
} as const;

type ApiErrorMessagesType =
Expand Down Expand Up @@ -46,6 +47,10 @@ export class HandlerApiError {
static badRequest(): Response {
return this.error(ApiErrorMessages.BAD_REQUEST, 400);
}

static notFound(): Response {
return this.error(ApiErrorMessages.NOT_FOUND, 404);
}
}

export class HandlerApiResponse {
Expand Down

0 comments on commit 76efd71

Please sign in to comment.