api
Some checks failed
Cloudflare Worker API Template / Deploy to ${{ github.ref_name }} environment (pull_request) Failing after 15s

This commit is contained in:
purvarao
2025-11-27 19:14:46 +05:30
parent 07a983d24f
commit 1ecaf8ff95
7 changed files with 420 additions and 33 deletions

View File

@@ -1,6 +1,7 @@
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';
@@ -20,7 +21,7 @@ app.use('*', cors());
// Create a group for protected routes with auth middleware
// Exclude Swagger docs routes from auth middleware
app.use('/api/cf-template/*', async (c, next) => {
app.use('/api/leave-agent/*', async (c, next) => {
const path = new URL(c.req.url).pathname;
// Skip auth for Swagger docs routes
@@ -33,7 +34,7 @@ app.use('/api/cf-template/*', async (c, next) => {
});
// Add auth validation endpoint (GET method)
app.get('/api/cf-template/auth/validate', async (c) => {
app.get('/api/leave-agent/auth/validate', async (c) => {
try {
// Get auth from query parameter
const authToken = c.req.query('auth');
@@ -58,7 +59,7 @@ app.get('/api/cf-template/auth/validate', async (c) => {
});
// Add auth validation endpoint (POST method)
app.post('/api/cf-template/auth/validate', async (c) => {
app.post('/api/leave-agent/auth/validate', async (c) => {
try {
// Get auth from request body
const body = await c.req.json();
@@ -83,8 +84,71 @@ app.post('/api/cf-template/auth/validate', async (c) => {
}
});
// 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/cf-template/auth/decrypt-and-save', async (c) => {
app.post('/api/leave-agent/auth/decrypt-and-save', async (c) => {
try {
// Get auth token from request body
const body = await c.req.json();
@@ -125,15 +189,15 @@ app.post('/api/cf-template/auth/decrypt-and-save', async (c) => {
});
// Add health check endpoint
app.get('/api/cf-template/health', (c) => {
app.get('/api/leave-agent/health', (c) => {
return c.json({ status: 'ok' });
});
// Add Swagger UI routes
app.get('/api/cf-template/docs', (c) => handleSwaggerRequest(c.req.raw, '/api/cf-template'));
app.get('/api/cf-template/docs/', (c) => handleSwaggerRequest(c.req.raw, '/api/cf-template'));
app.get('/api/cf-template/swagger.json', (c) => handleSwaggerRequest(c.req.raw, '/api/cf-template'));
app.get('/api/cf-template/openapi.json', (c) => handleSwaggerRequest(c.req.raw, '/api/cf-template'));
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 {

View File

@@ -11,7 +11,7 @@ import { saveUserDataLocal, getUserByUidLocal, initLocalDb } from './local-db-se
*/
async function initializeDatabase(env) {
try {
if (!env.CF_TEMPLATE_DB) {
if (!env.LEAVE_AGENT_DB) {
console.log('D1 database binding not found, skipping initialization');
return false;
}
@@ -19,11 +19,11 @@ async function initializeDatabase(env) {
// Create user_data table if it doesn't exist
try {
// Use a single-line SQL statement to avoid parsing issues with D1
await env.CF_TEMPLATE_DB.exec(`CREATE TABLE IF NOT EXISTS user_data (id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT UNIQUE NOT NULL, email TEXT NOT NULL, firebase_uid TEXT, firstname TEXT, lastname TEXT, company_name TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP);`);
await env.LEAVE_AGENT_DB.exec(`CREATE TABLE IF NOT EXISTS user_data (id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT UNIQUE NOT NULL, email TEXT NOT NULL, firebase_uid TEXT, firstname TEXT, lastname TEXT, company_name TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP);`);
// Create indexes in a single-line format as well
await env.CF_TEMPLATE_DB.exec(`CREATE INDEX IF NOT EXISTS idx_user_data_uid ON user_data(uid);`);
await env.CF_TEMPLATE_DB.exec(`CREATE INDEX IF NOT EXISTS idx_user_data_email ON user_data(email);`);
await env.LEAVE_AGENT_DB.exec(`CREATE INDEX IF NOT EXISTS idx_user_data_uid ON user_data(uid);`);
await env.LEAVE_AGENT_DB.exec(`CREATE INDEX IF NOT EXISTS idx_user_data_email ON user_data(email);`);
console.log('Database schema initialized successfully');
} catch (dbError) {
@@ -50,7 +50,7 @@ export async function saveUserData(userData, env) {
// Initialize database schema if needed
await initializeDatabase(env);
// Check if required database binding exists
if (!env.CF_TEMPLATE_DB) {
if (!env.LEAVE_AGENT_DB) {
console.log('D1 database binding not found, using local database');
// Use our local database implementation
return await saveUserDataLocal(userData);
@@ -66,7 +66,7 @@ export async function saveUserData(userData, env) {
try {
// Check if user already exists
const existingUser = await env.CF_TEMPLATE_DB.prepare(
const existingUser = await env.LEAVE_AGENT_DB.prepare(
'SELECT id FROM user_data WHERE uid = ?'
).bind(uid).first();
@@ -74,7 +74,7 @@ export async function saveUserData(userData, env) {
if (existingUser) {
// Update existing user
result = await env.CF_TEMPLATE_DB.prepare(`
result = await env.LEAVE_AGENT_DB.prepare(`
UPDATE user_data
SET email = ?, firebase_uid = ?, firstname = ?, lastname = ?, company_name = ?, updated_at = CURRENT_TIMESTAMP
WHERE uid = ?
@@ -88,7 +88,7 @@ export async function saveUserData(userData, env) {
};
} else {
// Insert new user
result = await env.CF_TEMPLATE_DB.prepare(`
result = await env.LEAVE_AGENT_DB.prepare(`
INSERT INTO user_data (uid, email, firebase_uid, firstname, lastname, company_name)
VALUES (?, ?, ?, ?, ?, ?)
`).bind(uid, email, firebase_uid, firstname, lastname, company_name).run();
@@ -128,13 +128,13 @@ export async function getUserByUid(uid, env) {
try {
// Initialize database schema if needed
await initializeDatabase(env);
if (!env.CF_TEMPLATE_DB) {
if (!env.LEAVE_AGENT_DB) {
console.log('D1 database binding not found, using local database');
// Use our local database implementation
return await getUserByUidLocal(uid);
}
const user = await env.CF_TEMPLATE_DB.prepare(
const user = await env.LEAVE_AGENT_DB.prepare(
'SELECT * FROM user_data WHERE uid = ?'
).bind(uid).first();

View File

@@ -3,6 +3,50 @@
*/
export const authValidateEndpoint = {
'/employees/me': {
get: {
summary: 'Get current employee profile',
description: 'Retrieves details and leave balances for the authenticated user',
security: [
{ ApiKeyAuth: [] },
{ BearerAuth: [] }
],
responses: {
'200': {
description: 'Successfully retrieved employee profile',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
id: { type: 'number', example: 1 },
name: { type: 'string', example: 'Purva Rao' },
email: { type: 'string', example: 'purva.rao@humanizeiq.ai' },
color: { type: 'string', example: '#3b82f6' },
leaveBalances: {
type: 'object',
properties: {
'Annual Leave': { type: 'number', example: 24 },
'Maternity Leave': { type: 'number', example: 182 },
'Paternity Leave': { type: 'number', example: 0 },
'Bereavement Leave': { type: 'number', example: 5 },
'Marriage Leave': { type: 'number', example: 5 }
}
}
}
}
}
}
},
'401': {
description: 'Unauthorized - Invalid or missing authentication token'
},
'500': {
description: 'Internal server error'
}
}
}
},
'/auth/validate': {
get: {
summary: 'Validate authentication token (GET)',

39
src/types/index.ts Normal file
View File

@@ -0,0 +1,39 @@
import { Context } from 'hono';
// Define the structure of user data from the database
export interface UserData {
id: number;
uid: string;
email: string;
firebase_uid?: string;
firstname?: string;
lastname?: string;
company_name?: string;
created_at: string;
updated_at: string;
}
// Define the structure of the environment variables
export interface Env {
CF_TEMPLATE_DB?: D1Database; // D1Database type from @cloudflare/workers-types
DECRYPT_API_URL?: string;
DECRYPT_API_KEY?: string;
}
// Extend the Hono context with our custom types
export interface CustomContext extends Context {
env: Env;
get: (key: string) => string | undefined;
set: (key: string, value: any) => void;
}
// Response type for the user profile endpoint
export interface UserProfileResponse {
id: number;
name: string;
email: string;
color: string;
leaveBalances: {
[key: string]: number;
};
}