Files
crm-wiki/README.md

74 lines
5.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# crm-wiki
Вики (база знаний) для CRM Tvorymo. Открывается в iframe из боковой панели CRM, пользователя узнаёт по токену CRM (postMessage `crm:ready` → `crm:auth`).
- Сайт: https://wiki-crm.tes.gd
- Спецификация: [SPEC.md](SPEC.md)
- Стек: ASP.NET Core 10 (minimal API) + SQLite (FTS5), фронтенд Vite + React + TipTap.
- Данные: `/data/wiki.db` и `/data/images/` на постоянном томе `wiki-data` (Coolify).
## Авторизация
Каждый запрос к `/api/*` (кроме `/api/verify` и `/api/config`) требует:
| Заголовок | Значение |
|---|---|
| `Authorization` | `Bearer <access_token Supabase из CRM>` |
| `X-Wiki-Workspace` | `shared` (по умолчанию), `personal:<uid>` или `<uuid>` своего workspace |
Сервер проверяет токен: `exp`, `aud = "authenticated"` и `GET <SUPABASE_URL>/auth/v1/user` с publishable-ключом (результат кэшируется на 5 минут по SHA-256 токена). Workspace из запроса не доверяется: `shared` доступен всем, `personal:<uid>` только владельцу `uid`, произвольный `<uuid>` проверяется запросом `GET /rest/v1/workspaces?id=eq.<uuid>` с токеном пользователя (RLS Supabase). Ответы: `401` — токен не принят или истёк, `403` — нет доступа к workspace.
Пока CRM не передаёт workspace (задача Олега `mv6p5b92slw1an`), фронтенд работает в `shared`. Когда в `crm:auth` появится `workspace: { id, kind, title }`, он подхватится автоматически: `kind: "shared"` → `shared`, `kind: "personal"` → `personal:<uid>`, иначе `id`. Сообщение о смене workspace тоже обрабатывается (`{ type: "crm:workspace", workspace: {…} }`), если Олег выберет другое имя — поправить в `frontend/src/crm.ts`.
## REST API
Все ответы JSON (кроме `/markdown` и картинок). Ошибки: `{ "error": "…" }`.
| Метод | Путь | Что делает |
|---|---|---|
| `GET` | `/health` | `{ status, dbCreatedAt }` — без авторизации; `dbCreatedAt` не меняется, пока жив том с базой |
| `GET` | `/api/config` | `{ crmOrigin }` — без авторизации |
| `POST` | `/api/verify` | `{ access_token }` → `{ ok, id, email, name, exp }` — без авторизации |
| `GET` | `/api/me` | текущий пользователь и workspace |
| `GET` | `/api/tree` | `{ workspace, nodes: [Node] }` — все папки и страницы workspace без текста |
| `GET` | `/api/nodes/{id}` | папка или страница (Page) |
| `GET` | `/api/pages/{id}` | страница (Page), `404` для папки |
| `GET` | `/api/pages/{id}/markdown` | текст страницы, `text/markdown` |
| `POST` | `/api/pages` | `{ title, parentId?, markdown? }` → `201` Page |
| `POST` | `/api/folders` | `{ title, parentId? }` → `201` Page (kind = folder) |
| `PUT` | `/api/pages/{id}` | `{ title?, markdown?, version }` → Page; если `version` устарела — `409 { error, current }` |
| `PATCH` | `/api/nodes/{id}` | переименовать/переместить: `{ title?, parentId?, version? }`; `parentId: null` — в корень. Перенос в себя/потомка или в страницу — `400` |
| `DELETE` | `/api/nodes/{id}` | удалить узел со всем содержимым → `{ deleted: N }` |
| `GET` | `/api/search?q=…&limit=30` | `{ results: [{ id, kind, title, snippet, path }] }` — FTS5 по названию и тексту, префиксный поиск по словам; совпадения в `snippet` обрамлены `\u0002…\u0003` |
| `POST` | `/api/images` | `multipart/form-data`: `file` (PNG/JPEG/GIF/WebP, до 10 МБ), `pageId?` → `201 { id, url }` |
| `GET` | `/api/images/{id}` | картинка; нужен токен и доступ к workspace картинки |
```ts
type Node = { id, parentId: string|null, kind: "folder"|"page", title, version: number,
createdAt, updatedAt, updatedBy /* uid */, updatedByName };
type Page = Node & { markdown: string, path: { id, kind, title }[] /* папки от корня */ };
```
Формат текста — Markdown (GFM: таблицы, списки задач). Ссылка на другую страницу: `[текст](/p/<id>)`, картинка: `![alt](/api/images/<id>)`. Ссылки по id не ломаются при переименовании и переносе.
Пример (агент или скрипт):
```sh
curl -H "Authorization: Bearer $TOKEN" https://wiki-crm.tes.gd/api/tree
curl -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"title":"Регламент","markdown":"# Привет"}' https://wiki-crm.tes.gd/api/pages
```
## Заголовки
`Content-Security-Policy: frame-ancestors https://test.units.tvorymo.com; …` (без `X-Frame-Options`). Origin CRM задаётся `Crm__Origin`, домен стилей CRM — `Crm__AssetsOrigin`.
## Разработка
```sh
cd backend/Wiki.Api && ASPNETCORE_URLS=http://127.0.0.1:5080 dotnet run # база в ./data
cd frontend && npm i && npm run dev # Vite, /api проксируется на 127.0.0.1:5080
```
Настройки (env): `Storage__DataDir`, `Crm__Origin`, `Crm__AssetsOrigin`, `Supabase__Url`, `Supabase__PublishableKey`.