359 lines
11 KiB
JavaScript
359 lines
11 KiB
JavaScript
// 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
|
|
};
|