import { Hono } from 'hono'; import { cors } from 'hono/cors'; import { getCookie } from 'hono/cookie'; import { Context } from 'hono'; import { decryptAuthCookie } from './services/decrypt-service'; import { handleSwaggerRequest } from './swagger-ui'; import { authMiddleware } from './middleware/auth'; import { saveUserData, getUserByUid } from './services/db-service'; // Create a new Hono app const app = new Hono(); // Add middleware app.use('*', async (c, next) => { console.log(`[${c.req.method}] ${c.req.url}`); await next(); }); // Add CORS middleware app.use('*', cors()); // Create a group for protected routes with auth middleware // Exclude Swagger docs routes from auth middleware app.use('/api/leave-agent/*', async (c, next) => { const path = new URL(c.req.url).pathname; // Skip auth for Swagger docs routes if (path.includes('/docs') || path.includes('/swagger.json') || path.includes('/openapi.json')) { return next(); } // Apply auth middleware for all other routes return authMiddleware(c, next); }); // Add auth validation endpoint (GET method) app.get('/api/leave-agent/auth/validate', async (c) => { try { // Get auth from query parameter const authToken = c.req.query('auth'); if (!authToken) { return c.json({ error: 'No auth parameter found' }, 401); } // Decrypt the auth token const decryptedData = await decryptAuthCookie(authToken, c.env); // Check if the decrypted data contains the expected fields if (!decryptedData || !decryptedData.firstname || !decryptedData.lastname) { console.error('Invalid decrypted data format:', decryptedData); return c.json({ error: 'Invalid auth parameter' }, 401); } return c.json(decryptedData, 200); } catch (error) { console.error('Authentication error:', error); return c.json({ error: 'Authentication failed' }, 500); } }); // Add auth validation endpoint (POST method) app.post('/api/leave-agent/auth/validate', async (c) => { try { // Get auth from request body const body = await c.req.json(); const authToken = body.auth; if (!authToken) { return c.json({ error: 'No auth parameter found in request body' }, 401); } // Decrypt the auth token const decryptedData = await decryptAuthCookie(authToken, c.env); // Check if the decrypted data contains the expected fields if (!decryptedData || !decryptedData.firstname || !decryptedData.lastname) { console.error('Invalid decrypted data format:', decryptedData); return c.json({ error: 'Invalid auth parameter' }, 401); } return c.json(decryptedData, 200); } catch (error) { console.error('Authentication error:', error); return c.json({ error: 'Authentication failed' }, 500); } }); // Define types for the user data interface UserData { id: number; uid: string; email: string; firstname?: string; lastname?: string; company_name?: string; created_at: string; updated_at: string; } // Define types for the context interface UserContext extends Context { get: (key: string) => string | undefined; env: { CF_TEMPLATE_DB?: any; // Replace 'any' with proper D1Database type if available }; } // Get current user's profile with leave balances app.get('/api/leave-agent/employees/me', async (c: UserContext) => { try { // Get user ID from the auth middleware const userId = c.get('userId'); if (!userId) { return c.json({ error: 'User not authenticated' }, 401); } // Get user data from the database const userData = await getUserByUid(userId, c.env); if (!userData) { return c.json({ error: 'User not found' }, 404); } // TODO: Replace with actual leave balance calculation from your database // This is a mock implementation const leaveBalances = { "Annual Leave": 24, "Maternity Leave": 182, "Paternity Leave": 0, "Bereavement Leave": 5, "Marriage Leave": 5 }; // Format the response const response = { id: userData.id, name: `${userData.firstname || ''} ${userData.lastname || ''}`.trim(), email: userData.email, color: '#3b82f6', // Default color, can be customized per user leaveBalances: leaveBalances }; return c.json(response); } catch (error) { console.error('Error fetching user profile:', error); return c.json({ error: 'Failed to fetch user profile' }, 500); } }); // Add endpoint to decrypt and save cookie data to D1 database app.post('/api/leave-agent/auth/decrypt-and-save', async (c) => { try { // Get auth token from request body const body = await c.req.json(); const authToken = body.auth; if (!authToken) { return c.json({ error: 'No auth parameter found in request body' }, 401); } // Decrypt the auth token const decryptedData = await decryptAuthCookie(authToken, c.env); // Check if the decrypted data contains the expected fields if (!decryptedData || !decryptedData.uid || !decryptedData.email) { console.error('Invalid decrypted data format:', decryptedData); return c.json({ error: 'Invalid auth parameter' }, 401); } // Save the decrypted data to the D1 database const result = await saveUserData(decryptedData, c.env); if (!result.success) { return c.json({ error: result.error || 'Failed to save user data' }, 500); } // Return success response with the saved data return c.json({ success: true, message: result.message, updated: result.updated, uid: result.uid, userData: decryptedData }, 200); } catch (error) { console.error('Error processing request:', error); return c.json({ error: 'Failed to process request', details: error.message }, 500); } }); // Add health check endpoint app.get('/api/leave-agent/health', (c) => { return c.json({ status: 'ok' }); }); // Add Swagger UI routes app.get('/api/leave-agent/docs', (c) => handleSwaggerRequest(c.req.raw, '/api/leave-agent')); app.get('/api/leave-agent/docs/', (c) => handleSwaggerRequest(c.req.raw, '/api/leave-agent')); app.get('/api/leave-agent/swagger.json', (c) => handleSwaggerRequest(c.req.raw, '/api/leave-agent')); app.get('/api/leave-agent/openapi.json', (c) => handleSwaggerRequest(c.req.raw, '/api/leave-agent')); // Export the app export default { fetch: app.fetch, };