---
title: "Ring File Cabinet"
description: "Персональний файловий менеджер із доступом для учасників, довіреними поширеннями, галереєю, альтернативними папками для досліджень товарів і збереженнями Image/Video chat поруч із джерелом через Generative Gallery"
locale: "uk"
---
# Ring File Cabinet

> **Info**
> Використовуйте вкладки **Founder** / **Developer** на бічній панелі документації, щоб відфільтрувати цю сторінку. Завантаження проходять через [RingFileBase](/docs/integrations/ring-filebase.md); байти публічної галереї — через [Ring CDN](/docs/integrations/ring-cdn.md) `/files/{uuid}`.

**Ring File Cabinet** — персональний файловий менеджер для клонів Ring: вкладені папки (максимальна глибина **3**), робочий простір для комп’ютера з трьома колонками, ACL `owner` / `trustee` (trustee = лише перегляд і завантаження), робочий стіл **shared-with-me** для кожного **subscriber+**, приватне упорядкування галереї та lightbox `/{username}/img` для елементів із `visibility=public`.

### Гейти доступу (перевірено)

| Поверхня | Хто | Поведінка |
|---------|-----|----------|
| `/file-cabinet` | Auth; **member+** | Власний менеджер — `FileCabinetWrapper` → `FileCabinetDesktop`. Нечлени бачать `MemberUpgradeGate` (той самий патерн, що й для add-entity). |
| `/profile/cabinet` | — | **Перенаправляє** на `/file-cabinet` (`ROUTES.PROFILE_CABINET` — псевдонім `FILE_CABINET`). |
| `/profile/shared` | **subscriber+** | Повноцінний drag-and-drop робочий стіл довірених поширень. Користувачі лише з рівнем subscriber також бачать CTA оновлення `membersUpgrade` → membership із `returnTo=/profile/shared`. |
| `/profile/gallery` | Auth; **member+** | Упорядковування зображень/відео → `private` \| `unlisted` \| `public` (`MemberUpgradeGate` для нечленів). |
| `/{username}/img` | Public | Lightbox для елементів із `visibility=public`. |
| `/{username}/player` | Public | Mood player (перейменовано з `/songs`; старого редиректу немає). Приватне керування залишається на `/profile/songs`. |

Права ACL надаються **одразу** (без прийняття запрошення). Максимальний розмір завантаження — **25MB** через `getCabinetStorageConfig()`. **Публікації** використовують окрему модель співавторів — це не ACL File Cabinet.

  **Власний** File Cabinet (`/file-cabinet`) і керування галереєю (`/profile/gallery`) потребують привілеїв **member** (`hasMemberPrivileges` + `MemberUpgradeGate`). **Усі subscriber** (і рівні вище) відкривають `/profile/shared` для довірених поширень — вони **не** отримують власний робочий стіл або поверхню завантаження, доки не оновляться до member.

### For founders

## Чому це важливо для вашого клону

Учасникам потрібне місце для документів, сканів і медіа, яке **не** перетворюється на хаос вкладень у чаті й **не** є сховищем Admin Wiki. File Cabinet — це персональне сховище з довіреними переглядачами, спільна скринька для співпрацівників і публічне обличчя-галерея — корисно для маркетплейсів, сервісних кілець і будь-якого клону, де довіра включає «покажи свою роботу».

### Кому що доступно (погляд оператора)

```mermaid
flowchart LR
  sub[Subscriber+] --> shared["/profile/shared\nробочий стіл trustee"]
  mem[Member+] --> own["/file-cabinet\nвласний менеджер"]
  mem --> gal["/profile/gallery"]
  own --> trust[Поширити як trustee]
  trust --> shared
  gal --> img["/{username}/img\nпублічні елементи"]
```

  
- **[Власний файловий менеджер (member+)](/docs/features/subscriptions.md)** — Учасники відкривають `/file-cabinet` — завантаження, папки, trustees через ContactPicker, розгортане дерево + рейка параметрів. Нечлени потрапляють на гейт оновлення membership.

  
- **[Спільний для всіх subscriber](/docs/features/subscriptions.md)** — Кожен **subscriber+** використовує `/profile/shared` для довірених поширень (робочий стіл перегляду/завантаження). CTA оновлення в рейці веде до membership для отримання власного менеджера.

  
- **[Публічна галерея](/docs/integrations/ring-cdn.md)** — Упорядковуйте на `/profile/gallery` (member+); публічні елементи з’являються на `/{username}/img` через URL CDN.

  
- **[Рівень сховища](/docs/integrations/ring-filebase.md)** — Об’єкти зберігаються в RingFileBase / MinIO; для зображень галереї можна запитувати `derivativesProfile: gallery`.

  
- **[Generative Gallery із кабінету](/docs/features/generative-media.md)** — Image/Video chat на панелі деталей відкриває generative fs-modal (`scope: cabinet`); збереження створює сусідні файли під батьківською папкою джерела.

### Типові сценарії

1. **Медіакіт продавця** — Member завантажує PDF і фотографії товарів на `/file-cabinet`; ділиться папкою з **trustee** (лише перегляд/завантаження, права надаються одразу).
2. **Альтернативна папка Store Agent Research** — Product **Research** записує `research-{runId}.md` і зображення з цитатами в `store/[storename]/[product-slug]/alt` (глибина ≤3). Продавці відкривають ці зображення на панелі деталей → **Image chat** / **Video chat**, щоб покращити або оживити їх, а потім зберігають сусідній файл поруч із джерелом.
3. **Співпрацівник-subscriber** — subscriber без membership усе одно відкриває `/profile/shared`, упорядковує довірені файли на своєму робочому столі й завантажує їх — без власного кабінету.
4. **Смужка публічного портфоліо** — власник позначає вибрані зображення як `public` → відвідувачі відкривають `/{username}/img`.
5. **Конфіденційне проти спільного** — порожній список trustee = **конфіденційно**; Share `FsModal` показує живий рядок Trustees (або «конфіденційно») і **Make confidential**, щоб відкликати всіх trustee.

### Чекліст оператора

- Переконайтеся, що застосовано міграцію `042_file_cabinet.sql` (таблиці `file_cabinet_*`) і образ застосунку містить UI File Cabinet.
- Env: `NEXT_PUBLIC_STORAGE_PROVIDER=ring_filebase` (або еквівалент), `RINGBASE_API_URL`, `RINGBASE_PUBLIC_URL`, `RINGBASE_API_TOKEN` — див. [RingFileBase](/docs/integrations/ring-filebase.md).
- Перевірте як **member**: завантаження на `/file-cabinet` → Share trustees → другий користувач (subscriber+) бачить елемент на `/profile/shared`.
- Перевірте як **subscriber** (не member): `/file-cabinet` показує гейт оновлення; `/profile/shared` далі працює для спільних файлів; рейка показує «Members get their own file manager».
- Перевірте галерею: додайте зображення → встановіть `public` → відкрийте `/{username}/img`.
- Не очікуйте підписаних URL HMAC/TTL — публічна доставка сьогодні використовує стабільний CDN `/files/{fileId}`.

### For developers

## Архітектура

```mermaid
flowchart TB
  UI["/file-cabinet · /profile/shared · gallery"] --> Wrap["FileCabinetWrapper"]
  Wrap --> Desk["FileCabinetDesktop\nRingRightRailLayout"]
  Desk --> Actions["app/_actions/file-cabinet.ts"]
  Actions --> Svc["features/file-cabinet/service.ts"]
  Actions --> File["file().upload"]
  File --> RFB["RingFileBase / RingBaseAdapter"]
  Svc --> Nodes["file_cabinet_nodes"]
  Svc --> Acl["file_cabinet_acl"]
  Svc --> DeskDb["file_cabinet_desktop\nicons + treeExpandedIds"]
  Svc --> Gal["file_cabinet_gallery_items"]
  Svc --> Tunnel["publishToUserTunnel\nfile-cabinet:desktop-icons"]
  Dl["/api/file-cabinet/download"] --> Svc
  Img["/{username}/img"] --> Gal
  CDN["RINGBASE_PUBLIC_URL/files/{id}"] --> RFB
```

### Рольові гейти в коді (перевірено)

| Поверхня | Гейт |
|---------|------|
| `app/.../file-cabinet/page.tsx` | `auth` → `hasMemberPrivileges`, інакше `MemberUpgradeGate` (`returnTo` = `ROUTES.FILE_CABINET`) |
| `app/.../profile/shared/page.tsx` | `auth` → `hasRoleAtLeast(..., subscriber)`, інакше unauthorized |
| `app/.../profile/gallery/page.tsx` | `auth` → `hasMemberPrivileges`, інакше `MemberUpgradeGate` |
| `app/.../profile/cabinet/page.tsx` | Redirect → `ROUTES.FILE_CABINET` |
| Actions (`listOwnCabinetAction`, upload, own desktop) | `requireMember()` |
| Actions (`listSharedCabinetAction`, shared desktop) | `requireSubscriber()`; `scope: 'own'` усе одно потребує member |

### UX-оболонка (перевірено)

`FileCabinetWrapper` — тонка клієнтська межа (`components/wrappers/file-cabinet-wrapper.tsx`), яка рендерить `FileCabinetDesktop`. Оболонка з трьома колонками (`RingRightRailLayout` + центральна панель Davinci) живе **всередині** компонента desktop.

| Частина | Роль |
|-------|------|
| Центральна панель | Хлібні крихти + New folder / Upload (праворуч) + робочий простір іконок (без картки desktop з рамкою) |
| Права рейка | Заголовок → розгортане дерево **лише папок** → параметри (`DavinciGlassPanel`, title = видиме ім’я файлу) → інформаційна панель без прокручування |
| Дерево | Розгортання `+/−`; вибраний каталог використовує повністю заокруглену (`rounded-[99px]`) активну поверхню; стан розгортання у `treeExpandedIds` документа desktop |
| Іконки desktop | SSOT видимого імені у `icons[].meta.filename` (перейменування перезаписує; історії немає); іконки зображень використовують `sync_thumb` через download `?variant=` |
| Порожня папка | Контрол видалення лише коли папка не має дочірніх елементів |
| Trustees | Рядок параметрів + Share `FsModal` (живі Trustees / **конфіденційно**, вбудований ContactPicker, **Make confidential** під час очищення trustee) |
| Завантаження | Skeleton фіксованої висоти для рядка trustees, метаданих і прев’ю зображення (без стрибка макета) |
| Оновлення shared | Лише subscriber + `scope === 'shared'` → CTA `t('membersUpgrade')` → `/membership?returnTo=/profile/shared` |
| Image / Video chat | Панель деталей (`file-cabinet-detail-panel.tsx`) на файлах зображень → `GenerativeMediaEditorFsModal` (`scope="cabinet"`) |
| Збереження поруч | `saveGeneratedMediaToDesktopAction` під `parentId` джерела як `{base}-enhanced-{ts}.{ext}` або `{base}-enlive-{ts}.{ext}` |

### Альтернативний шлях досліджень товару (знання Store Agent)

Коли продавці запускають **Research** для товару, `features/store/lib/product-cabinet-media.ts` забезпечує три вкладені каталоги в кабінеті власника-продавця (концептуальний корінь `files/` + `MAX_FOLDER_DEPTH = 3`):

`store/[storename]/[product-slug]/alt`

Артефакти: `research-{runId}.md` плюс завантажені зображення `research-{runId}-N.{ext}` (SSRF-перевірка HTTP(S) fetch → RingBase → `createFileNode`). Увімкнення галереї storefront залишається у формі товару (**Use in gallery**) — див. [Multi-Vendor Store](/docs/features/store.md). Pipeline: `cabinet.genmedia.sibling_save` у `scripts/PIPELINES.md`.

### Карта модулів

| Шлях | Роль |
|------|------|
| `features/file-cabinet/service.ts` | `server-only` CRUD, ACL (включно з обходом предків для успадкованих trustee), desktop, gallery |
| `features/file-cabinet/acl.ts` | `owner` \| `trustee` (legacy `editor` → `trustee`) |
| `features/file-cabinet/constants.ts` | `FILE_CABINET_DESKTOP_CHANNEL`, `FILE_CABINET_DOWNLOAD_PATH`, `MAX_FOLDER_DEPTH = 3` |
| `features/file-cabinet/desktop-filename.ts` | Допоміжні засоби видимого імені файлу |
| `features/file-cabinet/media-urls.ts` | Допоміжні засоби same-origin URL завантаження (`?variant=`) |
| `features/file-cabinet/components/*` | Desktop, дерево, параметри, деталі (Image/Video chat), FsModal поширення/перейменування, skeleton, gallery, public img |
| `app/_actions/file-cabinet.ts` | Server Actions + рольові гейти + `saveGeneratedMediaToDesktopAction` |
| `features/store/lib/product-cabinet-media.ts` | Store research → writer `store/…/alt` (SSRF-safe) |
| `app/api/file-cabinet/download/route.ts` | Перевірка ACL → потік байтів (`inline=1`, необов’язковий `variant=` для thumb/webp) |
| `lib/storage/storage-config.ts` | `getCabinetStorageConfig()` — 25MB + allowlist MIME |
| `components/file-tree/tree-helpers.ts` | Спільні помічники дерева (адаптери Admin Wiki) |
| `data/migrations/042_file_cabinet.sql` | Схема (+ дзеркало в `data/schema.sql`) |

### Модель ACL

| Роль | Можливості |
|------|------------|
| `owner` | Повний CRUD, встановлення trustee через ContactPicker (`setCabinetTrusteesAction`) |
| `trustee` | Перегляд / список / завантаження; макет shared desktop; **не може** змінювати або завантажувати |

Права одразу записуються в `file_cabinet_acl` — потоку прийняття запрошення немає. Під час переліку ACL для рейки параметрів виконується **обхід предків**, тому поширення папки відображаються і на вкладених файлах.

### Схема (колекції JSONB)

Таблиці (`id` + `data` JSONB): `file_cabinet_nodes`, `file_cabinet_acl`, `file_cabinet_desktop`, `file_cabinet_gallery_items`.

Документ desktop зберігає позиції іконок і `treeExpandedIds` для дерева папок лише з розгортанням/згортанням. Кеш макета для кожної папки зберігає позиції після перетягування під час переходу вкладеними папками.

  Передавайте id документа як `createDoc(collection, data, { id })`. Якщо покласти `id` лише всередину `data`, `PostgreSQLAdapter` може розійтися з `data.id`, згенерувавши інший `generateId()`.

### Завантаження у сховище (перевірено)

Завантаження в кабінет використовують `file().upload(...)` з `access: 'private'`, `derivativesProfile: 'gallery'` для зображень і перевірками MIME/розміру з `getCabinetStorageConfig()` (зображення, PDF, Office, text/csv/md, zip, а також `video/mp4` / `video/webm`).

Публічні елементи галереї зберігають CDN URL із завантаження (`storageUrl`). У поточному шляху `RingBaseAdapter` **немає** підписаних URL HMAC/TTL — доставка має вигляд `{RINGBASE_PUBLIC_URL}/files/{fileId}` (+ похідні `_v_*`). Приватні завантаження використовують ACL-проксі (`/api/file-cabinet/download`) із `Content-Disposition` з `node.name` / desktop `meta.filename`. У прямому CDN `/files/{uuid}` досі немає `Content-Disposition` — див. backlog.

### Desktop tunnel

`saveDesktop` публікує дані в персональний tunnel учасника:

{`// features/file-cabinet/constants.ts
export const FILE_CABINET_DESKTOP_CHANNEL = 'file-cabinet:desktop-icons'
// service: publishToUserTunnel(userId, FILE_CABINET_DESKTOP_CHANNEL, { scope, icons, treeExpandedIds, updatedAt })`}

Клієнт слухає через `useTunnelChannel` у `file-cabinet-desktop.tsx`.

### Кроки підключення

### Застосуйте міграцію

Переконайтеся, що `042_file_cabinet.sql` (і паритет `schema.sql`) застосовано до БД клону. Нові інсталяції отримують таблиці з `data/schema.sql`.

### Підтвердьте env сховища

Зверніться до [RingFileBase](/docs/integrations/ring-filebase.md): `NEXT_PUBLIC_STORAGE_PROVIDER=ring_filebase`, `RINGBASE_API_URL`, `RINGBASE_PUBLIC_URL`, `RINGBASE_API_TOKEN`.

### Викликайте через Server Actions

Віддавайте перевагу `app/_actions/file-cabinet.ts` із клієнтського UI — не обходьте ACL-помічники в `service.ts`. Власний список/завантаження потребує **member**; shared desktop потребує **subscriber+**; `getDesktop` / `saveDesktop` для `scope: 'own'` також потребують привілеїв member.

### Сторінка public img

`app/[locale]/[username]/img/page.tsx` завантажує `listPublicGalleryByOwner(ownerId)` і рендерить `PublicProfileImgGallery`. Відображаються лише елементи з `visibility=public`.

### Маршрути (константи)

| Константа | Шлях |
|----------|------|
| `ROUTES.FILE_CABINET` | `/file-cabinet` (канонічний власний менеджер) |
| `ROUTES.PROFILE_CABINET` | `/file-cabinet` (застарілий псевдонім; `/profile/cabinet` перенаправляє) |
| `ROUTES.PROFILE_SHARED` | `/profile/shared` |
| `ROUTES.PROFILE_GALLERY` | `/profile/gallery` |
| `ROUTES.PUBLIC_PROFILE_IMG(username)` | `/{username}/img` |
| `ROUTES.PUBLIC_PROFILE_PLAYER(username)` | `/{username}/player` |

Керування приватним mood-player залишається на `/profile/songs` (`ROUTES.PROFILE_SONGS`). Peer `/games` — споріднена поверхня public profile, а не сховище File Cabinet.

## Пов’язана документація

- [integrations/ring-filebase](/docs/integrations/ring-filebase.md) — Залежність: завантаження кабінету проходять через file() / RingBaseAdapter і getCabinetStorageConfig.

- [integrations/ring-cdn](/docs/integrations/ring-cdn.md) — Залежність: елементи публічної галереї використовують стабільний CDN /files/{uuid} (+ похідні _v_*).

- [features/public-profile](/docs/features/public-profile.md) — Наступний крок: публічні поверхні /img, /player і /games розташовані під /{username}.

- [features/peer-games](/docs/features/peer-games.md) — Див. також: публічна доступність /games — споріднена поверхня профілю (не ACL File Cabinet).

- [features/subscriptions](/docs/features/subscriptions.md) — Той самий процес: member gate для /file-cabinet проти shared desktop для subscriber+.

- [features/tunnel-protocol](/docs/features/tunnel-protocol.md) — Поглиблення: синхронізація іконок desktop використовує publishToUserTunnel на каналі file-cabinet:desktop-icons.

- [features/admin-wiki](/docs/features/admin-wiki.md) — Див. також: спільні помічники FileTree і патерн колекції JSONB id+data.

- [features/store](/docs/features/store.md) — Той самий процес: Research Agent Knowledge записує markdown + зображення в store/product/alt.

- [features/generative-media](/docs/features/generative-media.md) — Залежність: cabinet Image/Video chat повторно використовує GenerativeMediaEditorFsModal зі scope cabinet.
