api
Some checks failed
Cloudflare Worker API Template / Deploy to ${{ github.ref_name }} environment (pull_request) Failing after 15s
Some checks failed
Cloudflare Worker API Template / Deploy to ${{ github.ref_name }} environment (pull_request) Failing after 15s
This commit is contained in:
82
src/index.ts
82
src/index.ts
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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
39
src/types/index.ts
Normal 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;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user