From 54d4c49755e36760cb48438185a09277a5b092de Mon Sep 17 00:00:00 2001 From: lucieo Date: Thu, 21 Sep 2023 15:59:45 +0200 Subject: [PATCH] Add server actions --- next.config.js | 27 +- package.json | 16 +- src/actions/generate-api-key.ts | 53 ++ src/app/(routes)/teams/[teamSlug]/layout.tsx | 2 +- src/app/(routes)/teams/layout.tsx | 2 +- src/components/Droppable.tsx | 1 + src/components/bookmark/BookmarkListDnd.tsx | 2 + src/components/digests/BlockListDnd.tsx | 2 + src/components/pages/DigestEditPage.tsx | 1 + src/components/pages/Homepage.tsx | 1 - src/components/pages/Invitation.tsx | 4 +- src/components/teams/form/TeamAPIKey.tsx | 67 +- src/emails/index.ts | 1 + src/emails/templates/InvitationEmail.tsx | 5 + src/emails/templates/LoginEmail.tsx | 5 + src/emails/templates/NewsletterEmail.tsx | 12 + src/lib/queries.ts | 31 +- .../digests/[digestId]/newsletter.tsx | 1 + yarn.lock | 840 +++++++++++++----- 19 files changed, 751 insertions(+), 322 deletions(-) create mode 100644 src/actions/generate-api-key.ts diff --git a/next.config.js b/next.config.js index 662b9bc..b15cbdc 100644 --- a/next.config.js +++ b/next.config.js @@ -3,13 +3,12 @@ // https://nextjs.org/docs/api-reference/next.config.js/introduction // https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/ const { withSentryConfig } = require('@sentry/nextjs'); -const { withContentlayer } = require('next-contentlayer') - +const { withContentlayer } = require('next-contentlayer'); /** @type {import('next').NextConfig} */ const nextConfig = { experimental: { - appDir: true, + serverActions: true, serverComponentsExternalPackages: ['mjml', 'mjml-react'], }, reactStrictMode: false, @@ -17,24 +16,12 @@ const nextConfig = { module.exports = nextConfig; - const withBundleAnalyzer = require('@next/bundle-analyzer')({ enabled: process.env.ANALYZE === 'true', -}) - -module.exports = withBundleAnalyzer(withContentlayer(withSentryConfig( - module.exports, - { - // For all available options, see: - // https://github.com/getsentry/sentry-webpack-plugin#options - - // Suppresses source map uploading logs during build - silent: true, +}); - org: 'premier-octet-z6', - project: 'digestclub', - }, - { +module.exports = withBundleAnalyzer( + withContentlayer(module.exports, { // For all available options, see: // https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/ @@ -49,5 +36,5 @@ module.exports = withBundleAnalyzer(withContentlayer(withSentryConfig( // Automatically tree-shake Sentry logger statements to reduce bundle size disableLogger: true, - } -))); + }) +); diff --git a/package.json b/package.json index 9d41671..d4109d0 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "metascraper-twitter": "^5.33.7", "mjml": "^4.13.0", "mjml-react": "^2.0.8", - "next": "^13.3.0", + "next": "^13.5.2", "next-auth": "^4.20.1", "next-connect": "^1.0.0-next.3", "next-contentlayer": "^0.3.2", @@ -82,18 +82,18 @@ "@tailwindcss/typography": "^0.5.9", "@types/jest": "^29.5.2", "@types/lodash": "^4.14.194", - "@types/mjml": "^4.7.0", + "@types/mjml": "^4.7.1", "@types/mjml-react": "^2.0.6", "@types/node": "^18.14.5", "@types/nodemailer": "^6.4.7", - "@types/react": "18.0.28", - "@types/react-beautiful-dnd": "^13.1.3", - "@types/react-dom": "18.0.11", + "@types/react": "^18.2.22", + "@types/react-beautiful-dnd": "^13.1.4", + "@types/react-dom": "^18.2.7", "autoprefixer": "^10.4.14", "clsx": "^1.2.1", "cross-env": "^7.0.3", - "eslint": "8.35.0", - "eslint-config-next": "^13.3.0", + "eslint": "^8.49.0", + "eslint-config-next": "^13.5.2", "jest": "^29.5.0", "jest-environment-jsdom": "^29.5.0", "postcss": "^8.4.21", @@ -101,6 +101,6 @@ "tailwindcss-animate": "^1.0.5", "ts-jest": "^29.1.0", "ts-node": "^10.9.1", - "typescript": "^4.9.5" + "typescript": "^5.2.2" } } diff --git a/src/actions/generate-api-key.ts b/src/actions/generate-api-key.ts new file mode 100644 index 0000000..c6838c6 --- /dev/null +++ b/src/actions/generate-api-key.ts @@ -0,0 +1,53 @@ +'use server'; +import db from '@/lib/db'; +import * as Sentry from '@sentry/nextjs'; +import jwt from 'jsonwebtoken'; + +interface APIKeyGenerationResult { + error?: { + message: string; + }; + data?: { + key: string; + }; +} + +/** + * @description Generates an write to the database a new API key for the team + * @param teamId a string representing the team id + * @returns + */ +export default async function generateAPIKey( + teamId: string +): Promise { + if (!process.env.JWT_SECRET) { + return { + error: { + message: 'Internal server error', + }, + }; + } + try { + const token = jwt.sign({ teamId }, process.env.JWT_SECRET); + + await db.team.update({ + where: { id: teamId }, + data: { + apiKey: token, + }, + }); + + return { + data: { + key: token, + }, + }; + } catch (err) { + Sentry.captureException(err); + return { + error: { + message: 'Internal server error', + }, + }; + } +} diff --git a/src/app/(routes)/teams/[teamSlug]/layout.tsx b/src/app/(routes)/teams/[teamSlug]/layout.tsx index 93b21e7..679e3a1 100644 --- a/src/app/(routes)/teams/[teamSlug]/layout.tsx +++ b/src/app/(routes)/teams/[teamSlug]/layout.tsx @@ -13,5 +13,5 @@ export default async function Layout({ redirect(authOptions.pages!.signIn!); } - return children; + return <>{children}; } diff --git a/src/app/(routes)/teams/layout.tsx b/src/app/(routes)/teams/layout.tsx index 93b21e7..679e3a1 100644 --- a/src/app/(routes)/teams/layout.tsx +++ b/src/app/(routes)/teams/layout.tsx @@ -13,5 +13,5 @@ export default async function Layout({ redirect(authOptions.pages!.signIn!); } - return children; + return <>{children}; } diff --git a/src/components/Droppable.tsx b/src/components/Droppable.tsx index a925e36..542dafe 100644 --- a/src/components/Droppable.tsx +++ b/src/components/Droppable.tsx @@ -1,6 +1,7 @@ import dynamic from 'next/dynamic'; export default dynamic( + // @ts-expect-error () => import('react-beautiful-dnd').then((res) => res.Droppable), { ssr: false } ); diff --git a/src/components/bookmark/BookmarkListDnd.tsx b/src/components/bookmark/BookmarkListDnd.tsx index b766de0..3808d02 100644 --- a/src/components/bookmark/BookmarkListDnd.tsx +++ b/src/components/bookmark/BookmarkListDnd.tsx @@ -11,6 +11,7 @@ export type BookmarkListDndProps = { const BookmarkListDnd = ({ teamLinks, team, digest }: BookmarkListDndProps) => { return ( + {/* @ts-expect-error */} {(provided) => (