Add "API"

2026-04-12 20:46:44 +00:00
parent 3fdd9cf12f
commit bc010a31b3

137
API.md Normal file

@@ -0,0 +1,137 @@
# API Reference
Все эндпоинты (кроме auth) требуют cookie `va_token`. State-changing запросы проверяют CSRF (Origin/Referer).
## Аутентификация
| Метод | URL | Body / Params | Ответ |
|-------|-----|---------------|-------|
| POST | `/api/auth/login` | `{login, password}` | `{user: {id, login, name, role, projects}}` + cookie |
| POST | `/api/auth/logout` | — | `{ok}` + clear cookie |
| GET | `/api/auth/me` | — | `{user: {id, login, name, role, projects, theme, activePage, previewZoom}}` |
| PUT | `/api/auth/preferences` | `{theme?, activePage?, previewZoom?}` | `{ok}` |
## Админ (role === 'admin')
| Метод | URL | Body | Ответ |
|-------|-----|------|-------|
| GET | `/api/admin/users` | — | `[{id, login, name, role, projects}]` |
| POST | `/api/admin/users` | `{login, password, name, role, projects}` | `{user}` |
| PUT | `/api/admin/users/:id` | `{name?, role?, projects?, password?, login?}` | `{user}` |
| DELETE | `/api/admin/users/:id` | — | `{ok}` |
## Проекты
| Метод | URL | Ответ |
|-------|-----|-------|
| GET | `/api/projects` | `['vipavenue']` |
| GET | `/api/project/:name` | `{meta, settings, draft, presets, letters, notes, blockText}` |
## Данные проекта
| Метод | URL | Body | Описание |
|-------|-----|------|----------|
| PUT | `/api/project/:name/block` | `{blockText, sourceName}` | Сохранить Block.pug |
| PUT | `/api/project/:name/block-custom` | `{content}` | Сохранить block-custom.pug |
| PUT | `/api/project/:name/settings` | settings object | ⚠️ Admin only |
| PUT | `/api/project/:name/draft` | blocks array | Per-user draft |
| PUT | `/api/project/:name/presets` | presets array | Пресеты |
## Письма
Хранятся per-user (`letters/{userId}/`).
| Метод | URL | Body | Описание |
|-------|-----|------|----------|
| GET | `/api/project/:name/letters` | — | `{list, currentId}` |
| PUT | `/api/project/:name/letters` | `{list, currentId}` | Индекс |
| GET | `/api/project/:name/letter/:id` | — | Данные письма |
| PUT | `/api/project/:name/letter` | letter object (`{id, ...}`) | Создать/обновить |
| DELETE | `/api/project/:name/letter/:id` | — | Удалить |
| GET | `/api/project/:name/letter/:id/history` | — | Макс 20 снэпшотов |
| PUT | `/api/project/:name/letter/:id/history` | snapshot object | Prepend |
## Заметки
Проектные (не per-user).
| Метод | URL | Body | Описание |
|-------|-----|------|----------|
| GET | `/api/project/:name/notes` | — | `{list, currentId}` |
| PUT | `/api/project/:name/notes` | `{list, currentId}` | Индекс |
| GET | `/api/project/:name/note/:id` | — | Содержимое |
| PUT | `/api/project/:name/note` | `{id, content, ...}` | Сохранить |
| DELETE | `/api/project/:name/note/:id` | — | Удалить |
## Рендер
| Метод | URL | Body | Ответ |
|-------|-----|------|-------|
| POST | `/api/project/:name/render-email` | `{projectSlug, pug, preheader, gender}` | `{html, previewHtml, unavailableProducts, feedSyncedAt, generatedAt}` |
Лимит: 3 параллельных рендера (HTTP 429 при превышении).
## Фид товаров
| Метод | URL | Body | Ответ |
|-------|-----|------|-------|
| POST | `/api/project/:name/feed-refresh` | — | `{added, removed, total}` |
| POST | `/api/project/:name/feed-lookup` | `{ids: [...]}` | `{found, notFound}` |
| POST | `/api/project/:name/feed-suggest` | `{productId, limit?}` | `[{id, name, price, score, ...}]` |
## FTP/SFTP
| Метод | URL | Body | Ответ |
|-------|-----|------|-------|
| POST | `/api/project/:name/ftp/test` | — | `{ok, message}` |
| POST | `/api/project/:name/ftp/upload` | `{imageData, fileName, folder}` | `{url, name, steps}` |
| POST | `/api/project/:name/ftp/list` | `{folder}` | `{files: [{name, size, url}], folder}` |
| POST | `/api/project/:name/ftp/delete` | `{folder, fileName}` | `{ok}` |
## Проверки
| Метод | URL | Body | Ответ |
|-------|-----|------|-------|
| POST | `/api/check-links` | `{urls: [...]}` (макс 50) | `[{url, status, ok, redirected, finalUrl, error}]` |
| POST | `/api/upload-image` | `{imageData}` (base64) | `{url, name}` |
## Конфигурация
| Метод | URL | Body | Ответ |
|-------|-----|------|-------|
| GET | `/api/config` | — | `{yonote_token (masked), yonote_base_url, hasYonoteToken, upload_base_url}` |
| PUT | `/api/config` | `{yonote_token?, yonote_base_url?, upload_base_url?}` | ⚠️ Admin only |
## Yonote
| Метод | URL | Ответ |
|-------|-----|-------|
| GET | `/api/yonote/status` | `{configured, connected, error?}` |
| GET | `/api/yonote/databases` | `[{id, title}]` |
| GET | `/api/yonote/database/:id/properties` | `[{name, type, options?}]` |
| GET | `/api/yonote/database/:id/rows` | `{rows, hasMore}` |
| POST | `/api/yonote/row/update` | `{rowId, values}` | `{ok}` |
## Хедер/Футер
| Метод | URL | Params/Body | Ответ |
|-------|-----|-------------|-------|
| GET | `/api/parts-files` | — | `[{path, label}]` |
| GET | `/api/parts-file-read` | `?path=./parts/footer/footer-woman` | `{content}` |
| POST | `/api/parts-file-write` | `{path, content}` | `{ok}` |
## Статистика
| Метод | URL | Body | Ответ |
|-------|-----|------|-------|
| GET | `/api/project/:name/stats` | — | `[{letterId, userName, activeSeconds, ...}]` |
| POST | `/api/project/:name/stats` | stat entry | `{ok}` (upsert by letterId+userId) |
| GET | `/api/stats` | — | all stats |
## Прокси (без авторизации)
| URL | Проксируется на |
|-----|-----------------|
| `/typograf/*` | `http://typograf.artlebedev.ru` |
| `/speller/*` | `https://speller.yandex.net` |
| `/uploads/*` | Статические файлы из `data/uploads/` |