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",
"lucide-react": "^0.475.0",
"next": "15.3.3",
"next-auth": "5.0.0-beta.19",
"nodemailer": "^6.9.14",
"patch-package": "^8.0.0",
"pdf-lib": "^1.17.1",
@@ -92,6 +93,46 @@
"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": {
"version": "7.26.9",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.9.tgz",
@@ -2673,6 +2714,15 @@
"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": {
"version": "1.0.0",
"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==",
"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": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz",
@@ -7306,6 +7362,15 @@
"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": {
"version": "4.0.0",
"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": {
"version": "8.4.31",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
@@ -7940,6 +8032,15 @@
"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": {
"version": "4.1.1",
"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",
"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": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz",
@@ -8447,6 +8570,12 @@
"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": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+2
View File
@@ -1,3 +1,4 @@
{
"name": "nextn",
"version": "0.1.0",
@@ -49,6 +50,7 @@
"genkit": "^1.14.1",
"lucide-react": "^0.475.0",
"next": "15.3.3",
"next-auth": "5.0.0-beta.19",
"nodemailer": "^6.9.14",
"patch-package": "^8.0.0",
"pdf-lib": "^1.17.1",
+10 -7
View File
@@ -1,7 +1,5 @@
"use client"
import * as React from "react";
import { auth, signOut } from "@/auth"
import Link from 'next/link';
import {
Sidebar,
@@ -13,8 +11,6 @@ import {
SidebarContent,
SidebarInset,
SidebarProvider,
SidebarTrigger,
useSidebar,
} from "@/components/ui/sidebar"
import {
Home,
@@ -29,14 +25,21 @@ import {
Workflow
} from "lucide-react"
import { Button } from "@/components/ui/button";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { logout } from "@/lib/actions/auth";
function AdminLayout({
async function AdminLayout({
children,
}: {
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 (
<SidebarProvider>
<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';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import * as z from 'zod';
import { useActionState } from 'react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { useToast } from '@/hooks/use-toast';
import {
Card,
CardContent,
@@ -16,48 +13,23 @@ import {
CardHeader,
CardTitle,
} from '@/components/ui/card';
import { useRouter } from 'next/navigation';
import { login } from '@/lib/actions/auth';
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
import { AlertCircle } from 'lucide-react';
const loginSchema = z.object({
email: z.string().email({ message: 'Invalid email address.' }),
password: z.string().min(1, { message: 'Password is required.' }),
});
function SubmitButton() {
// This component will be updated by useFormStatus in a real app,
// 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() {
const router = useRouter();
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.',
});
}
};
const [state, formAction] = useActionState(login, undefined);
return (
<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>
</CardHeader>
<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">
<Label htmlFor="email">Email</Label>
<Input
id="email"
name="email"
type="email"
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 className="space-y-2">
<Label htmlFor="password">Password</Label>
<Input
id="password"
name="password"
type="password"
placeholder="password"
{...register('password')}
defaultValue="password"
required
/>
{errors.password && (
<p className="text-sm text-destructive">
{errors.password.message}
</p>
)}
</div>
<Button type="submit" className="w-full" disabled={isSubmitting}>
{isSubmitting ? 'Signing In...' : 'Sign In'}
</Button>
<SubmitButton />
</form>
</CardContent>
<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';
import { redirect } from 'next/navigation';
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;
import { signIn, signOut } from '@/auth';
export async function login(
prevState: { message: string } | undefined,
formData: FormData
) {
try {
const stmt = db.prepare('SELECT * FROM users WHERE email = ? AND password = ?');
const user = stmt.get(email, password);
if (user) {
// In a real app, you would set a session cookie here.
// For this simulated login, we'll just return success.
return { success: true, message: 'Login successful.' };
} else {
return { success: false, message: 'Invalid email or password.' };
await signIn('credentials', formData);
} catch (error: any) {
if (error) {
switch (error.type) {
case 'CredentialsSignin':
return { message: 'Invalid credentials.' };
default:
return { message: 'Something went wrong.' };
}
} catch (error) {
console.error('Login error:', error);
return { success: false, message: 'An internal error occurred.' };
}
throw error;
}
}
export async function logout() {
// In a real app with authentication, this would handle signing out the user.
// For now, it redirects to the login page to simulate logging out.
redirect('/login');
await signOut({ redirectTo: '/login' });
}
+17 -15
View File
@@ -4,6 +4,7 @@
import { z } from 'zod';
import db from '@/lib/db';
import { revalidatePath } from 'next/cache';
import { auth } from '@/auth';
const formSchema = z.object({
name: z.string().min(1, 'Name is required'),
@@ -14,28 +15,29 @@ const formSchema = z.object({
type UserFormValues = z.infer<typeof formSchema>;
/**
* Gets the user from the database.
* Since authentication isn't fully implemented, it defaults to the user with id 1.
* Gets the currently logged-in user from the session.
*/
export async function getUser(): Promise<{ id: number; name: string; email: string } | null> {
try {
const stmt = db.prepare('SELECT id, name, email FROM users WHERE id = ?');
// 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);
export async function getUser(): Promise<{ id: string; name: string; email: string } | null> {
const session = await auth();
if (!session?.user?.id || !session.user.email || !session.user.name) {
return null;
}
return {
id: session.user.id,
email: session.user.email,
name: session.user.name,
};
}
/**
* Updates a user's profile information in the database.
*/
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);
if (!validation.success) {
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;
try {
// For now, we'll assume we're updating the user with ID 1.
const userId = 1;
const userId = session.user.id;
// Check if the new email is already taken by another user
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 a new password is provided, update it along with name and email
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);
} else {
// 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
const db = new Database('local.db');
db.pragma('journal_mode = WAL');
// --- SCHEMA CREATION ---
// Auth.js tables
db.exec(`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
email TEXT UNIQUE NOT NULL,
password TEXT NOT NULL,
name TEXT NOT NULL
id TEXT PRIMARY KEY,
name TEXT,
email TEXT UNIQUE,
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(`
CREATE TABLE IF NOT EXISTS settings (
key TEXT PRIMARY KEY,
@@ -54,13 +95,14 @@ db.exec(`
console.log('Running database checks and seeding if necessary...');
// Seed default user
const userStmt = db.prepare('SELECT id FROM users WHERE id = ?');
const defaultUser = userStmt.get(1);
const userStmt = db.prepare('SELECT id FROM users WHERE email = ?');
const defaultUser = userStmt.get('admin@example.com');
if (!defaultUser) {
const insertUser = db.prepare(
"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.');
}
+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$).*)'],
};