setup
This commit is contained in:
@@ -7,7 +7,7 @@ This project uses ElysiaJS integrated with Next.js App Router to create high-per
|
||||
## Base URL
|
||||
|
||||
```
|
||||
http://localhost:3001
|
||||
http://localhost:3000
|
||||
```
|
||||
|
||||
## Endpoints
|
||||
@@ -32,19 +32,19 @@ GET /api/customers/:branch
|
||||
1. Get all customers from branch-01:
|
||||
|
||||
```bash
|
||||
curl http://localhost:3001/api/customers/branch-01
|
||||
curl http://localhost:3000/api/customers/branch-01
|
||||
```
|
||||
|
||||
2. Get active customers from branch-02:
|
||||
|
||||
```bash
|
||||
curl "http://localhost:3001/api/customers/branch-02?status=active"
|
||||
curl "http://localhost:3000/api/customers/branch-02?status=active"
|
||||
```
|
||||
|
||||
3. Get pending customers from head-office:
|
||||
|
||||
```bash
|
||||
curl "http://localhost:3001/api/customers/head-office?status=pending"
|
||||
curl "http://localhost:3000/api/customers/head-office?status=pending"
|
||||
```
|
||||
|
||||
**Response Format:**
|
||||
@@ -117,13 +117,13 @@ GET /api/quotations/:branch
|
||||
1. Get all quotations from branch-01:
|
||||
|
||||
```bash
|
||||
curl http://localhost:3001/api/quotations/branch-01
|
||||
curl http://localhost:3000/api/quotations/branch-01
|
||||
```
|
||||
|
||||
2. Get sent quotations from head-office:
|
||||
|
||||
```bash
|
||||
curl "http://localhost:3001/api/quotations/head-office?status=sent"
|
||||
curl "http://localhost:3000/api/quotations/head-office?status=sent"
|
||||
```
|
||||
|
||||
**Response Format:**
|
||||
@@ -181,6 +181,422 @@ DELETE /api/quotations/:branch/:id
|
||||
|
||||
---
|
||||
|
||||
### Master Options API
|
||||
|
||||
#### Get All Master Options
|
||||
|
||||
```
|
||||
GET /api/master-options
|
||||
```
|
||||
|
||||
**Query Parameters:**
|
||||
|
||||
- `category` (optional): Filter by category (e.g., `customer_type`, `payment_method`, `industry`)
|
||||
- `isActive` (optional): Filter by active status (`true` or `false`)
|
||||
- `search` (optional): Search in code, nameTh, or nameEn
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
curl "http://localhost:3000/api/master-options?category=customer_type&isActive=true"
|
||||
```
|
||||
|
||||
**Response Format:**
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": [
|
||||
{
|
||||
"id": "opt-001",
|
||||
"branchId": "branch-01",
|
||||
"category": "customer_type",
|
||||
"code": "CORPORATE",
|
||||
"nameTh": "องค์กร/บริษัท",
|
||||
"nameEn": "Corporate",
|
||||
"descriptionTh": "ลูกค้าประเภทองค์กร",
|
||||
"descriptionEn": "Corporate customers",
|
||||
"isActive": true,
|
||||
"createdAt": "2024-01-15T09:00:00Z",
|
||||
"updatedAt": "2024-01-15T09:00:00Z"
|
||||
}
|
||||
],
|
||||
"count": 1,
|
||||
"message": "Found 1 master option(s)"
|
||||
}
|
||||
```
|
||||
|
||||
#### Get Single Master Option
|
||||
|
||||
```
|
||||
GET /api/master-options/:id
|
||||
```
|
||||
|
||||
#### Create Master Option
|
||||
|
||||
```
|
||||
POST /api/master-options
|
||||
```
|
||||
|
||||
**Request Body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"category": "customer_type",
|
||||
"code": "INDIVIDUAL",
|
||||
"nameTh": "บุคคลธรรมดา",
|
||||
"nameEn": "Individual",
|
||||
"descriptionTh": "ลูกค้ารายบุคคล",
|
||||
"descriptionEn": "Individual customers"
|
||||
}
|
||||
```
|
||||
|
||||
#### Update Master Option
|
||||
|
||||
```
|
||||
PUT /api/master-options/:id
|
||||
```
|
||||
|
||||
#### Delete Master Option
|
||||
|
||||
```
|
||||
DELETE /api/master-options/:id
|
||||
```
|
||||
|
||||
#### Toggle Active Status
|
||||
|
||||
```
|
||||
PATCH /api/master-options/:id/toggle
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Locations API
|
||||
|
||||
#### Get All Locations
|
||||
|
||||
```
|
||||
GET /api/locations
|
||||
```
|
||||
|
||||
**Query Parameters:**
|
||||
|
||||
- `type` (optional): Filter by location type (`country`, `province`, `district`, `subdistrict`)
|
||||
- `parentId` (optional): Filter by parent location ID
|
||||
- `search` (optional): Search in code, nameTh, or nameEn
|
||||
- `isActive` (optional): Filter by active status
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
curl "http://localhost:3000/api/locations?type=province&search=กรุงเทพ"
|
||||
```
|
||||
|
||||
**Response Format:**
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": [
|
||||
{
|
||||
"id": "loc-001",
|
||||
"branchId": "head-office",
|
||||
"code": "TH-10",
|
||||
"nameTh": "กรุงเทพมหานคร",
|
||||
"nameEn": "Bangkok",
|
||||
"type": "province",
|
||||
"parentId": "country-th-id",
|
||||
"isActive": true,
|
||||
"createdAt": "2024-01-15T09:00:00Z",
|
||||
"updatedAt": "2024-01-15T09:00:00Z"
|
||||
}
|
||||
],
|
||||
"count": 1,
|
||||
"message": "Found 1 location(s)"
|
||||
}
|
||||
```
|
||||
|
||||
#### Get Locations by Type
|
||||
|
||||
```
|
||||
GET /api/locations/type/:type
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- `type` (path parameter): `country`, `province`, `district`, or `subdistrict`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
curl http://localhost:3000/api/locations/type/province
|
||||
```
|
||||
|
||||
#### Get Single Location
|
||||
|
||||
```
|
||||
GET /api/locations/:id
|
||||
```
|
||||
|
||||
#### Create Location
|
||||
|
||||
```
|
||||
POST /api/locations
|
||||
```
|
||||
|
||||
**Request Body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "TH-10",
|
||||
"nameTh": "กรุงเทพมหานคร",
|
||||
"nameEn": "Bangkok",
|
||||
"type": "province",
|
||||
"parentId": "country-th-id"
|
||||
}
|
||||
```
|
||||
|
||||
#### Update Location
|
||||
|
||||
```
|
||||
PUT /api/locations/:id
|
||||
```
|
||||
|
||||
#### Delete Location
|
||||
|
||||
```
|
||||
DELETE /api/locations/:id
|
||||
```
|
||||
|
||||
#### Toggle Active Status
|
||||
|
||||
```
|
||||
PATCH /api/locations/:id/toggle
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Industrial Estates API
|
||||
|
||||
#### Get All Industrial Estates
|
||||
|
||||
```
|
||||
GET /api/industrial-estates
|
||||
```
|
||||
|
||||
**Query Parameters:**
|
||||
|
||||
- `locationId` (optional): Filter by location ID
|
||||
- `isActive` (optional): Filter by active status
|
||||
- `search` (optional): Search in code, nameTh, or nameEn
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
curl "http://localhost:3000/api/industrial-estates?locationId=th-10&isActive=true"
|
||||
```
|
||||
|
||||
**Response Format:**
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": [
|
||||
{
|
||||
"id": "ie-001",
|
||||
"branchId": "head-office",
|
||||
"code": "BPL",
|
||||
"nameTh": "นิคมอุตสาหกรรมบางพลี",
|
||||
"nameEn": "Bangpoo Industrial Estate",
|
||||
"locationId": "th-10",
|
||||
"latitude": 13.5991,
|
||||
"longitude": 100.7015,
|
||||
"isActive": true,
|
||||
"createdAt": "2024-01-15T09:00:00Z",
|
||||
"updatedAt": "2024-01-15T09:00:00Z"
|
||||
}
|
||||
],
|
||||
"count": 1,
|
||||
"message": "Found 1 industrial estate(s)"
|
||||
}
|
||||
```
|
||||
|
||||
#### Get Industrial Estates by Location
|
||||
|
||||
```
|
||||
GET /api/industrial-estates/location/:locationId
|
||||
```
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
curl http://localhost:3000/api/industrial-estates/location/th-10
|
||||
```
|
||||
|
||||
#### Get Single Industrial Estate
|
||||
|
||||
```
|
||||
GET /api/industrial-estates/:id
|
||||
```
|
||||
|
||||
#### Create Industrial Estate
|
||||
|
||||
```
|
||||
POST /api/industrial-estates
|
||||
```
|
||||
|
||||
**Request Body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "BPL",
|
||||
"nameTh": "นิคมอุตสาหกรรมบางพลี",
|
||||
"nameEn": "Bangpoo Industrial Estate",
|
||||
"locationId": "th-10",
|
||||
"latitude": 13.5991,
|
||||
"longitude": 100.7015
|
||||
}
|
||||
```
|
||||
|
||||
#### Update Industrial Estate
|
||||
|
||||
```
|
||||
PUT /api/industrial-estates/:id
|
||||
```
|
||||
|
||||
#### Delete Industrial Estate
|
||||
|
||||
```
|
||||
DELETE /api/industrial-estates/:id
|
||||
```
|
||||
|
||||
#### Toggle Active Status
|
||||
|
||||
```
|
||||
PATCH /api/industrial-estates/:id/toggle
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Audit Logs API
|
||||
|
||||
**Note:** This API requires Admin/Superadmin/Auditor access level.
|
||||
|
||||
#### Get All Audit Logs
|
||||
|
||||
```
|
||||
GET /api/audit-logs
|
||||
```
|
||||
|
||||
**Query Parameters:**
|
||||
|
||||
- `startDate` (optional): Filter logs from this date (ISO 8601 format)
|
||||
- `endDate` (optional): Filter logs until this date (ISO 8601 format)
|
||||
- `action` (optional): Filter by action type (`CREATE`, `UPDATE`, `DELETE`, `READ`)
|
||||
- `entityType` (optional): Filter by entity type (`customer`, `quotation`, `location`, etc.)
|
||||
- `limit` (optional): Number of results to return (default: 50)
|
||||
- `offset` (optional): Number of results to skip (for pagination)
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
curl "http://localhost:3000/api/audit-logs?action=CREATE&limit=10"
|
||||
```
|
||||
|
||||
**Response Format:**
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": [
|
||||
{
|
||||
"id": "audit-001",
|
||||
"branchId": "branch-01",
|
||||
"userId": "user-123",
|
||||
"actorId": "user-123",
|
||||
"entityType": "customer",
|
||||
"entityId": "cust-001",
|
||||
"action": "CREATE",
|
||||
"actionTh": "สร้าง",
|
||||
"oldValues": null,
|
||||
"newValues": {
|
||||
"name": "สมชาย ใจดี",
|
||||
"email": "somchai@example.com"
|
||||
},
|
||||
"ipAddress": "192.168.1.100",
|
||||
"userAgent": "Mozilla/5.0...",
|
||||
"createdAt": "2024-01-15T09:00:00Z"
|
||||
}
|
||||
],
|
||||
"count": 1,
|
||||
"message": "Found 1 audit log(s)"
|
||||
}
|
||||
```
|
||||
|
||||
#### Get Audit Log Statistics
|
||||
|
||||
```
|
||||
GET /api/audit-logs/stats
|
||||
```
|
||||
|
||||
**Response Format:**
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"totalLogs": 1250,
|
||||
"byAction": {
|
||||
"CREATE": 350,
|
||||
"UPDATE": 500,
|
||||
"DELETE": 150,
|
||||
"READ": 250
|
||||
},
|
||||
"byEntityType": {
|
||||
"customer": 400,
|
||||
"quotation": 300,
|
||||
"location": 200,
|
||||
"industrial_estate": 100,
|
||||
"master_option": 250
|
||||
},
|
||||
"todayCount": 45,
|
||||
"thisWeekCount": 320
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Get Logs by Entity
|
||||
|
||||
```
|
||||
GET /api/audit-logs/entity/:entityType/:entityId
|
||||
```
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
curl http://localhost:3000/api/audit-logs/entity/customer/cust-001
|
||||
```
|
||||
|
||||
#### Get Logs by User
|
||||
|
||||
```
|
||||
GET /api/audit-logs/user/:userId
|
||||
```
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
curl http://localhost:3000/api/audit-logs/user/user-123
|
||||
```
|
||||
|
||||
#### Get Single Audit Log
|
||||
|
||||
```
|
||||
GET /api/audit-logs/:id
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Available Data
|
||||
|
||||
### Customers
|
||||
@@ -195,20 +611,62 @@ DELETE /api/quotations/:branch/:id
|
||||
- `branch-02`: 1 quotation (draft)
|
||||
- `head-office`: 1 quotation (sent)
|
||||
|
||||
### Master Options
|
||||
|
||||
- Categories: `customer_type`, `payment_method`, `industry`, `lead_source`
|
||||
- Each category has multiple options with Thai/English names
|
||||
|
||||
### Locations
|
||||
|
||||
- Countries: Thailand, etc.
|
||||
- Provinces: All Thai provinces
|
||||
- Districts/Subdistricts: Hierarchical data structure
|
||||
|
||||
### Industrial Estates
|
||||
|
||||
- Multiple industrial estates across Thailand
|
||||
- Linked to locations with GPS coordinates
|
||||
|
||||
### Audit Logs
|
||||
|
||||
- Complete audit trail for all operations
|
||||
- Admin-only access
|
||||
|
||||
## Testing with Browser
|
||||
|
||||
Simply open these URLs in your browser:
|
||||
|
||||
### Customers
|
||||
|
||||
- http://localhost:3001/api/customers/branch-01
|
||||
- http://localhost:3001/api/customers/branch-02?status=active
|
||||
- http://localhost:3001/api/customers/head-office
|
||||
- http://localhost:3000/api/customers/branch-01
|
||||
- http://localhost:3000/api/customers/branch-02?status=active
|
||||
- http://localhost:3000/api/customers/head-office
|
||||
|
||||
### Quotations
|
||||
|
||||
- http://localhost:3001/api/quotations/branch-01
|
||||
- http://localhost:3001/api/quotations/head-office?status=sent
|
||||
- http://localhost:3000/api/quotations/branch-01
|
||||
- http://localhost:3000/api/quotations/head-office?status=sent
|
||||
|
||||
### Master Options
|
||||
|
||||
- http://localhost:3000/api/master-options
|
||||
- http://localhost:3000/api/master-options?category=customer_type
|
||||
|
||||
### Locations
|
||||
|
||||
- http://localhost:3000/api/locations
|
||||
- http://localhost:3000/api/locations/type/province
|
||||
- http://localhost:3000/api/locations?search=กรุงเทพ
|
||||
|
||||
### Industrial Estates
|
||||
|
||||
- http://localhost:3000/api/industrial-estates
|
||||
- http://localhost:3000/api/industrial-estates?isActive=true
|
||||
|
||||
### Audit Logs (Admin only)
|
||||
|
||||
- http://localhost:3000/api/audit-logs
|
||||
- http://localhost:3000/api/audit-logs/stats
|
||||
|
||||
## Project Structure
|
||||
|
||||
@@ -225,7 +683,23 @@ src/
|
||||
│ │ ├── controller.ts # HTTP handlers & routing
|
||||
│ │ ├── service.ts # Business logic
|
||||
│ │ └── model.ts # Schemas & validation
|
||||
│ └── quotations/
|
||||
│ ├── quotations/
|
||||
│ │ ├── controller.ts # HTTP handlers & routing
|
||||
│ │ ├── service.ts # Business logic
|
||||
│ │ └── model.ts # Schemas & validation
|
||||
│ ├── master-options/
|
||||
│ │ ├── controller.ts # HTTP handlers & routing
|
||||
│ │ ├── service.ts # Business logic
|
||||
│ │ └── model.ts # Schemas & validation
|
||||
│ ├── locations/
|
||||
│ │ ├── controller.ts # HTTP handlers & routing
|
||||
│ │ ├── service.ts # Business logic
|
||||
│ │ └── model.ts # Schemas & validation
|
||||
│ ├── industrial-estates/
|
||||
│ │ ├── controller.ts # HTTP handlers & routing
|
||||
│ │ ├── service.ts # Business logic
|
||||
│ │ └── model.ts # Schemas & validation
|
||||
│ └── audit-logs/
|
||||
│ ├── controller.ts # HTTP handlers & routing
|
||||
│ ├── service.ts # Business logic
|
||||
│ └── model.ts # Schemas & validation
|
||||
@@ -233,6 +707,8 @@ src/
|
||||
│ └── customer.ts # Shared types
|
||||
├── lib/
|
||||
│ └── mock-data.ts # Mock data
|
||||
└── database/
|
||||
└── schema.ts # Drizzle ORM schema
|
||||
```
|
||||
|
||||
### File Responsibilities
|
||||
@@ -275,6 +751,10 @@ This project follows the **correct ElysiaJS + Next.js integration pattern**:
|
||||
- ✅ WinterCG compliant - works as normal Next.js API route
|
||||
- ✅ Feature-based MVC pattern for maintainability
|
||||
- ✅ Clear separation of concerns between Model, View, and Controller
|
||||
- ✅ Branch-level data scoping for multi-tenant architecture
|
||||
- ✅ Audit logging for all operations
|
||||
- ✅ Soft delete with `deletedAt` field
|
||||
- ✅ Multi-language support (Thai/English)
|
||||
|
||||
## Technologies Used
|
||||
|
||||
@@ -282,19 +762,28 @@ This project follows the **correct ElysiaJS + Next.js integration pattern**:
|
||||
- **Next.js 16**: React framework with App Router
|
||||
- **TypeScript**: Type safety throughout
|
||||
- **TypeBox**: Schema validation (via `@elysiajs/schema`)
|
||||
- **Drizzle ORM**: Type-safe SQL ORM
|
||||
- **PostgreSQL**: Primary database
|
||||
|
||||
## Features
|
||||
|
||||
✅ Feature-based MVC architecture
|
||||
✅ Dynamic branch parameter support
|
||||
✅ Type-safe request/response validation
|
||||
✅ Optional query parameter filtering
|
||||
✅ Mock data for customers and quotations
|
||||
✅ Full TypeScript support
|
||||
✅ Auto-generated API documentation (Swagger/OpenAPI ready)
|
||||
✅ Correct ElysiaJS + Next.js integration pattern
|
||||
✅ Scalable and maintainable code structure
|
||||
✅ Feature-based MVC architecture
|
||||
✅ Dynamic branch parameter support
|
||||
✅ Type-safe request/response validation
|
||||
✅ Optional query parameter filtering
|
||||
✅ Mock data for customers and quotations
|
||||
✅ Full TypeScript support
|
||||
✅ Auto-generated API documentation (Swagger/OpenAPI ready)
|
||||
✅ Correct ElysiaJS + Next.js integration pattern
|
||||
✅ Scalable and maintainable code structure
|
||||
✅ Clear separation of concerns
|
||||
✅ Multi-tenant architecture with branch scoping
|
||||
✅ Complete audit logging system
|
||||
✅ Soft delete for data integrity
|
||||
✅ Multi-language support (Thai/English)
|
||||
✅ Hierarchical data structures (locations)
|
||||
✅ GPS coordinate support (industrial estates)
|
||||
✅ Admin-only access control (audit logs)
|
||||
|
||||
## Adding New Modules
|
||||
|
||||
@@ -304,7 +793,8 @@ To add a new module (e.g., `products`):
|
||||
2. Create `model.ts` - Define schemas
|
||||
3. Create `service.ts` - Business logic
|
||||
4. Create `controller.ts` - Routes and handlers
|
||||
5. Update `src/app/api/[[...slugs]]/route.ts`:
|
||||
5. Create `index.ts` - Module exports
|
||||
6. Update `src/app/api/[[...slugs]]/route.ts`:
|
||||
|
||||
```typescript
|
||||
import { products } from "@/modules/products/controller";
|
||||
@@ -312,5 +802,32 @@ To add a new module (e.g., `products`):
|
||||
const app = new Elysia({ prefix: "/api" })
|
||||
.use(customers)
|
||||
.use(quotations)
|
||||
.use(masterOptions)
|
||||
.use(locations)
|
||||
.use(industrialEstates)
|
||||
.use(auditLogs)
|
||||
.use(products); // Add new module
|
||||
```
|
||||
|
||||
## Security & Access Control
|
||||
|
||||
### Branch Middleware
|
||||
|
||||
All routes use `branchMiddleware` which injects:
|
||||
|
||||
- `currentBranchId` - Current user's branch
|
||||
- `userId` - Current user ID
|
||||
- `userGroups` - User groups/roles
|
||||
- `accessibleBranches` - Branches user can access
|
||||
|
||||
### Permission Levels
|
||||
|
||||
- **Standard Users**: Access to branch-scoped data
|
||||
- **Admin/Superadmin**: Full access + audit logs
|
||||
- **Auditor**: Read-only access to audit logs
|
||||
|
||||
### Data Isolation
|
||||
|
||||
- All queries are automatically filtered by `branchId`
|
||||
- Cross-branch access is prevented
|
||||
- Soft delete ensures data integrity
|
||||
|
||||
Reference in New Issue
Block a user