first commit
This commit is contained in:
@@ -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"
|
||||
}
|
||||
```
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -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."
|
||||
}
|
||||
```
|
||||
@@ -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)
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -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"
|
||||
}
|
||||
```
|
||||
@@ -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
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user