aethex-forge/api/nexus-core/time-logs.ts
sirpiglr 4b0f5742af Update authentication and authorization logic across multiple API endpoints
Replaces direct Supabase client instantiation with a unified authentication and authorization helper, introducing role-based access control to sensitive endpoints like escrow and payroll, and standardizing compliance event logging.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 9203795e-937a-4306-b81d-b4d5c78c240e
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Event-Id: 5eb35c62-c5ab-4c7e-9552-8dc89efa29f3
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/7c94b7a0-29c7-4f2e-94ef-44b2153872b7/9203795e-937a-4306-b81d-b4d5c78c240e/aPpJgbb
Replit-Helium-Checkpoint-Created: true
2025-12-13 03:17:12 +00:00

161 lines
4.5 KiB
TypeScript

import type { VercelRequest, VercelResponse } from "@vercel/node";
import { authenticateRequest, requireAuth } from "../_auth";
export default async function handler(req: VercelRequest, res: VercelResponse) {
const auth = await authenticateRequest(req);
if (!requireAuth(auth, res)) return;
const { userClient, user } = auth;
const { data: talentProfile } = await userClient
.from('nexus_talent_profiles')
.select('id, az_eligible')
.eq('user_id', user.id)
.single();
if (!talentProfile) {
return res.status(400).json({ error: 'Talent profile not found. Create one first.' });
}
if (req.method === 'GET') {
const { contract_id, start_date, end_date, status } = req.query;
let query = userClient
.from('nexus_time_logs')
.select('*')
.eq('talent_profile_id', talentProfile.id)
.order('log_date', { ascending: false });
if (contract_id) query = query.eq('contract_id', contract_id);
if (start_date) query = query.gte('log_date', start_date);
if (end_date) query = query.lte('log_date', end_date);
if (status) query = query.eq('submission_status', status);
const { data, error } = await query;
if (error) {
return res.status(500).json({ error: error.message });
}
return res.status(200).json({ data });
}
if (req.method === 'POST') {
const body = req.body;
const azEligibleHours = body.location_state === 'AZ' && talentProfile.az_eligible
? body.hours_worked
: 0;
const { data, error } = await userClient
.from('nexus_time_logs')
.insert({
talent_profile_id: talentProfile.id,
contract_id: body.contract_id,
milestone_id: body.milestone_id,
log_date: body.log_date,
start_time: body.start_time,
end_time: body.end_time,
hours_worked: body.hours_worked,
description: body.description,
task_category: body.task_category,
location_type: body.location_type || 'remote',
location_state: body.location_state,
location_city: body.location_city,
location_latitude: body.location_latitude,
location_longitude: body.location_longitude,
az_eligible_hours: azEligibleHours,
billable: body.billable !== false,
submission_status: 'draft'
})
.select()
.single();
if (error) {
return res.status(500).json({ error: error.message });
}
return res.status(201).json({ data });
}
if (req.method === 'PUT') {
const { id } = req.query;
const body = req.body;
if (!id) {
return res.status(400).json({ error: 'Time log ID required' });
}
const { data: existingLog } = await userClient
.from('nexus_time_logs')
.select('*')
.eq('id', id)
.eq('talent_profile_id', talentProfile.id)
.single();
if (!existingLog) {
return res.status(404).json({ error: 'Time log not found' });
}
if (existingLog.submission_status !== 'draft' && existingLog.submission_status !== 'rejected') {
return res.status(400).json({ error: 'Can only edit draft or rejected time logs' });
}
const azEligibleHours = (body.location_state || existingLog.location_state) === 'AZ' && talentProfile.az_eligible
? (body.hours_worked || existingLog.hours_worked)
: 0;
const { data, error } = await userClient
.from('nexus_time_logs')
.update({
...body,
az_eligible_hours: azEligibleHours,
updated_at: new Date().toISOString()
})
.eq('id', id)
.select()
.single();
if (error) {
return res.status(500).json({ error: error.message });
}
return res.status(200).json({ data });
}
if (req.method === 'DELETE') {
const { id } = req.query;
if (!id) {
return res.status(400).json({ error: 'Time log ID required' });
}
const { data: existingLog } = await userClient
.from('nexus_time_logs')
.select('submission_status')
.eq('id', id)
.eq('talent_profile_id', talentProfile.id)
.single();
if (!existingLog) {
return res.status(404).json({ error: 'Time log not found' });
}
if (existingLog.submission_status !== 'draft') {
return res.status(400).json({ error: 'Can only delete draft time logs' });
}
const { error } = await userClient
.from('nexus_time_logs')
.delete()
.eq('id', id);
if (error) {
return res.status(500).json({ error: error.message });
}
return res.status(204).end();
}
return res.status(405).json({ error: 'Method not allowed' });
}