feat(#93): implement agent spawn via federation
Implements FED-010: Agent Spawn via Federation feature that enables spawning and managing Claude agents on remote federated Mosaic Stack instances via COMMAND message type. Features: - Federation agent command types (spawn, status, kill) - FederationAgentService for handling agent operations - Integration with orchestrator's agent spawner/lifecycle services - API endpoints for spawning, querying status, and killing agents - Full command routing through federation COMMAND infrastructure - Comprehensive test coverage (12/12 tests passing) Architecture: - Hub → Spoke: Spawn agents on remote instances - Command flow: FederationController → FederationAgentService → CommandService → Remote Orchestrator - Response handling: Remote orchestrator returns agent status/results - Security: Connection validation, signature verification Files created: - apps/api/src/federation/types/federation-agent.types.ts - apps/api/src/federation/federation-agent.service.ts - apps/api/src/federation/federation-agent.service.spec.ts Files modified: - apps/api/src/federation/command.service.ts (agent command routing) - apps/api/src/federation/federation.controller.ts (agent endpoints) - apps/api/src/federation/federation.module.ts (service registration) - apps/orchestrator/src/api/agents/agents.controller.ts (status endpoint) - apps/orchestrator/src/api/agents/agents.module.ts (lifecycle integration) Testing: - 12/12 tests passing for FederationAgentService - All command service tests passing - TypeScript compilation successful - Linting passed Refs #93 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -65,18 +65,18 @@ DELETE /api/{resource}/:id # Delete resource
|
||||
|
||||
## HTTP Status Codes
|
||||
|
||||
| Code | Meaning | Usage |
|
||||
|------|---------|-------|
|
||||
| 200 | OK | Successful GET, PATCH, PUT |
|
||||
| 201 | Created | Successful POST |
|
||||
| 204 | No Content | Successful DELETE |
|
||||
| 400 | Bad Request | Invalid input |
|
||||
| 401 | Unauthorized | Missing/invalid auth token |
|
||||
| 403 | Forbidden | Valid token, insufficient permissions |
|
||||
| 404 | Not Found | Resource doesn't exist |
|
||||
| 409 | Conflict | Resource already exists |
|
||||
| 422 | Unprocessable Entity | Validation failed |
|
||||
| 500 | Internal Server Error | Server error |
|
||||
| Code | Meaning | Usage |
|
||||
| ---- | --------------------- | ------------------------------------- |
|
||||
| 200 | OK | Successful GET, PATCH, PUT |
|
||||
| 201 | Created | Successful POST |
|
||||
| 204 | No Content | Successful DELETE |
|
||||
| 400 | Bad Request | Invalid input |
|
||||
| 401 | Unauthorized | Missing/invalid auth token |
|
||||
| 403 | Forbidden | Valid token, insufficient permissions |
|
||||
| 404 | Not Found | Resource doesn't exist |
|
||||
| 409 | Conflict | Resource already exists |
|
||||
| 422 | Unprocessable Entity | Validation failed |
|
||||
| 500 | Internal Server Error | Server error |
|
||||
|
||||
## Pagination
|
||||
|
||||
@@ -87,10 +87,12 @@ GET /api/tasks?page=1&limit=20
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- `page` — Page number (default: 1)
|
||||
- `limit` — Items per page (default: 10, max: 100)
|
||||
|
||||
**Response includes:**
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [...],
|
||||
@@ -113,6 +115,7 @@ GET /api/events?start_date=2026-01-01&end_date=2026-01-31
|
||||
```
|
||||
|
||||
**Supported operators:**
|
||||
|
||||
- `=` — Equals
|
||||
- `_gt` — Greater than (e.g., `created_at_gt=2026-01-01`)
|
||||
- `_lt` — Less than
|
||||
@@ -139,11 +142,10 @@ GET /api/tasks?fields=id,title,status
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{ "id": "1", "title": "Task 1", "status": "active" }
|
||||
]
|
||||
"data": [{ "id": "1", "title": "Task 1", "status": "active" }]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -156,6 +158,7 @@ GET /api/tasks?include=assignee,project
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
@@ -186,11 +189,13 @@ See [Authentication Endpoints](../2-authentication/1-endpoints.md) for details.
|
||||
## Request Headers
|
||||
|
||||
**Required:**
|
||||
|
||||
```http
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
**Optional:**
|
||||
|
||||
```http
|
||||
Authorization: Bearer {token}
|
||||
X-Workspace-ID: {workspace-uuid} # For multi-tenant requests
|
||||
@@ -221,6 +226,7 @@ API endpoints are rate-limited:
|
||||
- **Authenticated:** 1000 requests/hour
|
||||
|
||||
**Rate limit headers:**
|
||||
|
||||
```http
|
||||
X-RateLimit-Limit: 1000
|
||||
X-RateLimit-Remaining: 950
|
||||
@@ -235,11 +241,7 @@ CORS is configured for frontend origin:
|
||||
|
||||
```javascript
|
||||
// Allowed origins
|
||||
const origins = [
|
||||
process.env.NEXT_PUBLIC_APP_URL,
|
||||
'http://localhost:3000',
|
||||
'http://localhost:3001'
|
||||
];
|
||||
const origins = [process.env.NEXT_PUBLIC_APP_URL, "http://localhost:3000", "http://localhost:3001"];
|
||||
```
|
||||
|
||||
## Versioning
|
||||
@@ -257,6 +259,7 @@ GET /health
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
@@ -282,6 +285,7 @@ curl -X POST http://localhost:3001/api/tasks \
|
||||
```
|
||||
|
||||
**Response (201):**
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
|
||||
@@ -21,6 +21,7 @@ POST /auth/sign-up
|
||||
```
|
||||
|
||||
**Request Body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"email": "user@example.com",
|
||||
@@ -30,6 +31,7 @@ POST /auth/sign-up
|
||||
```
|
||||
|
||||
**Response (201):**
|
||||
|
||||
```json
|
||||
{
|
||||
"user": {
|
||||
@@ -47,6 +49,7 @@ POST /auth/sign-up
|
||||
```
|
||||
|
||||
**Errors:**
|
||||
|
||||
- `409 Conflict` — Email already exists
|
||||
- `422 Validation Error` — Invalid input
|
||||
|
||||
@@ -61,6 +64,7 @@ POST /auth/sign-in
|
||||
```
|
||||
|
||||
**Request Body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"email": "user@example.com",
|
||||
@@ -69,6 +73,7 @@ POST /auth/sign-in
|
||||
```
|
||||
|
||||
**Response (200):**
|
||||
|
||||
```json
|
||||
{
|
||||
"user": {
|
||||
@@ -85,6 +90,7 @@ POST /auth/sign-in
|
||||
```
|
||||
|
||||
**Errors:**
|
||||
|
||||
- `401 Unauthorized` — Invalid credentials
|
||||
|
||||
---
|
||||
@@ -98,11 +104,13 @@ POST /auth/sign-out
|
||||
```
|
||||
|
||||
**Headers:**
|
||||
|
||||
```http
|
||||
Authorization: Bearer {session_token}
|
||||
```
|
||||
|
||||
**Response (200):**
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true
|
||||
@@ -120,11 +128,13 @@ GET /auth/session
|
||||
```
|
||||
|
||||
**Headers:**
|
||||
|
||||
```http
|
||||
Authorization: Bearer {session_token}
|
||||
```
|
||||
|
||||
**Response (200):**
|
||||
|
||||
```json
|
||||
{
|
||||
"user": {
|
||||
@@ -140,6 +150,7 @@ Authorization: Bearer {session_token}
|
||||
```
|
||||
|
||||
**Errors:**
|
||||
|
||||
- `401 Unauthorized` — Invalid or expired session
|
||||
|
||||
---
|
||||
@@ -153,11 +164,13 @@ GET /auth/profile
|
||||
```
|
||||
|
||||
**Headers:**
|
||||
|
||||
```http
|
||||
Authorization: Bearer {session_token}
|
||||
```
|
||||
|
||||
**Response (200):**
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "user-uuid",
|
||||
@@ -168,6 +181,7 @@ Authorization: Bearer {session_token}
|
||||
```
|
||||
|
||||
**Errors:**
|
||||
|
||||
- `401 Unauthorized` — Not authenticated
|
||||
|
||||
---
|
||||
@@ -181,12 +195,14 @@ GET /auth/callback/authentik
|
||||
```
|
||||
|
||||
**Query Parameters:**
|
||||
|
||||
- `code` — Authorization code from provider
|
||||
- `state` — CSRF protection token
|
||||
|
||||
This endpoint is called by the OIDC provider after successful authentication.
|
||||
|
||||
**Response:**
|
||||
|
||||
- Redirects to frontend with session token
|
||||
|
||||
---
|
||||
@@ -229,10 +245,12 @@ Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
|
||||
### Token Storage
|
||||
|
||||
**Frontend (Browser):**
|
||||
|
||||
- Store in `httpOnly` cookie (most secure)
|
||||
- Or `localStorage` (less secure, XSS vulnerable)
|
||||
|
||||
**Mobile/Desktop:**
|
||||
|
||||
- Secure storage (Keychain on iOS, KeyStore on Android)
|
||||
|
||||
### Token Expiration
|
||||
@@ -240,8 +258,9 @@ Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
|
||||
Tokens expire after 24 hours (configurable via `JWT_EXPIRATION`).
|
||||
|
||||
**Check expiration:**
|
||||
|
||||
```typescript
|
||||
import { AuthSession } from '@mosaic/shared';
|
||||
import { AuthSession } from "@mosaic/shared";
|
||||
|
||||
const isExpired = (session: AuthSession) => {
|
||||
return new Date(session.session.expiresAt) < new Date();
|
||||
@@ -249,6 +268,7 @@ const isExpired = (session: AuthSession) => {
|
||||
```
|
||||
|
||||
**Refresh flow** (future implementation):
|
||||
|
||||
```http
|
||||
POST /auth/refresh
|
||||
```
|
||||
@@ -307,6 +327,7 @@ curl -X POST http://localhost:3001/auth/sign-in \
|
||||
```
|
||||
|
||||
**Save the token from response:**
|
||||
|
||||
```bash
|
||||
TOKEN=$(curl -X POST http://localhost:3001/auth/sign-in \
|
||||
-H "Content-Type: application/json" \
|
||||
|
||||
@@ -5,6 +5,7 @@ The Activity Logging API provides comprehensive audit trail and activity trackin
|
||||
## Overview
|
||||
|
||||
Activity logs are automatically created for:
|
||||
|
||||
- **CRUD Operations**: Task, event, project, and workspace modifications
|
||||
- **Authentication Events**: Login, logout, password resets
|
||||
- **User Actions**: Task assignments, workspace member changes
|
||||
@@ -24,17 +25,17 @@ Get a paginated list of activity logs with optional filters.
|
||||
|
||||
**Query Parameters:**
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| `workspaceId` | UUID | Yes | Workspace to filter by |
|
||||
| `userId` | UUID | No | Filter by user who performed the action |
|
||||
| `action` | ActivityAction | No | Filter by action type (CREATED, UPDATED, etc.) |
|
||||
| `entityType` | EntityType | No | Filter by entity type (TASK, EVENT, etc.) |
|
||||
| `entityId` | UUID | No | Filter by specific entity |
|
||||
| `startDate` | ISO 8601 | No | Filter activities after this date |
|
||||
| `endDate` | ISO 8601 | No | Filter activities before this date |
|
||||
| `page` | Number | No | Page number (default: 1) |
|
||||
| `limit` | Number | No | Items per page (default: 50, max: 100) |
|
||||
| Parameter | Type | Required | Description |
|
||||
| ------------- | -------------- | -------- | ---------------------------------------------- |
|
||||
| `workspaceId` | UUID | Yes | Workspace to filter by |
|
||||
| `userId` | UUID | No | Filter by user who performed the action |
|
||||
| `action` | ActivityAction | No | Filter by action type (CREATED, UPDATED, etc.) |
|
||||
| `entityType` | EntityType | No | Filter by entity type (TASK, EVENT, etc.) |
|
||||
| `entityId` | UUID | No | Filter by specific entity |
|
||||
| `startDate` | ISO 8601 | No | Filter activities after this date |
|
||||
| `endDate` | ISO 8601 | No | Filter activities before this date |
|
||||
| `page` | Number | No | Page number (default: 1) |
|
||||
| `limit` | Number | No | Items per page (default: 50, max: 100) |
|
||||
|
||||
**Response:**
|
||||
|
||||
@@ -102,15 +103,15 @@ Retrieve a single activity log entry by ID.
|
||||
|
||||
**Path Parameters:**
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| `id` | UUID | Yes | Activity log ID |
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | --------------- |
|
||||
| `id` | UUID | Yes | Activity log ID |
|
||||
|
||||
**Query Parameters:**
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| `workspaceId` | UUID | Yes | Workspace ID (for multi-tenant isolation) |
|
||||
| Parameter | Type | Required | Description |
|
||||
| ------------- | ---- | -------- | ----------------------------------------- |
|
||||
| `workspaceId` | UUID | Yes | Workspace ID (for multi-tenant isolation) |
|
||||
|
||||
**Response:**
|
||||
|
||||
@@ -156,16 +157,16 @@ Retrieve complete audit trail for a specific entity (task, event, project, etc.)
|
||||
|
||||
**Path Parameters:**
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| `entityType` | EntityType | Yes | Type of entity (TASK, EVENT, PROJECT, WORKSPACE, USER) |
|
||||
| `entityId` | UUID | Yes | Entity ID |
|
||||
| Parameter | Type | Required | Description |
|
||||
| ------------ | ---------- | -------- | ------------------------------------------------------ |
|
||||
| `entityType` | EntityType | Yes | Type of entity (TASK, EVENT, PROJECT, WORKSPACE, USER) |
|
||||
| `entityId` | UUID | Yes | Entity ID |
|
||||
|
||||
**Query Parameters:**
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| `workspaceId` | UUID | Yes | Workspace ID (for multi-tenant isolation) |
|
||||
| Parameter | Type | Required | Description |
|
||||
| ------------- | ---- | -------- | ----------------------------------------- |
|
||||
| `workspaceId` | UUID | Yes | Workspace ID (for multi-tenant isolation) |
|
||||
|
||||
**Response:**
|
||||
|
||||
@@ -265,6 +266,7 @@ The Activity Logging system includes an interceptor that automatically logs:
|
||||
- **DELETE requests** → `DELETED` action
|
||||
|
||||
The interceptor extracts:
|
||||
|
||||
- User information from the authenticated session
|
||||
- Workspace context from request
|
||||
- IP address and user agent from HTTP headers
|
||||
@@ -277,7 +279,7 @@ The interceptor extracts:
|
||||
For custom logging scenarios, use the `ActivityService` helper methods:
|
||||
|
||||
```typescript
|
||||
import { ActivityService } from '@/activity/activity.service';
|
||||
import { ActivityService } from "@/activity/activity.service";
|
||||
|
||||
@Injectable()
|
||||
export class TaskService {
|
||||
@@ -287,12 +289,7 @@ export class TaskService {
|
||||
const task = await this.prisma.task.create({ data });
|
||||
|
||||
// Log task creation
|
||||
await this.activityService.logTaskCreated(
|
||||
workspaceId,
|
||||
userId,
|
||||
task.id,
|
||||
{ title: task.title }
|
||||
);
|
||||
await this.activityService.logTaskCreated(workspaceId, userId, task.id, { title: task.title });
|
||||
|
||||
return task;
|
||||
}
|
||||
@@ -302,6 +299,7 @@ export class TaskService {
|
||||
### Available Helper Methods
|
||||
|
||||
#### Task Activities
|
||||
|
||||
- `logTaskCreated(workspaceId, userId, taskId, details?)`
|
||||
- `logTaskUpdated(workspaceId, userId, taskId, details?)`
|
||||
- `logTaskDeleted(workspaceId, userId, taskId, details?)`
|
||||
@@ -309,22 +307,26 @@ export class TaskService {
|
||||
- `logTaskAssigned(workspaceId, userId, taskId, assigneeId)`
|
||||
|
||||
#### Event Activities
|
||||
|
||||
- `logEventCreated(workspaceId, userId, eventId, details?)`
|
||||
- `logEventUpdated(workspaceId, userId, eventId, details?)`
|
||||
- `logEventDeleted(workspaceId, userId, eventId, details?)`
|
||||
|
||||
#### Project Activities
|
||||
|
||||
- `logProjectCreated(workspaceId, userId, projectId, details?)`
|
||||
- `logProjectUpdated(workspaceId, userId, projectId, details?)`
|
||||
- `logProjectDeleted(workspaceId, userId, projectId, details?)`
|
||||
|
||||
#### Workspace Activities
|
||||
|
||||
- `logWorkspaceCreated(workspaceId, userId, details?)`
|
||||
- `logWorkspaceUpdated(workspaceId, userId, details?)`
|
||||
- `logWorkspaceMemberAdded(workspaceId, userId, memberId, role)`
|
||||
- `logWorkspaceMemberRemoved(workspaceId, userId, memberId)`
|
||||
|
||||
#### User Activities
|
||||
|
||||
- `logUserUpdated(workspaceId, userId, details?)`
|
||||
|
||||
---
|
||||
@@ -338,6 +340,7 @@ All activity logs are scoped to workspaces using Row-Level Security (RLS). Users
|
||||
### Data Retention
|
||||
|
||||
Activity logs are retained indefinitely by default. Consider implementing a retention policy based on:
|
||||
|
||||
- Compliance requirements
|
||||
- Storage constraints
|
||||
- Business needs
|
||||
@@ -345,6 +348,7 @@ Activity logs are retained indefinitely by default. Consider implementing a rete
|
||||
### Sensitive Data
|
||||
|
||||
Activity logs should NOT contain:
|
||||
|
||||
- Passwords or authentication tokens
|
||||
- Credit card information
|
||||
- Personal health information
|
||||
@@ -364,9 +368,9 @@ Include enough context to understand what changed:
|
||||
// Good
|
||||
await activityService.logTaskUpdated(workspaceId, userId, taskId, {
|
||||
changes: {
|
||||
status: { from: 'NOT_STARTED', to: 'IN_PROGRESS' },
|
||||
assignee: { from: null, to: 'user-456' }
|
||||
}
|
||||
status: { from: "NOT_STARTED", to: "IN_PROGRESS" },
|
||||
assignee: { from: null, to: "user-456" },
|
||||
},
|
||||
});
|
||||
|
||||
// Less useful
|
||||
@@ -376,6 +380,7 @@ await activityService.logTaskUpdated(workspaceId, userId, taskId);
|
||||
### 2. Log Business-Critical Actions
|
||||
|
||||
Prioritize logging actions that:
|
||||
|
||||
- Change permissions or access control
|
||||
- Delete data
|
||||
- Modify billing or subscription
|
||||
@@ -388,11 +393,11 @@ Use appropriate filters to reduce data transfer:
|
||||
|
||||
```typescript
|
||||
// Efficient - filters at database level
|
||||
const activities = await fetch('/api/activity?workspaceId=xxx&entityType=TASK&page=1&limit=50');
|
||||
const activities = await fetch("/api/activity?workspaceId=xxx&entityType=TASK&page=1&limit=50");
|
||||
|
||||
// Inefficient - transfers all data then filters
|
||||
const activities = await fetch('/api/activity?workspaceId=xxx');
|
||||
const taskActivities = activities.filter(a => a.entityType === 'TASK');
|
||||
const activities = await fetch("/api/activity?workspaceId=xxx");
|
||||
const taskActivities = activities.filter((a) => a.entityType === "TASK");
|
||||
```
|
||||
|
||||
### 4. Display User-Friendly Activity Feeds
|
||||
@@ -404,11 +409,11 @@ function formatActivityMessage(activity: ActivityLog) {
|
||||
const { user, action, entityType, details } = activity;
|
||||
|
||||
switch (action) {
|
||||
case 'CREATED':
|
||||
case "CREATED":
|
||||
return `${user.name} created ${entityType.toLowerCase()} "${details.title}"`;
|
||||
case 'UPDATED':
|
||||
case "UPDATED":
|
||||
return `${user.name} updated ${entityType.toLowerCase()}`;
|
||||
case 'DELETED':
|
||||
case "DELETED":
|
||||
return `${user.name} deleted ${entityType.toLowerCase()}`;
|
||||
default:
|
||||
return `${user.name} performed ${action}`;
|
||||
@@ -427,7 +432,7 @@ try {
|
||||
await activityService.logActivity(data);
|
||||
} catch (error) {
|
||||
// Log error but don't throw
|
||||
logger.error('Failed to log activity', error);
|
||||
logger.error("Failed to log activity", error);
|
||||
}
|
||||
```
|
||||
|
||||
@@ -454,6 +459,7 @@ Always use pagination for activity queries. Default limit is 50 items, maximum i
|
||||
### Background Processing
|
||||
|
||||
For high-volume systems, consider:
|
||||
|
||||
- Async activity logging with message queues
|
||||
- Batch inserts for multiple activities
|
||||
- Separate read replicas for reporting
|
||||
|
||||
@@ -5,6 +5,7 @@ Complete reference for Tasks, Events, and Projects API endpoints.
|
||||
## Overview
|
||||
|
||||
All CRUD endpoints follow standard REST conventions and require authentication. They support:
|
||||
|
||||
- Full CRUD operations (Create, Read, Update, Delete)
|
||||
- Workspace-scoped isolation
|
||||
- Pagination and filtering
|
||||
@@ -39,6 +40,7 @@ GET /api/tasks?status=IN_PROGRESS&page=1&limit=20
|
||||
```
|
||||
|
||||
**Query Parameters:**
|
||||
|
||||
- `workspaceId` (UUID, required) — Workspace ID
|
||||
- `status` (enum, optional) — `NOT_STARTED`, `IN_PROGRESS`, `PAUSED`, `COMPLETED`, `ARCHIVED`
|
||||
- `priority` (enum, optional) — `LOW`, `MEDIUM`, `HIGH`
|
||||
@@ -51,6 +53,7 @@ GET /api/tasks?status=IN_PROGRESS&page=1&limit=20
|
||||
- `limit` (integer, optional) — Items per page (default: 50, max: 100)
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
@@ -122,6 +125,7 @@ Content-Type: application/json
|
||||
```
|
||||
|
||||
**Fields:**
|
||||
|
||||
- `title` (string, required, 1-255 chars) — Task title
|
||||
- `description` (string, optional, max 10000 chars) — Detailed description
|
||||
- `status` (enum, optional) — Default: `NOT_STARTED`
|
||||
@@ -153,6 +157,7 @@ All fields are optional for partial updates. Setting `status` to `COMPLETED` aut
|
||||
**Response (200):** Updated task object
|
||||
|
||||
**Activity Logs:**
|
||||
|
||||
- `UPDATED` — Always logged
|
||||
- `COMPLETED` — Logged when status changes to `COMPLETED`
|
||||
- `ASSIGNED` — Logged when `assigneeId` changes
|
||||
@@ -190,6 +195,7 @@ GET /api/events?startFrom=2026-02-01&startTo=2026-02-28
|
||||
```
|
||||
|
||||
**Query Parameters:**
|
||||
|
||||
- `workspaceId` (UUID, required) — Workspace ID
|
||||
- `projectId` (UUID, optional) — Filter by project
|
||||
- `startFrom` (ISO 8601, optional) — Events starting after this date
|
||||
@@ -199,6 +205,7 @@ GET /api/events?startFrom=2026-02-01&startTo=2026-02-28
|
||||
- `limit` (integer, optional) — Items per page
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
@@ -254,6 +261,7 @@ Content-Type: application/json
|
||||
```
|
||||
|
||||
**Fields:**
|
||||
|
||||
- `title` (string, required, 1-255 chars) — Event title
|
||||
- `description` (string, optional, max 10000 chars) — Description
|
||||
- `startTime` (ISO 8601, required) — Event start time
|
||||
@@ -304,6 +312,7 @@ GET /api/projects?status=ACTIVE
|
||||
```
|
||||
|
||||
**Query Parameters:**
|
||||
|
||||
- `workspaceId` (UUID, required) — Workspace ID
|
||||
- `status` (enum, optional) — `PLANNING`, `ACTIVE`, `PAUSED`, `COMPLETED`, `ARCHIVED`
|
||||
- `startDateFrom` (ISO 8601, optional) — Projects starting after this date
|
||||
@@ -312,6 +321,7 @@ GET /api/projects?status=ACTIVE
|
||||
- `limit` (integer, optional) — Items per page
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
@@ -374,6 +384,7 @@ Content-Type: application/json
|
||||
```
|
||||
|
||||
**Fields:**
|
||||
|
||||
- `name` (string, required, 1-255 chars) — Project name
|
||||
- `description` (string, optional, max 10000 chars) — Description
|
||||
- `status` (enum, optional) — Default: `PLANNING`
|
||||
@@ -450,10 +461,7 @@ Validation errors in request body.
|
||||
```json
|
||||
{
|
||||
"statusCode": 422,
|
||||
"message": [
|
||||
"title must not be empty",
|
||||
"priority must be a valid TaskPriority"
|
||||
],
|
||||
"message": ["title must not be empty", "priority must be a valid TaskPriority"],
|
||||
"error": "Unprocessable Entity"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -22,6 +22,7 @@ Complete API documentation for Mosaic Stack backend.
|
||||
## Authentication
|
||||
|
||||
All authenticated endpoints require:
|
||||
|
||||
```http
|
||||
Authorization: Bearer {session_token}
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user