docs: Restructure documentation with Bookstack-compatible hierarchy
- Organized docs into numbered shelf/book/chapter/page structure - Created comprehensive README.md with project overview - Added Getting Started book (quick start, installation, configuration) - Added Development book (workflow, testing, type sharing) - Added Architecture book (design principles, PDA-friendly patterns) - Added API Reference book (conventions, authentication) - Moved TYPE-SHARING.md to proper location - Updated all cross-references in main README - Created docs/README.md as master index - Removed old QA automation reports - Removed deprecated SETUP.md (content split into new structure) Documentation structure follows Bookstack best practices: - Numbered books: 1-getting-started, 2-development, 3-architecture, 4-api - Numbered chapters and pages for ordering - Clear hierarchy and navigation - Cross-referenced throughout Complete documentation available at: docs/README.md Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
319
docs/4-api/1-conventions/1-endpoints.md
Normal file
319
docs/4-api/1-conventions/1-endpoints.md
Normal file
@@ -0,0 +1,319 @@
|
||||
# API Endpoint Conventions
|
||||
|
||||
Standard patterns for all Mosaic Stack API endpoints.
|
||||
|
||||
## Base URL
|
||||
|
||||
- **Development:** `http://localhost:3001`
|
||||
- **Production:** `https://api.example.com`
|
||||
|
||||
## Standard REST Endpoints
|
||||
|
||||
```
|
||||
GET /api/{resource} # List resources (with pagination)
|
||||
GET /api/{resource}/:id # Get single resource
|
||||
POST /api/{resource} # Create new resource
|
||||
PATCH /api/{resource}/:id # Update resource (partial)
|
||||
PUT /api/{resource}/:id # Replace resource (full)
|
||||
DELETE /api/{resource}/:id # Delete resource
|
||||
```
|
||||
|
||||
## Response Format
|
||||
|
||||
### Success Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"id": "uuid",
|
||||
"name": "Example",
|
||||
"createdAt": "2026-01-28T12:00:00.000Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### List Response (with pagination)
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{ "id": "1", "name": "Item 1" },
|
||||
{ "id": "2", "name": "Item 2" }
|
||||
],
|
||||
"meta": {
|
||||
"total": 50,
|
||||
"page": 1,
|
||||
"limit": 10,
|
||||
"totalPages": 5
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Error Response
|
||||
|
||||
```json
|
||||
{
|
||||
"error": {
|
||||
"code": "VALIDATION_ERROR",
|
||||
"message": "Invalid input data",
|
||||
"details": {
|
||||
"email": "Invalid email format"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 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 |
|
||||
|
||||
## Pagination
|
||||
|
||||
All list endpoints support pagination via query parameters:
|
||||
|
||||
```
|
||||
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": [...],
|
||||
"meta": {
|
||||
"total": 150,
|
||||
"page": 1,
|
||||
"limit": 20,
|
||||
"totalPages": 8
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Filtering
|
||||
|
||||
Use query parameters for filtering:
|
||||
|
||||
```
|
||||
GET /api/tasks?status=active&priority=high
|
||||
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
|
||||
- `_gte` — Greater than or equal
|
||||
- `_lte` — Less than or equal
|
||||
- `_in` — In array (e.g., `status_in=active,pending`)
|
||||
|
||||
## Sorting
|
||||
|
||||
Use `sort` parameter:
|
||||
|
||||
```
|
||||
GET /api/tasks?sort=created_at # Ascending
|
||||
GET /api/tasks?sort=-created_at # Descending (minus prefix)
|
||||
GET /api/tasks?sort=-priority,created_at # Multiple fields
|
||||
```
|
||||
|
||||
## Field Selection
|
||||
|
||||
Request specific fields to reduce payload size:
|
||||
|
||||
```
|
||||
GET /api/tasks?fields=id,title,status
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{ "id": "1", "title": "Task 1", "status": "active" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Relationships
|
||||
|
||||
Include related resources:
|
||||
|
||||
```
|
||||
GET /api/tasks?include=assignee,project
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"id": "1",
|
||||
"title": "Task 1",
|
||||
"assignee": {
|
||||
"id": "user-1",
|
||||
"name": "John Doe"
|
||||
},
|
||||
"project": {
|
||||
"id": "proj-1",
|
||||
"name": "Project Alpha"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
All authenticated endpoints require Bearer token:
|
||||
|
||||
```http
|
||||
Authorization: Bearer {session_token}
|
||||
```
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
## Validation Errors
|
||||
|
||||
Validation errors return 422 with detailed field errors:
|
||||
|
||||
```json
|
||||
{
|
||||
"error": {
|
||||
"code": "VALIDATION_ERROR",
|
||||
"message": "Input validation failed",
|
||||
"details": {
|
||||
"email": "Invalid email format",
|
||||
"password": "Must be at least 8 characters"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Rate Limiting
|
||||
|
||||
API endpoints are rate-limited:
|
||||
|
||||
- **Anonymous:** 60 requests/hour
|
||||
- **Authenticated:** 1000 requests/hour
|
||||
|
||||
**Rate limit headers:**
|
||||
```http
|
||||
X-RateLimit-Limit: 1000
|
||||
X-RateLimit-Remaining: 950
|
||||
X-RateLimit-Reset: 1643723400
|
||||
```
|
||||
|
||||
When exceeded, returns 429 Too Many Requests.
|
||||
|
||||
## CORS
|
||||
|
||||
CORS is configured for frontend origin:
|
||||
|
||||
```javascript
|
||||
// Allowed origins
|
||||
const origins = [
|
||||
process.env.NEXT_PUBLIC_APP_URL,
|
||||
'http://localhost:3000',
|
||||
'http://localhost:3001'
|
||||
];
|
||||
```
|
||||
|
||||
## Versioning
|
||||
|
||||
Currently v1 (implicit). Future versions will use URL versioning:
|
||||
|
||||
```
|
||||
/api/v2/tasks
|
||||
```
|
||||
|
||||
## Health Check
|
||||
|
||||
```
|
||||
GET /health
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"timestamp": "2026-01-28T12:00:00.000Z",
|
||||
"uptime": 123456
|
||||
}
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Create Task
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3001/api/tasks \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer {token}" \
|
||||
-d '{
|
||||
"title": "Complete API documentation",
|
||||
"description": "Write comprehensive API docs",
|
||||
"priority": "high",
|
||||
"targetDate": "2026-01-30"
|
||||
}'
|
||||
```
|
||||
|
||||
**Response (201):**
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"id": "task-uuid",
|
||||
"title": "Complete API documentation",
|
||||
"status": "pending",
|
||||
"priority": "high",
|
||||
"createdAt": "2026-01-28T12:00:00.000Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### List Tasks with Filters
|
||||
|
||||
```bash
|
||||
curl "http://localhost:3001/api/tasks?status=active&sort=-created_at&limit=5" \
|
||||
-H "Authorization: Bearer {token}"
|
||||
```
|
||||
|
||||
### Update Task
|
||||
|
||||
```bash
|
||||
curl -X PATCH http://localhost:3001/api/tasks/task-uuid \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer {token}" \
|
||||
-d '{
|
||||
"status": "completed"
|
||||
}'
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- **Authentication** — [Authentication Endpoints](../2-authentication/1-endpoints.md)
|
||||
- **Type Definitions** — [API Types](../2-authentication/2-types.md)
|
||||
- **Implementation** — [Development Workflow](../../2-development/1-workflow/1-branching.md)
|
||||
393
docs/4-api/2-authentication/1-endpoints.md
Normal file
393
docs/4-api/2-authentication/1-endpoints.md
Normal file
@@ -0,0 +1,393 @@
|
||||
# Authentication Endpoints
|
||||
|
||||
Complete reference for authentication API endpoints powered by BetterAuth.
|
||||
|
||||
## Base URL
|
||||
|
||||
```
|
||||
http://localhost:3001/auth
|
||||
```
|
||||
|
||||
All authentication endpoints are prefixed with `/auth`.
|
||||
|
||||
## Endpoints
|
||||
|
||||
### Sign Up
|
||||
|
||||
Create a new user account with email and password.
|
||||
|
||||
```http
|
||||
POST /auth/sign-up
|
||||
```
|
||||
|
||||
**Request Body:**
|
||||
```json
|
||||
{
|
||||
"email": "user@example.com",
|
||||
"password": "SecurePass123!",
|
||||
"name": "John Doe"
|
||||
}
|
||||
```
|
||||
|
||||
**Response (201):**
|
||||
```json
|
||||
{
|
||||
"user": {
|
||||
"id": "user-uuid",
|
||||
"email": "user@example.com",
|
||||
"name": "John Doe",
|
||||
"emailVerified": false
|
||||
},
|
||||
"session": {
|
||||
"id": "session-uuid",
|
||||
"token": "eyJhbGciOiJIUzI1NiIs...",
|
||||
"expiresAt": "2026-01-29T12:00:00.000Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Errors:**
|
||||
- `409 Conflict` — Email already exists
|
||||
- `422 Validation Error` — Invalid input
|
||||
|
||||
---
|
||||
|
||||
### Sign In
|
||||
|
||||
Authenticate with email and password.
|
||||
|
||||
```http
|
||||
POST /auth/sign-in
|
||||
```
|
||||
|
||||
**Request Body:**
|
||||
```json
|
||||
{
|
||||
"email": "user@example.com",
|
||||
"password": "SecurePass123!"
|
||||
}
|
||||
```
|
||||
|
||||
**Response (200):**
|
||||
```json
|
||||
{
|
||||
"user": {
|
||||
"id": "user-uuid",
|
||||
"email": "user@example.com",
|
||||
"name": "John Doe"
|
||||
},
|
||||
"session": {
|
||||
"id": "session-uuid",
|
||||
"token": "eyJhbGciOiJIUzI1NiIs...",
|
||||
"expiresAt": "2026-01-29T12:00:00.000Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Errors:**
|
||||
- `401 Unauthorized` — Invalid credentials
|
||||
|
||||
---
|
||||
|
||||
### Sign Out
|
||||
|
||||
Invalidate current session.
|
||||
|
||||
```http
|
||||
POST /auth/sign-out
|
||||
```
|
||||
|
||||
**Headers:**
|
||||
```http
|
||||
Authorization: Bearer {session_token}
|
||||
```
|
||||
|
||||
**Response (200):**
|
||||
```json
|
||||
{
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Get Session
|
||||
|
||||
Retrieve current session information.
|
||||
|
||||
```http
|
||||
GET /auth/session
|
||||
```
|
||||
|
||||
**Headers:**
|
||||
```http
|
||||
Authorization: Bearer {session_token}
|
||||
```
|
||||
|
||||
**Response (200):**
|
||||
```json
|
||||
{
|
||||
"user": {
|
||||
"id": "user-uuid",
|
||||
"email": "user@example.com",
|
||||
"name": "John Doe"
|
||||
},
|
||||
"session": {
|
||||
"id": "session-uuid",
|
||||
"expiresAt": "2026-01-29T12:00:00.000Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Errors:**
|
||||
- `401 Unauthorized` — Invalid or expired session
|
||||
|
||||
---
|
||||
|
||||
### Get Profile
|
||||
|
||||
Get authenticated user's profile (custom endpoint).
|
||||
|
||||
```http
|
||||
GET /auth/profile
|
||||
```
|
||||
|
||||
**Headers:**
|
||||
```http
|
||||
Authorization: Bearer {session_token}
|
||||
```
|
||||
|
||||
**Response (200):**
|
||||
```json
|
||||
{
|
||||
"id": "user-uuid",
|
||||
"email": "user@example.com",
|
||||
"name": "John Doe",
|
||||
"emailVerified": false
|
||||
}
|
||||
```
|
||||
|
||||
**Errors:**
|
||||
- `401 Unauthorized` — Not authenticated
|
||||
|
||||
---
|
||||
|
||||
### OIDC Callback
|
||||
|
||||
OAuth callback handler for Authentik (and other OIDC providers).
|
||||
|
||||
```http
|
||||
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
|
||||
|
||||
---
|
||||
|
||||
## Authentication Flow
|
||||
|
||||
### Email/Password Flow
|
||||
|
||||
```
|
||||
1. User submits credentials → POST /auth/sign-in
|
||||
2. Server validates credentials
|
||||
3. Server creates session
|
||||
4. Server returns session token
|
||||
5. Client stores token
|
||||
6. Client includes token in subsequent requests
|
||||
```
|
||||
|
||||
### OIDC Flow
|
||||
|
||||
```
|
||||
1. User clicks "Sign in with Authentik"
|
||||
2. Frontend redirects to Authentik
|
||||
3. User authenticates with Authentik
|
||||
4. Authentik redirects to /auth/callback/authentik
|
||||
5. Server exchanges code for tokens
|
||||
6. Server creates/updates user
|
||||
7. Server creates session
|
||||
8. Server redirects to frontend with session token
|
||||
```
|
||||
|
||||
## Using Session Tokens
|
||||
|
||||
Include the session token in the `Authorization` header for all authenticated requests:
|
||||
|
||||
```http
|
||||
GET /api/tasks
|
||||
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
|
||||
|
||||
Tokens expire after 24 hours (configurable via `JWT_EXPIRATION`).
|
||||
|
||||
**Check expiration:**
|
||||
```typescript
|
||||
import { AuthSession } from '@mosaic/shared';
|
||||
|
||||
const isExpired = (session: AuthSession) => {
|
||||
return new Date(session.session.expiresAt) < new Date();
|
||||
};
|
||||
```
|
||||
|
||||
**Refresh flow** (future implementation):
|
||||
```http
|
||||
POST /auth/refresh
|
||||
```
|
||||
|
||||
## Error Responses
|
||||
|
||||
### 401 Unauthorized
|
||||
|
||||
```json
|
||||
{
|
||||
"error": {
|
||||
"code": "UNAUTHORIZED",
|
||||
"message": "Invalid or expired session token"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 422 Validation Error
|
||||
|
||||
```json
|
||||
{
|
||||
"error": {
|
||||
"code": "VALIDATION_ERROR",
|
||||
"message": "Input validation failed",
|
||||
"details": {
|
||||
"email": "Invalid email format",
|
||||
"password": "Must be at least 8 characters"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Sign Up
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3001/auth/sign-up \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"email": "jane@example.com",
|
||||
"password": "SecurePass123!",
|
||||
"name": "Jane Doe"
|
||||
}'
|
||||
```
|
||||
|
||||
### Sign In
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3001/auth/sign-in \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"email": "jane@example.com",
|
||||
"password": "SecurePass123!"
|
||||
}'
|
||||
```
|
||||
|
||||
**Save the token from response:**
|
||||
```bash
|
||||
TOKEN=$(curl -X POST http://localhost:3001/auth/sign-in \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"email":"jane@example.com","password":"SecurePass123!"}' \
|
||||
| jq -r '.session.token')
|
||||
```
|
||||
|
||||
### Get Profile
|
||||
|
||||
```bash
|
||||
curl http://localhost:3001/auth/profile \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
|
||||
### Sign Out
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3001/auth/sign-out \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Password Requirements
|
||||
|
||||
- Minimum 8 characters
|
||||
- At least one uppercase letter
|
||||
- At least one lowercase letter
|
||||
- At least one number
|
||||
|
||||
Configure in `apps/api/src/auth/auth.config.ts`.
|
||||
|
||||
### Rate Limiting
|
||||
|
||||
- Sign-up: 5 requests per hour per IP
|
||||
- Sign-in: 10 requests per hour per IP (prevents brute force)
|
||||
|
||||
### CSRF Protection
|
||||
|
||||
OIDC flow includes `state` parameter for CSRF protection.
|
||||
|
||||
### Token Security
|
||||
|
||||
- Tokens are signed with `JWT_SECRET`
|
||||
- Use strong secret (min 32 characters)
|
||||
- Rotate secret regularly in production
|
||||
- Never expose tokens in logs
|
||||
|
||||
## TypeScript Types
|
||||
|
||||
All authentication types are available from `@mosaic/shared`:
|
||||
|
||||
```typescript
|
||||
import type {
|
||||
AuthUser,
|
||||
AuthSession,
|
||||
LoginRequest,
|
||||
LoginResponse,
|
||||
} from '@mosaic/shared';
|
||||
|
||||
// Use in frontend
|
||||
const login = async (req: LoginRequest): Promise<AuthSession> => {
|
||||
const res = await fetch('/auth/sign-in', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(req),
|
||||
});
|
||||
return res.json();
|
||||
};
|
||||
|
||||
// Use in backend
|
||||
@Post('sign-in')
|
||||
async signIn(@Body() request: LoginRequest): Promise<LoginResponse> {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
See [API Types](2-types.md) for complete type definitions.
|
||||
|
||||
## Next Steps
|
||||
|
||||
- **Configure OIDC** — [Configuration → Authentik](../../1-getting-started/3-configuration/2-authentik.md)
|
||||
- **Review Types** — [Authentication Types](2-types.md)
|
||||
- **Understand Architecture** — [Architecture → Authentication](../../3-architecture/2-authentication/1-betterauth.md)
|
||||
32
docs/4-api/README.md
Normal file
32
docs/4-api/README.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# API Reference
|
||||
|
||||
Complete API documentation for Mosaic Stack backend.
|
||||
|
||||
## Chapters
|
||||
|
||||
1. **Conventions** — API design patterns, endpoints, and response formats
|
||||
2. **Authentication** — Auth endpoints and type definitions
|
||||
|
||||
## API Standards
|
||||
|
||||
- **REST Conventions:** Standard HTTP methods and status codes
|
||||
- **Type Safety:** All requests/responses typed via `@mosaic/shared`
|
||||
- **Error Handling:** Consistent error response format
|
||||
- **Authentication:** Bearer token via Authorization header
|
||||
|
||||
## Base URL
|
||||
|
||||
- **Development:** `http://localhost:3001`
|
||||
- **Production:** Configure via `NEXT_PUBLIC_APP_URL`
|
||||
|
||||
## Authentication
|
||||
|
||||
All authenticated endpoints require:
|
||||
```http
|
||||
Authorization: Bearer {session_token}
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Review **Architecture** for design context
|
||||
- Check **Development** for contributing guidelines
|
||||
Reference in New Issue
Block a user