Compare commits
2 Commits
07a983d24f
...
ae0d4dd99e
| Author | SHA1 | Date | |
|---|---|---|---|
| ae0d4dd99e | |||
|
|
1ecaf8ff95 |
219
docs/leave-agent-design.md
Normal file
219
docs/leave-agent-design.md
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
# Leave Agent - Database Design
|
||||||
|
|
||||||
|
## Database Schema
|
||||||
|
|
||||||
|
### 1. Employees
|
||||||
|
Stores employee information and their leave balances.
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE employees (
|
||||||
|
employee_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
email TEXT UNIQUE NOT NULL,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Leave Types
|
||||||
|
Defines different types of leaves available in the system.
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE leave_types (
|
||||||
|
leave_types_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
name TEXT UNIQUE NOT NULL, -- e.g., "Annual Leave", "Sick Leave"
|
||||||
|
description TEXT,
|
||||||
|
max_days INTEGER NOT NULL,
|
||||||
|
is_active BOOLEAN DEFAULT true,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Leave Balances
|
||||||
|
Tracks the number of leave days available for each employee by leave type.
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE leave_balances (
|
||||||
|
leave_balances_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
employee_id INTEGER NOT NULL,
|
||||||
|
leave_type_id INTEGER NOT NULL,
|
||||||
|
balance_days REAL NOT NULL,
|
||||||
|
year INTEGER NOT NULL,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (employee_id) REFERENCES employees(employee_id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (leave_type_id) REFERENCES leave_types(leave_types_id) ON DELETE CASCADE,
|
||||||
|
UNIQUE(employee_id, leave_type_id, year)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Leave Requests
|
||||||
|
Tracks all leave requests made by employees.
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE leave_requests (
|
||||||
|
leave_requests_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
employee_id INTEGER NOT NULL,
|
||||||
|
leave_type_id INTEGER NOT NULL,
|
||||||
|
start_date DATE NOT NULL,
|
||||||
|
end_date DATE NOT NULL,
|
||||||
|
reason TEXT,
|
||||||
|
status TEXT NOT NULL, -- 'pending', 'approved', 'rejected', 'cancelled'
|
||||||
|
business_days REAL NOT NULL,
|
||||||
|
notes TEXT,
|
||||||
|
approved_by INTEGER,
|
||||||
|
approved_at TIMESTAMP,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (employee_id) REFERENCES employees(employee_id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (leave_type_id) REFERENCES leave_types(leave_types_id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (approved_by) REFERENCES employees(employee_id) ON DELETE SET NULL
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Holidays
|
||||||
|
Stores public holidays.
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE holidays (
|
||||||
|
holidays_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
date DATE NOT NULL,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
is_recurring BOOLEAN DEFAULT false,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
UNIQUE(date, name)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Team Events
|
||||||
|
Stores company events and team activities.
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE team_events (
|
||||||
|
team_events_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
start_date DATE NOT NULL,
|
||||||
|
end_date DATE,
|
||||||
|
is_recurring BOOLEAN DEFAULT false,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Entity Relationship Diagram
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
erDiagram
|
||||||
|
employees ||--o{ leave_balances : has
|
||||||
|
employees ||--o{ leave_requests : makes
|
||||||
|
leave_types ||--o{ leave_balances : has
|
||||||
|
leave_types ||--o{ leave_requests : has
|
||||||
|
employees ||--o{ leave_requests : approves
|
||||||
|
|
||||||
|
employees {
|
||||||
|
int employee_id PK
|
||||||
|
string name
|
||||||
|
string email
|
||||||
|
timestamp created_at
|
||||||
|
timestamp updated_at
|
||||||
|
}
|
||||||
|
|
||||||
|
leave_types {
|
||||||
|
int leave_types_id PK
|
||||||
|
string name
|
||||||
|
string description
|
||||||
|
int max_days
|
||||||
|
boolean is_active
|
||||||
|
timestamp created_at
|
||||||
|
}
|
||||||
|
|
||||||
|
leave_balances {
|
||||||
|
int leave_balances_id PK
|
||||||
|
int employee_id FK
|
||||||
|
int leave_type_id FK
|
||||||
|
real balance_days
|
||||||
|
int year
|
||||||
|
timestamp created_at
|
||||||
|
timestamp updated_at
|
||||||
|
}
|
||||||
|
|
||||||
|
leave_requests {
|
||||||
|
int leave_requests_id PK
|
||||||
|
int employee_id FK
|
||||||
|
int leave_type_id FK
|
||||||
|
date start_date
|
||||||
|
date end_date
|
||||||
|
string reason
|
||||||
|
string status
|
||||||
|
real business_days
|
||||||
|
string notes
|
||||||
|
int approved_by FK
|
||||||
|
timestamp approved_at
|
||||||
|
timestamp created_at
|
||||||
|
timestamp updated_at
|
||||||
|
}
|
||||||
|
|
||||||
|
holidays {
|
||||||
|
int holidays_id PK
|
||||||
|
date date
|
||||||
|
string name
|
||||||
|
string description
|
||||||
|
boolean is_recurring
|
||||||
|
timestamp created_at
|
||||||
|
}
|
||||||
|
|
||||||
|
team_events {
|
||||||
|
int team_events_id PK
|
||||||
|
string name
|
||||||
|
string description
|
||||||
|
date start_date
|
||||||
|
date end_date
|
||||||
|
boolean is_recurring
|
||||||
|
timestamp created_at
|
||||||
|
timestamp updated_at
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Initial Data Setup
|
||||||
|
|
||||||
|
### Default Leave Types
|
||||||
|
```sql
|
||||||
|
INSERT INTO leave_types (name, description, max_days, is_active) VALUES
|
||||||
|
('Annual Leave', 'Paid time off for vacation or personal reasons', 24, true),
|
||||||
|
('Sick Leave', 'Paid time off for illness or medical appointments', 12, true),
|
||||||
|
('Maternity Leave', 'Paid leave for new mothers', 182, true),
|
||||||
|
('Paternity Leave', 'Paid leave for new fathers', 14, true),
|
||||||
|
('Bereavement Leave', 'Paid time off following the death of a family member', 5, true),
|
||||||
|
('Marriage Leave', 'Paid time off for employee''s own marriage', 5, true);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sample Holidays (2025)
|
||||||
|
```sql
|
||||||
|
INSERT INTO holidays (date, name, description, is_recurring) VALUES
|
||||||
|
('2025-01-01', 'New Year''s Day', 'First day of the year', true),
|
||||||
|
('2025-01-26', 'Republic Day', 'Indian Republic Day', true),
|
||||||
|
('2025-03-17', 'Holi', 'Festival of Colors', true),
|
||||||
|
('2025-04-14', 'Ambedkar Jayanti', 'Birth anniversary of Dr. B.R. Ambedkar', true),
|
||||||
|
('2025-08-15', 'Independence Day', 'Indian Independence Day', true),
|
||||||
|
('2025-10-02', 'Gandhi Jayanti', 'Birth anniversary of Mahatma Gandhi', true),
|
||||||
|
('2025-12-25', 'Christmas Day', 'Christmas holiday', true);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Indexes
|
||||||
|
|
||||||
|
For better query performance, the following indexes are recommended:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Indexes for leave_requests
|
||||||
|
CREATE INDEX idx_leave_requests_employee_id ON leave_requests(employee_id);
|
||||||
|
CREATE INDEX idx_leave_requests_status ON leave_requests(status);
|
||||||
|
CREATE INDEX idx_leave_requests_dates ON leave_requests(start_date, end_date);
|
||||||
|
|
||||||
|
-- Indexes for leave_balances
|
||||||
|
CREATE INDEX idx_leave_balances_employee ON leave_balances(employee_id, year);
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
82
src/index.ts
82
src/index.ts
@@ -1,6 +1,7 @@
|
|||||||
import { Hono } from 'hono';
|
import { Hono } from 'hono';
|
||||||
import { cors } from 'hono/cors';
|
import { cors } from 'hono/cors';
|
||||||
import { getCookie } from 'hono/cookie';
|
import { getCookie } from 'hono/cookie';
|
||||||
|
import { Context } from 'hono';
|
||||||
import { decryptAuthCookie } from './services/decrypt-service';
|
import { decryptAuthCookie } from './services/decrypt-service';
|
||||||
import { handleSwaggerRequest } from './swagger-ui';
|
import { handleSwaggerRequest } from './swagger-ui';
|
||||||
import { authMiddleware } from './middleware/auth';
|
import { authMiddleware } from './middleware/auth';
|
||||||
@@ -20,7 +21,7 @@ app.use('*', cors());
|
|||||||
|
|
||||||
// Create a group for protected routes with auth middleware
|
// Create a group for protected routes with auth middleware
|
||||||
// Exclude Swagger docs routes from 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;
|
const path = new URL(c.req.url).pathname;
|
||||||
|
|
||||||
// Skip auth for Swagger docs routes
|
// Skip auth for Swagger docs routes
|
||||||
@@ -33,7 +34,7 @@ app.use('/api/cf-template/*', async (c, next) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Add auth validation endpoint (GET method)
|
// 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 {
|
try {
|
||||||
// Get auth from query parameter
|
// Get auth from query parameter
|
||||||
const authToken = c.req.query('auth');
|
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)
|
// 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 {
|
try {
|
||||||
// Get auth from request body
|
// Get auth from request body
|
||||||
const body = await c.req.json();
|
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
|
// 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 {
|
try {
|
||||||
// Get auth token from request body
|
// Get auth token from request body
|
||||||
const body = await c.req.json();
|
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
|
// Add health check endpoint
|
||||||
app.get('/api/cf-template/health', (c) => {
|
app.get('/api/leave-agent/health', (c) => {
|
||||||
return c.json({ status: 'ok' });
|
return c.json({ status: 'ok' });
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add Swagger UI routes
|
// Add Swagger UI routes
|
||||||
app.get('/api/cf-template/docs', (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/cf-template/docs/', (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/cf-template/swagger.json', (c) => handleSwaggerRequest(c.req.raw, '/api/cf-template'));
|
app.get('/api/leave-agent/swagger.json', (c) => handleSwaggerRequest(c.req.raw, '/api/leave-agent'));
|
||||||
app.get('/api/cf-template/openapi.json', (c) => handleSwaggerRequest(c.req.raw, '/api/cf-template'));
|
app.get('/api/leave-agent/openapi.json', (c) => handleSwaggerRequest(c.req.raw, '/api/leave-agent'));
|
||||||
|
|
||||||
// Export the app
|
// Export the app
|
||||||
export default {
|
export default {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { saveUserDataLocal, getUserByUidLocal, initLocalDb } from './local-db-se
|
|||||||
*/
|
*/
|
||||||
async function initializeDatabase(env) {
|
async function initializeDatabase(env) {
|
||||||
try {
|
try {
|
||||||
if (!env.CF_TEMPLATE_DB) {
|
if (!env.LEAVE_AGENT_DB) {
|
||||||
console.log('D1 database binding not found, skipping initialization');
|
console.log('D1 database binding not found, skipping initialization');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -19,11 +19,11 @@ async function initializeDatabase(env) {
|
|||||||
// Create user_data table if it doesn't exist
|
// Create user_data table if it doesn't exist
|
||||||
try {
|
try {
|
||||||
// Use a single-line SQL statement to avoid parsing issues with D1
|
// 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
|
// 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.LEAVE_AGENT_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_email ON user_data(email);`);
|
||||||
|
|
||||||
console.log('Database schema initialized successfully');
|
console.log('Database schema initialized successfully');
|
||||||
} catch (dbError) {
|
} catch (dbError) {
|
||||||
@@ -50,7 +50,7 @@ export async function saveUserData(userData, env) {
|
|||||||
// Initialize database schema if needed
|
// Initialize database schema if needed
|
||||||
await initializeDatabase(env);
|
await initializeDatabase(env);
|
||||||
// Check if required database binding exists
|
// 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');
|
console.log('D1 database binding not found, using local database');
|
||||||
// Use our local database implementation
|
// Use our local database implementation
|
||||||
return await saveUserDataLocal(userData);
|
return await saveUserDataLocal(userData);
|
||||||
@@ -66,7 +66,7 @@ export async function saveUserData(userData, env) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Check if user already exists
|
// 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 = ?'
|
'SELECT id FROM user_data WHERE uid = ?'
|
||||||
).bind(uid).first();
|
).bind(uid).first();
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ export async function saveUserData(userData, env) {
|
|||||||
|
|
||||||
if (existingUser) {
|
if (existingUser) {
|
||||||
// Update existing user
|
// Update existing user
|
||||||
result = await env.CF_TEMPLATE_DB.prepare(`
|
result = await env.LEAVE_AGENT_DB.prepare(`
|
||||||
UPDATE user_data
|
UPDATE user_data
|
||||||
SET email = ?, firebase_uid = ?, firstname = ?, lastname = ?, company_name = ?, updated_at = CURRENT_TIMESTAMP
|
SET email = ?, firebase_uid = ?, firstname = ?, lastname = ?, company_name = ?, updated_at = CURRENT_TIMESTAMP
|
||||||
WHERE uid = ?
|
WHERE uid = ?
|
||||||
@@ -88,7 +88,7 @@ export async function saveUserData(userData, env) {
|
|||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// Insert new user
|
// 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)
|
INSERT INTO user_data (uid, email, firebase_uid, firstname, lastname, company_name)
|
||||||
VALUES (?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?)
|
||||||
`).bind(uid, email, firebase_uid, firstname, lastname, company_name).run();
|
`).bind(uid, email, firebase_uid, firstname, lastname, company_name).run();
|
||||||
@@ -128,13 +128,13 @@ export async function getUserByUid(uid, env) {
|
|||||||
try {
|
try {
|
||||||
// Initialize database schema if needed
|
// Initialize database schema if needed
|
||||||
await initializeDatabase(env);
|
await initializeDatabase(env);
|
||||||
if (!env.CF_TEMPLATE_DB) {
|
if (!env.LEAVE_AGENT_DB) {
|
||||||
console.log('D1 database binding not found, using local database');
|
console.log('D1 database binding not found, using local database');
|
||||||
// Use our local database implementation
|
// Use our local database implementation
|
||||||
return await getUserByUidLocal(uid);
|
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 = ?'
|
'SELECT * FROM user_data WHERE uid = ?'
|
||||||
).bind(uid).first();
|
).bind(uid).first();
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,50 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export const authValidateEndpoint = {
|
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': {
|
'/auth/validate': {
|
||||||
get: {
|
get: {
|
||||||
summary: 'Validate authentication token (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;
|
||||||
|
};
|
||||||
|
}
|
||||||
21
tsconfig.json
Normal file
21
tsconfig.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"module": "ESNext",
|
||||||
|
"lib": ["ES2020"],
|
||||||
|
"types": ["@cloudflare/workers-types"],
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"outDir": "dist",
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.ts"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
@@ -5,25 +5,25 @@ compatibility_date = "2025-03-10"
|
|||||||
compatibility_flags = ["nodejs_compat"]
|
compatibility_flags = ["nodejs_compat"]
|
||||||
|
|
||||||
[vars]
|
[vars]
|
||||||
name = 'cf-template-local'
|
name = 'leave-agent-local'
|
||||||
CM_BASE_URL = "https://cm.dev.svchub.com"
|
CM_BASE_URL = "https://cm.dev.svchub.com"
|
||||||
DECRYPT_API_URL = "https://www.dev.humanizeiq.ai/api/dashboard-backend"
|
DECRYPT_API_URL = "https://www.dev.humanizeiq.ai/api/dashboard-backend"
|
||||||
DECRYPT_API_KEY = "XMCKhjOTTPJJzoSiNTmRTcQauvtGKRnUZNFlhWpjhTEaIdIeIdLFM"
|
DECRYPT_API_KEY = "XMCKhjOTTPJJzoSiNTmRTcQauvtGKRnUZNFlhWpjhTEaIdIeIdLFM"
|
||||||
d1_databases= [
|
d1_databases= [
|
||||||
{ binding = "CF_TEMPLATE_DB", database_name = "cf-template_test-db", database_id = "c020574a-5623-407b-be0c-cd192bab9545" }
|
{ binding = "LEAVE_AGENT_DB", database_name = "leave-agent_test-db", database_id = "c020574a-5623-407b-be0c-cd192bab9545" }
|
||||||
]
|
]
|
||||||
|
|
||||||
# Playtest Environment
|
# Playtest Environment
|
||||||
[env.playtest]
|
[env.playtest]
|
||||||
name = "cf-template-playtest"
|
name = "leave-agent-playtest"
|
||||||
routes = [
|
routes = [
|
||||||
{ pattern = "cf-template.playtest.svchub.com", custom_domain = true }
|
{ pattern = "leave-agent.playtest.svchub.com", custom_domain = true }
|
||||||
]
|
]
|
||||||
d1_databases = [
|
d1_databases = [
|
||||||
{ binding = "CF_TEMPLATE_DB", database_name = "cf-template_playtest", database_id = "fd847b4b-e1e9-442e-9756-23c4540476e7" }
|
{ binding = "LEAVE_AGENT_DB", database_name = "leave-agent_playtest", database_id = "fd847b4b-e1e9-442e-9756-23c4540476e7" }
|
||||||
]
|
]
|
||||||
r2_buckets = [
|
r2_buckets = [
|
||||||
{ binding = "CF_TEMPLATE_BUCKET", bucket_name = "cf-template-playtest" }
|
{ binding = "LEAVE_AGENT_BUCKET", bucket_name = "leave-agent-playtest" }
|
||||||
]
|
]
|
||||||
[env.playtest.vars]
|
[env.playtest.vars]
|
||||||
WORKER_ENV = "playtest"
|
WORKER_ENV = "playtest"
|
||||||
@@ -32,15 +32,15 @@ DECRYPT_API_URL = "https://www.dev.humanizeiq.ai/api/dashboard-backend"
|
|||||||
|
|
||||||
# Dev Environment
|
# Dev Environment
|
||||||
[env.development]
|
[env.development]
|
||||||
name = "cf-template-dev"
|
name = "leave-agent-dev"
|
||||||
routes = [
|
routes = [
|
||||||
{ pattern = "cf-template.dev.svchub.com", custom_domain = true }
|
{ pattern = "leave-agent.dev.svchub.com", custom_domain = true }
|
||||||
]
|
]
|
||||||
d1_databases = [
|
d1_databases = [
|
||||||
{ binding = "CF_TEMPLATE_DB", database_name = "cf-template_dev", database_id = "08a710a9-27ae-4886-9cab-c0a7ab204de9" }
|
{ binding = "LEAVE_AGENT_DB", database_name = "leave-agent_dev", database_id = "08a710a9-27ae-4886-9cab-c0a7ab204de9" }
|
||||||
]
|
]
|
||||||
r2_buckets = [
|
r2_buckets = [
|
||||||
{ binding = "CF_TEMPLATE_BUCKET", bucket_name = "cf-template-dev" }
|
{ binding = "LEAVE_AGENT_BUCKET", bucket_name = "leave-agent-dev" }
|
||||||
]
|
]
|
||||||
[env.development.vars]
|
[env.development.vars]
|
||||||
WORKER_ENV = "development"
|
WORKER_ENV = "development"
|
||||||
@@ -49,15 +49,15 @@ DECRYPT_API_URL = "https://www.dev.humanizeiq.ai/api/dashboard-backend"
|
|||||||
|
|
||||||
# Production Environment
|
# Production Environment
|
||||||
[env.production]
|
[env.production]
|
||||||
name = "cf-template-prod"
|
name = " leave-agent-prod"
|
||||||
routes = [
|
routes = [
|
||||||
{ pattern = "cf-template.prod.svchub.com", custom_domain = true }
|
{ pattern = "leave-agent.prod.svchub.com", custom_domain = true }
|
||||||
]
|
]
|
||||||
d1_databases = [
|
d1_databases = [
|
||||||
{ binding = "CF_TEMPLATE_DB", database_name = "cf-template_prod", database_id = "2983ec1a-3c9c-451d-b85e-5a52ff9b114a" }
|
{ binding = "LEAVE_AGENT_DB", database_name = "leave-agent_prod", database_id = "2983ec1a-3c9c-451d-b85e-5a52ff9b114a" }
|
||||||
]
|
]
|
||||||
r2_buckets = [
|
r2_buckets = [
|
||||||
{ binding = "CF_TEMPLATE_BUCKET", bucket_name = "cf-template-prod" }
|
{ binding = "LEAVE_AGENT_BUCKET", bucket_name = "leave-agent-prod" }
|
||||||
]
|
]
|
||||||
[env.production.vars]
|
[env.production.vars]
|
||||||
WORKER_ENV = "production"
|
WORKER_ENV = "production"
|
||||||
|
|||||||
Reference in New Issue
Block a user