diff --git a/API.md b/API.md new file mode 100644 index 0000000..6a056da --- /dev/null +++ b/API.md @@ -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/` |