Files
web-hosts/domains/coppertone.tech/docs/api/work-management-service.md
2025-12-26 13:38:04 +01:00

1061 lines
21 KiB
Markdown

# Work Management Service API Documentation
**Base URL:** `/api/work` (via gateway) or `http://localhost:8083` (direct)
**Service Port:** 8083
## Overview
The Work Management Service handles all project, task, and work order management for the Coppertone.tech platform. It provides:
- Project management with approval workflow
- Task tracking with assignments and time tracking
- Work orders with IPFS document integration
- Role-based access control (RBAC)
## Authentication
All endpoints require a valid JWT token in the Authorization header:
```
Authorization: Bearer <jwt_token>
```
The token is validated against the auth-service and must contain:
- `user_id` / `userId`: User identifier
- `roles`: Array of user roles
## Role-Based Access
| Role | Projects | Tasks | Work Orders |
|------|----------|-------|-------------|
| `SUPERUSER` | Full access | Full access | Full access |
| `ADMIN` | Full access | Full access | Full access |
| `STAFF` | Full access | Full access | Full access |
| `CLIENT` | Own projects only | Tasks on own projects | No access |
**Note:** `SUPERUSER` role automatically grants full access to all endpoints.
---
## Health Check
```
GET /healthz
```
**Response:**
```
200 OK
ok
```
---
## Projects
### List Projects
Retrieve a list of approved projects. CLIENTs see only their own projects.
```
GET /projects
```
**Headers:**
```
Authorization: Bearer <token>
```
**Required Role:** Any authenticated user
**Response (200 OK):**
```json
[
{
"id": 1,
"name": "Website Redesign",
"description": "Complete redesign of corporate website",
"status": "IN_PROGRESS",
"clientId": 5,
"ipfsMetadataCid": "QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco",
"startDate": "2024-01-15",
"endDate": "2024-03-15",
"requestedBy": 5,
"approvedBy": 2,
"approvalStatus": "APPROVED",
"approvalDate": "2024-01-10T14:30:00Z",
"rejectionReason": null,
"createdAt": "2024-01-05T10:00:00Z",
"updatedAt": "2024-01-20T09:15:00Z"
}
]
```
**Authorization Logic:**
- `STAFF/ADMIN/SUPERUSER`: See all approved projects
- `CLIENT`: See only projects where `client_id` matches their user ID
---
### Get Project by ID
Retrieve a single project by ID.
```
GET /projects/{id}
```
**Headers:**
```
Authorization: Bearer <token>
```
**Required Role:** Any authenticated user (with access)
**Path Parameters:**
| Parameter | Type | Description |
|-----------|------|-------------|
| `id` | integer | Project ID |
**Response (200 OK):**
```json
{
"id": 1,
"name": "Website Redesign",
"description": "Complete redesign of corporate website",
"status": "IN_PROGRESS",
"clientId": 5,
"ipfsMetadataCid": "QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco",
"startDate": "2024-01-15",
"endDate": "2024-03-15",
"requestedBy": 5,
"approvedBy": 2,
"approvalStatus": "APPROVED",
"approvalDate": "2024-01-10T14:30:00Z",
"rejectionReason": null,
"createdAt": "2024-01-05T10:00:00Z",
"updatedAt": "2024-01-20T09:15:00Z"
}
```
**Error Responses:**
| Status | Condition | Response |
|--------|-----------|----------|
| 400 | Invalid ID | `Invalid project ID` |
| 403 | No access | `Forbidden: you do not have access to this project` |
| 404 | Not found | `Project not found` |
**Authorization Logic:**
- User must be: project owner (`client_id`), requester (`requested_by`), or `STAFF/ADMIN/SUPERUSER`
---
### Create Project
Create a new project. Projects created by STAFF/ADMIN are auto-approved.
```
POST /projects
```
**Headers:**
```
Authorization: Bearer <token>
```
**Required Role:** `STAFF`, `ADMIN`, or `SUPERUSER`
**Request Body:**
```json
{
"name": "New Mobile App",
"description": "iOS and Android app development",
"status": "PLANNING",
"clientId": 5,
"ipfsMetadataCid": "QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco",
"startDate": "2024-02-01",
"endDate": "2024-06-30"
}
```
| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `name` | string | Yes | - | Project name |
| `description` | string | No | - | Project description |
| `status` | string | No | `PLANNING` | Project status |
| `clientId` | integer | No | - | Assigned client user ID |
| `ipfsMetadataCid` | string | No | - | IPFS CID for metadata |
| `startDate` | string | No | - | Planned start date (YYYY-MM-DD) |
| `endDate` | string | No | - | Planned end date (YYYY-MM-DD) |
**Valid Status Values:**
- `PLANNING`
- `IN_PROGRESS`
- `ON_HOLD`
- `COMPLETED`
- `CANCELLED`
**Response (201 Created):**
```json
{
"id": 2,
"name": "New Mobile App",
"description": "iOS and Android app development",
"status": "PLANNING",
"clientId": 5,
"ipfsMetadataCid": "QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco",
"startDate": "2024-02-01",
"endDate": "2024-06-30",
"requestedBy": 2,
"approvedBy": 2,
"approvalStatus": "APPROVED",
"approvalDate": "2024-01-25T12:00:00Z",
"rejectionReason": null,
"createdAt": "2024-01-25T12:00:00Z",
"updatedAt": "2024-01-25T12:00:00Z"
}
```
**Error Responses:**
| Status | Condition | Response |
|--------|-----------|----------|
| 400 | Missing name | `Project name is required` |
| 403 | Insufficient role | `Insufficient permissions` |
---
### Update Project
Update an existing project.
```
PUT /projects/{id}
```
**Headers:**
```
Authorization: Bearer <token>
```
**Required Role:** `STAFF`, `ADMIN`, or `SUPERUSER` (or project owner)
**Path Parameters:**
| Parameter | Type | Description |
|-----------|------|-------------|
| `id` | integer | Project ID |
**Request Body:**
```json
{
"name": "Updated Project Name",
"description": "Updated description",
"status": "IN_PROGRESS",
"clientId": 5,
"ipfsMetadataCid": "QmNewCid...",
"startDate": "2024-02-01",
"endDate": "2024-07-31"
}
```
**Response (200 OK):**
```json
{
"id": 1,
"name": "Updated Project Name",
...
}
```
**Error Responses:**
| Status | Condition | Response |
|--------|-----------|----------|
| 403 | No permission | `Forbidden: you do not have permission to update this project` |
| 404 | Not found | `Project not found` |
---
### Delete Project
Delete a project. Only STAFF/ADMIN can delete projects.
```
DELETE /projects/{id}
```
**Headers:**
```
Authorization: Bearer <token>
```
**Required Role:** `STAFF`, `ADMIN`, or `SUPERUSER`
**Path Parameters:**
| Parameter | Type | Description |
|-----------|------|-------------|
| `id` | integer | Project ID |
**Response (204 No Content)**
**Error Responses:**
| Status | Condition | Response |
|--------|-----------|----------|
| 403 | No permission | `Forbidden: only STAFF or ADMIN can delete projects` |
| 404 | Not found | `Project not found` |
---
## Project Requests (Client Workflow)
CLIENTs can request new projects, which require STAFF/ADMIN approval.
### List My Project Requests
Retrieve all project requests made by the current user.
```
GET /project-requests
```
**Headers:**
```
Authorization: Bearer <token>
```
**Required Role:** Any authenticated user
**Response (200 OK):**
```json
[
{
"id": 3,
"name": "New Feature Request",
"description": "We need a new dashboard feature",
"status": "PENDING_APPROVAL",
"clientId": 5,
"requestedBy": 5,
"approvedBy": null,
"approvalStatus": "PENDING",
"approvalDate": null,
"rejectionReason": null,
"createdAt": "2024-01-25T10:00:00Z",
"updatedAt": "2024-01-25T10:00:00Z"
}
]
```
---
### Create Project Request
Submit a new project request for approval.
```
POST /project-requests
```
**Headers:**
```
Authorization: Bearer <token>
```
**Required Role:** Any authenticated user
**Request Body:**
```json
{
"name": "Custom Integration Project",
"description": "We need to integrate our CRM with the platform. This should include bidirectional sync and real-time updates."
}
```
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `name` | string | Yes | Project name |
| `description` | string | Yes | Detailed description of the project request |
**Response (201 Created):**
```json
{
"id": 4,
"name": "Custom Integration Project",
"description": "We need to integrate our CRM with the platform...",
"status": "PENDING_APPROVAL",
"clientId": 5,
"requestedBy": 5,
"approvalStatus": "PENDING",
"createdAt": "2024-01-25T14:30:00Z",
"updatedAt": "2024-01-25T14:30:00Z"
}
```
**Error Responses:**
| Status | Condition | Response |
|--------|-----------|----------|
| 400 | Missing name | `Project name is required` |
| 400 | Missing description | `Project description is required to help us understand your needs` |
---
### Get Project Request
Retrieve a specific project request.
```
GET /project-requests/{id}
```
**Headers:**
```
Authorization: Bearer <token>
```
**Required Role:** Request owner, or `STAFF/ADMIN/SUPERUSER`
**Response (200 OK):**
```json
{
"id": 4,
"name": "Custom Integration Project",
"description": "...",
"status": "PENDING_APPROVAL",
"approvalStatus": "PENDING",
...
}
```
---
### Cancel Project Request
Cancel a pending project request.
```
DELETE /project-requests/{id}
```
**Headers:**
```
Authorization: Bearer <token>
```
**Required Role:** Request owner only (cannot cancel after approval)
**Response (204 No Content)**
**Error Responses:**
| Status | Condition | Response |
|--------|-----------|----------|
| 403 | Not owner | `Forbidden: you can only cancel your own requests` |
| 403 | Already processed | `Cannot cancel: request has already been processed` |
| 404 | Not found | `Project request not found` |
---
## Project Approval (Admin Workflow)
### List Pending Projects
Retrieve all projects pending approval.
```
GET /projects/pending
```
**Headers:**
```
Authorization: Bearer <token>
```
**Required Role:** `STAFF`, `ADMIN`, or `SUPERUSER`
**Response (200 OK):**
```json
[
{
"id": 4,
"name": "Custom Integration Project",
"description": "...",
"status": "PENDING_APPROVAL",
"clientId": 5,
"requestedBy": 5,
"approvalStatus": "PENDING",
"createdAt": "2024-01-25T14:30:00Z",
"updatedAt": "2024-01-25T14:30:00Z"
}
]
```
---
### Approve or Reject Project
Approve or reject a pending project request.
```
POST /projects/approve/{id}
```
**Headers:**
```
Authorization: Bearer <token>
```
**Required Role:** `STAFF`, `ADMIN`, or `SUPERUSER`
**Path Parameters:**
| Parameter | Type | Description |
|-----------|------|-------------|
| `id` | integer | Project ID |
**Request Body (Approve):**
```json
{
"action": "approve"
}
```
**Request Body (Reject):**
```json
{
"action": "reject",
"reason": "The requested scope is too broad. Please break this into smaller projects."
}
```
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `action` | string | Yes | `approve` or `reject` |
| `reason` | string | If rejecting | Reason for rejection |
**Response (200 OK - Approved):**
```json
{
"message": "Project approved successfully",
"project": {
"id": 4,
"name": "Custom Integration Project",
"status": "PLANNING",
"approvalStatus": "APPROVED",
"approvedBy": 2,
"approvalDate": "2024-01-26T09:00:00Z",
...
}
}
```
**Response (200 OK - Rejected):**
```json
{
"message": "Project rejected",
"project": {
"id": 4,
"name": "Custom Integration Project",
"status": "CANCELLED",
"approvalStatus": "REJECTED",
"rejectionReason": "The requested scope is too broad...",
...
}
}
```
**Error Responses:**
| Status | Condition | Response |
|--------|-----------|----------|
| 400 | Invalid action | `Invalid action. Must be 'approve' or 'reject'` |
| 400 | Missing reason | `Rejection reason is required` |
| 400 | Not pending | `Project is not pending approval` |
| 404 | Not found | `Project not found` |
---
## Tasks
### List Tasks
Retrieve tasks, optionally filtered by project.
```
GET /tasks
GET /tasks?project_id=1
```
**Headers:**
```
Authorization: Bearer <token>
```
**Required Role:** Any authenticated user
**Query Parameters:**
| Parameter | Type | Description |
|-----------|------|-------------|
| `project_id` | integer | Optional. Filter tasks by project |
**Response (200 OK):**
```json
[
{
"id": 1,
"projectId": 1,
"title": "Design Homepage Mockup",
"description": "Create wireframes and visual design for homepage",
"status": "IN_PROGRESS",
"assigneeId": 3,
"dueDate": "2024-02-01",
"completedAt": null,
"priority": 2,
"estimatedHours": 8.0,
"actualHours": 3.5,
"createdAt": "2024-01-20T10:00:00Z",
"updatedAt": "2024-01-25T14:00:00Z"
}
]
```
**Authorization Logic:**
- `STAFF/ADMIN/SUPERUSER`: See all tasks (optionally filtered)
- `CLIENT`: See only tasks for projects they own
---
### Get Task by ID
Retrieve a single task.
```
GET /tasks/{id}
```
**Headers:**
```
Authorization: Bearer <token>
```
**Required Role:** Any authenticated user (with access to parent project)
**Response (200 OK):**
```json
{
"id": 1,
"projectId": 1,
"title": "Design Homepage Mockup",
"description": "Create wireframes and visual design for homepage",
"status": "IN_PROGRESS",
"assigneeId": 3,
"dueDate": "2024-02-01",
"completedAt": null,
"priority": 2,
"estimatedHours": 8.0,
"actualHours": 3.5,
"createdAt": "2024-01-20T10:00:00Z",
"updatedAt": "2024-01-25T14:00:00Z"
}
```
**Error Responses:**
| Status | Condition | Response |
|--------|-----------|----------|
| 403 | No access | `Forbidden: you do not have access to this task` |
| 404 | Not found | `Task not found` |
---
### Create Task
Create a new task for a project.
```
POST /tasks
```
**Headers:**
```
Authorization: Bearer <token>
```
**Required Role:** `STAFF`, `ADMIN`, or `SUPERUSER`
**Request Body:**
```json
{
"projectId": 1,
"title": "Implement API Endpoints",
"description": "Create REST endpoints for user management",
"status": "TODO",
"assigneeId": 3,
"dueDate": "2024-02-15",
"priority": 1,
"estimatedHours": 16.0
}
```
| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `projectId` | integer | Yes | - | Parent project ID |
| `title` | string | Yes | - | Task title |
| `description` | string | No | - | Task description |
| `status` | string | No | `TODO` | Task status |
| `assigneeId` | integer | No | - | Assigned user ID |
| `dueDate` | string | No | - | Due date (YYYY-MM-DD) |
| `priority` | integer | No | 0 | Priority (higher = more important) |
| `estimatedHours` | float | No | - | Estimated hours to complete |
**Valid Status Values:**
- `TODO`
- `IN_PROGRESS`
- `REVIEW`
- `COMPLETED`
- `BLOCKED`
**Response (201 Created):**
```json
{
"id": 5,
"projectId": 1,
"title": "Implement API Endpoints",
"status": "TODO",
...
}
```
**Error Responses:**
| Status | Condition | Response |
|--------|-----------|----------|
| 400 | Missing fields | `Task title and project_id are required` |
| 403 | No permission | `Insufficient permissions` |
---
### Update Task
Update an existing task.
```
PUT /tasks/{id}
```
**Headers:**
```
Authorization: Bearer <token>
```
**Required Role:** `STAFF`, `ADMIN`, or `SUPERUSER`
**Request Body:**
```json
{
"title": "Implement API Endpoints",
"description": "Updated description",
"status": "COMPLETED",
"assigneeId": 3,
"dueDate": "2024-02-15",
"priority": 1,
"estimatedHours": 16.0,
"actualHours": 14.5
}
```
**Special Behavior:**
- Setting `status` to `COMPLETED` automatically sets `completedAt` timestamp
- Setting `status` to anything else clears `completedAt`
**Response (200 OK):**
```json
{
"id": 5,
"projectId": 1,
"title": "Implement API Endpoints",
"status": "COMPLETED",
"completedAt": "2024-02-14T16:30:00Z",
...
}
```
---
### Delete Task
Delete a task.
```
DELETE /tasks/{id}
```
**Headers:**
```
Authorization: Bearer <token>
```
**Required Role:** `STAFF`, `ADMIN`, or `SUPERUSER`
**Response (204 No Content)**
---
## Work Orders
Work orders are internal documents for tracking billable work. **STAFF/ADMIN only.**
### List Work Orders
Retrieve work orders, optionally filtered by project.
```
GET /workorders
GET /workorders?project_id=1
```
**Headers:**
```
Authorization: Bearer <token>
```
**Required Role:** `STAFF`, `ADMIN`, or `SUPERUSER`
**Query Parameters:**
| Parameter | Type | Description |
|-----------|------|-------------|
| `project_id` | integer | Optional. Filter by project |
**Response (200 OK):**
```json
[
{
"id": 1,
"projectId": 1,
"title": "Initial Setup and Configuration",
"description": "Server setup, environment configuration, CI/CD pipeline",
"orderNumber": "WO-2024-001",
"ipfsDocumentCid": "QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco",
"createdBy": 2,
"createdAt": "2024-01-20T10:00:00Z",
"updatedAt": "2024-01-20T10:00:00Z"
}
]
```
---
### Get Work Order by ID
```
GET /workorders/{id}
```
**Headers:**
```
Authorization: Bearer <token>
```
**Required Role:** `STAFF`, `ADMIN`, or `SUPERUSER`
**Response (200 OK):**
```json
{
"id": 1,
"projectId": 1,
"title": "Initial Setup and Configuration",
"description": "Server setup, environment configuration, CI/CD pipeline",
"orderNumber": "WO-2024-001",
"ipfsDocumentCid": "QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco",
"createdBy": 2,
"createdAt": "2024-01-20T10:00:00Z",
"updatedAt": "2024-01-20T10:00:00Z"
}
```
---
### Create Work Order
```
POST /workorders
```
**Headers:**
```
Authorization: Bearer <token>
```
**Required Role:** `STAFF`, `ADMIN`, or `SUPERUSER`
**Request Body:**
```json
{
"projectId": 1,
"title": "Database Migration",
"description": "Migrate from PostgreSQL 14 to 16",
"orderNumber": "WO-2024-002",
"ipfsDocumentCid": "QmNewCid...",
"createdBy": 2
}
```
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `projectId` | integer | Yes | Parent project ID |
| `title` | string | Yes | Work order title |
| `orderNumber` | string | Yes | Unique order number |
| `description` | string | No | Work order description |
| `ipfsDocumentCid` | string | No | IPFS CID for attached document |
| `createdBy` | integer | No | User ID who created the work order |
**Response (201 Created):**
```json
{
"id": 2,
"projectId": 1,
"title": "Database Migration",
"orderNumber": "WO-2024-002",
...
}
```
**Error Responses:**
| Status | Condition | Response |
|--------|-----------|----------|
| 400 | Missing fields | `Work order title, project_id, and order_number are required` |
| 409 | Duplicate order number | `Work order number already exists` |
---
### Update Work Order
```
PUT /workorders/{id}
```
**Headers:**
```
Authorization: Bearer <token>
```
**Required Role:** `STAFF`, `ADMIN`, or `SUPERUSER`
**Request Body:**
```json
{
"title": "Updated Title",
"description": "Updated description",
"orderNumber": "WO-2024-002",
"ipfsDocumentCid": "QmUpdatedCid..."
}
```
**Response (200 OK)**
---
### Delete Work Order
```
DELETE /workorders/{id}
```
**Headers:**
```
Authorization: Bearer <token>
```
**Required Role:** `STAFF`, `ADMIN`, or `SUPERUSER`
**Response (204 No Content)**
---
## CORS Configuration
Same as auth-service:
| Header | Value |
|--------|-------|
| `Access-Control-Allow-Origin` | Configured via `CORS_ALLOW_ORIGIN` env var |
| `Access-Control-Allow-Methods` | `GET, POST, PUT, DELETE, OPTIONS` |
| `Access-Control-Allow-Headers` | `Content-Type, Authorization` |
| `Access-Control-Allow-Credentials` | `true` |
---
## Environment Variables
| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `JWT_SECRET` | Yes | - | Secret key for JWT validation (must match auth-service) |
| `DB_HOST` | Yes | - | PostgreSQL host |
| `DB_USER` | Yes | - | PostgreSQL user |
| `DB_PASSWORD` | Yes | - | PostgreSQL password |
| `DB_NAME` | Yes | - | PostgreSQL database name |
| `DB_SSL_MODE` | No | `require` | SSL mode |
| `DB_SCHEMA` | No | `public` | Schema: `dev`, `testing`, `prod` |
| `CORS_ALLOW_ORIGIN` | No | `http://localhost:8090` | Allowed CORS origin |
---
## Database Tables
### projects
```sql
CREATE TABLE projects (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description TEXT,
status VARCHAR(50) DEFAULT 'PLANNING',
client_id INTEGER REFERENCES users(id),
ipfs_metadata_cid VARCHAR(100),
start_date DATE,
end_date DATE,
requested_by INTEGER REFERENCES users(id),
approved_by INTEGER REFERENCES users(id),
approval_status VARCHAR(20) DEFAULT 'APPROVED',
approval_date TIMESTAMP,
rejection_reason TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
```
### tasks
```sql
CREATE TABLE tasks (
id SERIAL PRIMARY KEY,
project_id INTEGER NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
title VARCHAR(255) NOT NULL,
description TEXT,
status VARCHAR(50) DEFAULT 'TODO',
assignee_id INTEGER REFERENCES users(id),
due_date DATE,
completed_at TIMESTAMP,
priority INTEGER DEFAULT 0,
estimated_hours DECIMAL(5,2),
actual_hours DECIMAL(5,2),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
```
### work_orders
```sql
CREATE TABLE work_orders (
id SERIAL PRIMARY KEY,
project_id INTEGER NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
title VARCHAR(255) NOT NULL,
description TEXT,
order_number VARCHAR(50) NOT NULL UNIQUE,
ipfs_document_cid VARCHAR(100),
created_by INTEGER REFERENCES users(id),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
```