diff --git a/app/api/auth/login/route.ts b/app/api/auth/login/route.ts new file mode 100644 index 0000000..e4e3d34 --- /dev/null +++ b/app/api/auth/login/route.ts @@ -0,0 +1,84 @@ +import { NextRequest, NextResponse } from "next/server"; +import { getPool } from "@/lib/database"; +import bcrypt from "bcryptjs"; +import jwt from "jsonwebtoken"; + +export async function POST(request: NextRequest) { + try { + const { email, password } = await request.json(); + + // Validate input + if (!email || !password) { + return NextResponse.json( + { error: "Email and password are required" }, + { status: 400 } + ); + } + + const pool = getPool(); + const client = await pool.connect(); + + try { + // Find user by email + const result = await client.query( + `SELECT id, email, password_hash, first_name, last_name, role, subscription_tier, created_at + FROM users WHERE email = $1`, + [email] + ); + + if (result.rows.length === 0) { + return NextResponse.json( + { error: "Invalid email or password" }, + { status: 401 } + ); + } + + const user = result.rows[0]; + + // Verify password + const isValidPassword = await bcrypt.compare( + password, + user.password_hash + ); + if (!isValidPassword) { + return NextResponse.json( + { error: "Invalid email or password" }, + { status: 401 } + ); + } + + // Create JWT token + const token = jwt.sign( + { + userId: user.id, + email: user.email, + role: user.role, + }, + process.env.JWT_SECRET || "your-secret-key", + { expiresIn: "7d" } + ); + + return NextResponse.json({ + success: true, + token, + user: { + id: user.id, + email: user.email, + FirstName: user.first_name, + LastName: user.last_name, + role: user.role, + subscriptionTier: user.subscription_tier, + createdAt: user.created_at, + }, + }); + } finally { + client.release(); + } + } catch (error) { + console.error("Login error:", error); + return NextResponse.json( + { error: "Internal server error" }, + { status: 500 } + ); + } +} diff --git a/app/api/auth/register/route.ts b/app/api/auth/register/route.ts new file mode 100644 index 0000000..aca383f --- /dev/null +++ b/app/api/auth/register/route.ts @@ -0,0 +1,95 @@ +import { NextRequest, NextResponse } from "next/server"; +import { getPool } from "@/lib/database"; +import bcrypt from "bcryptjs"; + +export async function POST(request: NextRequest) { + try { + const { firstName, lastName, email, password } = await request.json(); + + // Validate input + if (!firstName || !lastName || !email || !password) { + return NextResponse.json( + { error: "All fields are required" }, + { status: 400 } + ); + } + + // Validate email format + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!emailRegex.test(email)) { + return NextResponse.json( + { error: "Invalid email format" }, + { status: 400 } + ); + } + + // Validate password length + if (password.length < 6) { + return NextResponse.json( + { error: "Password must be at least 6 characters" }, + { status: 400 } + ); + } + + const pool = getPool(); + const client = await pool.connect(); + + try { + // Check if user already exists + const existingUser = await client.query( + "SELECT id FROM users WHERE email = $1", + [email] + ); + + if (existingUser.rows.length > 0) { + return NextResponse.json( + { error: "User with this email already exists" }, + { status: 409 } + ); + } + + // Hash the password + const saltRounds = 12; + const passwordHash = await bcrypt.hash(password, saltRounds); + + // Insert new user + const result = await client.query( + `INSERT INTO users (email, password_hash, first_name, last_name, role, subscription_tier) + VALUES ($1, $2, $3, $4, $5, $6) + RETURNING id, email, first_name, last_name, role, subscription_tier, created_at`, + [email, passwordHash, firstName, lastName, "USER", "BASIC"] + ); + + const newUser = result.rows[0]; + + // Create a basic subscription for the user + await client.query( + `INSERT INTO subscriptions (user_id, tier, status, storage_limit_gb, current_usage_gb) + VALUES ($1, $2, $3, $4, $5)`, + [newUser.id, "BASIC", "ACTIVE", 5, 0] + ); + + return NextResponse.json({ + success: true, + message: "User created successfully", + user: { + id: newUser.id, + email: newUser.email, + firstName: newUser.first_name, + lastName: newUser.last_name, + role: newUser.role, + subscriptionTier: newUser.subscription_tier, + createdAt: newUser.created_at, + }, + }); + } finally { + client.release(); + } + } catch (error) { + console.error("Registration error:", error); + return NextResponse.json( + { error: "Internal server error" }, + { status: 500 } + ); + } +} diff --git a/app/layout.tsx b/app/layout.tsx index 5cbf4e6..83a06f8 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,5 +1,6 @@ import type { Metadata } from "next"; import { Geist, Geist_Mono } from "next/font/google"; +import { AuthProvider } from "@/lib/auth-context"; import "./globals.css"; const geistSans = Geist({ @@ -13,8 +14,8 @@ const geistMono = Geist_Mono({ }); export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", + title: "Reya Render", + description: "Professional 3D rendering service", }; export default function RootLayout({ @@ -39,7 +40,7 @@ export default function RootLayout({
- {children} +