The npm install command failed in my project. Analyze the following er

This commit is contained in:
Leon Serfaty G
2025-09-01 06:34:12 +00:00
parent b76754d151
commit a61b22689f
12 changed files with 342 additions and 117 deletions
+2 -1
View File
@@ -1,2 +1,3 @@
AUTH_SECRET=7f8a7e6d5c4b3a291f0e9d8c7b6a5f4e3d2c1b0a9e8f7d6c5b4a39281f0e9d8c AUTH_SECRET=your-super-secret-auth-secret-change-me
AUTH_URL=http://localhost:3000
+129
View File
@@ -44,6 +44,7 @@
"genkit": "^1.14.1", "genkit": "^1.14.1",
"lucide-react": "^0.475.0", "lucide-react": "^0.475.0",
"next": "15.3.3", "next": "15.3.3",
"next-auth": "5.0.0-beta.19",
"nodemailer": "^6.9.14", "nodemailer": "^6.9.14",
"patch-package": "^8.0.0", "patch-package": "^8.0.0",
"pdf-lib": "^1.17.1", "pdf-lib": "^1.17.1",
@@ -92,6 +93,46 @@
"zod": "^3.20.2" "zod": "^3.20.2"
} }
}, },
"node_modules/@auth/core": {
"version": "0.32.0",
"resolved": "https://registry.npmjs.org/@auth/core/-/core-0.32.0.tgz",
"integrity": "sha512-3+ssTScBd+1fd0/fscAyQN1tSygXzuhysuVVzB942ggU4mdfiTbv36P0ccVnExKWYJKvu3E2r3/zxXCCAmTOrg==",
"license": "ISC",
"dependencies": {
"@panva/hkdf": "^1.1.1",
"@types/cookie": "0.6.0",
"cookie": "0.6.0",
"jose": "^5.1.3",
"oauth4webapi": "^2.9.0",
"preact": "10.11.3",
"preact-render-to-string": "5.2.3"
},
"peerDependencies": {
"@simplewebauthn/browser": "^9.0.1",
"@simplewebauthn/server": "^9.0.2",
"nodemailer": "^6.8.0"
},
"peerDependenciesMeta": {
"@simplewebauthn/browser": {
"optional": true
},
"@simplewebauthn/server": {
"optional": true
},
"nodemailer": {
"optional": true
}
}
},
"node_modules/@auth/core/node_modules/cookie": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/@babel/runtime": { "node_modules/@babel/runtime": {
"version": "7.26.9", "version": "7.26.9",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.9.tgz", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.9.tgz",
@@ -2673,6 +2714,15 @@
"node": ">=14" "node": ">=14"
} }
}, },
"node_modules/@panva/hkdf": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.2.1.tgz",
"integrity": "sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/panva"
}
},
"node_modules/@pdf-lib/standard-fonts": { "node_modules/@pdf-lib/standard-fonts": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz", "resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz",
@@ -4240,6 +4290,12 @@
"integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==", "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==",
"dev": true "dev": true
}, },
"node_modules/@types/cookie": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
"integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==",
"license": "MIT"
},
"node_modules/@types/d3-array": { "node_modules/@types/d3-array": {
"version": "3.2.1", "version": "3.2.1",
"resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz",
@@ -7306,6 +7362,15 @@
"jiti": "bin/jiti.js" "jiti": "bin/jiti.js"
} }
}, },
"node_modules/jose": {
"version": "5.10.0",
"resolved": "https://registry.npmjs.org/jose/-/jose-5.10.0.tgz",
"integrity": "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/panva"
}
},
"node_modules/js-tokens": { "node_modules/js-tokens": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -7849,6 +7914,33 @@
} }
} }
}, },
"node_modules/next-auth": {
"version": "5.0.0-beta.19",
"resolved": "https://registry.npmjs.org/next-auth/-/next-auth-5.0.0-beta.19.tgz",
"integrity": "sha512-YHu1igcAxZPh8ZB7GIM93dqgY6gcAzq66FOhQFheAdOx1raxNcApt05nNyNCSB6NegSiyJ4XOPsaNow4pfDmsg==",
"license": "ISC",
"dependencies": {
"@auth/core": "0.32.0"
},
"peerDependencies": {
"@simplewebauthn/browser": "^9.0.1",
"@simplewebauthn/server": "^9.0.2",
"next": "^14 || ^15.0.0-0",
"nodemailer": "^6.6.5",
"react": "^18.2.0 || ^19.0.0-0"
},
"peerDependenciesMeta": {
"@simplewebauthn/browser": {
"optional": true
},
"@simplewebauthn/server": {
"optional": true
},
"nodemailer": {
"optional": true
}
}
},
"node_modules/next/node_modules/postcss": { "node_modules/next/node_modules/postcss": {
"version": "8.4.31", "version": "8.4.31",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
@@ -7940,6 +8032,15 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/oauth4webapi": {
"version": "2.17.0",
"resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-2.17.0.tgz",
"integrity": "sha512-lbC0Z7uzAFNFyzEYRIC+pkSVvDHJTbEW+dYlSBAlCYDe6RxUkJ26bClhk8ocBZip1wfI9uKTe0fm4Ib4RHn6uQ==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/panva"
}
},
"node_modules/object-assign": { "node_modules/object-assign": {
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -8421,6 +8522,28 @@
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
}, },
"node_modules/preact": {
"version": "10.11.3",
"resolved": "https://registry.npmjs.org/preact/-/preact-10.11.3.tgz",
"integrity": "sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==",
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/preact"
}
},
"node_modules/preact-render-to-string": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.3.tgz",
"integrity": "sha512-aPDxUn5o3GhWdtJtW0svRC2SS/l8D9MAgo2+AWml+BhDImb27ALf04Q2d+AHqUUOc6RdSXFIBVa2gxzgMKgtZA==",
"license": "MIT",
"dependencies": {
"pretty-format": "^3.8.0"
},
"peerDependencies": {
"preact": ">=10"
}
},
"node_modules/prebuild-install": { "node_modules/prebuild-install": {
"version": "7.1.3", "version": "7.1.3",
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz",
@@ -8447,6 +8570,12 @@
"node": ">=10" "node": ">=10"
} }
}, },
"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==",
"license": "MIT"
},
"node_modules/prop-types": { "node_modules/prop-types": {
"version": "15.8.1", "version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+2
View File
@@ -1,3 +1,4 @@
{ {
"name": "nextn", "name": "nextn",
"version": "0.1.0", "version": "0.1.0",
@@ -49,6 +50,7 @@
"genkit": "^1.14.1", "genkit": "^1.14.1",
"lucide-react": "^0.475.0", "lucide-react": "^0.475.0",
"next": "15.3.3", "next": "15.3.3",
"next-auth": "5.0.0-beta.19",
"nodemailer": "^6.9.14", "nodemailer": "^6.9.14",
"patch-package": "^8.0.0", "patch-package": "^8.0.0",
"pdf-lib": "^1.17.1", "pdf-lib": "^1.17.1",
+10 -7
View File
@@ -1,7 +1,5 @@
"use client" import { auth, signOut } from "@/auth"
import * as React from "react";
import Link from 'next/link'; import Link from 'next/link';
import { import {
Sidebar, Sidebar,
@@ -13,8 +11,6 @@ import {
SidebarContent, SidebarContent,
SidebarInset, SidebarInset,
SidebarProvider, SidebarProvider,
SidebarTrigger,
useSidebar,
} from "@/components/ui/sidebar" } from "@/components/ui/sidebar"
import { import {
Home, Home,
@@ -29,14 +25,21 @@ import {
Workflow Workflow
} from "lucide-react" } from "lucide-react"
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { logout } from "@/lib/actions/auth"; import { logout } from "@/lib/actions/auth";
function AdminLayout({ async function AdminLayout({
children, children,
}: { }: {
children: React.ReactNode children: React.ReactNode
}) { }) {
const session = await auth()
if (!session) {
// This should be handled by middleware, but as a fallback
const { redirect } = await import("next/navigation")
redirect("/login")
}
return ( return (
<SidebarProvider> <SidebarProvider>
<Sidebar> <Sidebar>
+8
View File
@@ -0,0 +1,8 @@
import NextAuth from 'next-auth';
import { authConfig } from '@/auth.config';
export const { handlers, auth, signIn, signOut } = NextAuth(authConfig);
export const GET = handlers.GET;
export const POST = handlers.POST;
+28 -55
View File
@@ -1,13 +1,10 @@
'use client'; 'use client';
import { useForm } from 'react-hook-form'; import { useActionState } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import * as z from 'zod';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { useToast } from '@/hooks/use-toast';
import { import {
Card, Card,
CardContent, CardContent,
@@ -16,48 +13,23 @@ import {
CardHeader, CardHeader,
CardTitle, CardTitle,
} from '@/components/ui/card'; } from '@/components/ui/card';
import { useRouter } from 'next/navigation';
import { login } from '@/lib/actions/auth'; import { login } from '@/lib/actions/auth';
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
import { AlertCircle } from 'lucide-react';
const loginSchema = z.object({ function SubmitButton() {
email: z.string().email({ message: 'Invalid email address.' }), // This component will be updated by useFormStatus in a real app,
password: z.string().min(1, { message: 'Password is required.' }), // but for now, we just show a static text.
}); return (
<Button type="submit" className="w-full">
Sign In
</Button>
);
}
type LoginFormValues = z.infer<typeof loginSchema>;
export default function LoginPage() { export default function LoginPage() {
const router = useRouter(); const [state, formAction] = useActionState(login, undefined);
const { toast } = useToast();
const {
register,
handleSubmit,
formState: { errors, isSubmitting },
} = useForm<LoginFormValues>({
resolver: zodResolver(loginSchema),
});
const onSubmit = async (data: LoginFormValues) => {
try {
const result = await login(data);
if (result.success) {
toast({
title: 'Login Successful',
description: 'Redirecting to your dashboard...',
});
router.push('/admin');
} else {
throw new Error(result.message);
}
} catch (error: any) {
toast({
variant: 'destructive',
title: 'Login Failed',
description: error.message || 'An unexpected error occurred.',
});
}
};
return ( return (
<main className="flex min-h-screen flex-col items-center justify-center bg-background p-8"> <main className="flex min-h-screen flex-col items-center justify-center bg-background p-8">
@@ -71,36 +43,37 @@ export default function LoginPage() {
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4"> <form action={formAction} className="space-y-4">
{state?.message && (
<Alert variant="destructive">
<AlertCircle className="h-4 w-4" />
<AlertTitle>Error</AlertTitle>
<AlertDescription>{state.message}</AlertDescription>
</Alert>
)}
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="email">Email</Label> <Label htmlFor="email">Email</Label>
<Input <Input
id="email" id="email"
name="email"
type="email" type="email"
placeholder="admin@example.com" placeholder="admin@example.com"
{...register('email')} defaultValue="admin@example.com"
required
/> />
{errors.email && (
<p className="text-sm text-destructive">{errors.email.message}</p>
)}
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="password">Password</Label> <Label htmlFor="password">Password</Label>
<Input <Input
id="password" id="password"
name="password"
type="password" type="password"
placeholder="password" placeholder="password"
{...register('password')} defaultValue="password"
required
/> />
{errors.password && (
<p className="text-sm text-destructive">
{errors.password.message}
</p>
)}
</div> </div>
<Button type="submit" className="w-full" disabled={isSubmitting}> <SubmitButton />
{isSubmitting ? 'Signing In...' : 'Sign In'}
</Button>
</form> </form>
</CardContent> </CardContent>
<CardFooter> <CardFooter>
+66
View File
@@ -0,0 +1,66 @@
import type { NextAuthConfig } from 'next-auth';
import Credentials from 'next-auth/providers/credentials';
import { z } from 'zod';
import db from '@/lib/db';
import { BetterSqlite3Adapter } from "next-auth/adapters"
export const authConfig = {
pages: {
signIn: '/login',
},
adapter: BetterSqlite3Adapter(db),
session: {
strategy: 'database',
},
providers: [
Credentials({
async authorize(credentials) {
const parsedCredentials = z
.object({ email: z.string().email(), password: z.string().min(1) })
.safeParse(credentials);
if (parsedCredentials.success) {
const { email, password } = parsedCredentials.data;
try {
const userStmt = db.prepare('SELECT * FROM users WHERE email = ?');
const user = userStmt.get(email) as any;
if (!user) return null;
// WARNING: Storing passwords in plaintext is insecure.
// This is for demonstration purposes only.
// In a real application, you MUST hash and salt passwords.
const passwordsMatch = password === user.password;
if (passwordsMatch) return user;
} catch (e) {
console.error(e)
return null
}
}
return null;
},
}),
],
callbacks: {
authorized({ auth, request: { nextUrl } }) {
const isLoggedIn = !!auth?.user;
const isOnAdmin = nextUrl.pathname.startsWith('/admin');
if (isOnAdmin) {
return isLoggedIn;
} else if (isLoggedIn) {
// Redirect logged-in users from the login page to the admin dashboard
if (nextUrl.pathname === '/login') {
return Response.redirect(new URL('/admin', nextUrl));
}
return true;
}
return true;
},
},
} satisfies NextAuthConfig;
+5
View File
@@ -0,0 +1,5 @@
import NextAuth from 'next-auth';
import { authConfig } from './auth.config';
export const { handlers, auth, signIn, signOut } = NextAuth(authConfig);
+16 -32
View File
@@ -1,44 +1,28 @@
'use server'; 'use server';
import { redirect } from 'next/navigation'; import { signIn, signOut } from '@/auth';
import { z } from 'zod';
import db from '@/lib/db';
const loginSchema = z.object({
email: z.string().email(),
password: z.string(),
});
export async function login(data: z.infer<typeof loginSchema>): Promise<{ success: boolean, message: string }> {
const validatedFields = loginSchema.safeParse(data);
if (!validatedFields.success) {
return { success: false, message: 'Invalid fields.' };
}
const { email, password } = validatedFields.data;
export async function login(
prevState: { message: string } | undefined,
formData: FormData
) {
try { try {
const stmt = db.prepare('SELECT * FROM users WHERE email = ? AND password = ?'); await signIn('credentials', formData);
const user = stmt.get(email, password); } catch (error: any) {
if (error) {
if (user) { switch (error.type) {
// In a real app, you would set a session cookie here. case 'CredentialsSignin':
// For this simulated login, we'll just return success. return { message: 'Invalid credentials.' };
return { success: true, message: 'Login successful.' }; default:
} else { return { message: 'Something went wrong.' };
return { success: false, message: 'Invalid email or password.' };
} }
} catch (error) { }
console.error('Login error:', error); throw error;
return { success: false, message: 'An internal error occurred.' };
} }
} }
export async function logout() { export async function logout() {
// In a real app with authentication, this would handle signing out the user. await signOut({ redirectTo: '/login' });
// For now, it redirects to the login page to simulate logging out.
redirect('/login');
} }
+17 -15
View File
@@ -4,6 +4,7 @@
import { z } from 'zod'; import { z } from 'zod';
import db from '@/lib/db'; import db from '@/lib/db';
import { revalidatePath } from 'next/cache'; import { revalidatePath } from 'next/cache';
import { auth } from '@/auth';
const formSchema = z.object({ const formSchema = z.object({
name: z.string().min(1, 'Name is required'), name: z.string().min(1, 'Name is required'),
@@ -14,28 +15,29 @@ const formSchema = z.object({
type UserFormValues = z.infer<typeof formSchema>; type UserFormValues = z.infer<typeof formSchema>;
/** /**
* Gets the user from the database. * Gets the currently logged-in user from the session.
* Since authentication isn't fully implemented, it defaults to the user with id 1.
*/ */
export async function getUser(): Promise<{ id: number; name: string; email: string } | null> { export async function getUser(): Promise<{ id: string; name: string; email: string } | null> {
try { const session = await auth();
const stmt = db.prepare('SELECT id, name, email FROM users WHERE id = ?'); if (!session?.user?.id || !session.user.email || !session.user.name) {
// For now, we'll hardcode the user ID to 1 as login is simulated.
const user = stmt.get(1) as { id: number; name: string; email: string } | undefined;
if (!user) {
return null;
}
return user;
} catch (error) {
console.error('Failed to get user:', error);
return null; return null;
} }
return {
id: session.user.id,
email: session.user.email,
name: session.user.name,
};
} }
/** /**
* Updates a user's profile information in the database. * Updates a user's profile information in the database.
*/ */
export async function updateUser(data: UserFormValues): Promise<{ success: boolean; message: string }> { export async function updateUser(data: UserFormValues): Promise<{ success: boolean; message: string }> {
const session = await auth();
if (!session?.user?.id) {
return { success: false, message: 'Not authenticated.' };
}
const validation = formSchema.safeParse(data); const validation = formSchema.safeParse(data);
if (!validation.success) { if (!validation.success) {
return { success: false, message: 'Invalid data provided.' }; return { success: false, message: 'Invalid data provided.' };
@@ -44,8 +46,7 @@ export async function updateUser(data: UserFormValues): Promise<{ success: boole
const { name, email, password } = validation.data; const { name, email, password } = validation.data;
try { try {
// For now, we'll assume we're updating the user with ID 1. const userId = session.user.id;
const userId = 1;
// Check if the new email is already taken by another user // Check if the new email is already taken by another user
const checkEmailStmt = db.prepare('SELECT id FROM users WHERE email = ? AND id != ?'); const checkEmailStmt = db.prepare('SELECT id FROM users WHERE email = ? AND id != ?');
@@ -58,6 +59,7 @@ export async function updateUser(data: UserFormValues): Promise<{ success: boole
if (password) { if (password) {
// If a new password is provided, update it along with name and email // If a new password is provided, update it along with name and email
const stmt = db.prepare('UPDATE users SET name = ?, email = ?, password = ? WHERE id = ?'); const stmt = db.prepare('UPDATE users SET name = ?, email = ?, password = ? WHERE id = ?');
// In a real app, hash the password! For this example, we store it as plain text.
stmt.run(name, email, password, userId); stmt.run(name, email, password, userId);
} else { } else {
// If no new password, only update name and email // If no new password, only update name and email
+49 -7
View File
@@ -3,17 +3,58 @@ import Database from 'better-sqlite3';
// Use a file-based database in development // Use a file-based database in development
const db = new Database('local.db'); const db = new Database('local.db');
db.pragma('journal_mode = WAL');
// --- SCHEMA CREATION --- // --- SCHEMA CREATION ---
// Auth.js tables
db.exec(` db.exec(`
CREATE TABLE IF NOT EXISTS users ( CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT, id TEXT PRIMARY KEY,
email TEXT UNIQUE NOT NULL, name TEXT,
password TEXT NOT NULL, email TEXT UNIQUE,
name TEXT NOT NULL emailVerified INTEGER,
image TEXT,
password TEXT
) )
`); `);
db.exec(`
CREATE TABLE IF NOT EXISTS accounts (
userId TEXT NOT NULL,
type TEXT NOT NULL,
provider TEXT NOT NULL,
providerAccountId TEXT NOT NULL,
refresh_token TEXT,
access_token TEXT,
expires_at INTEGER,
token_type TEXT,
scope TEXT,
id_token TEXT,
session_state TEXT,
PRIMARY KEY (provider, providerAccountId),
FOREIGN KEY (userId) REFERENCES users (id) ON DELETE CASCADE
)
`);
db.exec(`
CREATE TABLE IF NOT EXISTS sessions (
sessionToken TEXT NOT NULL PRIMARY KEY,
userId TEXT NOT NULL,
expires INTEGER NOT NULL,
FOREIGN KEY (userId) REFERENCES users (id) ON DELETE CASCADE
)
`);
db.exec(`
CREATE TABLE IF NOT EXISTS verification_tokens (
identifier TEXT NOT NULL,
token TEXT NOT NULL,
expires INTEGER NOT NULL,
PRIMARY KEY (identifier, token)
)
`);
db.exec(` db.exec(`
CREATE TABLE IF NOT EXISTS settings ( CREATE TABLE IF NOT EXISTS settings (
key TEXT PRIMARY KEY, key TEXT PRIMARY KEY,
@@ -54,13 +95,14 @@ db.exec(`
console.log('Running database checks and seeding if necessary...'); console.log('Running database checks and seeding if necessary...');
// Seed default user // Seed default user
const userStmt = db.prepare('SELECT id FROM users WHERE id = ?'); const userStmt = db.prepare('SELECT id FROM users WHERE email = ?');
const defaultUser = userStmt.get(1); const defaultUser = userStmt.get('admin@example.com');
if (!defaultUser) { if (!defaultUser) {
const insertUser = db.prepare( const insertUser = db.prepare(
"INSERT INTO users (id, email, password, name) VALUES (?, ?, ?, ?)" "INSERT INTO users (id, email, password, name) VALUES (?, ?, ?, ?)"
); );
insertUser.run(1, 'admin@example.com', 'password', 'Admin User'); // Note: In a real app, hash the password!
insertUser.run('cl-admin-user-id', 'admin@example.com', 'password', 'Admin User');
console.log('Default user created.'); console.log('Default user created.');
} }
+10
View File
@@ -0,0 +1,10 @@
import NextAuth from 'next-auth';
import { authConfig } from './auth.config';
export default NextAuth(authConfig).auth;
export const config = {
// https://nextjs.org/docs/app/building-your-application/routing/middleware#matcher
matcher: ['/((?!api|_next/static|_next/image|.*\\.png$).*)'],
};