update 9/20/25

This commit is contained in:
Ra
2025-09-20 21:44:02 -07:00
parent c713c2ed5e
commit b41f489fe6
58 changed files with 11529 additions and 2769 deletions

View File

@@ -0,0 +1,358 @@
// 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
};