// Complete Migration Helper Script for remaining functions // This script contains the migrated versions of key functions const { executeQuery, executeTransaction } = require('./database'); /** * Get User Organizations - Migrated to PostgreSQL */ exports.getUserOrganizations = onRequest({cors: true}, async (req, res) => { const authContext = await validateAuthAndGetContext(req, res); if (!authContext) { return; } if (req.method !== "GET") { return res.status(405).json({ error: "Method not allowed" }); } try { // Get user's organizations const userOrgsRows = await executeQuery( `SELECT uo.organization_id as orgId, uo.role, uo.joined_at as joinedAt, uo.onboarding_completed as onboardingCompleted, o.name FROM user_organizations uo JOIN organizations o ON uo.organization_id = o.id WHERE uo.user_id = $1`, [authContext.userId] ); const organizations = userOrgsRows.map(row => ({ orgId: row.orgid, name: row.name, role: row.role, onboardingCompleted: row.onboardingcompleted, joinedAt: row.joinedat })); res.json({ success: true, organizations, }); } catch (error) { console.error("Get user organizations error:", error); res.status(500).json({ error: "Failed to get user organizations" }); } }); /** * Get Organization Data - Migrated to PostgreSQL */ exports.getOrgData = onRequest({cors: true}, async (req, res) => { const authContext = await validateAuthAndGetContext(req, res); if (!authContext) { return; } if (req.method !== "GET") { return res.status(405).json({ error: "Method not allowed" }); } try { const orgId = authContext.orgId; if (!orgId) { return res.status(400).json({ error: "User has no associated organizations" }); } // Get organization data const orgRows = await executeQuery( 'SELECT * FROM organizations WHERE id = $1', [orgId] ); if (orgRows.length === 0) { return res.status(404).json({ error: "Organization not found" }); } const orgData = { id: orgRows[0].id, name: orgRows[0].name, industry: orgRows[0].industry, description: orgRows[0].description, mission: orgRows[0].mission, vision: orgRows[0].vision, values: orgRows[0].values, onboardingCompleted: orgRows[0].onboarding_completed, onboardingData: orgRows[0].onboarding_data, createdAt: orgRows[0].created_at, updatedAt: orgRows[0].updated_at }; res.json({ success: true, org: orgData }); } catch (error) { console.error("Get org data error:", error); res.status(500).json({ error: "Failed to get organization data" }); } }); /** * Update Organization Data - Migrated to PostgreSQL */ exports.updateOrgData = onRequest({cors: true}, async (req, res) => { const authContext = await validateAuthAndGetContext(req, res); if (!authContext) { return; } if (req.method !== "PUT") { return res.status(405).json({ error: "Method not allowed" }); } try { const { data } = req.body; if (!data) { return res.status(400).json({ error: "Data is required" }); } const orgId = authContext.orgId; if (!orgId) { return res.status(400).json({ error: "User has no associated organizations" }); } // Build dynamic update query const updateFields = []; const values = []; let paramCount = 1; for (const [key, value] of Object.entries(data)) { // Convert camelCase to snake_case for database columns const dbKey = key.replace(/([A-Z])/g, '_$1').toLowerCase(); updateFields.push(`${dbKey} = $${paramCount}`); values.push(typeof value === 'object' ? JSON.stringify(value) : value); paramCount++; } if (updateFields.length === 0) { return res.status(400).json({ error: "No valid fields to update" }); } updateFields.push(`updated_at = $${paramCount}`); values.push(Date.now()); values.push(orgId); // for WHERE clause const query = `UPDATE organizations SET ${updateFields.join(', ')} WHERE id = $${paramCount + 1}`; await executeQuery(query, values); res.json({ success: true, message: "Organization data updated successfully" }); } catch (error) { console.error("Update org data error:", error); res.status(500).json({ error: "Failed to update organization data" }); } }); /** * Get Employees - Migrated to PostgreSQL */ exports.getEmployees = onRequest({cors: true}, async (req, res) => { const authContext = await validateAuthAndGetContext(req, res); if (!authContext) { return; } if (req.method !== "GET") { return res.status(405).json({ error: "Method not allowed" }); } try { const orgId = authContext.orgId; if (!orgId) { return res.status(400).json({ error: "User has no associated organizations" }); } // Get all employees const employeesRows = await executeQuery( 'SELECT * FROM employees WHERE organization_id = $1 AND status != $2 ORDER BY name', [orgId, 'deleted'] ); const employees = employeesRows.map(row => ({ id: row.id, name: row.name, email: row.email, role: row.role, department: row.department, status: row.status, joinedAt: row.joined_at, createdAt: row.created_at })); res.json({ success: true, employees }); } catch (error) { console.error("Get employees error:", error); res.status(500).json({ error: "Failed to get employees" }); } }); /** * Create Invitation - Migrated to PostgreSQL */ exports.createInvitation = onRequest({cors: true}, async (req, res) => { const authContext = await validateAuthAndGetContext(req, res); if (!authContext) { return; } if (req.method !== "POST") { return res.status(405).json({ error: "Method not allowed" }); } try { const { name, email, role = "employee", department } = req.body; if (!email || !name) { return res.status(400).json({ error: "Name and email are required" }); } const orgId = authContext.orgId; if (!orgId) { return res.status(400).json({ error: "User has no associated organizations" }); } // Generate invite code const code = Math.random().toString(36).substring(2, 15); // Generate employee ID const employeeId = `emp_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; const currentTime = Date.now(); // Create employee data const employeeData = { id: employeeId, name: name.trim(), email: email.trim(), role: role?.trim() || "employee", department: department?.trim() || "General", status: "invited", inviteCode: code }; await executeTransaction(async (client) => { // Store invitation if (process.env.USE_NEON_SERVERLESS === 'true') { await client( `INSERT INTO invites (code, organization_id, email, employee_data, status, created_at, expires_at) VALUES ($1, $2, $3, $4, $5, $6, $7)`, [ code, orgId, email, JSON.stringify(employeeData), 'pending', currentTime, currentTime + (7 * 24 * 60 * 60 * 1000) // 7 days ] ); // Create employee record await client( `INSERT INTO employees (id, organization_id, name, email, role, department, status, invite_code, created_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`, [employeeId, orgId, name.trim(), email.trim(), role || 'employee', department || 'General', 'invited', code, currentTime] ); } else { await client.query( `INSERT INTO invites (code, organization_id, email, employee_data, status, created_at, expires_at) VALUES ($1, $2, $3, $4, $5, $6, $7)`, [ code, orgId, email, JSON.stringify(employeeData), 'pending', currentTime, currentTime + (7 * 24 * 60 * 60 * 1000) // 7 days ] ); // Create employee record await client.query( `INSERT INTO employees (id, organization_id, name, email, role, department, status, invite_code, created_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`, [employeeId, orgId, name.trim(), email.trim(), role || 'employee', department || 'General', 'invited', code, currentTime] ); } }); // Generate invite links const baseUrl = process.env.CLIENT_URL || 'https://auditly-one.vercel.app'; const inviteLink = `${baseUrl}/#/employee-form/${code}`; console.log(`📧 Invite link: ${inviteLink}`); res.json({ success: true, code, employee: employeeData, inviteLink, message: "Invitation sent successfully", }); } catch (error) { console.error("Create invitation error:", error); res.status(500).json({ error: "Failed to create invitation" }); } }); /** * Get Invitation Status - Migrated to PostgreSQL */ exports.getInvitationStatus = onRequest({cors: true}, async (req, res) => { if (req.method === 'OPTIONS') { res.status(204).send(''); return; } if (req.method !== "GET") { return res.status(405).json({ error: "Method not allowed" }); } const { code } = req.query; if (!code) { return res.status(400).json({ error: "Invitation code is required" }); } try { const inviteRows = await executeQuery( 'SELECT * FROM invites WHERE code = $1', [code] ); if (inviteRows.length === 0) { return res.status(404).json({ error: "Invitation not found" }); } const invite = inviteRows[0]; // Check if expired if (Date.now() > invite.expires_at) { return res.status(400).json({ error: "Invitation has expired" }); } res.json({ success: true, used: invite.status !== 'pending', employee: invite.employee_data, invite: { ...invite, employee: invite.employee_data }, }); } catch (error) { console.error("Get invitation status error:", error); res.status(500).json({ error: "Failed to get invitation status" }); } }); module.exports = { // Export these functions so they can be used to replace the existing ones };