navidocs/server/routes/README.md
ggq-admin 155a8c0305 feat: NaviDocs MVP - Complete codebase extraction from lilian1
## Backend (server/)
- Express 5 API with security middleware (helmet, rate limiting)
- SQLite database with WAL mode (schema from docs/architecture/)
- Meilisearch integration with tenant tokens
- BullMQ + Redis background job queue
- OCR pipeline with Tesseract.js
- File safety validation (extension, MIME, size)
- 4 API route modules: upload, jobs, search, documents

## Frontend (client/)
- Vue 3 with Composition API (<script setup>)
- Vite 5 build system with HMR
- Tailwind CSS (Meilisearch-inspired design)
- UploadModal with drag-and-drop
- FigureZoom component (ported from lilian1)
- Meilisearch search integration with tenant tokens
- Job polling composable
- Clean SVG icons (no emojis)

## Code Extraction
-  manuals.js → UploadModal.vue, useJobPolling.js
-  figure-zoom.js → FigureZoom.vue
-  service-worker.js → client/public/service-worker.js (TODO)
-  glossary.json → Merged into Meilisearch synonyms
-  Discarded: quiz.js, persona.js, gamification.js (Frank-AI junk)

## Documentation
- Complete extraction plan in docs/analysis/
- README with quick start guide
- Architecture summary in docs/architecture/

## Build Status
- Server dependencies:  Installed (234 packages)
- Client dependencies:  Installed (160 packages)
- Client build:  Successful (2.63s)

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-19 01:55:44 +02:00

9.5 KiB

NaviDocs API Routes

This directory contains the backend API route modules for NaviDocs server.

Route Modules

1. Upload Route (upload.js)

Endpoint: POST /api/upload

Handles PDF file uploads with validation, storage, and OCR queue processing.

Request:

  • Content-Type: multipart/form-data
  • Body:
    • file: PDF file (max 50MB)
    • title: Document title (string, required)
    • documentType: Document type (string, required)
      • Values: owner-manual, component-manual, service-record, etc.
    • organizationId: Organization UUID (string, required)
    • entityId: Entity UUID (string, optional)
    • subEntityId: Sub-entity UUID (string, optional)
    • componentId: Component UUID (string, optional)

Response:

{
  "jobId": "uuid",
  "documentId": "uuid",
  "message": "File uploaded successfully and queued for processing"
}

Status Codes:

  • 201: Created - File uploaded successfully
  • 400: Bad Request - Invalid file or missing fields
  • 401: Unauthorized - Authentication required
  • 500: Internal Server Error

Security:

  • File extension validation (.pdf only)
  • MIME type verification (magic number detection)
  • File size limit (50MB default)
  • Filename sanitization
  • SHA256 hash for deduplication

2. Jobs Route (jobs.js)

Endpoints:

Get Job Status

GET /api/jobs/:id

Query OCR job status and progress.

Response:

{
  "jobId": "uuid",
  "documentId": "uuid",
  "status": "pending|processing|completed|failed",
  "progress": 0-100,
  "error": "error message or null",
  "startedAt": timestamp,
  "completedAt": timestamp,
  "createdAt": timestamp,
  "document": {
    "id": "uuid",
    "status": "processing|indexed|failed",
    "pageCount": 42
  }
}

List Jobs

GET /api/jobs

List jobs with optional filtering.

Query Parameters:

  • status: Filter by status (pending, processing, completed, failed)
  • limit: Results per page (default: 50, max: 100)
  • offset: Pagination offset (default: 0)

Response:

{
  "jobs": [
    {
      "jobId": "uuid",
      "documentId": "uuid",
      "documentTitle": "Owner Manual",
      "documentType": "owner-manual",
      "status": "completed",
      "progress": 100,
      "error": null,
      "startedAt": timestamp,
      "completedAt": timestamp,
      "createdAt": timestamp
    }
  ],
  "pagination": {
    "limit": 50,
    "offset": 0
  }
}

Status Codes:

  • 200: OK
  • 400: Bad Request - Invalid job ID
  • 401: Unauthorized
  • 404: Not Found - Job not found

3. Search Route (search.js)

Endpoints:

Generate Tenant Token

POST /api/search/token

Generate Meilisearch tenant token for client-side search with 1-hour TTL.

Request Body:

{
  "expiresIn": 3600
}

Response:

{
  "token": "tenant-token-string",
  "expiresAt": "2025-10-19T12:00:00.000Z",
  "expiresIn": 3600,
  "indexName": "navidocs-pages",
  "searchUrl": "http://127.0.0.1:7700"
}

Security:

  • Token scoped to user's organizations
  • Row-level security via filters
  • Maximum expiration: 24 hours
  • Filters: userId = X OR organizationId IN [Y, Z]

POST /api/search

Perform server-side search (optional, for server-rendered results).

Request Body:

{
  "q": "search query",
  "filters": {
    "documentType": "owner-manual",
    "entityId": "uuid",
    "language": "en"
  },
  "limit": 20,
  "offset": 0
}

Response:

{
  "hits": [
    {
      "id": "page-uuid",
      "text": "highlighted text",
      "pageNumber": 42,
      "documentId": "uuid",
      "documentTitle": "Owner Manual"
    }
  ],
  "estimatedTotalHits": 150,
  "query": "search query",
  "processingTimeMs": 12,
  "limit": 20,
  "offset": 0
}

Health Check

GET /api/search/health

Check Meilisearch connectivity.

Response:

{
  "status": "ok",
  "meilisearch": {
    "status": "available"
  }
}

4. Documents Route (documents.js)

Endpoints:

Get Document

GET /api/documents/:id

Query document metadata with ownership verification.

Response:

{
  "id": "uuid",
  "organizationId": "uuid",
  "entityId": "uuid",
  "subEntityId": "uuid",
  "componentId": "uuid",
  "uploadedBy": "user-uuid",
  "title": "Owner Manual",
  "documentType": "owner-manual",
  "fileName": "manual.pdf",
  "fileSize": 1024000,
  "mimeType": "application/pdf",
  "pageCount": 42,
  "language": "en",
  "status": "indexed",
  "createdAt": timestamp,
  "updatedAt": timestamp,
  "metadata": {},
  "filePath": "/path/to/file.pdf",
  "pages": [
    {
      "id": "page-uuid",
      "pageNumber": 1,
      "ocrConfidence": 0.95,
      "ocrLanguage": "en",
      "ocrCompletedAt": timestamp,
      "searchIndexedAt": timestamp
    }
  ],
  "entity": {
    "id": "uuid",
    "name": "My Boat",
    "entityType": "boat"
  },
  "component": {
    "id": "uuid",
    "name": "Main Engine",
    "manufacturer": "Caterpillar",
    "modelNumber": "C7.1"
  }
}

Status Codes:

  • 200: OK
  • 400: Bad Request - Invalid document ID
  • 401: Unauthorized
  • 403: Forbidden - No access to document
  • 404: Not Found

Security:

  • Ownership verification
  • Organization membership check
  • Document share permissions

List Documents

GET /api/documents

List documents with filtering.

Query Parameters:

  • organizationId: Filter by organization
  • entityId: Filter by entity
  • documentType: Filter by document type
  • status: Filter by status
  • limit: Results per page (default: 50)
  • offset: Pagination offset (default: 0)

Response:

{
  "documents": [
    {
      "id": "uuid",
      "organizationId": "uuid",
      "entityId": "uuid",
      "title": "Owner Manual",
      "documentType": "owner-manual",
      "fileName": "manual.pdf",
      "fileSize": 1024000,
      "pageCount": 42,
      "status": "indexed",
      "createdAt": timestamp,
      "updatedAt": timestamp
    }
  ],
  "pagination": {
    "total": 150,
    "limit": 50,
    "offset": 0,
    "hasMore": true
  }
}

Delete Document

DELETE /api/documents/:id

Soft delete a document (marks as deleted).

Response:

{
  "message": "Document deleted successfully",
  "documentId": "uuid"
}

Status Codes:

  • 200: OK
  • 401: Unauthorized
  • 403: Forbidden - No permission to delete
  • 404: Not Found

Permissions:

  • Document uploader
  • Organization admin
  • Organization manager

Authentication

All routes require authentication via JWT token (except health checks).

Header:

Authorization: Bearer <jwt-token>

The authentication middleware attaches req.user with:

{
  id: "user-uuid",
  email: "user@example.com",
  name: "User Name"
}

Error Handling

All routes follow consistent error response format:

{
  "error": "Error message",
  "message": "Detailed error description"
}

Common Status Codes:

  • 400: Bad Request - Invalid input
  • 401: Unauthorized - Missing or invalid authentication
  • 403: Forbidden - Insufficient permissions
  • 404: Not Found - Resource not found
  • 500: Internal Server Error - Server error

Database Schema

Routes use the database schema defined in /server/db/schema.sql:

Tables:

  • documents - Document metadata
  • document_pages - OCR results per page
  • ocr_jobs - Background job queue
  • users - User accounts
  • organizations - Organizations
  • user_organizations - Membership
  • entities - Boats, marinas, condos
  • components - Engines, panels, appliances
  • document_shares - Sharing permissions

Dependencies

Services:

  • db/db.js - SQLite database connection
  • services/file-safety.js - File validation
  • services/queue.js - BullMQ job queue
  • config/meilisearch.js - Meilisearch client

External:

  • Meilisearch - Search engine (port 7700)
  • Redis - Job queue backend (port 6379)
  • SQLite - Database storage

Testing

Upload Example

curl -X POST http://localhost:3001/api/upload \
  -H "Authorization: Bearer <token>" \
  -F "file=@manual.pdf" \
  -F "title=Owner Manual" \
  -F "documentType=owner-manual" \
  -F "organizationId=<uuid>"

Get Job Status

curl http://localhost:3001/api/jobs/<job-id> \
  -H "Authorization: Bearer <token>"

Generate Search Token

curl -X POST http://localhost:3001/api/search/token \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"expiresIn": 3600}'

Get Document

curl http://localhost:3001/api/documents/<doc-id> \
  -H "Authorization: Bearer <token>"

Security Considerations

  1. File Validation

    • Extension check (.pdf only)
    • MIME type verification (magic numbers)
    • File size limits (50MB default)
    • Filename sanitization
  2. Access Control

    • JWT authentication required
    • Organization-based permissions
    • Row-level security in Meilisearch
    • Document sharing permissions
  3. Input Sanitization

    • UUID format validation
    • SQL injection prevention (prepared statements)
    • XSS prevention (no user input in HTML)
  4. Rate Limiting

    • 100 requests per 15 minutes per IP
    • Configurable via environment variables

Environment Variables

# Server
PORT=3001
NODE_ENV=development

# Database
DATABASE_PATH=./db/navidocs.db

# Meilisearch
MEILISEARCH_HOST=http://127.0.0.1:7700
MEILISEARCH_MASTER_KEY=your-master-key-here
MEILISEARCH_INDEX_NAME=navidocs-pages

# Redis
REDIS_HOST=127.0.0.1
REDIS_PORT=6379

# Authentication
JWT_SECRET=your-jwt-secret-here

# File Upload
MAX_FILE_SIZE=52428800
UPLOAD_DIR=./uploads

# Rate Limiting
RATE_LIMIT_WINDOW_MS=900000
RATE_LIMIT_MAX_REQUESTS=100