Открыть меню
Переключить меню настроек
Открыть персональное меню
Вы не представились системе
Ваш IP-адрес будет виден всем, если вы внесёте какие-либо изменения.

CDN:API Docs: различия между версиями

Материал из DenchikP Docs
Новая страница: «= DP‑CDN‑API (v4.4.0) = __TOC__ == Обзор == '''Base URL:''' https://cdnapi.denchikp.com<br> '''CDN URL файлов:''' https://cdn.denchikp.com/<domain>/<folder>/<filename><br> '''Rate limits:''' не применяются (не реализованы) DP‑CDN‑API — API для загрузки/удаления/просмотра файлов в CDN, управления квотами, вебхуками, статисти...»
 
Нет описания правки
Строка 1: Строка 1:
= DP‑CDN‑API (v4.4.0) =
= DP‑CDN‑API (v4.4.1) =
__TOC__
__TOC__



Версия от 00:21, 17 июня 2026

DP‑CDN‑API (v4.4.1)

Обзор

Base URL: https://cdnapi.denchikp.com
CDN URL файлов: https://cdn.denchikp.com/<domain>/<folder>/<filename>
Rate limits: не применяются (не реализованы)

DP‑CDN‑API — API для загрузки/удаления/просмотра файлов в CDN, управления квотами, вебхуками, статистикой и админ‑операциями. Также есть внутренние эндпоинты для Discord‑бота.

Важно: не публикуйте реальные API‑ключи. Если ключ попал в публичный доступ — выполните ротацию ключа (см. Admin → Rotate).

Термины и ограничения

Домен (domain)

  • Если domain пустой или не передан — используется public.
  • Разрешён только основной домен: ровно одна точка (пример: example.com). Поддомены запрещены.
  • Запрещены символы/паттерны: .., /, \.
  • Значение public разрешено.

Папка (folder)

Допустимые папки (строго):

  • img, css, js, fonts, files

Лимиты

  • Максимальный размер одного файла: 500 MB
  • Предпросмотр/редактор текста: максимум 2 MB
  • Bulk delete: максимум 500 URL за запрос
  • GET /files: limit максимум 200

Аутентификация (Security)

API Key (основной способ)

Передавайте ключ в заголовке:

Header Тип Обязательно Описание
X-Api-Key string да* API‑ключ клиента

Примечание: часть эндпоинтов может работать через cookie‑сессию (OAuth/CDN login), но для API‑интеграций рекомендуется всегда использовать X-Api-Key.

Ограничения ключа

  • enabled=false → 403
  • IP allowlist: если у ключа задан список IP, запросы разрешены только с этих IP (используется request.client.host)
  • Domain allowlist: если задан список доменов — доступ только к ним (и/или public, если он в списке)
  • is_admin=true: доступ к админ‑эндпоинтам, SSE и админ‑статистике

Cookie‑сессии (для браузера)

Cookie‑сессия создаётся после:

  • OAuth: /auth/google, /auth/discord, /auth/github (+ callback)
  • CDN login по ключу: /auth/cdn/login

Если OAuth‑аккаунт привязан к API‑ключу (/auth/bind-api-key), ключ может резолвиться из сессии без заголовка.

Bot token (внутреннее API для Discord‑бота)

Все эндпоинты /bot/* требуют заголовок:

Header Обязательно Описание
X-Bot-Token да должен совпадать с DISCORD_BOT_INTERNAL_TOKEN на сервере

Права и квоты

Админ‑доступ (is_admin)

Только админам:

  • /admin/*
  • /events (SSE)
  • большинство /stats/* (кроме публичной сводки)

Квоты (для не‑админов)

Квоты применяются к операциям загрузки/копирования:

  • max_file_bytes
  • max_total_bytes
  • max_total_files
  • max_monthly_upload_bytes
  • max_monthly_upload_files

Эндпоинты квот/использования:

  • GET /quota
  • GET /usage

Админ меняет квоты:

  • PUT /admin/api-keys/{api_key_id}/quota

Ошибки (Errors)

Код Значение
400 Некорректные параметры / URL / имя файла / папка
401 Нет ключа (и нет валидной сессии)
403 Запрещено: ключ отключен / IP не разрешён / домен не разрешён / не админ / квоты
404 Не найдено (файл, upload_id, webhook, ключ, запись)
409 Конфликт имени файла (если не overwrite)
413 Слишком большой файл
500 Внутренняя ошибка

FastAPI обычно возвращает:

{"detail":"..."}

Пример конфликта при загрузке (409):

{
  "status": "conflict",
  "message": "FILE_EXISTS",
  "requested_filename": "a.png",
  "existing_url": "https://cdn.denchikp.com/public/img/a.png",
  "domain": "public",
  "folder": "img"
}

Monitoring

GET /healthz

Проверка «жив».

Response 200

{"status":"ok"}

GET /readyz

Проверка готовности: MySQL + BASE_PATH + свободное место.

Response 200

{
  "status": "ready",
  "mysql": "ok",
  "storage": {
    "free_bytes": 123,
    "total_bytes": 456
  }
}

GET /version

Версия сервиса.

Response 200

{
  "service": "DP-CDN-API",
  "version": "4.4.0",
  "time": "2026-06-16T12:00:00"
}

GET /status

Сводка состояния API+CDN (файлы/размер/домены, версия API).

CDN Core

GET /api-key/me

Информация о текущем ключе.

Auth: X-Api-Key или cookie‑сессия

Response 200

{
  "status": "ok",
  "key": {
    "name": "ClientName",
    "is_admin": false,
    "domains": ["public"],
    "ips": ["1.2.3.4"]
  }
}

GET /domains

Доступные домены для ключа.

Auth: требуется

Response 200 (пример)

{"status":"ok","mode":"allowlist","domains":["public","example.com"]}

GET /usage

Текущее использование (без лимитов).

GET /quota

Квота и текущее использование (usage + remaining).

Response 200 (пример)

{
  "status": "ok",
  "api_key_name": "ClientName",
  "is_admin": false,
  "quota": {
    "max_file_bytes": 524288000,
    "max_total_bytes": 10737418240,
    "max_total_files": 50000,
    "max_monthly_upload_bytes": 5368709120,
    "max_monthly_upload_files": null
  },
  "usage": {
    "total_files": 120,
    "total_bytes": 123456789,
    "month": "2026-06",
    "monthly_uploaded_files": 10,
    "monthly_uploaded_bytes": 12300000
  },
  "remaining": {
    "total_bytes": 10000000000,
    "total_files": 49880,
    "monthly_upload_bytes": 5300000000,
    "monthly_upload_files": null
  }
}

Files (DB / Meta)

GET /files

Список «моих» файлов из БД (с пагинацией).

Auth: требуется

Query parameters:

Name Type Default Description
q string поиск по filename/original_filename (LIKE)
domain string фильтр домена (нормализуется)
folder string фильтр папки
ext string расширение
exists_on_disk bool true фильтр по наличию на диске
limit int 50 max 200
offset int 0 смещение

Response 200 (пример)

{
  "status": "ok",
  "total": 123,
  "limit": 50,
  "offset": 0,
  "files": [
    {
      "id": 1,
      "domain": "public",
      "folder": "img",
      "filename": "a.png",
      "original_filename": "cat.png",
      "url": "https://cdn.denchikp.com/public/img/a.png",
      "size": 12345,
      "created_at": "2026-06-16T10:00:00",
      "exists_on_disk": true
    }
  ]
}

POST /file/meta

Метаданные файла (диск + БД).

Auth: требуется Body:

{"url":"https://cdn.denchikp.com/public/img/a.png"}

POST /file/exists

Проверка существования файла (в БД и на диске).

Auth: требуется Body:

{"url":"https://cdn.denchikp.com/public/img/a.png"}

Response 200

{"status":"ok","exists_in_db":true,"exists_on_disk":true}

GET /file/redirect

Публичный редирект на CDN URL.

Query:

Upload

POST /upload

Загрузка файла (multipart/form-data).

Auth: требуется Max size: 500 MB

Form fields:

Field Type Required Description
file file да загружаемый файл
domain string нет "" → public
name_mode string нет original | custom
custom_filename string нет используется при name_mode=custom
conflict_action string нет overwrite → перезаписать (с проверками прав)

Пример:

curl -X POST "https://cdnapi.denchikp.com/upload" \
  -H "X-Api-Key: <API_KEY>" \
  -F "domain=public" \
  -F "name_mode=uuid" \
  -F "file=@./cat.png"

Response 200 (пример)

{
  "status": "ok",
  "url": "https://cdn.denchikp.com/public/img/...",
  "size": 12345,
  "owner": "ClientName",
  "ip": "1.2.3.4",
  "domain": "public",
  "folder": "img",
  "filename": "..."
}

Response 409 (конфликт) — см. раздел Ошибки.

Webhook event: UPLOAD (payload.source="api")

POST /upload/by-url

Сервер скачивает файл по URL и кладёт в CDN.

Auth: требуется Body:

{"source_url":"https://example.com/file.png","domain":"public","folder":"img"}

Ограничения:

  • только http/https
  • SSRF защита: запрещены private/loopback/link-local/reserved адреса
  • max 500 MB

Webhook event: UPLOAD (payload.source="by_url")

Multipart Upload

POST /upload/session/start

Создать multipart‑сессию.

Auth: требуется Body:

{"domain":"public","ext":"mp4","mime_type":"video/mp4","folder":"files"}

Response 200

{"status":"ok","upload_id":"<uuid>","chunk_size":8388608}

PUT /upload/session/{upload_id}/part/{part_no}

Загрузка части (multipart/form-data).

Auth: требуется Form-data:

  • file — chunk

Response 200

{"status":"ok","part_no":1,"bytes":8388608}

POST /upload/session/{upload_id}/complete

Склеивает части, создаёт итоговый файл, проверяет квоты и max size.

Auth: требуется Webhook event: UPLOAD (payload.source="multipart")

POST /upload/session/{upload_id}/abort

Отмена/очистка multipart‑сессии.

Auth: требуется

File Operations

POST /files/bulk-delete

Удалить множество файлов (до 500 URL).

Auth: требуется Body:

{"urls":["https://cdn.denchikp.com/public/files/a.txt","..."]}

Response 200 (пример)

{
  "status": "ok",
  "results": [
    {"url":"https://cdn.denchikp.com/public/files/a.txt","status":"ok"},
    {"url":"https://cdn.denchikp.com/public/files/b.txt","status":"error","error":"..."}
  ]
}

Webhook event: BULK_DELETE

POST /file/move

Перемещение файла.

Auth: требуется Body:

{
  "url":"https://cdn.denchikp.com/public/img/a.png",
  "new_domain":"public",
  "new_folder":"files",
  "new_filename":"b.png"
}

Webhook event: FILE_MOVE

POST /file/rename

Alias для /file/move. Требует new_filename, домен/папка не меняются.

POST /file/copy

Копирование файла (учитывает квоты как новая загрузка).

Auth: требуется Body:

{
  "url":"https://cdn.denchikp.com/public/img/a.png",
  "target_domain":"public",
  "target_folder":"img",
  "target_filename":"copy.png"
}

Webhook event: FILE_COPY

POST /file/hash

Хэш файла.

Auth: требуется Body:

{"url":"https://cdn.denchikp.com/public/files/a.bin","algo":"sha256"}

Поддерживаемые алгоритмы: sha256, sha1, md5.

Browse / Folders / Download

POST /browse

Просмотр файлов на диске в домене.

Auth: требуется Body:

{"domain":"public","folder":"img","filter":"cat"}

GET /folders/{domain}

Возвращает папки домена и агрегаты (кол-во файлов/размер).

Auth: требуется

GET /download/{domain}/{folder}/{filename:path}

Скачать файл (FileResponse).

Auth: требуется Response: бинарный поток (application/octet-stream)

Validate URL

POST /validate

Проверяет URL (http/https), выполняет HEAD/GET и возвращает заголовки. Если URL — CDN URL, дополнительно парсит domain/folder/filename и проверяет наличие на диске.

Auth: требуется Body:

{"url":"https://example.com/file.png"}

Delete file

DELETE /delete

Удалить файл по CDN URL.

Auth: требуется Body:

{"url":"https://cdn.denchikp.com/public/files/a.txt"}

Webhook event: DELETE (payload.source="api")

Text Editor (Read/Save)

Условия:

  • только «текстовые» расширения/MIME
  • размер файла ≤ 2 MB

POST /file/content

Получить содержимое текстового файла.

Auth: требуется Body:

{"url":"https://cdn.denchikp.com/public/files/app.js"}

POST /file/save

Сохранить содержимое текстового файла.

Auth: требуется Body:

{"url":"https://cdn.denchikp.com/public/files/app.js","content":"console.log('hi');\n"}

Webhook event: FILE_SAVE

Webhooks

POST /webhooks

Создать webhook.

Auth: требуется Body:

{
  "url":"https://example.com/hook",
  "events":["UPLOAD","DELETE"],
  "secret":"optional",
  "enabled":true
}

GET /webhooks

Список webhooks текущего ключа.

Auth: требуется

DELETE /webhooks/{webhook_id}

Удалить webhook.

Auth: требуется

Формат доставки

На URL вебхука отправляется POST JSON:

{"event":"UPLOAD","payload":{...}}

События

  • UPLOAD
  • DELETE
  • BULK_DELETE
  • FILE_MOVE
  • FILE_COPY
  • FILE_SAVE
  • SCAN_FINISHED

Подпись (HMAC)

Если на сервере включена подпись и у webhook задан secret, рекомендуется отправлять:

Headers:

  • X-Webhook-Event: UPLOAD
  • X-Webhook-Timestamp: 1718550000
  • X-Webhook-Signature: sha256=<hex>

Алгоритм:

  • base = {timestamp}.{raw_body}
  • signature = HMAC_SHA256(secret, base) (hex)
  • получатель проверяет подпись и свежесть timestamp (например ±300 секунд)

Admin

Все admin endpoints требуют is_admin=true.

Scan

  • POST /admin/scan/start
  • GET /admin/scan/status

Webhook event: SCAN_FINISHED

API Keys

GET /admin/api-keys

Query: show_full=false (если true — вернёт ключи полностью)

POST /admin/api-keys

Body:

{
  "name":"Client",
  "enabled":true,
  "is_admin":false,
  "ips":["1.2.3.4"],
  "domains":["public"]
}

PATCH /admin/api-keys/{api_key_id}

Body:

{"name":"NewName","enabled":true,"is_admin":false}

POST /admin/api-keys/{api_key_id}/rotate

Ротация ключа.

DELETE /admin/api-keys/{api_key_id}

Удаление ключа.

IP allowlist

  • POST /admin/api-keys/{api_key_id}/ips body: {"value":"1.2.3.4"}
  • DELETE /admin/api-keys/{api_key_id}/ips/{ip}

Domain allowlist

  • POST /admin/api-keys/{api_key_id}/domains body: {"value":"example.com"}
  • DELETE /admin/api-keys/{api_key_id}/domains/{domain}

Quota

PUT /admin/api-keys/{api_key_id}/quota

{
  "max_file_bytes": 524288000,
  "max_total_bytes": 10737418240,
  "max_total_files": 50000,
  "max_monthly_upload_bytes": 5368709120,
  "max_monthly_upload_files": null
}

Admin: Files / Logs / SSE

GET /admin/files

Query: domain, owner, exists_on_disk

GET /admin/logs

Query: action, level, domain, owner, limit (≤1000)

GET /events (SSE)

Поток text/event-stream, event: cdn_log. Query: last_id=0.

Stats

Public

GET /stats/public-summary

Публичная краткая статистика (без ключа).

Admin (is_admin)

  • POST /stats — детальная статистика + графики (base64 PNG data URL)
  • GET /stats/summary
  • GET /stats/domains
  • GET /stats/realtime
  • GET /stats/top-files?period=month&by=downloads|size&limit=50
  • GET /stats/export?period=month&kind=uploads|downloads|deletions|browses&format=csv

Примечание по реализации: часть статистики берётся из MySQL таблиц (uploads/downloads/deletions/browses + files), часть — из SQLite файла cdn_stats.db.

Auth (sessions)

OAuth redirects/callbacks

  • GET /auth/google, /auth/google/callback
  • GET /auth/discord, /auth/discord/callback
  • GET /auth/github, /auth/github/callback

CDN session login

POST /auth/cdn/login

Body:

{"api_key":"<API_KEY>"}

POST /auth/cdn/logout

Выход из CDN‑сессии.

Current session

GET /auth/me

Возвращает текущую активную сессию (cdn/google/discord/github) и информацию о привязанном ключе (если есть).

Bind/unbind API key

POST /auth/bind-api-key

Body:

{"api_key":"<API_KEY>"}

Требуется активная OAuth‑сессия (google/discord/github).

POST /auth/unbind-api-key

Отвязка ключа от текущей OAuth‑сессии.

Logout

GET /auth/logout

Выход из любой активной сессии (очистка cookie‑состояния).

Bot (internal)

Все /bot/* требуют X-Bot-Token.

Discord binding

  • POST /bot/discord/bind
  • POST /bot/discord/unbind
  • GET /bot/discord/me/{discord_id}

Bot files

  • POST /bot/upload (multipart/form-data: discord_id, file, domain, name_mode, custom_filename, conflict_action)
  • DELETE /bot/delete body: {"discord_id":"...","url":"https://cdn.denchikp.com/..."}
  • GET /bot/download/{discord_id}/{domain}/{folder}/{filename:path}
  • POST /bot/browse
  • GET /bot/folders/{discord_id}/{domain}
  • POST /bot/validate
  • POST /bot/file/content (только текст, ≤2MB)

Bot stats

  • GET /bot/public-summary
  • GET /bot/stats/{discord_id} (только если привязанный ключ is_admin=true)
Содержание