diff --git a/apps/web/next.config.js b/apps/web/next.config.js index 09760f806..fa6c0d1ac 100644 --- a/apps/web/next.config.js +++ b/apps/web/next.config.js @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/no-var-requires */ const path = require('path'); +const { version } = require('./package.json'); const { parsed: env } = require('dotenv').config({ path: path.join(__dirname, '../../.env.local'), @@ -18,7 +19,10 @@ const config = { '@documenso/ui', '@documenso/email', ], - env, + env: { + ...env, + APP_VERSION: version, + }, modularizeImports: { 'lucide-react': { transform: 'lucide-react/dist/esm/icons/{{ kebabCase member }}', diff --git a/apps/web/src/app/(dashboard)/admin/layout.tsx b/apps/web/src/app/(dashboard)/admin/layout.tsx new file mode 100644 index 000000000..3aa47d1a9 --- /dev/null +++ b/apps/web/src/app/(dashboard)/admin/layout.tsx @@ -0,0 +1,30 @@ +import React from 'react'; + +import { redirect } from 'next/navigation'; + +import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-server-session'; +import { isAdmin } from '@documenso/lib/next-auth/guards/is-admin'; + +import { AdminNav } from './nav'; + +export type AdminSectionLayoutProps = { + children: React.ReactNode; +}; + +export default async function AdminSectionLayout({ children }: AdminSectionLayoutProps) { + const user = await getRequiredServerComponentSession(); + + if (!isAdmin(user)) { + redirect('/documents'); + } + + return ( +
diff --git a/packages/lib/next-auth/guards/is-admin.ts b/packages/lib/next-auth/guards/is-admin.ts
new file mode 100644
index 000000000..2801305dd
--- /dev/null
+++ b/packages/lib/next-auth/guards/is-admin.ts
@@ -0,0 +1,5 @@
+import { Role, User } from '@documenso/prisma/client';
+
+const isAdmin = (user: User) => user.roles.includes(Role.ADMIN);
+
+export { isAdmin };
diff --git a/packages/lib/server-only/admin/get-documents-stats.ts b/packages/lib/server-only/admin/get-documents-stats.ts
new file mode 100644
index 000000000..e0d53373f
--- /dev/null
+++ b/packages/lib/server-only/admin/get-documents-stats.ts
@@ -0,0 +1,26 @@
+import { prisma } from '@documenso/prisma';
+import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
+
+export const getDocumentStats = async () => {
+ const counts = await prisma.document.groupBy({
+ by: ['status'],
+ _count: {
+ _all: true,
+ },
+ });
+
+ const stats: Record