diff --git a/frontends/nextjs/src/app/api/packages/data/[packageId]/handlers/delete-package-data.ts b/frontends/nextjs/src/app/api/packages/data/[packageId]/handlers/delete-package-data.ts index f2e9cbbb1..127e18850 100644 --- a/frontends/nextjs/src/app/api/packages/data/[packageId]/handlers/delete-package-data.ts +++ b/frontends/nextjs/src/app/api/packages/data/[packageId]/handlers/delete-package-data.ts @@ -1,7 +1,7 @@ import type { NextRequest } from 'next/server' import { NextResponse } from 'next/server' -import { deletePackageData } from '@/lib/db/packages/delete-package-data' +import { db } from '@/lib/db-client' import { getSessionUser, STATUS } from '@/lib/routing' import { getRoleLevel, ROLE_LEVELS } from '@/lib/constants' import { PackageSchemas } from '@/lib/validation' @@ -42,7 +42,9 @@ export async function DELETE( ) } - await deletePackageData(resolvedParams.packageId) + // Delete package data using DBAL + await db.packageData.delete(resolvedParams.packageId) + return NextResponse.json({ deleted: true }) } catch (error) { console.error('Error deleting package data:', error) diff --git a/frontends/nextjs/src/app/api/packages/data/[packageId]/handlers/get-package-data.ts b/frontends/nextjs/src/app/api/packages/data/[packageId]/handlers/get-package-data.ts index f7cb164f3..fa4139a30 100644 --- a/frontends/nextjs/src/app/api/packages/data/[packageId]/handlers/get-package-data.ts +++ b/frontends/nextjs/src/app/api/packages/data/[packageId]/handlers/get-package-data.ts @@ -1,7 +1,7 @@ import type { NextRequest } from 'next/server' import { NextResponse } from 'next/server' -import { getPackageData } from '@/lib/db/packages/get-package-data' +import { db } from '@/lib/db-client' import { getSessionUser, STATUS } from '@/lib/routing' import { PackageSchemas } from '@/lib/validation' @@ -30,7 +30,16 @@ export async function GET( ) } - const data = await getPackageData(resolvedParams.packageId) + // Get package data using DBAL + const packageData = await db.packageData.read(resolvedParams.packageId) + + if (!packageData) { + return NextResponse.json({ data: null }) + } + + // Parse the JSON data field + const data = packageData.data ? JSON.parse(packageData.data) : null + return NextResponse.json({ data }) } catch (error) { console.error('Error fetching package data:', error) diff --git a/frontends/nextjs/src/app/api/packages/data/[packageId]/handlers/put-package-data.ts b/frontends/nextjs/src/app/api/packages/data/[packageId]/handlers/put-package-data.ts index a4f4b2e4e..9e37f50df 100644 --- a/frontends/nextjs/src/app/api/packages/data/[packageId]/handlers/put-package-data.ts +++ b/frontends/nextjs/src/app/api/packages/data/[packageId]/handlers/put-package-data.ts @@ -2,7 +2,7 @@ import type { NextRequest } from 'next/server' import { NextResponse } from 'next/server' import { readJson } from '@/lib/api/read-json' -import { setPackageData } from '@/lib/db/packages/set-package-data' +import { db } from '@/lib/db-client' import type { PackageSeedData } from '@/lib/package-types' import { getSessionUser, STATUS } from '@/lib/routing' import { getRoleLevel, ROLE_LEVELS } from '@/lib/constants' @@ -53,7 +53,17 @@ export async function PUT( return NextResponse.json({ error: 'Package data is required' }, { status: 400 }) } - await setPackageData(resolvedParams.packageId, body.data) + // Save package data using DBAL + const dataJson = JSON.stringify(body.data) + + // Use upsert to create or update + const { getAdapter } = await import('@/lib/db/dbal-client') + const adapter = getAdapter() + await adapter.upsert('PackageData', + { packageId: resolvedParams.packageId }, + { packageId: resolvedParams.packageId, data: dataJson } + ) + return NextResponse.json({ saved: true }) } catch (error) { console.error('Error saving package data:', error) diff --git a/frontends/nextjs/src/lib/auth/api/fetch-session.ts b/frontends/nextjs/src/lib/auth/api/fetch-session.ts index 08c4e7329..4e6cfd294 100644 --- a/frontends/nextjs/src/lib/auth/api/fetch-session.ts +++ b/frontends/nextjs/src/lib/auth/api/fetch-session.ts @@ -1,13 +1,11 @@ /** * Fetch current session * - * Retrieves the current user based on session token from cookies or headers + * Retrieves the current user based on session token from cookies */ import type { User } from '@/lib/types/level-types' -import { getSessionByToken } from '@/lib/db/sessions/getters/get-session-by-token' -import { mapUserRecord } from '@/lib/db/users/map-user-record' -import { getAdapter } from '@/lib/db/core/dbal-client' +import { db } from '@/lib/db-client' import { cookies } from 'next/headers' /** @@ -17,33 +15,42 @@ import { cookies } from 'next/headers' */ export async function fetchSession(): Promise { try { - // Get session token from cookies const cookieStore = await cookies() const sessionToken = cookieStore.get('session_token')?.value - if (sessionToken === undefined || sessionToken.length === 0) { + if (!sessionToken || sessionToken.length === 0) { return null } - // Get session from token - const session = await getSessionByToken(sessionToken) - - if (session === null) { - return null - } - - // Get user from session - const adapter = getAdapter() - const userRecord = await adapter.findFirst('User', { - where: { id: session.userId }, + // Get session from token using DBAL + const sessions = await db.sessions.list({ + filter: { token: sessionToken } }) - - if (userRecord === null || userRecord === undefined) { + + const session = sessions.data?.[0] + + if (!session) { return null } - const user = mapUserRecord(userRecord as Record) - return user + // Get user from session using DBAL + const user = await db.users.read(session.userId) + + if (!user) { + return null + } + + return { + id: user.id, + username: user.username, + email: user.email, + role: user.role, + isInstanceOwner: user.isInstanceOwner || false, + profilePicture: user.profilePicture || null, + bio: user.bio || null, + createdAt: Number(user.createdAt), + tenantId: user.tenantId || null, + } } catch (error) { console.error('Error fetching session:', error) return null diff --git a/frontends/nextjs/src/lib/auth/api/login.ts b/frontends/nextjs/src/lib/auth/api/login.ts index 706ff6900..36dc56561 100644 --- a/frontends/nextjs/src/lib/auth/api/login.ts +++ b/frontends/nextjs/src/lib/auth/api/login.ts @@ -2,12 +2,11 @@ * Login API * * Authenticates a user and returns user data on success - * - * TODO: Implement authentication in DBAL auth operations - * Currently returns error until auth migration is complete */ import type { User } from '@/lib/types/level-types' +import { db } from '@/lib/db-client' +import crypto from 'crypto' export interface LoginCredentials { username: string @@ -21,13 +20,97 @@ export interface LoginResult { requiresPasswordChange?: boolean } +/** + * Hash password using SHA-512 + */ +async function hashPassword(password: string): Promise { + return crypto.createHash('sha512').update(password).digest('hex') +} + +/** + * Verify password against hash + */ +async function verifyPassword(password: string, hash: string): Promise { + const passwordHash = await hashPassword(password) + return passwordHash === hash +} + export async function login(identifier: string, password: string): Promise { - // TODO: Implement authentication using DBAL - // The old authentication logic was in the deleted db-old-to-delete directory - // This needs to be reimplemented in DBAL's auth operations - return { - success: false, - user: null, - error: 'Authentication not yet migrated to DBAL. This is being tracked as part of the database migration.', + try { + // Find user by username or email + const users = await db.users.list({ + filter: { + username: identifier + } + }) + + let user = users.data?.[0] + + // If not found by username, try email + if (!user) { + const usersByEmail = await db.users.list({ + filter: { + email: identifier + } + }) + user = usersByEmail.data?.[0] + } + + if (!user) { + return { + success: false, + user: null, + error: 'Invalid username or password', + } + } + + // Get credential for this user + const { getAdapter } = await import('@/lib/db/dbal-client') + const adapter = getAdapter() + const credential = await adapter.findFirst('Credential', { + where: { username: user.username } + }) + + if (!credential || !credential.passwordHash) { + return { + success: false, + user: null, + error: 'Invalid username or password', + } + } + + // Verify password + const isValid = await verifyPassword(password, String(credential.passwordHash)) + + if (!isValid) { + return { + success: false, + user: null, + error: 'Invalid username or password', + } + } + + return { + success: true, + user: { + id: user.id, + username: user.username, + email: user.email, + role: user.role, + isInstanceOwner: user.isInstanceOwner || false, + profilePicture: user.profilePicture || null, + bio: user.bio || null, + createdAt: Number(user.createdAt), + tenantId: user.tenantId || null, + }, + requiresPasswordChange: false, + } + } catch (error) { + console.error('Login error:', error) + return { + success: false, + user: null, + error: error instanceof Error ? error.message : 'Login failed', + } } } diff --git a/frontends/nextjs/src/lib/auth/api/register.ts b/frontends/nextjs/src/lib/auth/api/register.ts index a203c6b44..f2e29ce4b 100644 --- a/frontends/nextjs/src/lib/auth/api/register.ts +++ b/frontends/nextjs/src/lib/auth/api/register.ts @@ -5,10 +5,8 @@ */ import type { User } from '@/lib/types/level-types' -import { getAdapter } from '@/lib/db/core/dbal-client' -import { hashPassword } from '@/lib/db/password/hash-password' -import { getUserByUsername } from '@/lib/db/auth/queries/get-user-by-username' -import { getUserByEmail } from '@/lib/db/auth/queries/get-user-by-email' +import { db } from '@/lib/db-client' +import crypto from 'crypto' export interface RegisterData { username: string @@ -22,10 +20,17 @@ export interface RegisterResult { error?: string } +/** + * Hash password using SHA-512 + */ +async function hashPassword(password: string): Promise { + return crypto.createHash('sha512').update(password).digest('hex') +} + export async function register(username: string, email: string, password: string): Promise { try { // Validate input - if (username.length === 0 || email.length === 0 || password.length === 0) { + if (!username || !email || !password) { return { success: false, user: null, @@ -34,8 +39,11 @@ export async function register(username: string, email: string, password: string } // Check if username already exists - const existingUserByUsername = await getUserByUsername(username) - if (existingUserByUsername !== null) { + const existingByUsername = await db.users.list({ + filter: { username } + }) + + if (existingByUsername.data && existingByUsername.data.length > 0) { return { success: false, user: null, @@ -44,8 +52,11 @@ export async function register(username: string, email: string, password: string } // Check if email already exists - const existingUserByEmail = await getUserByEmail(email) - if (existingUserByEmail !== null) { + const existingByEmail = await db.users.list({ + filter: { email } + }) + + if (existingByEmail.data && existingByEmail.data.length > 0) { return { success: false, user: null, @@ -57,44 +68,40 @@ export async function register(username: string, email: string, password: string const passwordHash = await hashPassword(password) // Create user - const adapter = getAdapter() const userId = crypto.randomUUID() - await adapter.create('User', { - id: userId, - username, - email, - role: 'user', // Default role - createdAt: BigInt(Date.now()), - isInstanceOwner: false, - }) - - // Create credentials - await adapter.create('Credential', { - username, - passwordHash, - }) - - // Fetch the created user - const userRecord = await adapter.findFirst('User', { - where: { id: userId }, - }) - - if (userRecord === null || userRecord === undefined) { - return { - success: false, - user: null, - error: 'Failed to create user', - } - } - - const user: User = { + const newUser = await db.users.create({ id: userId, username, email, role: 'user', - createdAt: Date.now(), + createdAt: BigInt(Date.now()), isInstanceOwner: false, + tenantId: null, + profilePicture: null, + bio: null, + }) + + // Create credentials + const { getAdapter } = await import('@/lib/db/dbal-client') + const adapter = getAdapter() + await adapter.create('Credential', { + id: `cred_${userId}`, + username, + passwordHash, + userId, + }) + + const user: User = { + id: newUser.id, + username: newUser.username, + email: newUser.email, + role: newUser.role, + createdAt: Number(newUser.createdAt), + isInstanceOwner: newUser.isInstanceOwner || false, + tenantId: newUser.tenantId || null, + profilePicture: newUser.profilePicture || null, + bio: newUser.bio || null, } return { @@ -102,6 +109,7 @@ export async function register(username: string, email: string, password: string user, } } catch (error) { + console.error('Registration error:', error) return { success: false, user: null, diff --git a/frontends/nextjs/src/lib/routing/index.ts b/frontends/nextjs/src/lib/routing/index.ts index c69bab27e..856ebc09d 100644 --- a/frontends/nextjs/src/lib/routing/index.ts +++ b/frontends/nextjs/src/lib/routing/index.ts @@ -208,7 +208,7 @@ export async function executeDbalOperation( body?: unknown } ): Promise<{ success: boolean; data?: unknown; error?: string; meta?: unknown }> { - const { getAdapter } = await import('@/lib/db/core/dbal-client') + const { getAdapter } = await import('@/lib/db/dbal-client') const adapter = getAdapter() try { @@ -281,7 +281,7 @@ export async function executePackageAction( // Package actions are custom operations defined by packages // Load package config and execute the registered action handler try { - const { getAdapter } = await import('@/lib/db/core/dbal-client') + const { getAdapter } = await import('@/lib/db/dbal-client') const adapter = getAdapter() // Get package configuration @@ -365,7 +365,7 @@ export async function validateTenantAccess( // For lower levels, verify tenant membership try { - const { getAdapter } = await import('@/lib/db/core/dbal-client') + const { getAdapter } = await import('@/lib/db/dbal-client') const adapter = getAdapter() const tenant = await adapter.findFirst('Tenant', {