fix(api): align frontend API client with backend endpoints

- Fix addDocumentTags to use POST /documents/:id/tags (was using PUT)
- Add getDocumentsByTag(tag) -> GET /tags/:tag
- Add getLibraryTree(id) -> GET /libraries/:id/tree
- Add getLibraryDocuments(id) -> GET /libraries/:id/documents
- Add getProjectDocuments(id) -> GET /projects/:id/documents
- Add getFolder(id) -> GET /folders/:id
- Add getFolderDocuments(id) -> GET /folders/:id/documents
- Add getFolderTree(id) -> GET /folders/:id/tree
- Add api-endpoints.md with full API documentation
- Remove duplicate/unused dead code blocks
This commit is contained in:
Hiro
2026-03-28 17:12:38 +00:00
parent 5b234cb819
commit 007c51a98f
2 changed files with 472 additions and 12 deletions

422
api-endpoints.md Normal file
View File

@@ -0,0 +1,422 @@
# SimpleNote Web - API Endpoints
**Base URL:** `/api/v1`
**Auth:** All endpoints require `Authorization: Bearer <token>` header unless noted.
---
## Auth
### `POST /auth/token`
Generate a new API token (admin only).
**Request body:**
```json
{ "label": "my-token-label" }
```
**Response `201`:**
```json
{ "token": "snk_...", "label": "my-token-label", "createdAt": "2026-03-28T..." }
```
---
### `GET /auth/verify`
Verify a token is valid.
**Response `200`:**
```json
{ "valid": true, "token": "snk_..." }
```
---
## Projects
### `GET /projects`
List all projects.
**Response `200`:**
```json
{ "projects": [...] }
```
---
### `POST /projects`
Create a project.
**Request body:**
```json
{ "name": "Project Name", "description": "optional" }
```
**Response `201`:**
```json
{ "id": "...", "name": "...", "description": "...", "createdAt": "...", "updatedAt": "..." }
```
---
### `GET /projects/:id`
Get a single project.
**Response `200`:**
```json
{ "id": "...", "name": "...", "description": "...", "createdAt": "...", "updatedAt": "..." }
```
**Response `404`:**
```json
{ "error": "Project not found", "code": "NOT_FOUND" }
```
---
### `PUT /projects/:id`
Update a project.
**Request body:**
```json
{ "name": "New Name", "description": "New desc" }
```
**Response `200`:**
```json
{ "id": "...", "name": "...", "description": "...", "createdAt": "...", "updatedAt": "..." }
```
---
### `DELETE /projects/:id`
Delete a project.
**Response `200`:**
```json
{ "deleted": true, "id": "..." }
```
---
### `GET /projects/:id/tree`
Get full project tree (folder hierarchy + documents).
**Response `200`:**
```json
{
"project": { "id": "...", "name": "..." },
"documents": [...],
"folders": [...],
"totalDocuments": 5
}
```
---
### `GET /projects/:id/documents`
List documents directly in a project (not in sub-folders).
**Response `200`:**
```json
{ "documents": [...], "total": 3 }
```
---
## Folders
### `GET /folders?projectId=X&parentId=Y`
List folders in a project. `projectId` is required.
**Query params:**
- `projectId` (required): Project ID
- `parentId` (optional): Parent folder ID. Omit for root folders.
**Response `200`:**
```json
{ "folders": [...] }
```
**Response `400`:**
```json
{ "error": "projectId query parameter is required", "code": "VALIDATION_ERROR" }
```
---
### `POST /folders`
Create a folder.
**Request body:**
```json
{ "name": "Folder Name", "projectId": "...", "parentId": "..." }
```
**Response `201`:**
```json
{ "id": "...", "name": "...", "projectId": "...", "parentId": null, "createdAt": "...", "updatedAt": "..." }
```
---
### `GET /folders/:id`
Get a single folder.
**Response `200`:**
```json
{ "id": "...", "name": "...", "projectId": "...", "parentId": null, "createdAt": "...", "updatedAt": "..." }
```
---
### `PUT /folders/:id`
Update a folder (rename).
**Request body:**
```json
{ "name": "New Folder Name" }
```
**Response `200`:**
```json
{ "id": "...", "name": "New Folder Name", ... }
```
---
### `DELETE /folders/:id`
Delete a folder.
**Response `200`:**
```json
{ "deleted": true, "id": "..." }
```
---
### `GET /folders/:id/documents`
List documents directly in a folder.
**Response `200`:**
```json
{ "documents": [...], "total": 2 }
```
---
### `GET /folders/:id/tree`
Get full folder tree (sub-folders + documents recursively).
**Response `200`:**
```json
{
"folder": { "id": "...", "name": "..." },
"documents": [...],
"subFolders": [...],
"totalDocuments": 4
}
```
---
## Documents
### `GET /documents`
List documents with optional filters.
**Query params:**
- `tag` - Filter by tag
- `library` - Filter by library ID
- `project` - Filter by project ID
- `folder` - Filter by folder ID
- `type` - Filter by type (e.g., `requirement`, `general`)
- `status` - Filter by status (e.g., `draft`, `approved`)
- `limit` - Max results (default 50)
- `offset` - Pagination offset (default 0)
**Response `200`:**
```json
{ "documents": [...], "total": 10, "limit": 50, "offset": 0 }
```
---
### `POST /documents`
Create a document.
**Request body:**
```json
{
"title": "Doc Title",
"libraryId": "...",
"projectId": "...",
"folderId": "...",
"content": "# Markdown content",
"tags": ["tag1", "tag2"],
"type": "requirement",
"priority": "high",
"status": "draft"
}
```
**Response `201`:**
```json
{ "id": "...", "title": "...", "content": "...", "tags": [...], "type": "...", "priority": "...", "status": "...", "createdAt": "...", "updatedAt": "..." }
```
---
### `GET /documents/:id`
Get a single document.
**Response `200`:**
```json
{ "id": "...", "title": "...", "content": "...", "tags": [...], "type": "...", "priority": "...", "status": "...", "createdAt": "...", "updatedAt": "..." }
```
---
### `PUT /documents/:id`
Update a document.
**Request body:**
```json
{ "title": "New Title", "content": "...", "tags": [...], "type": "...", "priority": "...", "status": "...", "folderId": "..." }
```
**Response `200`:** Document object.
---
### `DELETE /documents/:id`
Delete a document.
**Response `200`:**
```json
{ "deleted": true, "id": "..." }
```
---
### `GET /documents/:id/export`
Export document as markdown.
**Response `200`:** Raw text/markdown (Content-Type: `text/markdown`).
---
### `POST /documents/:id/tags`
Add tags to a document.
**Request body:**
```json
{ "tags": ["new-tag", "another"] }
```
**Response `200`:** Updated document object.
---
## Tags
### `GET /tags`
List all tags with counts.
**Response `200`:**
```json
{ "tags": [{ "name": "backend", "count": 5 }, ...], "total": 12 }
```
---
### `GET /tags/:tag`
Get all documents with a specific tag.
**Response `200`:**
```json
{ "tag": "backend", "documents": [...], "count": 5 }
```
---
## Libraries
### `GET /libraries`
List root libraries.
**Response `200`:**
```json
{ "libraries": [...] }
```
---
### `POST /libraries`
Create a library.
**Request body:**
```json
{ "name": "Library Name", "parentId": "..." }
```
**Response `201`:**
```json
{ "id": "...", "name": "...", "parentId": null, "createdAt": "...", "updatedAt": "..." }
```
---
### `GET /libraries/:id`
Get library contents.
**Response `200`:**
```json
{ "id": "...", "name": "...", "parentId": null, ... }
```
---
### `GET /libraries/:id/tree`
Get full library tree.
**Response `200`:**
```json
{
"library": { ... },
"documents": [...],
"subLibraries": [...],
"totalDocuments": 3
}
```
---
### `GET /libraries/:id/documents`
List documents in a library.
**Response `200`:**
```json
{ "documents": [...], "total": 3 }
```
---
### `DELETE /libraries/:id`
Delete a library.
**Response `200`:**
```json
{ "deleted": true, "id": "..." }
```
---
## Error Format
All errors follow this structure:
```json
{ "error": "Human-readable message", "code": "ERROR_CODE" }
```
Common codes: `VALIDATION_ERROR`, `NOT_FOUND`, `UNAUTHORIZED`, `INTERNAL_ERROR`