Initial commit
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
# File Manager Module
|
||||
|
||||
The File Manager module provides authenticated file browsing and file operations for the admin app. It has a backend API for storage operations and a frontend UI for browsing, previewing, uploading, renaming, deleting, duplicating, and editing supported files.
|
||||
|
||||
## Source Map
|
||||
|
||||
| Area | Path |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------- |
|
||||
| Backend module | `backend/app/base_modules/FileManager/` |
|
||||
| Backend storage config | `backend/config/drive.ts` |
|
||||
| Backend S3 adapter | `backend/app/base_modules/FileManager/services/storage_service/s3_storage_service.ts` |
|
||||
| Frontend module | `apps/admin/src/core/FileManager/` |
|
||||
| Frontend file assets | `apps/admin/public/media/files/` |
|
||||
| Frontend routes | `apps/admin/src/core/FileManager/config/routing.tsx` |
|
||||
|
||||
## Documentation
|
||||
|
||||
| Page | Purpose |
|
||||
| ------------------------- | ---------------------------------------------------------------------------------- |
|
||||
| [Backend](./backend.md) | API routes, services, storage adapter contract, permissions, and extension points. |
|
||||
| [Frontend](./frontend.md) | UI structure, React Query and MobX, cache updates, pages, and widgets. |
|
||||
|
||||
## Main Capabilities
|
||||
|
||||
- Browse folders and files.
|
||||
- Filter, sort, and paginate directory entries.
|
||||
- Preview file details and text content for supported extensions.
|
||||
- Stream raw files for preview or download.
|
||||
- Create folders.
|
||||
- Upload files with progress and cancellation support.
|
||||
- Rename, delete, and duplicate entries.
|
||||
- Search entries.
|
||||
- Open an image editor for image files.
|
||||
- Pick files through reusable frontend picker components.
|
||||
- Insert files into RichText editor through reusable frontend picker components.
|
||||
- Use local filesystem storage or S3-compatible storage through Adonis Drive.
|
||||
|
||||
## High-Level Flow
|
||||
|
||||
```txt
|
||||
Admin route
|
||||
-> FileManager frontend page
|
||||
-> React Query API hook
|
||||
-> /file-manager backend route
|
||||
-> FileManagerController
|
||||
-> FileManagerService
|
||||
-> StorageService adapter
|
||||
-> Adonis Drive fs or s3 disk
|
||||
```
|
||||
|
||||
## Data Contract
|
||||
|
||||
Backend responses use `Entry` objects:
|
||||
|
||||
```ts
|
||||
type Entry = FileEntry | FolderEntry;
|
||||
|
||||
type FileEntry = {
|
||||
type: "file";
|
||||
name: string;
|
||||
path: string;
|
||||
url: string;
|
||||
size?: number;
|
||||
updatedAt?: string;
|
||||
};
|
||||
|
||||
type FolderEntry = {
|
||||
type: "folder";
|
||||
name: string;
|
||||
path: string;
|
||||
updatedAt?: string;
|
||||
};
|
||||
```
|
||||
|
||||
The frontend maps DTOs into frontend entities and adds UI-oriented metadata such as `parent` and `extension`.
|
||||
|
||||
## Ownership Rules
|
||||
|
||||
- Backend owns storage, validation, authorization, path safety, and API response shape.
|
||||
- Frontend owns navigation, selection, view settings, operation progress, and user interactions.
|
||||
- React Query owns server state in the frontend.
|
||||
- MobX stores own local UI and operation state in the frontend.
|
||||
- Storage adapters must implement `storage_service_interface.ts`; callers should depend on that interface, not a concrete disk implementation.
|
||||
- The active storage adapter is selected by `DRIVE_DISK`.
|
||||
|
||||
## Common Change Guide
|
||||
|
||||
| Change | Start Here |
|
||||
| --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Add or change an API route | `backend/app/base_modules/FileManager/routes.ts` and `controller/file_manager_controller.ts` |
|
||||
| Change path validation or naming rules | `services/path_service.ts` |
|
||||
| Change list filtering, sorting, or pagination | `services/entry_list_service.ts` and frontend query payloads |
|
||||
| Add or change a storage backend | `services/storage_service/storage_service_interface.ts`, `create_storage_service.ts`, and [Backend storage docs](./backend.md#storage) |
|
||||
| Add a frontend operation | `apps/admin/src/core/FileManager/data/queries/` and relevant store/widget |
|
||||
| Add file type icons | `apps/admin/public/media/files/` and `shared/file/fileTypeImportMap.ts` |
|
||||
| Add preview support | Backend `file_preview_service.ts` and frontend `view/ui/FilePreview/` |
|
||||
|
||||
## Planned Work
|
||||
|
||||
- Add more non-filesystem storage adapters such as:
|
||||
- Google Cloud Storage
|
||||
- Supabase Storage
|
||||
- Digital Ocean Spaces
|
||||
- Cloudflare R2
|
||||
- Add testing notes once module-level backend and frontend tests are established.
|
||||
- Add libs for rendering large lists.
|
||||
@@ -0,0 +1,368 @@
|
||||
# File Manager Backend
|
||||
|
||||
The backend module exposes authenticated file operations under `/file-manager`. It validates requests, authorizes actions, normalizes paths, orchestrates file-manager behavior, and delegates raw storage work to a storage adapter.
|
||||
|
||||
Source root:
|
||||
|
||||
```txt
|
||||
backend/app/base_modules/FileManager/
|
||||
```
|
||||
|
||||
## Request Flow
|
||||
|
||||
```txt
|
||||
routes.ts
|
||||
-> controller/file_manager_controller.ts
|
||||
-> validator/*
|
||||
-> policy/file_manager_policy.ts
|
||||
-> services/file_manager_service.ts
|
||||
-> services/storage_service/storage_service_interface.ts
|
||||
-> services/storage_service/create_storage_service.ts
|
||||
-> services/storage_service/fs_storage_service.ts | services/storage_service/s3_storage_service.ts
|
||||
-> Adonis Drive
|
||||
```
|
||||
|
||||
The controller should stay thin. It handles HTTP concerns: auth policy checks, request validation, path extraction, status codes, and response payload shape.
|
||||
|
||||
`FileManagerService` owns business orchestration. Storage adapters own raw persistence operations.
|
||||
|
||||
## Routes
|
||||
|
||||
All routes are prefixed with `/file-manager` and use `middleware.auth()`.
|
||||
|
||||
| Method | Route | Controller Method | Purpose |
|
||||
| -------- | ------------------ | ----------------- | ------------------------------------------------------- |
|
||||
| `GET` | `/` | `index` | List root directory entries. |
|
||||
| `GET` | `/*` | `index` | List entries for a nested directory. |
|
||||
| `GET` | `/files/details/*` | `show` | Return file metadata and optional text preview content. |
|
||||
| `GET` | `/files/raw/*` | `raw` | Stream raw file content. |
|
||||
| `GET` | `/search?q=value` | `search` | Search entries recursively. |
|
||||
| `POST` | `/folders` | `createFolder` | Create a folder. |
|
||||
| `POST` | `/files` | `upload` | Upload a file. |
|
||||
| `POST` | `/files/duplicate` | `duplicate` | Duplicate one or more **file** entries. |
|
||||
| `PUT` | `/entries/rename` | `rename` | Rename or move an entry within its parent path. |
|
||||
| `DELETE` | `/*` | `destroy` | Delete an entry. |
|
||||
|
||||
## Authorization
|
||||
|
||||
Policy file:
|
||||
|
||||
```txt
|
||||
policy/file_manager_policy.ts
|
||||
```
|
||||
|
||||
Controller methods call policy abilities before performing work:
|
||||
|
||||
| Ability | Used By |
|
||||
| ---------- | ---------------------------------------------- |
|
||||
| `viewList` | List entries, search entries. |
|
||||
| `view` | File details, raw file stream. |
|
||||
| `create` | Create folder, upload file, duplicate entries. |
|
||||
| `update` | Rename entry. |
|
||||
| `delete` | Delete entry. |
|
||||
|
||||
When adding a new operation, add or reuse an explicit policy ability before adding service logic.
|
||||
|
||||
## Validation
|
||||
|
||||
Validators live in:
|
||||
|
||||
```txt
|
||||
validator/
|
||||
```
|
||||
|
||||
Current validators:
|
||||
|
||||
| File | Purpose |
|
||||
| -------------------------------- | ----------------------------------------------------------------- |
|
||||
| `list_validator.ts` | Validates filter, sort, order, max results, and pagination token. |
|
||||
| `create_directory_validator.ts` | Validates parent path and folder name. |
|
||||
| `rename_entry_validator.ts` | Validates source path and new name. |
|
||||
| `upload_file_validator.ts` | Validates upload path and file payload. |
|
||||
| `duplicate_entries_validator.ts` | Validates duplicate source paths. |
|
||||
| `schemas.ts` | Shared validation schema helpers. |
|
||||
|
||||
Path extraction from wildcard routes goes through:
|
||||
|
||||
```txt
|
||||
helpers/get_validated_path_from_request.ts
|
||||
```
|
||||
|
||||
Keep path validation centralized. Avoid parsing wildcard paths directly in controllers or storage adapters.
|
||||
|
||||
## Services
|
||||
|
||||
### `file_manager_service.ts`
|
||||
|
||||
Main orchestration service.
|
||||
|
||||
Responsibilities:
|
||||
|
||||
- List directory entries with filtering, sorting, and pagination.
|
||||
- Compose file detail responses with preview content.
|
||||
- Create directory response entries.
|
||||
- Validate and move entries.
|
||||
- Generate unique upload names.
|
||||
- Generate duplicate copy names.
|
||||
- Search entries.
|
||||
- Coordinate storage adapter calls.
|
||||
|
||||
### `path_service.ts`
|
||||
|
||||
Shared path and filename utility service.
|
||||
|
||||
Responsibilities:
|
||||
|
||||
- Convert client paths to storage paths.
|
||||
- Convert storage paths to client paths.
|
||||
- Join paths safely.
|
||||
- Get dirname and basename.
|
||||
- Parse file extension metadata.
|
||||
- Sanitize filenames.
|
||||
- Generate unique names.
|
||||
|
||||
Use this service for path behavior instead of string manipulation in feature code.
|
||||
|
||||
### `entry_list_service.ts`
|
||||
|
||||
Applies list behavior to raw storage entries.
|
||||
|
||||
Responsibilities:
|
||||
|
||||
- Filter entries by category.
|
||||
- Sort entries.
|
||||
- Keep folders before files.
|
||||
- Paginate entries.
|
||||
- Encode and decode pagination tokens.
|
||||
|
||||
Frontend list controls must stay compatible with this service's accepted filter and sort values.
|
||||
|
||||
### `file_preview_service.ts`
|
||||
|
||||
Adds text preview content to previewable files.
|
||||
|
||||
Previewable text extensions are currently defined in:
|
||||
|
||||
```txt
|
||||
services/file_preview_service.ts
|
||||
```
|
||||
|
||||
If a file type needs rich frontend preview, keep the backend response small and stream raw content through `/files/raw/*` when possible.
|
||||
|
||||
### `entry_search_service.ts`
|
||||
|
||||
Searches entries by path and includes matching parent folders so the frontend can display useful context.
|
||||
|
||||
Search receives an abort signal from the controller. Long-running search implementations should respect that signal.
|
||||
|
||||
## Storage
|
||||
|
||||
Storage abstractions live in:
|
||||
|
||||
```txt
|
||||
services/storage_service/
|
||||
```
|
||||
|
||||
### `storage_service_interface.ts`
|
||||
|
||||
Defines the storage adapter contract. Adapters expose raw operations:
|
||||
|
||||
- List directory entries.
|
||||
- List recursive entries.
|
||||
- Check entry existence.
|
||||
- Get file metadata.
|
||||
- Read file content.
|
||||
- Stream file content.
|
||||
- Write file.
|
||||
- Create directory.
|
||||
- Move entry.
|
||||
- Delete entry.
|
||||
- Copy entry.
|
||||
|
||||
Services should call the interface, not a concrete adapter.
|
||||
|
||||
### `fs_storage_service.ts`
|
||||
|
||||
Filesystem storage adapter.
|
||||
|
||||
Responsibilities:
|
||||
|
||||
- Call the Adonis Drive filesystem disk.
|
||||
- Map filesystem objects to File Manager entries.
|
||||
- Handle file streams and metadata.
|
||||
- Create, move, copy, and delete filesystem entries.
|
||||
- Preserve filesystem directory marker behavior.
|
||||
|
||||
### `s3_storage_service.ts`
|
||||
|
||||
S3-compatible storage adapter.
|
||||
|
||||
Responsibilities:
|
||||
|
||||
- Call the Adonis Drive `s3` disk.
|
||||
- Map S3 objects and common prefixes to File Manager entries.
|
||||
- Upload multipart files by streaming the temporary upload file into S3 with `putStream`.
|
||||
- Read file content and streams from S3.
|
||||
- Create folders with directory marker objects.
|
||||
- Move folders by moving every object under the folder prefix.
|
||||
- Delete files with exact object deletion and folders with folder-prefix deletion.
|
||||
- Copy file entries.
|
||||
|
||||
S3 has no native directories. The adapter uses the same `.keep` marker convention as the filesystem adapter so empty folders can exist.
|
||||
|
||||
Folder operations must use slash-terminated prefixes. For example, deleting `test/` must not delete objects such as `test123.txt`.
|
||||
|
||||
### `fs_directory_marker_service.ts`
|
||||
|
||||
Encapsulates the filesystem directory marker convention.
|
||||
|
||||
Current marker:
|
||||
|
||||
```txt
|
||||
.keep
|
||||
```
|
||||
|
||||
Do not duplicate marker logic outside this service. Storage adapters that need virtual folders should use this marker service behind the storage interface.
|
||||
|
||||
### `create_storage_service.ts`
|
||||
|
||||
Storage service factory.
|
||||
|
||||
Current supported storage:
|
||||
|
||||
```txt
|
||||
fs
|
||||
s3
|
||||
```
|
||||
|
||||
The active adapter is selected by `DRIVE_DISK`.
|
||||
|
||||
```env
|
||||
DRIVE_DISK=fs
|
||||
```
|
||||
|
||||
or:
|
||||
|
||||
```env
|
||||
DRIVE_DISK=s3
|
||||
```
|
||||
|
||||
## Drive Configuration
|
||||
|
||||
Adonis Drive config lives in:
|
||||
|
||||
```txt
|
||||
backend/config/drive.ts
|
||||
```
|
||||
|
||||
Configured disks:
|
||||
|
||||
| Disk | Driver | Purpose |
|
||||
| ---- | ------------- | ---------------------------------------------------- |
|
||||
| `fs` | `services.fs` | Local development and filesystem-backed storage. |
|
||||
| `s3` | `services.s3` | S3-compatible object storage, including local MinIO. |
|
||||
|
||||
The S3 disk expects these environment variables:
|
||||
|
||||
| Variable | Local Docker Value | Purpose |
|
||||
| ----------------------- | --------------------------------------------------------------------- | --------------------------------------------- |
|
||||
| `AWS_ACCESS_KEY_ID` | `minioadmin` | S3 access key. |
|
||||
| `AWS_SECRET_ACCESS_KEY` | `minioadmin` | S3 secret key. |
|
||||
| `AWS_REGION` | `us-east-1` | S3 region. |
|
||||
| `S3_BUCKET` | `crux-drive` | Bucket used by the file manager. |
|
||||
| `S3_ENDPOINT` | `http://minio:9000` in Docker, `http://localhost:9000` outside Docker | S3 API endpoint. |
|
||||
| `S3_CDN_URL` | `http://localhost:9000/crux-drive/` | Browser-facing public URL base for file URLs. |
|
||||
|
||||
For MinIO, the S3 disk uses path-style addressing:
|
||||
|
||||
```ts
|
||||
forcePathStyle: true;
|
||||
```
|
||||
|
||||
The adapter also disables ACL API calls:
|
||||
|
||||
```ts
|
||||
supportsACL: false;
|
||||
```
|
||||
|
||||
Bucket public-read behavior is handled by the Docker bucket setup container.
|
||||
|
||||
## Local S3 Simulation
|
||||
|
||||
Local S3 is simulated with MinIO in:
|
||||
|
||||
```txt
|
||||
docker-compose.yml
|
||||
```
|
||||
|
||||
Services:
|
||||
|
||||
| Service | Purpose |
|
||||
| --------------------- | --------------------------------------------------------------------------------------- |
|
||||
| `minio` | Runs the S3-compatible object storage server. |
|
||||
| `minio_create_bucket` | One-shot setup container that creates the bucket and enables anonymous download access. |
|
||||
|
||||
MinIO console:
|
||||
|
||||
```txt
|
||||
http://localhost:9001
|
||||
```
|
||||
|
||||
Default local credentials:
|
||||
|
||||
```txt
|
||||
user: minioadmin
|
||||
pass: minioadmin
|
||||
```
|
||||
|
||||
The `minio_create_bucket` container is expected to exit after setup. A stopped container with successful bucket logs is normal.
|
||||
|
||||
To rebuild after dependency or Dockerfile changes:
|
||||
|
||||
```sh
|
||||
docker compose build --no-cache
|
||||
docker compose up
|
||||
```
|
||||
|
||||
## Response Shape
|
||||
|
||||
Directory and file operations return `Entry` objects:
|
||||
|
||||
```ts
|
||||
interface EntryBase {
|
||||
name: string;
|
||||
path: string;
|
||||
updatedAt?: string;
|
||||
}
|
||||
|
||||
interface FileEntry extends EntryBase {
|
||||
type: "file";
|
||||
size?: number;
|
||||
url: string;
|
||||
}
|
||||
|
||||
interface FolderEntry extends EntryBase {
|
||||
type: "folder";
|
||||
}
|
||||
```
|
||||
|
||||
List responses include pagination metadata:
|
||||
|
||||
```ts
|
||||
{
|
||||
data: Entry[];
|
||||
meta: {
|
||||
nextPaginationToken: string | null;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## Backend Change Checklist
|
||||
|
||||
Before changing backend behavior:
|
||||
|
||||
1. Confirm the policy ability for the operation.
|
||||
2. Add or update a validator for all request input.
|
||||
3. Keep client paths and storage paths separated through `path_service.ts`.
|
||||
4. Update the frontend DTO or mapper if response shape changes.
|
||||
5. Update this documentation when routes, permissions, storage contracts, or response shapes change.
|
||||
@@ -0,0 +1,439 @@
|
||||
# File Manager Frontend
|
||||
|
||||
The frontend module implements the admin file manager UI. It provides file browsing, previews, uploads, deletion, duplication, renaming, image editing, settings, and reusable file picker widgets.
|
||||
|
||||
This module follows the general [React frontend architecture](../../apps-architecture/react-frontend/Readme.md) inside its own module boundary.
|
||||
|
||||
Source root:
|
||||
|
||||
```txt
|
||||
apps/admin/src/core/FileManager/
|
||||
```
|
||||
|
||||
## Main Principle
|
||||
|
||||
React Query owns server state. MobX owns local UI and operation state.
|
||||
|
||||
Server state examples:
|
||||
|
||||
- Directory entries.
|
||||
- File details.
|
||||
- Raw file blobs.
|
||||
- Search results.
|
||||
- Create, rename, delete, duplicate, and upload mutations.
|
||||
|
||||
Local state examples:
|
||||
|
||||
- Active path.
|
||||
- Selected paths.
|
||||
- Expanded tree paths.
|
||||
- Filter and sort controls.
|
||||
- Dialog open state.
|
||||
- Upload and delete operation progress.
|
||||
- Cached lookup of already loaded entries for UI behavior.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```txt
|
||||
FileManager/
|
||||
config/ Routes, menu entries, messages, translations.
|
||||
data/
|
||||
api/ Raw HTTP methods and DTOs.
|
||||
hooks/ Feature hooks that combine queries with stores.
|
||||
mappers/ DTO-to-entity mapping.
|
||||
queries/ React Query hooks, keys, mutations, and cache helpers.
|
||||
stores/ MobX local state stores.
|
||||
entities/ Domain entities and settings types.
|
||||
providers/ Feature providers and context hooks.
|
||||
pages/ Route-level screens.
|
||||
shared/ FileManager utilities that do not depend on view code.
|
||||
view/ UI components, widgets, forms, and layouts.
|
||||
```
|
||||
|
||||
## Routes
|
||||
|
||||
Frontend route config:
|
||||
|
||||
```txt
|
||||
config/routing.tsx
|
||||
```
|
||||
|
||||
Mounted module path:
|
||||
|
||||
```txt
|
||||
/file-manager/*
|
||||
```
|
||||
|
||||
Current child routes:
|
||||
|
||||
| Route | Page | Purpose |
|
||||
| -------------------------------- | ------------------- | ----------------------------------- |
|
||||
| `/file-manager/drive/explorer/*` | `DriveExplorerPage` | Browse folders and files. |
|
||||
| `/file-manager/drive/viewer/-/*` | `DriveViewerPage` | View file details and preview. |
|
||||
| `/file-manager/drive/settings` | `DriveSettingsPage` | Configure file-manager UI settings. |
|
||||
| `/file-manager/editor/image/-/*` | `ImageEditorPage` | Edit image files. |
|
||||
|
||||
Path helpers live in:
|
||||
|
||||
```txt
|
||||
shared/routing/driveLinks.ts
|
||||
shared/routing/openEntryNewTab.ts
|
||||
shared/path/leadingSlash.ts
|
||||
```
|
||||
|
||||
Use these helpers instead of manually building file-manager URLs.
|
||||
|
||||
## API Boundary
|
||||
|
||||
Raw backend calls are centralized in:
|
||||
|
||||
```txt
|
||||
data/api/api.ts
|
||||
data/api/dto.ts
|
||||
```
|
||||
|
||||
The frontend calls the backend routes documented in [backend.md](./backend.md):
|
||||
|
||||
| API Method | Backend Route |
|
||||
| ----------------- | ------------------------------------ |
|
||||
| `getEntries` | `GET /file-manager/*` |
|
||||
| `getFile` | `GET /file-manager/files/details/*` |
|
||||
| `getRawFile` | `GET /file-manager/files/raw/*` |
|
||||
| `createDirectory` | `POST /file-manager/folders` |
|
||||
| `renameEntry` | `PUT /file-manager/entries/rename` |
|
||||
| `removeEntry` | `DELETE /file-manager/*` |
|
||||
| `uploadFile` | `POST /file-manager/files` |
|
||||
| `duplicateFiles` | `POST /file-manager/files/duplicate` |
|
||||
| `searchEntries` | `GET /file-manager/search` |
|
||||
|
||||
DTOs should be mapped before they reach UI code. Entity types live in:
|
||||
|
||||
```txt
|
||||
entities/entry.ts
|
||||
data/mappers/entryMapper.ts
|
||||
```
|
||||
|
||||
## Data Flow
|
||||
|
||||
### Reading Entries
|
||||
|
||||
Directory entries are fetched through:
|
||||
|
||||
```ts
|
||||
useGetEntries();
|
||||
```
|
||||
|
||||
The higher-level hook:
|
||||
|
||||
```ts
|
||||
useEntries();
|
||||
```
|
||||
|
||||
does three things:
|
||||
|
||||
1. Builds the query payload from `ExplorerStore`.
|
||||
2. Fetches entries with React Query.
|
||||
3. Syncs loaded entries into `EntriesStore`.
|
||||
|
||||
`EntriesStore` is not the fetch owner. It is an index of loaded entries used by the sidebar tree, selection lookups, active entry lookup, and operation state.
|
||||
|
||||
### Deep Links
|
||||
|
||||
When opening a nested folder directly, `useEntries()` preloads ancestor folders through React Query. This keeps the sidebar tree and entry index usable for paths like:
|
||||
|
||||
```txt
|
||||
/folder/subfolder/current
|
||||
```
|
||||
|
||||
### Mutations
|
||||
|
||||
Mutations use React Query hooks:
|
||||
|
||||
- `useCreateDirectory`
|
||||
- `useRenameEntry`
|
||||
- `useDeleteEntry`
|
||||
- `useDuplicateFiles`
|
||||
- `useUploadFile`
|
||||
|
||||
After a successful mutation, the UI updates local cache and stores directly. It does not invalidate entries queries after every operation.
|
||||
|
||||
This avoids unnecessary refetches when many items are created, deleted, or uploaded in sequence.
|
||||
|
||||
## Query Cache Updates
|
||||
|
||||
Entries cache helpers live in:
|
||||
|
||||
```txt
|
||||
data/queries/cache.ts
|
||||
```
|
||||
|
||||
Current helpers:
|
||||
|
||||
```ts
|
||||
addEntriesToCache(parentPath, entries);
|
||||
removeEntriesFromCache(paths);
|
||||
renameEntryInCache(prevPath, renamedEntry);
|
||||
```
|
||||
|
||||
These helpers update existing React Query cache data in memory. They do not trigger a network request.
|
||||
|
||||
Important behavior:
|
||||
|
||||
- Added entries are inserted into cached lists for the matching parent folder.
|
||||
- Added entries are filter-aware.
|
||||
- Deleted entries are removed from all cached entry pages.
|
||||
- Renamed entries are updated across cached entry pages.
|
||||
|
||||
Tradeoff: because the module avoids refetching, exact backend sort order may not be perfect after local cache insertion until the folder is naturally refetched or reloaded.
|
||||
|
||||
## Stores
|
||||
|
||||
### `EntriesStore`
|
||||
|
||||
File:
|
||||
|
||||
```txt
|
||||
data/stores/entriesStore.ts
|
||||
```
|
||||
|
||||
Purpose:
|
||||
|
||||
- Keep an index of loaded entries by path.
|
||||
- Provide child lookup by parent path.
|
||||
- Support selected-entry and active-entry lookup.
|
||||
- Support local remove and rename updates for already loaded subtrees.
|
||||
|
||||
It should not fetch data.
|
||||
|
||||
### `ExplorerStore`
|
||||
|
||||
File:
|
||||
|
||||
```txt
|
||||
data/stores/explorerStore.ts
|
||||
```
|
||||
|
||||
Purpose:
|
||||
|
||||
- Active path.
|
||||
- Selected paths.
|
||||
- Expanded tree paths.
|
||||
- Breadcrumbs.
|
||||
- Entries filter.
|
||||
- Sort field and direction.
|
||||
|
||||
### `DriveDialogStore`
|
||||
|
||||
File:
|
||||
|
||||
```txt
|
||||
data/stores/driveDialogStore.ts
|
||||
```
|
||||
|
||||
Purpose:
|
||||
|
||||
- Create-directory dialog state.
|
||||
- Rename-entry dialog state.
|
||||
- Current entry selected for rename.
|
||||
|
||||
### `UploadOperationStore`
|
||||
|
||||
File:
|
||||
|
||||
```txt
|
||||
data/stores/uploadOperationStore.ts
|
||||
```
|
||||
|
||||
Purpose:
|
||||
|
||||
- Upload queue.
|
||||
- Upload progress.
|
||||
- Upload retry, remove, and abort.
|
||||
- Upload operation card visibility.
|
||||
|
||||
It receives the React Query mutation function from the view layer and manages only operation state.
|
||||
|
||||
### `DeleteOperationStore`
|
||||
|
||||
File:
|
||||
|
||||
```txt
|
||||
data/stores/deleteOperationStore.ts
|
||||
```
|
||||
|
||||
Purpose:
|
||||
|
||||
- Delete queue.
|
||||
- Delete progress.
|
||||
- Delete retry and remove.
|
||||
- Delete operation card visibility.
|
||||
|
||||
It receives the React Query mutation function from the view layer and manages only operation state.
|
||||
|
||||
### `SettingsStore`
|
||||
|
||||
File:
|
||||
|
||||
```txt
|
||||
data/stores/settingsStore.ts
|
||||
```
|
||||
|
||||
Purpose:
|
||||
|
||||
- Persisted view mode.
|
||||
- Compact mode.
|
||||
- Sidebar visibility.
|
||||
- Grid size and related display preferences.
|
||||
|
||||
## Providers
|
||||
|
||||
`DriveProvider` creates and exposes feature stores:
|
||||
|
||||
```ts
|
||||
entriesStore;
|
||||
explorerStore;
|
||||
driveDialogStore;
|
||||
uploadOperationStore;
|
||||
deleteOperationStore;
|
||||
```
|
||||
|
||||
Use:
|
||||
|
||||
```ts
|
||||
const { entriesStore, explorerStore } = useDrive();
|
||||
```
|
||||
|
||||
`SettingsProvider` owns persisted settings such as view mode, compact mode, sidebar visibility, and grid size.
|
||||
|
||||
## Pages And Widgets
|
||||
|
||||
### `DriveExplorerPage`
|
||||
|
||||
Main file browser page. Composes:
|
||||
|
||||
- `DriveProvider`
|
||||
- Explorer toolbar.
|
||||
- Explorer content.
|
||||
- Explorer sidebar.
|
||||
- Footer.
|
||||
- Operation watcher.
|
||||
|
||||
### `DriveViewerPage`
|
||||
|
||||
File details and preview page. Uses:
|
||||
|
||||
```ts
|
||||
useGetFile();
|
||||
```
|
||||
|
||||
### `DriveSettingsPage`
|
||||
|
||||
Settings page for persisted file-manager UI preferences.
|
||||
|
||||
### `ImageEditorPage`
|
||||
|
||||
Image editor page. Uses:
|
||||
|
||||
```ts
|
||||
useGetFile();
|
||||
useGetRawFile();
|
||||
useUploadFile();
|
||||
```
|
||||
|
||||
Image editor config and helpers live in the widget layer:
|
||||
|
||||
```txt
|
||||
view/widgets/ImageEditor/config.ts
|
||||
view/widgets/ImageEditor/helpers.ts
|
||||
```
|
||||
|
||||
This prevents `view` code from importing from `pages`.
|
||||
|
||||
### `FilePickerDialog`
|
||||
|
||||
Reusable picker widgets live in:
|
||||
|
||||
```txt
|
||||
view/widgets/FilePickerDialog/
|
||||
```
|
||||
|
||||
Keep picker behavior feature-scoped, but pass reusable UI state through props and context instead of coupling low-level UI components to page-level stores.
|
||||
|
||||
## View Layer Rules
|
||||
|
||||
Feature widgets may use File Manager providers such as `useDrive()` and `useSettings()`.
|
||||
|
||||
Lower-level reusable UI components should stay prop-driven. Examples:
|
||||
|
||||
- `ExplorerFileEntryGrid`
|
||||
- `ExplorerFolderEntryList`
|
||||
- `ExplorerToolbarFilterDropdown`
|
||||
- `FileManagerFileIcon`
|
||||
- `FilePreview`
|
||||
|
||||
Do not import backend DTOs into view components. View components should consume frontend entities and props.
|
||||
|
||||
## File Types And Icons
|
||||
|
||||
File helpers live in:
|
||||
|
||||
```txt
|
||||
shared/file/
|
||||
```
|
||||
|
||||
File icons live in:
|
||||
|
||||
```txt
|
||||
apps/admin/public/media/files/
|
||||
```
|
||||
|
||||
To add or change an icon:
|
||||
|
||||
1. Add the asset under `apps/admin/public/media/files/`.
|
||||
2. Update `shared/file/fileTypeImportMap.ts`.
|
||||
3. Confirm `FileManagerFileIcon` renders the expected fallback for unknown extensions.
|
||||
|
||||
## Adding A New Query
|
||||
|
||||
1. Add or reuse a method in:
|
||||
|
||||
```txt
|
||||
data/api/api.ts
|
||||
```
|
||||
|
||||
2. Add a key in:
|
||||
|
||||
```txt
|
||||
data/queries/keys.ts
|
||||
```
|
||||
|
||||
3. Create a query file:
|
||||
|
||||
```txt
|
||||
data/queries/example.ts
|
||||
```
|
||||
|
||||
4. Map DTOs to entities before returning app-facing data.
|
||||
|
||||
Do not import MobX stores or providers inside `data/queries`.
|
||||
|
||||
## Adding A New Mutation
|
||||
|
||||
1. Add a raw API method in `data/api/api.ts`.
|
||||
2. Add a mutation hook in `data/queries`.
|
||||
3. In the consuming widget or operation store flow, call `mutateAsync`.
|
||||
4. Update `EntriesStore` if lookup state must change.
|
||||
5. Update `data/queries/cache.ts` helpers if visible query cache must change.
|
||||
|
||||
Do not invalidate entries queries after each operation unless a full refetch is intentionally required.
|
||||
|
||||
## Frontend Change Checklist
|
||||
|
||||
Before changing frontend behavior:
|
||||
|
||||
1. Identify whether the data is server state or local UI state.
|
||||
2. Keep backend calls in `data/api`.
|
||||
3. Keep React Query hooks free of MobX provider imports.
|
||||
4. Map DTOs before passing data to view components.
|
||||
5. Update cache helpers when mutation results should appear immediately.
|
||||
6. Update backend docs if the frontend change requires a backend contract change.
|
||||
Reference in New Issue
Block a user