How to Set Up Document Management with File Attachments in TREK: A Complete Guide

Enable the Documents addon in TREK's admin panel, configure file_upload permissions, and use the built-in FilesService API or React useFileManager hook to upload, link, and manage attachments in real-time.

TREK implements document management as a first-class addon called Documents, providing a complete solution for handling file attachments within trip planning workflows. This guide walks through the architecture spanning NestJS 11 backend services and React 19 frontend hooks, showing exactly how to enable and configure document management with file attachments in TREK according to the source code in mauriceboe/TREK.

Backend Architecture: FilesService and REST API

The backend implements a robust file handling layer using NestJS decorators and custom interceptors, storing metadata in SQLite while persisting binaries on the filesystem.

Core Service Implementation

The FilesService class in server/src/nest/files/files.service.ts handles the core business logic for document management. It manages disk storage under the uploads/ directory, where files are saved with UUID-based filenames to prevent collisions. The service performs permission checks against file_upload, file_edit, and file_delete permissions, implements soft-delete/restore functionality, and manages linking files to places or reservations.

Every mutation operation triggers WebSocket broadcasts via the broadcast method, emitting events like file:created, file:updated, or file:deleted to synchronize all connected clients in real-time.

REST Endpoints and Upload Flow

The FilesController in server/src/nest/files/files.controller.ts exposes routes under /api/trips/:tripId/files. The upload flow follows this sequence:

  1. Request validation: The controller enforces trip ownership, demo-mode restrictions, and MIME type validation before accepting multipart data.
  2. File interception: FileInterceptor (configured with UPLOAD constants) saves the binary to uploads/<uuid>.<ext>.
  3. Metadata persistence: FilesService.createFile writes a row to the SQLite files table containing mime_type, original_name, optional place_id/reservation_id, and the uploader's user ID.
  4. Real-time sync: A file:created WebSocket event fires, allowing the frontend to instantly display the new attachment.

Configuration and Security Constraints

Security constraints are defined in server/src/services/fileService.ts:

  • Maximum file size: MAX_FILE_SIZE = 50 * 1024 * 1024 (50 MiB)
  • Blocked extensions: .svg, .exe, and other dangerous file types are rejected
  • Allowed types: Admin-configurable via the ALLOWED_FILE_TYPES environment variable or UI settings

Permissions are declared in server/src/nest/services/permissions.ts with three file-related keys:

  • file_upload (default: trip_member)
  • file_edit (default: trip_member)
  • file_delete (default: admin)

Frontend Implementation: useFileManager Hook

The React frontend encapsulates file management logic in a dedicated hook that handles drag-and-drop, paste operations, and permission-gated UI controls.

Drag-and-Drop and Upload Handling

The useFileManager hook in client/src/components/Files/useFileManager.ts integrates react-dropzone for drag-and-drop functionality. Key implementation details include:

  • Size enforcement: The dropzone respects the 50 MiB limit (maxSize: 50 * 1024 * 1024) defined in the backend constants.
  • Paste handling: The hook intercepts clipboard paste events and validates content before upload.
  • Auto-assignment: After successful upload, the hook automatically opens the "assign" modal to link the file to a place or reservation (see lines 18-22 of the hook).

The hook communicates with filesApi in client/src/api/client.ts, which provides methods for list, upload, toggleStar, restore, permanentDelete, emptyTrash, and link operations.

Permission Gates and UI Controls

Before enabling upload buttons or delete actions, the UI checks useCanDo from client/src/store/permissionsStore.ts. This utility returns a boolean indicating whether the current user holds the required permission level (trip_member, trip_owner, or admin) for the specific file operation.

The FilesPanel.tsx component renders filter tabs (All, PDF, Images, Docs, Starred, Collab), a lightbox for image previews, and modals for linking files to itinerary items.

Enabling and Configuring the Documents Addon

Document management requires explicit activation and configuration through the admin interface.

Activating the Addon in Admin Panel

Navigate to Admin → Addons and toggle the Documents addon to enable file attachment functionality system-wide. This exposes the Files tab in the trip planner interface and activates the REST endpoints.

Configuring Upload Limits and Allowed File Types

Admins can adjust constraints by modifying server/src/services/fileService.ts:

// server/src/services/fileService.ts
export const MAX_FILE_SIZE = 50 * 1024 * 1024; // 50 MiB default
export const BLOCKED_EXTENSIONS = ['.svg', '.exe', '.bat', '.sh'];

Allowed file types can be configured via environment variables or the Admin-Addons UI page, which writes to the application's configuration store.

Permission Levels for File Operations

Permission defaults are configured in server/src/nest/services/permissions.ts:

// server/src/nest/services/permissions.ts
export const PERMISSIONS = [
  { key: 'file_upload', defaultLevel: 'trip_member', allowedLevels: ['admin', 'trip_owner', 'trip_member'] },
  { key: 'file_edit',   defaultLevel: 'trip_member', allowedLevels: ['admin', 'trip_owner', 'trip_member'] },
  { key: 'file_delete', defaultLevel: 'admin',       allowedLevels: ['admin'] },
];

Changing defaultLevel or allowedLevels instantly updates the UI because useCanDo reads this runtime configuration to enable or disable action buttons.

Practical Implementation Examples

Uploading and Linking Files via REST API

Use the /api/trips/:tripId/files endpoint to upload and immediately associate files with itinerary items:


# Upload a PDF and link to reservation #42

curl -X POST "http://localhost:3000/api/trips/7/files" \
  -H "Authorization: Bearer <jwt>" \
  -F "file=@invoice.pdf;type=application/pdf" \
  -F "reservation_id=42" \
  -H "x-socket-id: abc123"

# Star the uploaded file

curl -X PATCH "http://localhost:3000/api/trips/7/files/23/star" \
  -H "Authorization: Bearer <jwt>" \
  -H "x-socket-id: abc123"

# List all non-trashed files

curl -X GET "http://localhost:3000/api/trips/7/files" \
  -H "Authorization: Bearer <jwt>"

React Component Integration

Implement document management in custom components using the useFileManager hook:

import { useFileManager } from '@/components/Files/useFileManager';
import { filesApi } from '@/api/client';

function TripFiles({ tripId, places, reservations }) {
  const { files, uploading, filterType, setFilterType, handleStar, handleDelete } =
    useFileManager({
      tripId,
      places,
      reservations,
      onUpload: (fd) => filesApi.upload(tripId, fd),
      onDelete: (id) => filesApi.remove(tripId, id),
    });

  return (
    <section>
      <header>
        <select value={filterType} onChange={e => setFilterType(e.target.value)}>
          <option value="all">All</option>
          <option value="pdf">PDF</option>
          <option value="image">Images</option>
          <option value="doc">Docs</option>
        </select>
      </header>

      {uploading && <p>Uploading…</p>}

      <ul>
        {files.map(f => (
          <li key={f.id}>
            {f.name}
            <button onClick={() => handleStar(f.id)}>
              {f.starred ? '★' : '☆'}
            </button>
            <button onClick={() => handleDelete(f.id)}>Trash</button>
          </li>
        ))}
      </ul>
    </section>
  );
}

Customizing Permission Levels

To restrict file deletion to administrators only, modify the permissions array:

// server/src/nest/services/permissions.ts
{
  key: 'file_delete',
  defaultLevel: 'admin',
  allowedLevels: ['admin', 'trip_owner'] // Expand if needed
}

Link existing files to places using the linkFile API wrapper:

// Link to a specific place
import { linkFile } from '@/api/client';

await linkFile(tripId, fileId, { place_id: 12 });
// Hits POST /api/trips/:tripId/files/:fileId/link

Summary

  • Enable the Documents addon via Admin → Addons to activate file attachment functionality.
  • Configure storage limits in server/src/services/fileService.ts (default 50 MiB) and define allowed file types.
  • Set permission levels in server/src/nest/services/permissions.ts to control who can upload, edit, or delete files.
  • Use FilesService for backend operations with automatic WebSocket synchronization.
  • Implement useFileManager in React components to handle drag-and-drop, filtering, and permission-gated actions.
  • Store files on the filesystem under uploads/ with UUID filenames while tracking metadata in SQLite.

Frequently Asked Questions

What file types are supported for upload in TREK?

TREK supports configurable file types defined by the admin via the ALLOWED_FILE_TYPES setting. By default, the system blocks dangerous extensions like .svg, .exe, .bat, and .sh as defined in server/src/services/fileService.ts. Administrators can whitelist specific MIME types through the Admin → Addons page or environment variables.

How does TREK handle file storage and security?

Uploaded files are stored on the server's filesystem under the uploads/ directory with UUID-based filenames to prevent directory traversal attacks. Metadata including original names, MIME types, and ownership is stored in the SQLite database at data/travel.db. The FilesController enforces trip ownership validation and permission checks before any file operation, ensuring users can only access documents within trips they belong to.

Yes, the FilesService supports linking files to places and reservations through the place_id and reservation_id foreign keys. When uploading via POST /api/trips/:tripId/files, include these IDs as form fields. Alternatively, use the linkFile API method or the UI modal that appears automatically after upload to associate existing files with itinerary items.

What is the maximum file size allowed in TREK document management?

The default maximum file size is 50 MiB (50 * 1024 * 1024 bytes), enforced by both the frontend useFileManager hook and the backend FilesController. Administrators can modify this limit by changing the MAX_FILE_SIZE constant in server/src/services/fileService.ts and ensuring the frontend dropzone configuration matches the new value.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →