diff --git a/apps/envited.ascs.digital/.env.example b/apps/envited.ascs.digital/.env.example index 295af54c..9fba3a9e 100644 --- a/apps/envited.ascs.digital/.env.example +++ b/apps/envited.ascs.digital/.env.example @@ -1,5 +1,7 @@ ENV='development' REGION='eu-central-1' +NEXTAUTH_URL='http://localhost:4200' +NEXTAUTH_SECRET='my-secret' # .env.development only POSTGRES_HOST='localhost' diff --git a/apps/envited.ascs.digital/app/api/auth/[...nextauth]/route.ts b/apps/envited.ascs.digital/app/api/auth/[...nextauth]/route.ts new file mode 100644 index 00000000..bb0000d9 --- /dev/null +++ b/apps/envited.ascs.digital/app/api/auth/[...nextauth]/route.ts @@ -0,0 +1,62 @@ +import NextAuth from 'next-auth' +import type { NextAuthOptions } from 'next-auth' +import CredentialsProvider from 'next-auth/providers/credentials' + +export const authOptions: NextAuthOptions = { + providers: [ + CredentialsProvider({ + // The name to display on the sign in form (e.g. 'Sign in with...') + name: 'Sign in with Your Credentials', + // The credentials is used to generate a suitable form on the sign in page. + // You can specify whatever fields you are expecting to be submitted. + // e.g. domain, username, password, 2FA token, etc. + // You can pass any HTML attribute to the tag through the object. + credentials: { + pkh: { label: 'Address', type: 'text', placeholder: 'tz...' }, + }, + async authorize(credentials) { + if (!credentials) { + return { + id: '', + pkh: '', + memberId: '', + role: '', + } + } + const { pkh } = credentials + + return { + id: '', + pkh, + memberId: '', + role: '', + } + }, + }), + ], + session: { + strategy: 'jwt', + }, + callbacks: { + async jwt({ token, user }) { + if (user) { + token.user = user + } + + return token + }, + async session({ session, token }: { session: any; token: any }) { + session.user.pkh = token.user.pkh + session.user.role = token.user.role + session.user.memberId = token.user.memberId + session.user.id = token.sub + session.user.email = undefined + session.user.image = undefined + return session + }, + }, +} + +const handler = NextAuth(authOptions) + +export { handler as GET, handler as POST } diff --git a/apps/envited.ascs.digital/app/api/hello/route.ts b/apps/envited.ascs.digital/app/api/hello/route.ts index 52c7cefd..2f86b30e 100644 --- a/apps/envited.ascs.digital/app/api/hello/route.ts +++ b/apps/envited.ascs.digital/app/api/hello/route.ts @@ -1,5 +1,5 @@ -import { ok } from '../../../common/utils' import { db } from '../../../common/database/queries' +import { ok } from '../../../common/utils' export async function GET(request: Request) { try { diff --git a/apps/envited.ascs.digital/app/dashboard/page.tsx b/apps/envited.ascs.digital/app/dashboard/page.tsx new file mode 100644 index 00000000..a4762e88 --- /dev/null +++ b/apps/envited.ascs.digital/app/dashboard/page.tsx @@ -0,0 +1,14 @@ +import { getServerSession } from '../../common/session' +import { Header } from '../../modules/Header' + +export default async function Index() { + const session = await getServerSession() + return ( + <> +
+
+
{session ? JSON.stringify(session) : 'No session'}
+
+ + ) +} diff --git a/apps/envited.ascs.digital/app/layout.tsx b/apps/envited.ascs.digital/app/layout.tsx index 45156432..309b4de4 100644 --- a/apps/envited.ascs.digital/app/layout.tsx +++ b/apps/envited.ascs.digital/app/layout.tsx @@ -2,16 +2,20 @@ import { Providers } from '../modules/Theme/Providers' import './global.css' export const metadata = { - title: 'Welcome to marketplace', - description: 'Generated by create-nx-workspace', + title: 'Envited Marketplace', + description: '', } export default function RootLayout({ children }: { children: React.ReactNode }) { return ( - - - {children} - + + + {metadata.title} + + + + {children} + ) } diff --git a/apps/envited.ascs.digital/app/page.tsx b/apps/envited.ascs.digital/app/page.tsx index 1dc92e94..fbb260d2 100644 --- a/apps/envited.ascs.digital/app/page.tsx +++ b/apps/envited.ascs.digital/app/page.tsx @@ -1,10 +1,8 @@ -'use client' - import { ExampleTable } from '../modules/ExampleTable' import { Header } from '../modules/Header' import { HeroHeader } from '../modules/HeroHeader' -export default function Index() { +export default async function Index() { return ( <>
diff --git a/apps/envited.ascs.digital/common/database/migrate.ts b/apps/envited.ascs.digital/common/database/migrate.ts index d20ab562..9d1d008f 100644 --- a/apps/envited.ascs.digital/common/database/migrate.ts +++ b/apps/envited.ascs.digital/common/database/migrate.ts @@ -1,15 +1,14 @@ import { RDSDataClient } from '@aws-sdk/client-rds-data' import { fromIni } from '@aws-sdk/credential-providers' +import { drizzle as RDSDrizzle } from 'drizzle-orm/aws-data-api/pg' +import { migrate as AWSDataApiMigrate } from 'drizzle-orm/aws-data-api/pg/migrator' import { PostgresJsDatabase } from 'drizzle-orm/postgres-js' import { migrate as PGMigrate } from 'drizzle-orm/postgres-js/migrator' -import { migrate as AWSDataApiMigrate } from 'drizzle-orm/aws-data-api/pg/migrator' -import { drizzle as RDSDrizzle } from 'drizzle-orm/aws-data-api/pg' import { connectDb } from './database' import * as schema from './schema' const runMigration = async () => { - try { if (process.env.ENV === 'development') { const db = await connectDb() @@ -21,7 +20,7 @@ const runMigration = async () => { credentials: fromIni({ profile: process.env.AWS_PROFILE || '' }), region: 'eu-central-1', }) - + const db = RDSDrizzle(rdsClient, { database: process.env.RDS_DB_NAME || '', secretArn: process.env.RDS_SECRET_ARN || '', diff --git a/apps/envited.ascs.digital/common/database/seed.ts b/apps/envited.ascs.digital/common/database/seed.ts index 5bba5e86..9b014f7d 100644 --- a/apps/envited.ascs.digital/common/database/seed.ts +++ b/apps/envited.ascs.digital/common/database/seed.ts @@ -7,14 +7,13 @@ import { connectDb } from './database' import { role } from './schema' import * as schema from './schema' -const insertRoles = (connection: any) => async (roles: any[]) => - connection.insert(role).values(roles).execute() +const insertRoles = (connection: any) => async (roles: any[]) => connection.insert(role).values(roles).execute() const seed = async () => { try { // Insert seeding requirements here let connection = null - + if (process.env.ENV === 'development') { connection = await connectDb() return @@ -23,7 +22,7 @@ const seed = async () => { credentials: fromIni({ profile: process.env.AWS_PROFILE || '' }), region: 'eu-central-1', }) - + connection = RDSDrizzle(rdsClient, { database: process.env.RDS_DB_NAME || '', secretArn: process.env.RDS_SECRET_ARN || '', diff --git a/apps/envited.ascs.digital/common/session/index.ts b/apps/envited.ascs.digital/common/session/index.ts new file mode 100644 index 00000000..205e1a2b --- /dev/null +++ b/apps/envited.ascs.digital/common/session/index.ts @@ -0,0 +1 @@ +export { getServerSession } from './session' diff --git a/apps/envited.ascs.digital/common/session/session.test.ts b/apps/envited.ascs.digital/common/session/session.test.ts new file mode 100644 index 00000000..0b683c6a --- /dev/null +++ b/apps/envited.ascs.digital/common/session/session.test.ts @@ -0,0 +1,17 @@ +import { _getServerSession } from './session' + +describe('common/session', () => { + describe('getServerSession', () => { + it('should should fetch a server session with the correct parameters', async () => { + // when ... we want to get the current session server side + // then ... it should call the getServerSession function with the correct parameters + const authOptions = 'AUTH_OPTIONS' as any + const NAGetServerSession = jest.fn().mockResolvedValue('SESSION') + + const session = await _getServerSession(NAGetServerSession)(authOptions)() + + expect(NAGetServerSession).toHaveBeenCalledWith(authOptions) + expect(session).toEqual('SESSION') + }) + }) +}) diff --git a/apps/envited.ascs.digital/common/session/session.ts b/apps/envited.ascs.digital/common/session/session.ts new file mode 100644 index 00000000..0bc18cb9 --- /dev/null +++ b/apps/envited.ascs.digital/common/session/session.ts @@ -0,0 +1,8 @@ +import { getServerSession as NAGetServerSession, NextAuthOptions } from 'next-auth' + +import { authOptions } from '../../app/api/auth/[...nextauth]/route' + +export const _getServerSession = (NAGetServerSession: any) => (authOptions: NextAuthOptions) => () => + NAGetServerSession(authOptions) + +export const getServerSession = _getServerSession(NAGetServerSession)(authOptions) diff --git a/apps/envited.ascs.digital/common/types/types.ts b/apps/envited.ascs.digital/common/types/types.ts index bc1235a9..8802fbc9 100644 --- a/apps/envited.ascs.digital/common/types/types.ts +++ b/apps/envited.ascs.digital/common/types/types.ts @@ -33,8 +33,15 @@ export type Action = { data?: Obj } -export type Role = { +export interface IRole { id: string name: string description: string } + +export enum Role { + admin = 'admin', + user = 'user', + principal = 'principal', + federator = 'federator', +} diff --git a/apps/envited.ascs.digital/common/utils/utils.test.ts b/apps/envited.ascs.digital/common/utils/utils.test.ts index d5775ce9..d4a2ab20 100644 --- a/apps/envited.ascs.digital/common/utils/utils.test.ts +++ b/apps/envited.ascs.digital/common/utils/utils.test.ts @@ -1,14 +1,13 @@ /** * @jest-environment node */ - -import { RESPONSES } from '../constants'; +import { RESPONSES } from '../constants' import * as SUT from './utils' describe('common/utils', () => { - const mockFetch = Promise.resolve({ json: () => Promise.resolve('') }); - global.fetch = jest.fn().mockImplementation(() => mockFetch); - + const mockFetch = Promise.resolve({ json: () => Promise.resolve('') }) + global.fetch = jest.fn().mockImplementation(() => mockFetch) + describe('ok', () => { it.each([ ['Message', 'Message'], diff --git a/apps/envited.ascs.digital/common/utils/utils.ts b/apps/envited.ascs.digital/common/utils/utils.ts index a5bafee0..5e4f854b 100644 --- a/apps/envited.ascs.digital/common/utils/utils.ts +++ b/apps/envited.ascs.digital/common/utils/utils.ts @@ -2,10 +2,17 @@ import { RESPONSES } from '../constants' export const ok = (data: any) => Response.json(data) -export const badRequest = () => Response.json(null, { status: RESPONSES.badRequest.status, statusText: RESPONSES.badRequest.statusText }) +export const badRequest = () => + Response.json(null, { status: RESPONSES.badRequest.status, statusText: RESPONSES.badRequest.statusText }) -export const internalServerError = () => Response.json(null, { status: RESPONSES.internalServerError.status, statusText: RESPONSES.internalServerError.statusText }) +export const internalServerError = () => + Response.json(null, { + status: RESPONSES.internalServerError.status, + statusText: RESPONSES.internalServerError.statusText, + }) -export const notFound = () => Response.json(null, { status: RESPONSES.notFound.status, statusText: RESPONSES.notFound.statusText}) +export const notFound = () => + Response.json(null, { status: RESPONSES.notFound.status, statusText: RESPONSES.notFound.statusText }) -export const noContent = () => new Response(null, { status: RESPONSES.noContent.status, statusText: RESPONSES.noContent.statusText }) +export const noContent = () => + new Response(null, { status: RESPONSES.noContent.status, statusText: RESPONSES.noContent.statusText }) diff --git a/apps/envited.ascs.digital/drizzle/staging/meta/0000_snapshot.json b/apps/envited.ascs.digital/drizzle/staging/meta/0000_snapshot.json index fb6be74b..ce9c4c88 100644 --- a/apps/envited.ascs.digital/drizzle/staging/meta/0000_snapshot.json +++ b/apps/envited.ascs.digital/drizzle/staging/meta/0000_snapshot.json @@ -98,9 +98,7 @@ "issuer_id_unique": { "name": "issuer_id_unique", "nullsNotDistinct": false, - "columns": [ - "id" - ] + "columns": ["id"] } } }, @@ -134,9 +132,7 @@ "role_id_unique": { "name": "role_id_unique", "nullsNotDistinct": false, - "columns": [ - "id" - ] + "columns": ["id"] } } }, @@ -265,12 +261,8 @@ "name": "user_issuer_id_issuer_id_fk", "tableFrom": "user", "tableTo": "issuer", - "columnsFrom": [ - "issuer_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["issuer_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -278,12 +270,8 @@ "name": "user_address_type_id_addressType_id_fk", "tableFrom": "user", "tableTo": "addressType", - "columnsFrom": [ - "address_type_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["address_type_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -293,9 +281,7 @@ "user_id_unique": { "name": "user_id_unique", "nullsNotDistinct": false, - "columns": [ - "id" - ] + "columns": ["id"] } } }, @@ -322,12 +308,8 @@ "name": "usersToCredentialTypes_user_id_user_id_fk", "tableFrom": "usersToCredentialTypes", "tableTo": "user", - "columnsFrom": [ - "user_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["user_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -335,12 +317,8 @@ "name": "usersToCredentialTypes_credential_type_id_credentialType_id_fk", "tableFrom": "usersToCredentialTypes", "tableTo": "credentialType", - "columnsFrom": [ - "credential_type_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["credential_type_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -371,12 +349,8 @@ "name": "usersToRoles_user_id_user_id_fk", "tableFrom": "usersToRoles", "tableTo": "user", - "columnsFrom": [ - "user_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["user_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -384,12 +358,8 @@ "name": "usersToRoles_role_id_role_id_fk", "tableFrom": "usersToRoles", "tableTo": "role", - "columnsFrom": [ - "role_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["role_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -405,4 +375,4 @@ "schemas": {}, "tables": {} } -} \ No newline at end of file +} diff --git a/apps/envited.ascs.digital/drizzle/staging/meta/_journal.json b/apps/envited.ascs.digital/drizzle/staging/meta/_journal.json index adf49e29..657a65fe 100644 --- a/apps/envited.ascs.digital/drizzle/staging/meta/_journal.json +++ b/apps/envited.ascs.digital/drizzle/staging/meta/_journal.json @@ -10,4 +10,4 @@ "breakpoints": true } ] -} \ No newline at end of file +} diff --git a/apps/envited.ascs.digital/middleware.ts b/apps/envited.ascs.digital/middleware.ts new file mode 100644 index 00000000..176d330b --- /dev/null +++ b/apps/envited.ascs.digital/middleware.ts @@ -0,0 +1,3 @@ +export { default } from 'next-auth/middleware' + +export const config = { matcher: ['/dashboard'] } diff --git a/apps/envited.ascs.digital/modules/ExampleTable/ExampleTable.tsx b/apps/envited.ascs.digital/modules/ExampleTable/ExampleTable.tsx index f34c8f41..b7d96117 100644 --- a/apps/envited.ascs.digital/modules/ExampleTable/ExampleTable.tsx +++ b/apps/envited.ascs.digital/modules/ExampleTable/ExampleTable.tsx @@ -1,3 +1,5 @@ +'use client' + import { Table, TableBody, TableCell, TableHeader, TableRow } from '@envited-marketplace/design-system' import React from 'react' diff --git a/apps/envited.ascs.digital/modules/Header/Header.tsx b/apps/envited.ascs.digital/modules/Header/Header.tsx index b01334ab..f446db37 100644 --- a/apps/envited.ascs.digital/modules/Header/Header.tsx +++ b/apps/envited.ascs.digital/modules/Header/Header.tsx @@ -1,3 +1,5 @@ +'use client' + import { Button } from '@envited-marketplace/design-system' import Image from 'next/image' import Link from 'next/link' diff --git a/apps/envited.ascs.digital/modules/HeroHeader/HeroHeader.tsx b/apps/envited.ascs.digital/modules/HeroHeader/HeroHeader.tsx index 0202e31e..67bd9c6b 100644 --- a/apps/envited.ascs.digital/modules/HeroHeader/HeroHeader.tsx +++ b/apps/envited.ascs.digital/modules/HeroHeader/HeroHeader.tsx @@ -1,3 +1,5 @@ +'use client' + import { Button, Card, Grid, GridRow, Heading, HeadingWithTooltip, Tooltip } from '@envited-marketplace/design-system' import React, { FC } from 'react' diff --git a/apps/envited.ascs.digital/setupTests.ts b/apps/envited.ascs.digital/setupTests.ts new file mode 100644 index 00000000..54afb4d6 --- /dev/null +++ b/apps/envited.ascs.digital/setupTests.ts @@ -0,0 +1,3 @@ +import { TextDecoder, TextEncoder } from 'util' + +Object.assign(global, { TextDecoder, TextEncoder }) diff --git a/jest.preset.js b/jest.preset.js index 4ddde84a..579e808d 100644 --- a/jest.preset.js +++ b/jest.preset.js @@ -2,5 +2,5 @@ const nxPreset = require('@nx/jest/preset').default module.exports = { ...nxPreset, - setupFilesAfterEnv: ['/src/setupTests.ts'], + setupFilesAfterEnv: ['/setupTests.ts'], } diff --git a/package-lock.json b/package-lock.json index 33b2fb09..c1aab2c2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "date-fns": "^3.0.6", "drizzle-orm": "^0.29.3", "next": "14.0.4", + "next-auth": "^4.24.5", "next-themes": "^0.2.1", "postgres": "^3.4.3", "ramda": "^0.29.1", @@ -9290,6 +9291,14 @@ "node": ">=8" } }, + "node_modules/@panva/hkdf": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.1.1.tgz", + "integrity": "sha512-dhPeilub1NuIG0X5Kvhh9lH4iW3ZsHlnzwgwbOlgwQ2wG1IqFzsgHqmKPk3WzsdWAeaxKJxgM0+W433RmN45GA==", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/@peculiar/asn1-schema": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.3.8.tgz", @@ -20161,7 +20170,6 @@ "version": "0.5.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -30991,7 +30999,6 @@ "version": "4.15.4", "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.4.tgz", "integrity": "sha512-W+oqK4H+r5sITxfxpSU+MMdr/YSWGvgZMQDIsNoBDGGy4i7GBPTtvFKibQzW06n3U3TqHjhvBJsirShsEJ6eeQ==", - "dev": true, "funding": { "url": "https://github.com/sponsors/panva" } @@ -32475,6 +32482,33 @@ } } }, + "node_modules/next-auth": { + "version": "4.24.5", + "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.24.5.tgz", + "integrity": "sha512-3RafV3XbfIKk6rF6GlLE4/KxjTcuMCifqrmD+98ejFq73SRoj2rmzoca8u764977lH/Q7jo6Xu6yM+Re1Mz/Og==", + "dependencies": { + "@babel/runtime": "^7.20.13", + "@panva/hkdf": "^1.0.2", + "cookie": "^0.5.0", + "jose": "^4.11.4", + "oauth": "^0.9.15", + "openid-client": "^5.4.0", + "preact": "^10.6.3", + "preact-render-to-string": "^5.1.19", + "uuid": "^8.3.2" + }, + "peerDependencies": { + "next": "^12.2.5 || ^13 || ^14", + "nodemailer": "^6.6.5", + "react": "^17.0.2 || ^18", + "react-dom": "^17.0.2 || ^18" + }, + "peerDependenciesMeta": { + "nodemailer": { + "optional": true + } + } + }, "node_modules/next-themes": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.2.1.tgz", @@ -33167,6 +33201,11 @@ "node": ">=6" } }, + "node_modules/oauth": { + "version": "0.9.15", + "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", + "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==" + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -33180,7 +33219,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", - "dev": true, "engines": { "node": ">= 6" } @@ -33331,7 +33369,6 @@ "version": "5.0.3", "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.3.tgz", "integrity": "sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==", - "dev": true, "engines": { "node": "^10.13.0 || >=12.0.0" } @@ -33407,7 +33444,6 @@ "version": "5.6.4", "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.6.4.tgz", "integrity": "sha512-T1h3B10BRPKfcObdBklX639tVz+xh34O7GjofqrqiAQdm7eHsQ00ih18x6wuJ/E6FxdtS2u3FmUGPDeEcMwzNA==", - "dev": true, "dependencies": { "jose": "^4.15.4", "lru-cache": "^6.0.0", @@ -33422,7 +33458,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -33433,8 +33468,7 @@ "node_modules/openid-client/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/optionator": { "version": "0.9.3", @@ -34777,6 +34811,31 @@ "url": "https://github.com/sponsors/porsager" } }, + "node_modules/preact": { + "version": "10.19.3", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.19.3.tgz", + "integrity": "sha512-nHHTeFVBTHRGxJXKkKu5hT8C/YWBkPso4/Gad6xuj5dbptt9iF9NZr9pHbPhBrnT2klheu7mHTxTZ/LjwJiEiQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/preact-render-to-string": { + "version": "5.2.6", + "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz", + "integrity": "sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==", + "dependencies": { + "pretty-format": "^3.8.0" + }, + "peerDependencies": { + "preact": ">=10" + } + }, + "node_modules/preact-render-to-string/node_modules/pretty-format": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz", + "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==" + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", diff --git a/package.json b/package.json index 54cec410..f6f1adf0 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "date-fns": "^3.0.6", "drizzle-orm": "^0.29.3", "next": "14.0.4", + "next-auth": "^4.24.5", "next-themes": "^0.2.1", "postgres": "^3.4.3", "ramda": "^0.29.1",