first commit

This commit is contained in:
Isuru Bimsara
2026-08-14 22:46:02 +05:30
commit 81d0e65686
132 changed files with 24398 additions and 0 deletions
+125
View File
@@ -0,0 +1,125 @@
# Auth API
#### Request OTP
**Endpoint**
```
POST: http://localhost:3070/api/auth/req-otp
```
**Request Body**
```json
{
"email": "sathira@niolla.lk",
"password": "Niolla@123"
}
```
**Respond**
```json
{
"success": true,
"message": "OTP Sent Successfully"
}
```
---
#### Login
**Endpoint**
```
POST: http://localhost:3070/api/auth/login
```
**Request Body**
```json
{
"email": "sathira@niolla.lk",
"otp": "922304"
}
```
**Respond**
```json
{
"success": true,
"message": "Login Successful",
"data": {
"id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4",
"email": "sathira@niolla.lk",
"firstName": "Jhon",
"lastName": "Doe",
"role": "System Developer",
"accountType": "admin"
}
}
```
---
#### Get Current User
**Endpoint**
```
GET: http://localhost:3070/api/auth/me
```
**Authorization**: Required (Bearer token)
**Request Body**
```
No Body
```
**Response (200)**
```json
{
"authenticated": true,
"user": {
"id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4",
"firstName": "Sathira",
"lastName": "Sri Sathsara",
"email": "sathira@niolla.lk",
"role": "System Developer",
"accountType": "admin",
"iat": 1778865265,
"exp": 1778868865,
"permissions": []
}
}
```
---
### Logout
**Endpoint**
```
POST: http://localhost:3070/api/auth/logout
```
**Request Body**
```
No Body
```
**Respond**
```json
{
"success": true,
"message": "Logged out successfully"
}
```
+740
View File
@@ -0,0 +1,740 @@
# Document API Documentation
## Overview
The Document API allows authenticated users to generate documents asynchronously (PDF, Excel, etc.) from provided data. The system uses a queue-based architecture for reliable, scalable document generation.
**Key Features:**
- ✅ Non-blocking requests (returns immediately with jobId)
- ✅ Job status polling to track progress
- ✅ Automatic retries with exponential backoff
- ✅ Support for multiple document types and formats
- ✅ Concurrent document generation
- ✅ Case-insensitive document names with whitespace and punctuation normalization
---
## Architecture
```
Client Request → Queue Job → Return jobId (202)
↓
Worker processes
↓
Client polls status
↓
Job complete → Download file
```
---
## Endpoints
### 1. Generate Document
**Endpoint:** `POST /api/document/generate`
**URL:** `http://localhost:3070/api/document/generate`
**Authentication:** Required ✓
**Authorization:** Required - User must have one of the following roles:
- `admin`
- `management`
- `team_head`
- `user`
**HTTP Status:** `202 Accepted`
#### Request Headers
```
Content-Type: application/json
Authorization: Bearer <JWT_TOKEN>
```
#### Request Body
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `document` | string | Yes | Type of document (e.g., "INVOICE", "DISPATCHNOTE", "DELIVERYNOTE") |
| `documentType` | string | Yes | Output format (e.g., "pdf", "excel") |
| `documentData` | object | Yes | Data object for the document |
#### Response (202 Accepted)
```json
{
"success": true,
"message": "Document generation started",
"jobId": "1"
}
```
#### Error Responses
**400 Bad Request:**
```json
{
"success": false,
"message": "document, documentType, and documentData are required"
}
```
**500 Server Error:**
```json
{
"success": false,
"message": "Error message describing the issue"
}
```
### Supported Document Values
You can send the document name in any case. Spaces, underscores, and hyphens are ignored by the generator.
- `PRECOST`
- `INVOICE`
- `DISPATCHNOTE`
- `DELIVERYNOTE`
- `CUSTOMDOCUMENT`
- `CREDITNOTE`
- `PO`
## Sample Request Bodies
All examples below are for `POST /api/document/generate` with `documentType: "pdf"`.
### PreCost
```json
{
"document": "PRECOST",
"documentType": "pdf",
"documentData": {
"vessel_name": "MV OCEAN STAR",
"supplyPort": "DUBAI",
"clientRfqNum": "RFQ-2026-001",
"omsRfqNum": "OMS-RFQ-0001",
"date": "08/07/2026",
"items": [
{
"name": "Marine Rope",
"description": "High strength rope",
"qty": 10,
"unitPrice": 12.5
},
{
"name": "Safety Gloves",
"description": "Blue gloves",
"qty": 5,
"unitPrice": 8
}
],
"sub_total": 125,
"discount": 0,
"additionalCharges": 0,
"total_cost": 125
}
}
```
### Invoice
```json
{
"document": "INVOICE",
"documentType": "pdf",
"documentData": {
"jobReference": "JOB-2026-001",
"poNumber": "PO-20254161",
"date": "08/07/2026",
"pageLabel": "Page 01 of 02",
"billToName": "SUNRICH SHIP CHANDLERS L.L.C",
"billToAddress": "BUSINESS BAY - ASPECT TOWER, 22ND FLOOR, OFFICE NO. 2201, P.O. BOX NO 39976, DUBAI",
"vesselName": "MV OCEAN STAR",
"purposeOfCall": "SUPPLY OF PROVISIONS",
"supplyDate": "08/07/2026",
"grt": "12345",
"imoNumber": "9876543",
"portCountry": "DUBAI / UAE",
"items": [
{
"description": "Marine rope",
"remarks": "Delivered as requested",
"quantity": 10,
"unit_price": 12.5
},
{
"description": "Safety gloves",
"remarks": "Blue color",
"quantity": 5,
"unit_price": 8
}
],
"subtotal": 165,
"tax": 0,
"discount": 0,
"total": 165,
"paymentTerms": "Payment due within 30 days",
"notes": "Please verify quantities upon delivery."
}
}
```
### Dispatch Note
```json
{
"document": "DISPATCHNOTE",
"documentType": "pdf",
"documentData": {
"referenceNumber": "DN/2026/001",
"date": "08/07/2026",
"vessel": "MV OCEAN STAR",
"captain": "CAPT. JOHN DOE",
"cook": "SAMUEL",
"agent": "OCEANIC AGENT LTD",
"details": "Delivery of provisions and stores",
"placeOfDelivery": "DUBAI PORT",
"eta": "08/07/2026 08:00",
"etd": "08/07/2026 18:00",
"numberOfCrew": 18,
"nextMainOrderIn": "24 HRS",
"port": "DUBAI",
"company": "OCEANIC SHIP CHANDLERS (PVT) LTD",
"fileNo": "FILE-2026-04",
"items": [
{
"product": "Rice",
"qty": 10,
"unit": "BAGS",
"supp": "OMS",
"poNo": "PO-001",
"receivedQty": 10,
"issuedQty": 10,
"expDate": "12/07/2026",
"receivedTime": "09:15",
"date": "08/07/2026",
"rejects": "",
"fatCon": "",
"remark": "Delivered"
}
],
"showSignatures": true,
"preparedBy": "Admin User",
"approvedBy": "Manager User"
}
}
```
### Delivery Note
```json
{
"document": "DELIVERYNOTE",
"documentType": "pdf",
"documentData": {
"referenceNumber": "DN/OSC/2602/01",
"date": "08/07/2026",
"billToName": "SUNRICH SHIP CHANDLERS L.L.C",
"billToAddress": "BUSINESS BAY - ASPECT TOWER, 22ND FLOOR, OFFICE NO. 2201, P.O. BOX NO 39976, DUBAI",
"supplyDate": "08/07/2026",
"poNumber": "PO-20254161",
"items": [
{
"description": "Marine rope",
"remarks": "Delivered as requested",
"unit": "PCS",
"quantity": 10
},
{
"description": "Safety gloves",
"remarks": "Blue color",
"unit": "BOX",
"quantity": 5
}
]
}
}
```
### Custom Document
```json
{
"document": "CUSTOMDOCUMENT",
"documentType": "pdf",
"documentData": {
"title": "CUSTOM DOCUMENT",
"date": "08/07/2026",
"telePhone": "+94 112 083 206",
"emailAddress": "shipsupply@oceanicmsol.com",
"officeAddressLine1": "Level 5D, Valiant Towers, Nawala Mawatha",
"officeAddressLine2": "Colombo 02",
"officeAddressLine3": "SRI LANKA",
"directorOfCustoms": "The Director of Customs",
"vehicleNo": "WPLO 9767",
"permitNo": "MV20231214006001 / Permit No. MP20240717017006",
"chiefSecurityOfficer": "Chief Security Officer",
"exportGate": "Export Gate",
"slpa": "SLPA",
"gateNo": "Gate No. 03",
"permissionText": "Please grant permission to pass these Customs entry papers to supply goods to the vessel",
"companyName": "Oceanic Maritime Solutions (Pvt) Ltd",
"subtitle": "LICENSED SHIPCHANDLERS",
"sectionCode": "SC/FORM/06",
"items": [
{
"quantity": 10,
"unit": "PCS",
"description": "Marine rope",
"rate": 12.5,
"total": 125,
"confirmOrderRate": ""
},
{
"quantity": 5,
"unit": "PCS",
"description": "Safety gloves",
"rate": 8,
"total": 40,
"confirmOrderRate": ""
}
]
}
}
```
### Credit Note
```json
{
"document": "CREDITNOTE",
"documentType": "pdf",
"documentData": {
"companyName": "GREEK-LANKA MARITIME SERVICES (PVT) LTD",
"companyAddressLine1": "No. 56/2, Dharmapala Mw, Kotte",
"companyAddressLine2": "Sri Jayewardenepura",
"companyAddressLine3": "Sri Lanka",
"companyPostal": "Postal Code : 10100",
"companyContact": "Tel:+94 11 2083206 / Mobile (24/7) : +94 777 232 271",
"companyBR": "BR No : PV 0022630",
"companyLicense": "License No : SA00317-2024",
"crnNo": "GLMS/COLOMBO/474/CRN01",
"date": "26/07/2024",
"billToName": "Master & Owner of MV TEAM VENTURES",
"billToDetails": "VRIDHI MARITIME SHIP MANAGEMENT & OPERATION LLC\nOffice No: 2004, The Prism Tower, Dubai\n20th Floor, Business Bay,\nDubai - PO Box 29583.",
"vesselName": "MV \"TEAM VENTURES\"",
"grt": "25,543 MT",
"port": "REPAIRS AT COLOMBO DOCK YARD",
"imoNumber": "9339765",
"nameOfAgent": "29/04/2024 AT 21:00 HRS.LT",
"portCountry": "COLOMBO / SRI LANKA",
"items": [
{
"description": "SIGN OFF C/E MR. DHANSINGH BHAURYAL AND M/S J/E MR. SENTHIL",
"amount": 50
},
{
"description": "VISA + Handling Charges - M/S M/S MR. SENTHIL",
"amount": 100
},
{
"description": "Transport from Airport to Dockyard",
"amount": 70
}
],
"totalAmount": 220,
"approvedBy": "GLMS Finance Dept.",
"approvedByLabel": "Approved by GLMS Finance Dept.",
"departmentLabel": "GLMS - Accounts Department"
}
}
```
}
```
---
### 2. Get Job Status
**Endpoint:** `GET /api/document/job/:jobId/status`
**URL:** `http://localhost:3070/api/document/job/1/status`
**Authentication:** Required ✓
**Authorization:** Required
**HTTP Status:** `200 OK`
#### Request Headers
```
Authorization: Bearer <JWT_TOKEN>
```
#### Response (Waiting/Active)
```json
{
"success": true,
"jobId": "1",
"state": "waiting",
"result": null,
"error": null,
"attempts": 0,
"stacktrace": []
}
```
#### Response (Completed)
```json
{
"success": true,
"jobId": "83",
"state": "completed",
"result": {
"fileName": "precost-1781614132061.pdf",
"mimeType": "application/pdf",
"size": 220008,
"s3Key": "uploads/16adf29b-c0c2-45f9-8808-d6e15cd3d755.pdf",
"documentId": "16adf29b-c0c2-45f9-8808-d6e15cd3d755"
},
"error": null,
"attempts": 1,
"stacktrace": []
}
```
#### Response (Failed)
```json
{
"success": true,
"jobId": "1",
"state": "failed",
"result": null,
"error": "Error message explaining the failure",
"attempts": 3,
"stacktrace": ["stack trace line 1", "stack trace line 2"]
}
```
#### Job States
| State | Meaning | Next State |
|-------|---------|-----------|
| `waiting` | Job queued, waiting for worker | `active` |
| `active` | Worker currently processing | `completed` or `failed` |
| `completed` | Job done, result available | (final) |
| `failed` | Job failed after 3 retries | (final) |
| `delayed` | Scheduled for later processing | `waiting` |
#### Error Responses
**404 Not Found:**
```json
{
"success": false,
"message": "Job not found"
}
```
---
### 3. Download Document
**Endpoint:** `GET /api/document/download/:uuid`
**URL:** `http://localhost:3070/api/document/download/16adf29b-c0c2-45f9-8808-d6e15cd3d755`
**Authentication:** Required ✓
**Authorization:** Required
**HTTP Status:** `200 OK`
#### Request Headers
```
Authorization: Bearer <JWT_TOKEN>
```
#### Response (Success)
Returns the binary file with appropriate headers:
| Header | Value |
|--------|-------|
| `Content-Type` | `application/pdf` or `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` |
| `Content-Disposition` | `attachment; filename="[fileName]"` |
**Response Body:** Binary file content
#### Error Responses
**400 Bad Request:**
```json
{
"success": false,
"message": "fileName is required"
}
```
**404 Not Found:**
```json
{
"success": false,
"message": "Document not found"
}
```
---
### 4. Cancel Job
**Endpoint:** `DELETE /api/document/job/:jobId`
**URL:** `http://localhost:3070/api/document/job/1`
**Authentication:** Required ✓
**Authorization:** Required
**HTTP Status:** `200 OK`
#### Request Headers
```
Authorization: Bearer <JWT_TOKEN>
```
#### Response (Success)
```json
{
"success": true,
"message": "Job cancelled successfully",
"jobId": "1"
}
```
#### Error Responses
**404 Not Found:**
```json
{
"success": false,
"message": "Job not found"
}
```
---
## Document Data Notes
The generator is tolerant of different request shapes and common field aliases. For example, it accepts both `documentData` and nested `data`, and it normalizes document names by removing spaces, underscores, and hyphens.
If a document does not use every field in a sample payload, you can omit the unused keys.
---
## Complete Workflow Example
### 1. Generate Document
```bash
curl -X POST http://localhost:3070/api/document/generate \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-d '{
"document": "precost",
"documentType": "pdf",
"documentData": {
"customerName": "Acme Corp",
"items": [
{"name": "Item 1", "description": "Test", "qty": 2, "unitPrice": 100}
]
}
}'
```
**Response:**
```json
{
"success": true,
"message": "Document generation started",
"jobId": "1"
}
```
### 2. Check Job Status
```bash
curl http://localhost:3070/api/document/job/1/status \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
```
**Polling Response (Still Processing):**
```json
{
"success": true,
"jobId": "1",
"state": "active"
}
```
**Polling Response (Completed):**
```json
{
"success": true,
"jobId": "1",
"state": "completed",
"result": {
"fileName": "precost-1715339340000.pdf"
}
}
```
### 3. Download File
```bash
curl http://localhost:3070/api/document/download/16adf29b-c0c2-45f9-8808-d6e15cd3d755 \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-o my-document.pdf
```
---
## JavaScript/Fetch Example
```javascript
async function generateAndDownloadDocument(token) {
// Step 1: Generate document
const generateRes = await fetch("/api/document/generate", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
document: "precost",
documentType: "pdf",
documentData: {
customerName: "Acme Corp",
items: [
{ name: "Item 1", description: "Test", qty: 2, unitPrice: 100 }
]
}
})
});
const { jobId } = await generateRes.json();
console.log("Job queued:", jobId);
// Step 2: Poll for completion
let isComplete = false;
let result = null;
while (!isComplete) {
const statusRes = await fetch(`/api/document/job/${jobId}/status`, {
headers: { "Authorization": `Bearer ${token}` }
});
const status = await statusRes.json();
console.log("Job state:", status.state);
if (status.state === "completed") {
result = status.result;
isComplete = true;
} else if (status.state === "failed") {
throw new Error(`Job failed: ${status.error}`);
} else {
// Wait 2 seconds before polling again
await new Promise(r => setTimeout(r, 2000));
}
}
// Step 3: Download the file
const downloadRes = await fetch(`/api/document/download/${result.fileName}`, {
headers: { "Authorization": `Bearer ${token}` }
});
const blob = await downloadRes.blob();
// Trigger browser download
const url = window.URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = result.fileName;
a.click();
window.URL.revokeObjectURL(url);
console.log("Download complete!");
}
```
---
## Important Notes
### Authentication & Authorization
- All endpoints require JWT token in `Authorization: Bearer <token>` header
- User must have one of these roles: `admin`, `management`, `team_head`, `user`
### Polling Strategy
- Poll status every 2-5 seconds
- Max recommended: 60 attempts (5 minutes timeout)
- Move to download immediately when state is `completed`
### Error Handling
- Jobs automatically retry up to 3 times with exponential backoff
- Failed jobs remain in queue for debugging
- Check `error` and `stacktrace` fields for failure details
### File Management
- Generated files stored in `/app/documents/`
- File names auto-generated: `{document}-{timestamp}.{ext}`
- Always sanitize fileName in download requests
### Supported Formats
- **PDF:** `documentType: "pdf"`
- **Excel:** `documentType: "excel"`
### Performance
- Worker concurrency: 2 concurrent jobs
- Timeout per job: 5 minutes
- Retry attempts: 3 with exponential backoff
---
## Common Response Codes
| Code | Meaning |
|------|---------|
| `202` | Accepted - Job queued successfully |
| `200` | OK - Successful operation |
| `400` | Bad Request - Invalid input |
| `401` | Unauthorized - Missing/invalid token |
| `403` | Forbidden - Insufficient permissions |
| `404` | Not Found - Job or file not found |
| `500` | Server Error - Internal error |
---
## Migration Notes
**If upgrading from synchronous API:**
**Old (Synchronous):**
- Request returned file immediately
- Long request timeout
- Blocking operation
**New (Asynchronous):**
- Request returns jobId immediately (202)
- Poll `/api/document/job/{jobId}/status` for progress
- Non-blocking, scalable architecture
- Automatic retries built-in
+137
View File
@@ -0,0 +1,137 @@
# Notification API
## Create New Notification
**Endpoint**
```
POST: http://localhost:3070/api/notification
```
**Request Body**
```json
{
"notificationHeadline": "System Maintenance",
"notificationDescription": "The system will be unavailable from 10 PM to 12 AM.",
"notificationType": "ANNOUNCEMENT"
}
```
**Respond**
```json
{
"success": true,
"message": "Notification created successfully",
"notification": {
"notification_id": "notif_1782553200000",
"notificationHeadline": "System Maintenance",
"notificationDescription": "The system will be unavailable from 10 PM to 12 AM.",
"notificationType": "ANNOUNCEMENT",
"dateCreated": "2026-06-27T05:30:00.000Z",
"isActive": true,
"updatedAt": "2026-06-27T05:30:00.000Z",
"createdAt": "2026-06-27T05:30:00.000Z"
}
}
```
## Get Announcements
**Endpoint**
```
GET: http://localhost:3070/api/notification/announcements
```
**Respond**
```json
{
"success": true,
"announcements": [
{
"notification_id": "notif_1782553200000",
"notificationHeadline": "System Maintenance",
"notificationDescription": "The system will be unavailable from 10 PM to 12 AM.",
"notificationType": "ANNOUNCEMENT",
"isActive": true,
"dateCreated": "2026-06-27T05:30:00.000Z",
"createdAt": "2026-06-27T05:30:00.000Z",
"updatedAt": "2026-06-27T05:30:00.000Z"
}
]
}
```
---
## Get User Notifications
**Endpoint**
```
GET: http://localhost:3070/api/notification/user/:userId
```
Example:
```
GET: http://localhost:3070/api/notification/user/usr_12345
```
**Respond**
```json
{
"success": true,
"notifications": [
{
"id": "userNotif_001",
"user_id": "usr_12345",
"notification_id": "notif_1782553200000",
"isRead": false,
"createdAt": "2026-06-27T05:30:00.000Z",
"updatedAt": "2026-06-27T05:30:00.000Z",
"notification": {
"notification_id": "notif_1782553200000",
"notificationHeadline": "System Maintenance",
"notificationDescription": "The system will be unavailable from 10 PM to 12 AM.",
"notificationType": "ANNOUNCEMENT",
"isActive": true
}
}
]
}
```
---
## Mark Notification As Read
**Endpoint**
```
PATCH: http://localhost:3070/api/notification/user/:userId/notification/:notificationId/read
```
Example:
```
PATCH: http://localhost:3070/api/notification/user/usr_12345/notification/notif_1782553200000/read
```
**Respond**
```json
{
"success": true,
"message": "Notification marked as read"
}
```
+755
View File
@@ -0,0 +1,755 @@
# Permission API
## Authentication
All endpoints require authentication token. Include in header:
```
Authorization: Bearer <token>
```
---
## Permission Endpoints
### Create Permission
**Endpoint**
```
POST: http://localhost:3070/api/permission
```
**Authorization**: Admin only
**Request Body**
```json
{
"permissionName": "Create Inquiry",
"permissionDescription": "Permission to create new inquiries",
"page": "inquiry",
"module": "inquiry_management",
"action": "create"
}
```
**Response (201)**
```json
{
"success": true,
"data": {
"permission_id": "inquiry_management.inquiry.create",
"permissionName": "Create Inquiry",
"permissionDescription": "Permission to create new inquiries",
"page": "inquiry",
"module": "inquiry_management",
"action": "create",
"createdAt": "2026-05-15T10:30:00Z",
"updatedAt": "2026-05-15T10:30:00Z"
}
}
```
---
### Bulk Create Permissions
**Endpoint**
```
POST: http://localhost:3070/api/permission/bulk
```
**Authorization**: Admin only
**Request Body**
```json
{
"permissions": [
{
"permissionName": "Create Inquiry",
"permissionDescription": "Permission to create new inquiries",
"page": "inquiry",
"module": "inquiry_management",
"action": "create"
},
{
"permissionName": "View Inquiry",
"permissionDescription": "Permission to view inquiries",
"page": "inquiry",
"module": "inquiry_management",
"action": "view"
},
{
"permissionName": "Edit Inquiry",
"permissionDescription": "Permission to edit inquiries",
"page": "inquiry",
"module": "inquiry_management",
"action": "edit"
}
]
}
```
**Response (201)**
```json
{
"success": true,
"data": {
"created": [
{
"permission_id": "inquiry_management.inquiry.create",
"permissionName": "Create Inquiry",
"permissionDescription": "Permission to create new inquiries",
"page": "inquiry",
"module": "inquiry_management",
"action": "create"
},
{
"permission_id": "inquiry_management.inquiry.view",
"permissionName": "View Inquiry",
"permissionDescription": "Permission to view inquiries",
"page": "inquiry",
"module": "inquiry_management",
"action": "view"
},
{
"permission_id": "inquiry_management.inquiry.edit",
"permissionName": "Edit Inquiry",
"permissionDescription": "Permission to edit inquiries",
"page": "inquiry",
"module": "inquiry_management",
"action": "edit"
}
],
"failed": []
}
}
```
---
### Get All Permissions
**Endpoint**
```
GET: http://localhost:3070/api/permission
```
**Response (200)**
```json
{
"success": true,
"data": [
{
"permission_id": "inquiry_management.inquiry.create",
"permissionName": "Create Inquiry",
"permissionDescription": "Permission to create new inquiries",
"page": "inquiry",
"module": "inquiry_management",
"action": "create"
},
{
"permission_id": "inquiry_management.inquiry.view",
"permissionName": "View Inquiry",
"permissionDescription": "Permission to view inquiries",
"page": "inquiry",
"module": "inquiry_management",
"action": "view"
}
]
}
```
---
### Get Permission by ID
**Endpoint**
```
GET: http://localhost:3070/api/permission/:permissionId
```
**Example**
```
GET: http://localhost:3070/api/permission/inquiry_management.inquiry.create
```
**Response (200)**
```json
{
"success": true,
"data": {
"permission_id": "inquiry_management.inquiry.create",
"permissionName": "Create Inquiry",
"permissionDescription": "Permission to create new inquiries",
"page": "inquiry",
"module": "inquiry_management",
"action": "create"
}
}
```
---
### Update Permission
**Endpoint**
```
PUT: http://localhost:3070/api/permission/:permissionId
```
**Authorization**: Admin only
**Request Body**
```json
{
"permissionName": "Create New Inquiry",
"permissionDescription": "Updated permission to create new inquiries"
}
```
**Response (200)**
```json
{
"success": true,
"data": {
"permission_id": "inquiry_management.inquiry.create",
"permissionName": "Create New Inquiry",
"permissionDescription": "Updated permission to create new inquiries",
"page": "inquiry",
"module": "inquiry_management",
"action": "create"
},
"message": "Permission updated successfully"
}
```
---
### Delete Permission
**Endpoint**
```
DELETE: http://localhost:3070/api/permission/:permissionId
```
**Authorization**: Admin only
**Response (200)**
```json
{
"success": true,
"message": "Permission deleted successfully"
}
```
---
## Role Endpoints
### Create Role
**Endpoint**
```
POST: http://localhost:3070/api/permission/roles
```
**Authorization**: Admin only
**Request Body**
```json
{
"roleName": "Inquiry Manager",
"roleDescription": "Role for managing inquiries"
}
```
**Response (201)**
```json
{
"success": true,
"data": {
"role_id": "role_1715843400000",
"roleName": "Inquiry Manager",
"roleDescription": "Role for managing inquiries",
"createdAt": "2026-05-15T10:30:00Z",
"updatedAt": "2026-05-15T10:30:00Z"
}
}
```
---
### Get All Roles
**Endpoint**
```
GET: http://localhost:3070/api/permission/roles
```
**Response (200)**
```json
{
"success": true,
"data": [
{
"role_id": "role_1715843400000",
"roleName": "Inquiry Manager",
"roleDescription": "Role for managing inquiries",
"rolePermissions": [
{
"rp_id": "rp_1715843500000",
"role_id": "role_1715843400000",
"permission_id": "inquiry_management.inquiry.create",
"permission": {
"permission_id": "inquiry_management.inquiry.create",
"permissionName": "Create Inquiry",
"page": "inquiry",
"module": "inquiry_management",
"action": "create"
}
}
]
}
]
}
```
---
### Get Role by ID
**Endpoint**
```
GET: http://localhost:3070/api/permission/roles/:roleId
```
**Example**
```
GET: http://localhost:3070/api/permission/roles/role_1715843400000
```
**Response (200)**
```json
{
"success": true,
"data": {
"role_id": "role_1715843400000",
"roleName": "Inquiry Manager",
"roleDescription": "Role for managing inquiries",
"rolePermissions": [
{
"rp_id": "rp_1715843500000",
"role_id": "role_1715843400000",
"permission_id": "inquiry_management.inquiry.create",
"permission": {
"permission_id": "inquiry_management.inquiry.create",
"permissionName": "Create Inquiry",
"page": "inquiry",
"module": "inquiry_management",
"action": "create"
}
}
]
}
}
```
---
### Update Role
**Endpoint**
```
PUT: http://localhost:3070/api/permission/roles/:roleId
```
**Authorization**: Admin only
**Request Body**
```json
{
"roleName": "Senior Inquiry Manager",
"roleDescription": "Updated role for managing inquiries"
}
```
**Response (200)**
```json
{
"success": true,
"data": {
"role_id": "role_1715843400000",
"roleName": "Senior Inquiry Manager",
"roleDescription": "Updated role for managing inquiries"
},
"message": "Role updated successfully"
}
```
---
### Delete Role
**Endpoint**
```
DELETE: http://localhost:3070/api/permission/roles/:roleId
```
**Authorization**: Admin only
**Response (200)**
```json
{
"success": true,
"message": "Role deleted successfully"
}
```
---
## Role-Permission Assignment Endpoints
### Assign Permission to Role
**Endpoint**
```
POST: http://localhost:3070/api/permission/roles/:roleId/permissions
```
**Authorization**: Admin only
**Request Body**
```json
{
"role_id": "role_1715843400000",
"permission_id": "inquiry_management.inquiry.create"
}
```
**Response (201)**
```json
{
"success": true,
"data": {
"rp_id": "rp_1715843500000",
"role_id": "role_1715843400000",
"permission_id": "inquiry_management.inquiry.create",
"createdAt": "2026-05-15T10:30:00Z",
"updatedAt": "2026-05-15T10:30:00Z"
}
}
```
---
### Bulk Assign Permissions to Role
**Endpoint**
```
POST: http://localhost:3070/api/permission/roles/permissions/bulk
```
**Authorization**: Admin only
**Request Body**
```json
{
"role_id": "role_1715843400000",
"permission_ids": [
"inquiry_management.inquiry.create",
"inquiry_management.inquiry.view",
"inquiry_management.inquiry.edit",
"vendor_management.vendor.view"
]
}
```
**Response (201)**
```json
{
"success": true,
"data": {
"created": [
{
"rp_id": "rp_1715843500001",
"role_id": "role_1715843400000",
"permission_id": "inquiry_management.inquiry.create"
},
{
"rp_id": "rp_1715843500002",
"role_id": "role_1715843400000",
"permission_id": "inquiry_management.inquiry.view"
},
{
"rp_id": "rp_1715843500003",
"role_id": "role_1715843400000",
"permission_id": "inquiry_management.inquiry.edit"
},
{
"rp_id": "rp_1715843500004",
"role_id": "role_1715843400000",
"permission_id": "vendor_management.vendor.view"
}
],
"failed": []
}
}
```
**Response with Failures (201)**
```json
{
"success": false,
"data": {
"created": [
{
"rp_id": "rp_1715843500001",
"role_id": "role_1715843400000",
"permission_id": "inquiry_management.inquiry.create"
}
],
"failed": [
{
"role_id": "role_1715843400000",
"permission_id": "inquiry_management.inquiry.view",
"error": "Role permission already exists"
},
{
"role_id": "role_1715843400000",
"permission_id": "nonexistent_permission",
"error": "Permission not found"
}
]
}
}
```
---
## User Permission Endpoints
### Get User Permissions
**Endpoint**
```
GET: http://localhost:3070/api/permission/users/:userId/permissions
```
**Example**
```
GET: http://localhost:3070/api/permission/users/usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4/permissions
```
**Response (200)**
```json
{
"success": true,
"data": [
{
"up_id": 1,
"user_id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4",
"permission_id": "inquiry_management.inquiry.create",
"permission": {
"permission_id": "inquiry_management.inquiry.create",
"permissionName": "Create Inquiry",
"page": "inquiry",
"module": "inquiry_management",
"action": "create"
}
}
]
}
```
---
### Create User Permission
**Endpoint**
```
POST: http://localhost:3070/api/permission/users/permissions
```
**Authorization**: Admin only
**Request Body**
```json
{
"user_id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4",
"permission_id": "inquiry_management.inquiry.create"
}
```
**Response (201)**
```json
{
"success": true,
"data": {
"up_id": 1,
"user_id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4",
"permission_id": "inquiry_management.inquiry.create",
"createdAt": "2026-05-15T10:30:00Z",
"updatedAt": "2026-05-15T10:30:00Z"
}
}
```
---
### Bulk Create User Permissions
**Endpoint**
```
POST: http://localhost:3070/api/permission/users/permissions/bulk
```
**Authorization**: Admin only
**Request Body**
```json
{
"user_id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4",
"permission_ids": [
"inquiry_management.inquiry.create",
"inquiry_management.inquiry.view",
"inquiry_management.inquiry.edit",
"vendor_management.vendor.view"
]
}
```
**Response (201)**
```json
{
"success": true,
"data": {
"created": [
{
"up_id": 1,
"user_id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4",
"permission_id": "inquiry_management.inquiry.create"
},
{
"up_id": 2,
"user_id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4",
"permission_id": "inquiry_management.inquiry.view"
},
{
"up_id": 3,
"user_id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4",
"permission_id": "inquiry_management.inquiry.edit"
},
{
"up_id": 4,
"user_id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4",
"permission_id": "vendor_management.vendor.view"
}
],
"failed": []
}
}
```
**Response with Failures (201)**
```json
{
"success": false,
"data": {
"created": [
{
"up_id": 1,
"user_id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4",
"permission_id": "inquiry_management.inquiry.create"
}
],
"failed": [
{
"user_id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4",
"permission_id": "inquiry_management.inquiry.view",
"error": "User permission already exists"
},
{
"user_id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4",
"permission_id": "nonexistent_permission",
"error": "Permission not found"
}
]
}
}
```
---
### Update User Permission
**Endpoint**
```
PUT: http://localhost:3070/api/permission/users/permissions/:upId
```
**Authorization**: Admin only
**Request Body**
```json
{
"permission_id": "inquiry_management.inquiry.edit"
}
```
**Response (200)**
```json
{
"success": true,
"data": {
"up_id": 1,
"user_id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4",
"permission_id": "inquiry_management.inquiry.edit"
},
"message": "User permission updated successfully"
}
```
---
### Delete User Permission
**Endpoint**
```
DELETE: http://localhost:3070/api/permission/users/permissions/:upId
```
**Authorization**: Admin only
**Response (200)**
```json
{
"success": true,
"message": "User permission deleted successfully"
}
```
---
## Error Responses
### Bad Request (400)
```json
{
"success": false,
"message": "Permission name, page, module, and action are required"
}
```
### Not Found (404)
```json
{
"success": false,
"message": "Permission not found"
}
```
### Internal Server Error (500)
```json
{
"success": false,
"message": "An error occurred while fetching permissions."
}
```
+253
View File
@@ -0,0 +1,253 @@
# Profile API
#### Get Profile Avatar
**Endpoint**
```
GET: http://localhost:3070/api/profile/avatar/:userId
```
**Path Parameters**
| Parameter | Type | Required | Description |
|-----------|--------|----------|-----------------|
| userId | string | Yes | The user ID |
**Headers**
```
Authorization: Bearer <token>
```
**Response**
```json
{
"success": true,
"data": {
"userId": "usr_763107d9-b56f-4b81-9d86-1889f98a6e6c",
"profilePictureUrl": "https://signed-s3-url.com/profile-picture.jpg"
}
}
```
**Error Responses**
```json
{
"success": false,
"message": "Profile not found",
"error": "Profile not found"
}
```
```json
{
"success": false,
"message": "Internal server error",
"error": "error message"
}
```
---
#### Get Profile Background Image
**Endpoint**
```
GET: http://localhost:3070/api/profile/background/:userId
```
**Path Parameters**
| Parameter | Type | Required | Description |
|-----------|--------|----------|-----------------|
| userId | string | Yes | The user ID |
**Headers**
```
Authorization: Bearer <token>
```
**Response**
```json
{
"success": true,
"data": {
"userId": "usr_763107d9-b56f-4b81-9d86-1889f98a6e6c",
"backgroundImageUrl": "https://signed-s3-url.com/background-image.jpg"
}
}
```
**Error Responses**
```json
{
"success": false,
"message": "Profile not found",
"error": "Profile not found"
}
```
```json
{
"success": false,
"message": "Internal server error",
"error": "error message"
}
```
---
#### Profile Object Structure
The Profile object contains the following fields:
| Field | Type | Description |
|--------------------|---------|----------------------------------|
| profile_id | string | Unique profile identifier |
| user_id | string | Associated user ID |
| theme | string | User theme preference (light/dark) |
| notificationsEnabled | boolean | Notification settings |
| profilePicture_id | string | Upload ID for profile picture |
| backgroundImage_id | string | Upload ID for background image |
| dob | date | Date of birth |
| phone_number | string | Phone number |
| createdAt | datetime| Profile creation timestamp |
| updatedAt | datetime| Profile last update timestamp |
---
#### Request Password Reset
**Endpoint**
```
POST: http://localhost:3070/api/profile/req-reset-password
```
**Request Body**
```json
{
"email": "sathira.nirmal@gmail.com"
}
```
**Response**
```json
{
"success": true,
"message": "If an account exists with this email address, a password reset email has been sent."
}
```
**Description**
This endpoint generates a password reset token and sends a reset link to the user's email. The reset link will be sent in email format:
```
http://localhost:3000/reset-password?token=TOKEN_HERE&email=EMAIL_HERE
```
Example reset link:
```
http://localhost:3000/reset-password?token=aa27def4222adedcf72a417dfc40b69cd01f8bdb07f37b324a8501f3bdb37e44&email=sathira.nirmal%40gmail.com
```
**Notes**
- This endpoint does not require authentication
- No error is returned if email doesn't exist (for security reasons)
- Token expires after the time specified in `PASSWORD_RESET_EXPIRY_TIME` environment variable (default: 10 minutes)
---
#### Reset Password
**Endpoint**
```
POST: http://localhost:3070/api/profile/reset-password
```
**Request Body**
```json
{
"email": "sathira.nirmal@gmail.com",
"token": "168e5d9d4dfb124571f412c5521874e8440a0f9e4c151fa24518ae57ca1c4f8f",
"newPassword": "niolla"
}
```
**Response**
```json
{
"success": true,
"message": "Password reset successfully."
}
```
**Error Responses**
```json
{
"success": false,
"message": "Invalid email address."
}
```
```json
{
"success": false,
"message": "Invalid or expired token."
}
```
**Description**
This endpoint resets the user's password using a valid reset token. The token must be obtained from the password reset email.
**Notes**
- This endpoint does not require authentication
- Token must be valid and not expired
- New password will replace the old password immediately
---
---
#### Authentication
All endpoints require authentication token unless otherwise noted. Include the Bearer token in the Authorization header:
```
Authorization: Bearer <your_jwt_token>
```
**Authentication Required:**
- GET /avatar/:userId
- GET /background/:userId
- POST /change-password
**No Authentication Required:**
- POST /req-reset-password
- POST /reset-password
#### Authorization
Access levels for profile endpoints:
- **GET /avatar/:userId** - Accessible by: admin, management, team_head, user
- **GET /background/:userId** - Accessible by: admin, management, team_head, user
- **POST /change-password** - Accessible by: admin, management, team_head, user (authenticated users)
+61
View File
@@ -0,0 +1,61 @@
# Upload API
#### Upload A File
**Endpoint**
```
POST: http://localhost:3070/api/upload
```
**Request Body**
```
Key: file
Value: uploadfile.pdf / avatar.png
```
**Respond**
```json
{
"success": true,
"message": "File uploaded successfully",
"data": {
"id": 4,
"file_path": "uploads/1f642d1d-dc79-423f-aad0-36df9938c1ff.pdf",
"file_type": "application/pdf",
"file_size": 15640,
"original_name": "SamplePDF.pdf",
"use_for": "vendor_documents",
"uploaded_by": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4",
"updatedAt": "2026-04-04T19:44:03.252Z",
"createdAt": "2026-04-04T19:44:03.252Z",
"file_url": "https://oceanic-bucket.s3.ap-southeast-1.amazonaws.com/uploads/1f642d1d-dc79-423f-aad0-36df9938c1ff.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIASP2AP6EU76EJ63PD%2F20260404%2Fap-southeast-1%2Fs3%2Faws4_request&X-Amz-Date=20260404T194403Z&X-Amz-Expires=3600&X-Amz-Signature=f9ecb283dfe206e87fa9bbd9f59194dfca4cdb11d8bb02f0ae5abd89db7ddc04&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject"
}
}
```
#### Get Uploaded File
**Endpoint**
```
POST: http://localhost:3070/api/upload/signed-url/:id
```
Example: http://localhost:3070/api/upload/signed-url/3
**Respond**
```json
{
"success": true,
"message": "File URL retrieved successfully",
"data": {
"id": 3,
"file_url": "https://oceanic-bucket.s3.ap-southeast-1.amazonaws.com/uploads/9c441266-c1ee-4813-9b81-cf13276535c9.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIASP2AP6EU76EJ63PD%2F20260404%2Fap-southeast-1%2Fs3%2Faws4_request&X-Amz-Date=20260404T195205Z&X-Amz-Expires=3600&X-Amz-Signature=de8f9a4658b2802af4230f0ebba25511fb57e75cce4be75aa2e34d40a771bffb&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject"
}
}
```
+223
View File
@@ -0,0 +1,223 @@
# User API
#### Create New User
**Endpoint**
```
POST: http://localhost:3070/api/user
```
**Request Body**
```json
{
"firstName": "Jhon",
"lastName": "Doe",
"email": "kalanajayasekara@niolla.lk",
"role": "System Developer",
"accountType": "admin",
"department": "IT Department"
}
```
**Respond**
```json
{
"success": true,
"message": "User Created Successfully",
"data": {
"id": "usr_763107d9-b56f-4b81-9d86-1889f98a6e6c",
"firstName": "Jhon",
"lastName": "Doe",
"email": "kalanajayasekara@niolla.lk",
"accountType": "admin",
"role": "System Developer",
"department": "IT Department"
}
}
```
#### Get All Users
**Endpoint**
```
GET: http://localhost:3070/api/user
```
**Respond**
```json
{
"success": true,
"data": [
{
"id": "usr_b00045f7-aafb-482a-8587-d28beb5195ec",
"firstName": "Kalana",
"lastName": "Jayasekara",
"email": "kalanamanupiya32@gmail.com",
"accountType": "admin",
"role": "Manager",
"department": "Operations",
"createdAt": "2026-02-14T19:14:25.000Z",
"updatedAt": "2026-02-14T19:14:25.000Z"
},
{
"id": "usr_763107d9-b56f-4b81-9d86-1889f98a6e6c",
"firstName": "Kalana",
"lastName": "Manupiya",
"email": "kalanajayasekara@niolla.lk",
"accountType": "admin",
"role": "System Developer",
"department": "IT Department",
"createdAt": "2026-02-12T19:32:15.000Z",
"updatedAt": "2026-02-12T19:32:15.000Z"
},
{
"id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4",
"firstName": "Jhon",
"lastName": "Doe",
"email": "sathira@niolla.lk",
"accountType": "admin",
"role": "System Developer",
"department": "IT Department",
"createdAt": "2026-02-12T19:24:04.000Z",
"updatedAt": "2026-02-12T19:24:04.000Z"
}
],
"pagination": {
"totalUsers": 3,
"totalPages": 1,
"currentPage": 1,
"pageSize": 20
}
}
```
This API uses Pagination:
```
GET /user?page=1
GET /user?page=2
GET /user?page=3
```
#### Get User by ID
**Endpoint**
```
GET: http://localhost:3070/api/user/:id
```
Ex: http://localhost:3070/api/user/usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4
**Response**
```json
{
"success": true,
"data": {
"id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4",
"firstName": "Sathira",
"lastName": "Sri Sathsara",
"email": "sathira@niolla.lk",
"accountType": "admin",
"role": "System Developer",
"department": "IT Department",
"createdAt": "2026-02-12T19:24:04.000Z",
"updatedAt": "2026-02-14T21:33:19.751Z",
"profile": {
"profile_id": "prof_a1b2c3d4-e5f6-7890-1234-567890abcdef",
"user_id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4",
"theme": "light",
"notificationsEnabled": true,
"profilePicture_id": "up_xyz789",
"backgroundImage_id": "up_abc123",
"dob": "1995-03-15T00:00:00.000Z",
"phone_number": "+1 234-567-8900",
"createdAt": "2026-02-12T19:24:04.000Z",
"updatedAt": "2026-02-12T19:24:04.000Z"
}
}
}
```
#### Update User
**Endpoint**
```
PATCH: http://localhost:3070/api/user/:id
```
Ex: http://localhost:3070/api/user/usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4
**Request Body**
Can send single field or multiple fields. The following fields can be updated:
```json
{
"firstName": "Sathira",
"lastName": "Sri Sathsara",
"email": "sathira@niolla.lk",
"role": "System Developer",
"roleID": "role_123",
"accountType": "admin",
"department": "IT Department",
"theme": "dark",
"notificationsEnabled": false,
"profilePicture_id": "up_profile_123",
"backgroundImage_id": "up_background_456",
"dob": "1995-03-15",
"phone_number": "+1 234-567-8900"
}
```
**Response**
```json
{
"success": true,
"message": "User updated successfully",
"data": {
"id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4",
"firstName": "Sathira",
"lastName": "Sri Sathsara",
"email": "sathira@niolla.lk",
"accountType": "admin",
"role": "System Developer",
"department": "IT Department",
"profile": {
"theme": "dark",
"notificationsEnabled": false,
"profilePicture_id": "up_profile_123",
"backgroundImage_id": "up_background_456",
"dob": "1995-03-15T00:00:00.000Z",
"phone_number": "+1 234-567-8900"
}
}
}
```
#### Delete User
**Endpoint**
```
DELETE: http://localhost:3070/api/user/:id
```
Ex: http://localhost:3070/api/user/usr_b00045f7-aafb-482a-8587-d28beb5195ec
**Respond**
```json
{
"success": true,
"message": "User deleted successfully"
}
```
+113
View File
@@ -0,0 +1,113 @@
# User Activity API
#### Get All Users Activies
**Endpoint**
```
POST: http://localhost:3070/api/activity
```
**Respond**
```json
{
"success": true,
"message": "User activities retrieved successfully",
"data": [
{
"id": 3,
"user_id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4",
"username": "Sathira",
"activity_description": "Created vendor with ID: vnd_009270f9-e8ef-42d9-a3e4-5398c9466972",
"activity_type": "CREATE_VENDOR",
"module": "Vendor Management",
"activity_time": "2026-03-30T19:39:45.000Z",
"activity_date": "2026-03-30",
"createdAt": "2026-03-30T19:39:45.000Z",
"updatedAt": "2026-03-30T19:39:45.000Z"
},
{
"id": 2,
"user_id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4",
"username": "Sathira",
"activity_description": "Created vendor with ID: vnd_95018101-b539-45a4-b9a6-4551cecda990",
"activity_type": "CREATE_VENDOR",
"module": "Vendor Management",
"activity_time": "2026-03-30T19:17:58.000Z",
"activity_date": "2026-03-30",
"createdAt": "2026-03-30T19:17:58.000Z",
"updatedAt": "2026-03-30T19:17:58.000Z"
},
{
"id": 1,
"user_id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4",
"username": "Sathira",
"activity_description": "Created vendor with ID: vnd_c666b2cd-36fe-46db-aecd-18ec7677ccff",
"activity_type": "CREATE_VENDOR",
"module": null,
"activity_time": "2026-03-30T19:16:41.000Z",
"activity_date": "2026-03-30",
"createdAt": "2026-03-30T19:16:41.000Z",
"updatedAt": "2026-03-30T19:16:41.000Z"
}
]
}
```
#### Get Activies by User ID
**Endpoint**
```
POST: http://localhost:3070/api/activity/user/:userID
```
Example: http://localhost:3070/api/activity/user/usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4
**Respond**
```json
{
"success": true,
"message": "User activities retrieved successfully",
"data": [
{
"id": 3,
"user_id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4",
"username": "Sathira",
"activity_description": "Created vendor with ID: vnd_009270f9-e8ef-42d9-a3e4-5398c9466972",
"activity_type": "CREATE_VENDOR",
"module": "Vendor Management",
"activity_time": "2026-03-30T19:39:45.000Z",
"activity_date": "2026-03-30",
"createdAt": "2026-03-30T19:39:45.000Z",
"updatedAt": "2026-03-30T19:39:45.000Z"
},
{
"id": 2,
"user_id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4",
"username": "Sathira",
"activity_description": "Created vendor with ID: vnd_95018101-b539-45a4-b9a6-4551cecda990",
"activity_type": "CREATE_VENDOR",
"module": "Vendor Management",
"activity_time": "2026-03-30T19:17:58.000Z",
"activity_date": "2026-03-30",
"createdAt": "2026-03-30T19:17:58.000Z",
"updatedAt": "2026-03-30T19:17:58.000Z"
},
{
"id": 1,
"user_id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4",
"username": "Sathira",
"activity_description": "Created vendor with ID: vnd_c666b2cd-36fe-46db-aecd-18ec7677ccff",
"activity_type": "CREATE_VENDOR",
"module": null,
"activity_time": "2026-03-30T19:16:41.000Z",
"activity_date": "2026-03-30",
"createdAt": "2026-03-30T19:16:41.000Z",
"updatedAt": "2026-03-30T19:16:41.000Z"
}
]
}
```
File diff suppressed because one or more lines are too long
+119
View File
@@ -0,0 +1,119 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Docs Home</title>
<style>
body {
margin: 0;
background: #0d1117;
color: #c9d1d9;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
.container {
text-align: center;
}
h1 {
margin-bottom: 40px;
}
.card-container {
display: flex;
gap: 30px;
}
.card {
background: #161b22;
border: 1px solid #30363d;
padding: 30px;
border-radius: 10px;
cursor: pointer;
width: 220px;
transition: 0.2s;
}
.card:hover {
background: #21262d;
transform: translateY(-5px);
}
.card h2 {
margin-bottom: 10px;
}
.card p {
font-size: 14px;
color: #8b949e;
}
.logout {
position: absolute;
top: 20px;
right: 20px;
cursor: pointer;
color: #f85149;
}
</style>
</head>
<body>
<div class="logout" onclick="logout()">Logout</div>
<div class="container">
<h1>Documentation Portal</h1>
<div class="card-container">
<div class="card" onclick="goDocs()">
<h2>📘 API Documentation</h2>
<p>View markdown API docs</p>
</div>
<div class="card" onclick="goCollection()">
<h2>📦 API Collections</h2>
<p>Interactive API testing</p>
</div>
</div>
</div>
<script>
// 🔐 check if user is logged in
async function checkAuth() {
const res = await fetch('/api/docs/markdown-files', {
credentials: "include"
});
if (!res.ok) {
window.location.href = "/Documentation/index.html";
}
}
function goDocs() {
window.location.href = "/Documentation/markdown.html";
}
function goCollection() {
window.location.href = "/Documentation/api-collection.html";
}
async function logout() {
await fetch('/api/docs/logout', {
method: "POST",
credentials: "include"
});
window.location.href = "/Documentation/index.html";
}
checkAuth();
</script>
</body>
</html>
+134
View File
@@ -0,0 +1,134 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Docs Login</title>
<style>
body {
margin: 0;
background: #0d1117;
color: #c9d1d9;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
.login-container {
background: #161b22;
padding: 40px;
border-radius: 12px;
border: 1px solid #30363d;
width: 320px;
box-shadow: 0 0 20px rgba(0,0,0,0.5);
}
h2 {
text-align: center;
margin-bottom: 25px;
}
.input-group {
margin-bottom: 15px;
}
input {
width: 90%;
padding: 10px;
border-radius: 6px;
border: 1px solid #30363d;
background: #0d1117;
color: #c9d1d9;
outline: none;
font-size: 14px;
}
input:focus {
border-color: #58a6ff;
}
button {
width: 100%;
padding: 10px;
background: #238636;
border: none;
border-radius: 6px;
color: white;
font-weight: 600;
cursor: pointer;
transition: 0.2s;
}
button:hover {
background: #2ea043;
}
.error {
margin-top: 10px;
color: #f85149;
font-size: 13px;
text-align: center;
}
.footer {
margin-top: 20px;
text-align: center;
font-size: 12px;
color: #8b949e;
}
</style>
</head>
<body>
<div class="login-container">
<h2>Docs Login</h2>
<div class="input-group">
<input id="username" placeholder="Username">
</div>
<div class="input-group">
<input id="password" type="password" placeholder="Password">
</div>
<button onclick="login()">Login</button>
<div id="error" class="error"></div>
<div class="footer">
Internal Documentation Access
</div>
</div>
<script>
async function login() {
const errorDiv = document.getElementById("error");
errorDiv.textContent = "";
const res = await fetch('/api/docs/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: document.getElementById('username').value,
password: document.getElementById('password').value
}),
credentials: "include"
});
if (res.ok) {
window.location.href = "/Documentation/home.html";
} else {
errorDiv.textContent = "Invalid username or password";
}
}
</script>
</body>
</html>
+209
View File
@@ -0,0 +1,209 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Markdown Documentation</title>
<!-- Markdown parser -->
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<!-- GitHub style -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/github-markdown-css/github-markdown-dark.min.css">
<!-- Highlight.js -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/highlight.js/styles/github-dark.min.css">
<script src="https://cdn.jsdelivr.net/npm/highlight.js/lib/common.min.js"></script>
<style>
body {
margin: 0;
background: #0d1117;
color: #c9d1d9;
display: flex;
height: 100vh;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial;
}
.sidebar {
width: 280px;
background: #161b22;
border-right: 1px solid #30363d;
padding: 20px;
overflow-y: auto;
}
.viewer-wrapper {
flex: 1;
overflow-y: auto;
padding: 20px;
}
.markdown-body {
max-width: 900px;
margin: auto;
}
input {
width: 80%;
padding: 8px;
margin-bottom: 10px;
background: #0d1117;
border: 1px solid #30363d;
color: white;
border-radius: 6px;
}
li {
padding: 8px;
cursor: pointer;
border-radius: 6px;
}
li:hover {
background: #21262d;
}
.highlight {
background: yellow;
color: black;
}
.search-controls {
display: flex;
gap: 5px;
margin-bottom: 10px;
}
button {
padding: 5px 10px;
cursor: pointer;
background: #21262d;
border: none;
color: white;
border-radius: 5px;
}
</style>
</head>
<body>
<div class="sidebar">
<h3>Docs</h3>
<!-- File search -->
<input type="text" id="fileSearch" placeholder="Search files..." onkeyup="filterFiles()" />
<!-- Content search -->
<input type="text" id="contentSearch" placeholder="Search in document..." onkeyup="searchInContent()" />
<div class="search-controls">
<button onclick="prevMatch()">⬆</button>
<button onclick="nextMatch()">⬇</button>
</div>
<ul id="fileList"></ul>
</div>
<div class="viewer-wrapper">
<div id="viewer" class="markdown-body">
<h2>Select a file</h2>
</div>
</div>
<script>
let allFiles = [];
let matches = [];
let currentMatchIndex = 0;
async function loadFiles() {
const res = await fetch('/api/docs/markdown-files', {
credentials: "include"
});
const result = await res.json();
allFiles = result.data;
renderList(allFiles);
}
function renderList(files) {
const list = document.getElementById('fileList');
list.innerHTML = "";
files.forEach(file => {
const li = document.createElement('li');
li.textContent = file;
li.onclick = () => loadMarkdown(file);
list.appendChild(li);
});
}
function filterFiles() {
const q = document.getElementById('fileSearch').value.toLowerCase();
renderList(allFiles.filter(f => f.toLowerCase().includes(q)));
}
async function loadMarkdown(file) {
const res = await fetch(`/api/docs/view/${file}`, {
credentials: "include"
});
const text = await res.text();
const html = marked.parse(text);
const viewer = document.getElementById('viewer');
viewer.innerHTML = html;
document.querySelectorAll('pre code').forEach(block => {
hljs.highlightElement(block);
});
matches = [];
currentMatchIndex = 0;
}
function searchInContent() {
const query = document.getElementById('contentSearch').value;
const viewer = document.getElementById('viewer');
if (!query) {
// reset
viewer.innerHTML = viewer.textContent;
return;
}
const regex = new RegExp(`(${query})`, 'gi');
viewer.innerHTML = viewer.innerHTML.replace(/<mark class="highlight">(.*?)<\/mark>/g, '$1');
viewer.innerHTML = viewer.innerHTML.replace(regex, '<mark class="highlight">$1</mark>');
matches = Array.from(document.querySelectorAll('.highlight'));
currentMatchIndex = 0;
if (matches.length) scrollToMatch(0);
}
function scrollToMatch(index) {
matches[index].scrollIntoView({
behavior: "smooth",
block: "center"
});
}
function nextMatch() {
if (!matches.length) return;
currentMatchIndex = (currentMatchIndex + 1) % matches.length;
scrollToMatch(currentMatchIndex);
}
function prevMatch() {
if (!matches.length) return;
currentMatchIndex = (currentMatchIndex - 1 + matches.length) % matches.length;
scrollToMatch(currentMatchIndex);
}
loadFiles();
</script>
</body>
</html>