mirror of
https://github.com/getnora-io/nora.git
synced 2026-04-12 12:40:31 +00:00
docs: sync CHANGELOG and OpenAPI with actual implementation
- Fix CHANGELOG: add missing versions v0.2.4-v0.2.12 - Implement GET /v2/_catalog endpoint for Docker repository listing - Add missing OpenAPI endpoints: - Docker: PUT manifest, POST/PATCH/PUT blob uploads, HEAD blob - Maven: PUT artifact upload - Cargo: GET metadata, GET download (was completely undocumented) - Metrics: GET /metrics - Update OpenAPI version to 0.2.12
This commit is contained in:
179
CHANGELOG.md
179
CHANGELOG.md
@@ -4,18 +4,14 @@ All notable changes to NORA will be documented in this file.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## [0.3.0] - 2026-01-30
|
## [0.2.12] - 2026-01-30
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
#### Configurable Rate Limiting
|
#### Configurable Rate Limiting
|
||||||
- Rate limits now configurable via `config.toml` and environment variables
|
- Rate limits now configurable via `config.toml` and environment variables
|
||||||
- New config section `[rate_limit]` with 6 parameters:
|
- New config section `[rate_limit]` with parameters: `auth_rps`, `auth_burst`, `upload_rps`, `upload_burst`, `general_rps`, `general_burst`
|
||||||
- `auth_rps` / `auth_burst` - Authentication endpoints (brute-force protection)
|
|
||||||
- `upload_rps` / `upload_burst` - Upload endpoints (Docker push, etc.)
|
|
||||||
- `general_rps` / `general_burst` - General API endpoints
|
|
||||||
- Environment variables: `NORA_RATE_LIMIT_{AUTH|UPLOAD|GENERAL}_{RPS|BURST}`
|
- Environment variables: `NORA_RATE_LIMIT_{AUTH|UPLOAD|GENERAL}_{RPS|BURST}`
|
||||||
- Rate limit configuration logged at startup
|
|
||||||
|
|
||||||
#### Secrets Provider Architecture
|
#### Secrets Provider Architecture
|
||||||
- Trait-based secrets management (`SecretsProvider` trait)
|
- Trait-based secrets management (`SecretsProvider` trait)
|
||||||
@@ -23,14 +19,78 @@ All notable changes to NORA will be documented in this file.
|
|||||||
- Protected secrets with `zeroize` (memory zeroed on drop)
|
- Protected secrets with `zeroize` (memory zeroed on drop)
|
||||||
- Redacted Debug impl prevents secret leakage in logs
|
- Redacted Debug impl prevents secret leakage in logs
|
||||||
- New config section `[secrets]` with `provider` and `clear_env` options
|
- New config section `[secrets]` with `provider` and `clear_env` options
|
||||||
- Foundation for future AWS Secrets Manager, Vault, K8s integration
|
|
||||||
|
#### Docker Image Metadata
|
||||||
|
- Support for image metadata retrieval
|
||||||
|
|
||||||
|
#### Documentation
|
||||||
|
- Bilingual onboarding guide (EN/RU)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [0.2.11] - 2026-01-26
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Internationalization (i18n) support
|
||||||
|
- PyPI registry proxy
|
||||||
|
- UI improvements
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [0.2.10] - 2026-01-26
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Rate limiting functions now accept `&RateLimitConfig` parameter
|
- Dark theme applied to all UI pages
|
||||||
- Improved error messages with `.expect()` instead of `.unwrap()`
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [0.2.9] - 2026-01-26
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Version bump release
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [0.2.8] - 2026-01-26
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Dashboard endpoint added to OpenAPI documentation
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [0.2.7] - 2026-01-26
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Dynamic version display in UI sidebar
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [0.2.6] - 2026-01-26
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
#### Dashboard Metrics
|
||||||
|
- Global stats panel: downloads, uploads, artifacts, cache hit rate, storage
|
||||||
|
- Extended registry cards with artifact count, size, counters
|
||||||
|
- Activity log (last 20 events)
|
||||||
|
|
||||||
|
#### UI
|
||||||
|
- Dark theme (bg: #0f172a, cards: #1e293b)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [0.2.5] - 2026-01-26
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Rate limiting was hardcoded in v0.2.0, now user-configurable
|
- Docker push/pull: added PATCH endpoint for chunked uploads
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [0.2.4] - 2026-01-26
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Rate limiting: health/metrics endpoints now exempt
|
||||||
|
- Increased upload rate limits for Docker parallel requests
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -82,7 +142,6 @@ All notable changes to NORA will be documented in this file.
|
|||||||
- JSON error responses with request_id support
|
- JSON error responses with request_id support
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- `StorageError` now uses `thiserror` derive macro
|
- `StorageError` now uses `thiserror` derive macro
|
||||||
- `TokenError` now uses `thiserror` derive macro
|
- `TokenError` now uses `thiserror` derive macro
|
||||||
- Storage wrapper validates keys before delegating to backend
|
- Storage wrapper validates keys before delegating to backend
|
||||||
@@ -90,7 +149,6 @@ All notable changes to NORA will be documented in this file.
|
|||||||
- Body size limit set to 100MB default via `DefaultBodyLimit`
|
- Body size limit set to 100MB default via `DefaultBodyLimit`
|
||||||
|
|
||||||
### Dependencies Added
|
### Dependencies Added
|
||||||
|
|
||||||
- `thiserror = "2"` - typed error handling
|
- `thiserror = "2"` - typed error handling
|
||||||
- `tower_governor = "0.8"` - rate limiting
|
- `tower_governor = "0.8"` - rate limiting
|
||||||
- `governor = "0.10"` - rate limiting backend
|
- `governor = "0.10"` - rate limiting backend
|
||||||
@@ -98,7 +156,6 @@ All notable changes to NORA will be documented in this file.
|
|||||||
- `wiremock = "0.6"` (dev) - HTTP mocking for S3 tests
|
- `wiremock = "0.6"` (dev) - HTTP mocking for S3 tests
|
||||||
|
|
||||||
### Files Added
|
### Files Added
|
||||||
|
|
||||||
- `src/validation.rs` - input validation module
|
- `src/validation.rs` - input validation module
|
||||||
- `src/migrate.rs` - storage migration module
|
- `src/migrate.rs` - storage migration module
|
||||||
- `src/error.rs` - application error types
|
- `src/error.rs` - application error types
|
||||||
@@ -110,7 +167,6 @@ All notable changes to NORA will be documented in this file.
|
|||||||
## [0.1.0] - 2026-01-24
|
## [0.1.0] - 2026-01-24
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Multi-protocol support: Docker Registry v2, Maven, npm, Cargo, PyPI
|
- Multi-protocol support: Docker Registry v2, Maven, npm, Cargo, PyPI
|
||||||
- Web UI dashboard
|
- Web UI dashboard
|
||||||
- Swagger UI (`/api-docs`)
|
- Swagger UI (`/api-docs`)
|
||||||
@@ -125,7 +181,6 @@ All notable changes to NORA will be documented in this file.
|
|||||||
- Graceful shutdown (SIGTERM/SIGINT)
|
- Graceful shutdown (SIGTERM/SIGINT)
|
||||||
- Backup/restore commands
|
- Backup/restore commands
|
||||||
|
|
||||||
---
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Журнал изменений (RU)
|
# Журнал изменений (RU)
|
||||||
@@ -134,6 +189,96 @@ All notable changes to NORA will be documented in this file.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## [0.2.12] - 2026-01-30
|
||||||
|
|
||||||
|
### Добавлено
|
||||||
|
|
||||||
|
#### Настраиваемый Rate Limiting
|
||||||
|
- Rate limits настраиваются через `config.toml` и переменные окружения
|
||||||
|
- Новая секция `[rate_limit]` с параметрами: `auth_rps`, `auth_burst`, `upload_rps`, `upload_burst`, `general_rps`, `general_burst`
|
||||||
|
- Переменные окружения: `NORA_RATE_LIMIT_{AUTH|UPLOAD|GENERAL}_{RPS|BURST}`
|
||||||
|
|
||||||
|
#### Архитектура Secrets Provider
|
||||||
|
- Trait-based управление секретами (`SecretsProvider` trait)
|
||||||
|
- ENV provider по умолчанию (12-Factor App паттерн)
|
||||||
|
- Защищённые секреты с `zeroize` (память обнуляется при drop)
|
||||||
|
- Redacted Debug impl предотвращает утечку секретов в логи
|
||||||
|
- Новая секция `[secrets]` с опциями `provider` и `clear_env`
|
||||||
|
|
||||||
|
#### Docker Image Metadata
|
||||||
|
- Поддержка получения метаданных образов
|
||||||
|
|
||||||
|
#### Документация
|
||||||
|
- Двуязычный onboarding guide (EN/RU)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [0.2.11] - 2026-01-26
|
||||||
|
|
||||||
|
### Добавлено
|
||||||
|
- Поддержка интернационализации (i18n)
|
||||||
|
- PyPI registry proxy
|
||||||
|
- Улучшения UI
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [0.2.10] - 2026-01-26
|
||||||
|
|
||||||
|
### Изменено
|
||||||
|
- Тёмная тема применена ко всем страницам UI
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [0.2.9] - 2026-01-26
|
||||||
|
|
||||||
|
### Изменено
|
||||||
|
- Релиз с обновлением версии
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [0.2.8] - 2026-01-26
|
||||||
|
|
||||||
|
### Добавлено
|
||||||
|
- Dashboard endpoint добавлен в OpenAPI документацию
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [0.2.7] - 2026-01-26
|
||||||
|
|
||||||
|
### Добавлено
|
||||||
|
- Динамическое отображение версии в сайдбаре UI
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [0.2.6] - 2026-01-26
|
||||||
|
|
||||||
|
### Добавлено
|
||||||
|
|
||||||
|
#### Dashboard Metrics
|
||||||
|
- Глобальная панель статистики: downloads, uploads, artifacts, cache hit rate, storage
|
||||||
|
- Расширенные карточки реестров с количеством артефактов, размером, счётчиками
|
||||||
|
- Лог активности (последние 20 событий)
|
||||||
|
|
||||||
|
#### UI
|
||||||
|
- Тёмная тема (bg: #0f172a, cards: #1e293b)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [0.2.5] - 2026-01-26
|
||||||
|
|
||||||
|
### Исправлено
|
||||||
|
- Docker push/pull: добавлен PATCH endpoint для chunked uploads
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [0.2.4] - 2026-01-26
|
||||||
|
|
||||||
|
### Исправлено
|
||||||
|
- Rate limiting: health/metrics endpoints теперь исключены
|
||||||
|
- Увеличены лимиты upload для параллельных Docker запросов
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## [0.2.0] - 2026-01-25
|
## [0.2.0] - 2026-01-25
|
||||||
|
|
||||||
### Добавлено
|
### Добавлено
|
||||||
@@ -182,7 +327,6 @@ All notable changes to NORA will be documented in this file.
|
|||||||
- JSON-ответы об ошибках с поддержкой request_id
|
- JSON-ответы об ошибках с поддержкой request_id
|
||||||
|
|
||||||
### Изменено
|
### Изменено
|
||||||
|
|
||||||
- `StorageError` теперь использует макрос `thiserror`
|
- `StorageError` теперь использует макрос `thiserror`
|
||||||
- `TokenError` теперь использует макрос `thiserror`
|
- `TokenError` теперь использует макрос `thiserror`
|
||||||
- Storage wrapper валидирует ключи перед делегированием backend
|
- Storage wrapper валидирует ключи перед делегированием backend
|
||||||
@@ -190,7 +334,6 @@ All notable changes to NORA will be documented in this file.
|
|||||||
- Лимит размера body установлен в 100MB через `DefaultBodyLimit`
|
- Лимит размера body установлен в 100MB через `DefaultBodyLimit`
|
||||||
|
|
||||||
### Добавлены зависимости
|
### Добавлены зависимости
|
||||||
|
|
||||||
- `thiserror = "2"` - типизированная обработка ошибок
|
- `thiserror = "2"` - типизированная обработка ошибок
|
||||||
- `tower_governor = "0.8"` - rate limiting
|
- `tower_governor = "0.8"` - rate limiting
|
||||||
- `governor = "0.10"` - backend для rate limiting
|
- `governor = "0.10"` - backend для rate limiting
|
||||||
@@ -198,7 +341,6 @@ All notable changes to NORA will be documented in this file.
|
|||||||
- `wiremock = "0.6"` (dev) - HTTP-мокирование для S3 тестов
|
- `wiremock = "0.6"` (dev) - HTTP-мокирование для S3 тестов
|
||||||
|
|
||||||
### Добавлены файлы
|
### Добавлены файлы
|
||||||
|
|
||||||
- `src/validation.rs` - модуль валидации ввода
|
- `src/validation.rs` - модуль валидации ввода
|
||||||
- `src/migrate.rs` - модуль миграции хранилища
|
- `src/migrate.rs` - модуль миграции хранилища
|
||||||
- `src/error.rs` - типы ошибок приложения
|
- `src/error.rs` - типы ошибок приложения
|
||||||
@@ -210,7 +352,6 @@ All notable changes to NORA will be documented in this file.
|
|||||||
## [0.1.0] - 2026-01-24
|
## [0.1.0] - 2026-01-24
|
||||||
|
|
||||||
### Добавлено
|
### Добавлено
|
||||||
|
|
||||||
- Мульти-протокольная поддержка: Docker Registry v2, Maven, npm, Cargo, PyPI
|
- Мульти-протокольная поддержка: Docker Registry v2, Maven, npm, Cargo, PyPI
|
||||||
- Web UI дашборд
|
- Web UI дашборд
|
||||||
- Swagger UI (`/api-docs`)
|
- Swagger UI (`/api-docs`)
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ use crate::AppState;
|
|||||||
#[openapi(
|
#[openapi(
|
||||||
info(
|
info(
|
||||||
title = "Nora",
|
title = "Nora",
|
||||||
version = "0.2.10",
|
version = "0.2.12",
|
||||||
description = "Multi-protocol package registry supporting Docker, Maven, npm, Cargo, and PyPI",
|
description = "Multi-protocol package registry supporting Docker, Maven, npm, Cargo, and PyPI",
|
||||||
license(name = "MIT"),
|
license(name = "MIT"),
|
||||||
contact(name = "DevITWay", url = "https://github.com/getnora-io/nora")
|
contact(name = "DevITWay", url = "https://github.com/getnora-io/nora")
|
||||||
@@ -25,6 +25,7 @@ use crate::AppState;
|
|||||||
),
|
),
|
||||||
tags(
|
tags(
|
||||||
(name = "health", description = "Health check endpoints"),
|
(name = "health", description = "Health check endpoints"),
|
||||||
|
(name = "metrics", description = "Prometheus metrics"),
|
||||||
(name = "dashboard", description = "Dashboard & Metrics API"),
|
(name = "dashboard", description = "Dashboard & Metrics API"),
|
||||||
(name = "docker", description = "Docker Registry v2 API"),
|
(name = "docker", description = "Docker Registry v2 API"),
|
||||||
(name = "maven", description = "Maven Repository API"),
|
(name = "maven", description = "Maven Repository API"),
|
||||||
@@ -37,18 +38,30 @@ use crate::AppState;
|
|||||||
// Health
|
// Health
|
||||||
crate::openapi::health_check,
|
crate::openapi::health_check,
|
||||||
crate::openapi::readiness_check,
|
crate::openapi::readiness_check,
|
||||||
|
// Metrics
|
||||||
|
crate::openapi::prometheus_metrics,
|
||||||
// Dashboard
|
// Dashboard
|
||||||
crate::openapi::dashboard_metrics,
|
crate::openapi::dashboard_metrics,
|
||||||
// Docker
|
// Docker - Read
|
||||||
crate::openapi::docker_version,
|
crate::openapi::docker_version,
|
||||||
crate::openapi::docker_catalog,
|
crate::openapi::docker_catalog,
|
||||||
crate::openapi::docker_tags,
|
crate::openapi::docker_tags,
|
||||||
crate::openapi::docker_manifest,
|
crate::openapi::docker_manifest_get,
|
||||||
crate::openapi::docker_blob,
|
crate::openapi::docker_blob_head,
|
||||||
|
crate::openapi::docker_blob_get,
|
||||||
|
// Docker - Write
|
||||||
|
crate::openapi::docker_manifest_put,
|
||||||
|
crate::openapi::docker_blob_upload_start,
|
||||||
|
crate::openapi::docker_blob_upload_patch,
|
||||||
|
crate::openapi::docker_blob_upload_put,
|
||||||
// Maven
|
// Maven
|
||||||
crate::openapi::maven_artifact,
|
crate::openapi::maven_artifact_get,
|
||||||
|
crate::openapi::maven_artifact_put,
|
||||||
// npm
|
// npm
|
||||||
crate::openapi::npm_package,
|
crate::openapi::npm_package,
|
||||||
|
// Cargo
|
||||||
|
crate::openapi::cargo_metadata,
|
||||||
|
crate::openapi::cargo_download,
|
||||||
// PyPI
|
// PyPI
|
||||||
crate::openapi::pypi_simple,
|
crate::openapi::pypi_simple,
|
||||||
crate::openapi::pypi_package,
|
crate::openapi::pypi_package,
|
||||||
@@ -258,6 +271,8 @@ pub struct ActivityEntry {
|
|||||||
|
|
||||||
// ============ Path Operations (documentation only) ============
|
// ============ Path Operations (documentation only) ============
|
||||||
|
|
||||||
|
// -------------------- Health --------------------
|
||||||
|
|
||||||
/// Health check endpoint
|
/// Health check endpoint
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
get,
|
get,
|
||||||
@@ -282,6 +297,23 @@ pub async fn health_check() {}
|
|||||||
)]
|
)]
|
||||||
pub async fn readiness_check() {}
|
pub async fn readiness_check() {}
|
||||||
|
|
||||||
|
// -------------------- Metrics --------------------
|
||||||
|
|
||||||
|
/// Prometheus metrics endpoint
|
||||||
|
///
|
||||||
|
/// Returns metrics in Prometheus text format for scraping.
|
||||||
|
#[utoipa::path(
|
||||||
|
get,
|
||||||
|
path = "/metrics",
|
||||||
|
tag = "metrics",
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "Prometheus metrics", content_type = "text/plain")
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
pub async fn prometheus_metrics() {}
|
||||||
|
|
||||||
|
// -------------------- Dashboard --------------------
|
||||||
|
|
||||||
/// Dashboard metrics and activity
|
/// Dashboard metrics and activity
|
||||||
///
|
///
|
||||||
/// Returns comprehensive metrics including downloads, uploads, cache statistics,
|
/// Returns comprehensive metrics including downloads, uploads, cache statistics,
|
||||||
@@ -296,6 +328,8 @@ pub async fn readiness_check() {}
|
|||||||
)]
|
)]
|
||||||
pub async fn dashboard_metrics() {}
|
pub async fn dashboard_metrics() {}
|
||||||
|
|
||||||
|
// -------------------- Docker Registry v2 - Read Operations --------------------
|
||||||
|
|
||||||
/// Docker Registry version check
|
/// Docker Registry version check
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
get,
|
get,
|
||||||
@@ -325,7 +359,7 @@ pub async fn docker_catalog() {}
|
|||||||
path = "/v2/{name}/tags/list",
|
path = "/v2/{name}/tags/list",
|
||||||
tag = "docker",
|
tag = "docker",
|
||||||
params(
|
params(
|
||||||
("name" = String, Path, description = "Repository name")
|
("name" = String, Path, description = "Repository name (e.g., 'alpine' or 'library/nginx')")
|
||||||
),
|
),
|
||||||
responses(
|
responses(
|
||||||
(status = 200, description = "Tag list", body = DockerTags),
|
(status = 200, description = "Tag list", body = DockerTags),
|
||||||
@@ -341,14 +375,30 @@ pub async fn docker_tags() {}
|
|||||||
tag = "docker",
|
tag = "docker",
|
||||||
params(
|
params(
|
||||||
("name" = String, Path, description = "Repository name"),
|
("name" = String, Path, description = "Repository name"),
|
||||||
("reference" = String, Path, description = "Tag or digest")
|
("reference" = String, Path, description = "Tag or digest (sha256:...)")
|
||||||
),
|
),
|
||||||
responses(
|
responses(
|
||||||
(status = 200, description = "Manifest content"),
|
(status = 200, description = "Manifest content"),
|
||||||
(status = 404, description = "Manifest not found")
|
(status = 404, description = "Manifest not found")
|
||||||
)
|
)
|
||||||
)]
|
)]
|
||||||
pub async fn docker_manifest() {}
|
pub async fn docker_manifest_get() {}
|
||||||
|
|
||||||
|
/// Check if blob exists
|
||||||
|
#[utoipa::path(
|
||||||
|
head,
|
||||||
|
path = "/v2/{name}/blobs/{digest}",
|
||||||
|
tag = "docker",
|
||||||
|
params(
|
||||||
|
("name" = String, Path, description = "Repository name"),
|
||||||
|
("digest" = String, Path, description = "Blob digest (sha256:...)")
|
||||||
|
),
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "Blob exists, Content-Length header contains size"),
|
||||||
|
(status = 404, description = "Blob not found")
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
pub async fn docker_blob_head() {}
|
||||||
|
|
||||||
/// Get blob
|
/// Get blob
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
@@ -364,7 +414,79 @@ pub async fn docker_manifest() {}
|
|||||||
(status = 404, description = "Blob not found")
|
(status = 404, description = "Blob not found")
|
||||||
)
|
)
|
||||||
)]
|
)]
|
||||||
pub async fn docker_blob() {}
|
pub async fn docker_blob_get() {}
|
||||||
|
|
||||||
|
// -------------------- Docker Registry v2 - Write Operations --------------------
|
||||||
|
|
||||||
|
/// Push manifest
|
||||||
|
#[utoipa::path(
|
||||||
|
put,
|
||||||
|
path = "/v2/{name}/manifests/{reference}",
|
||||||
|
tag = "docker",
|
||||||
|
params(
|
||||||
|
("name" = String, Path, description = "Repository name"),
|
||||||
|
("reference" = String, Path, description = "Tag or digest")
|
||||||
|
),
|
||||||
|
responses(
|
||||||
|
(status = 201, description = "Manifest created, Docker-Content-Digest header contains digest"),
|
||||||
|
(status = 400, description = "Invalid manifest")
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
pub async fn docker_manifest_put() {}
|
||||||
|
|
||||||
|
/// Start blob upload
|
||||||
|
///
|
||||||
|
/// Initiates a resumable blob upload. Returns a Location header with the upload URL.
|
||||||
|
#[utoipa::path(
|
||||||
|
post,
|
||||||
|
path = "/v2/{name}/blobs/uploads/",
|
||||||
|
tag = "docker",
|
||||||
|
params(
|
||||||
|
("name" = String, Path, description = "Repository name")
|
||||||
|
),
|
||||||
|
responses(
|
||||||
|
(status = 202, description = "Upload started, Location header contains upload URL")
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
pub async fn docker_blob_upload_start() {}
|
||||||
|
|
||||||
|
/// Upload blob chunk (chunked upload)
|
||||||
|
///
|
||||||
|
/// Uploads a chunk of data to an in-progress upload session.
|
||||||
|
#[utoipa::path(
|
||||||
|
patch,
|
||||||
|
path = "/v2/{name}/blobs/uploads/{uuid}",
|
||||||
|
tag = "docker",
|
||||||
|
params(
|
||||||
|
("name" = String, Path, description = "Repository name"),
|
||||||
|
("uuid" = String, Path, description = "Upload session UUID")
|
||||||
|
),
|
||||||
|
responses(
|
||||||
|
(status = 202, description = "Chunk accepted, Range header indicates bytes received")
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
pub async fn docker_blob_upload_patch() {}
|
||||||
|
|
||||||
|
/// Complete blob upload
|
||||||
|
///
|
||||||
|
/// Finalizes the blob upload. Can include final chunk data in the body.
|
||||||
|
#[utoipa::path(
|
||||||
|
put,
|
||||||
|
path = "/v2/{name}/blobs/uploads/{uuid}",
|
||||||
|
tag = "docker",
|
||||||
|
params(
|
||||||
|
("name" = String, Path, description = "Repository name"),
|
||||||
|
("uuid" = String, Path, description = "Upload session UUID"),
|
||||||
|
("digest" = String, Query, description = "Expected blob digest (sha256:...)")
|
||||||
|
),
|
||||||
|
responses(
|
||||||
|
(status = 201, description = "Blob created"),
|
||||||
|
(status = 400, description = "Digest mismatch or missing")
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
pub async fn docker_blob_upload_put() {}
|
||||||
|
|
||||||
|
// -------------------- Maven --------------------
|
||||||
|
|
||||||
/// Get Maven artifact
|
/// Get Maven artifact
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
@@ -379,7 +501,24 @@ pub async fn docker_blob() {}
|
|||||||
(status = 404, description = "Artifact not found, trying upstream proxies")
|
(status = 404, description = "Artifact not found, trying upstream proxies")
|
||||||
)
|
)
|
||||||
)]
|
)]
|
||||||
pub async fn maven_artifact() {}
|
pub async fn maven_artifact_get() {}
|
||||||
|
|
||||||
|
/// Upload Maven artifact
|
||||||
|
#[utoipa::path(
|
||||||
|
put,
|
||||||
|
path = "/maven2/{path}",
|
||||||
|
tag = "maven",
|
||||||
|
params(
|
||||||
|
("path" = String, Path, description = "Artifact path")
|
||||||
|
),
|
||||||
|
responses(
|
||||||
|
(status = 201, description = "Artifact uploaded"),
|
||||||
|
(status = 500, description = "Storage error")
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
pub async fn maven_artifact_put() {}
|
||||||
|
|
||||||
|
// -------------------- npm --------------------
|
||||||
|
|
||||||
/// Get npm package metadata
|
/// Get npm package metadata
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
@@ -387,7 +526,7 @@ pub async fn maven_artifact() {}
|
|||||||
path = "/npm/{name}",
|
path = "/npm/{name}",
|
||||||
tag = "npm",
|
tag = "npm",
|
||||||
params(
|
params(
|
||||||
("name" = String, Path, description = "Package name")
|
("name" = String, Path, description = "Package name (e.g., 'lodash' or '@scope/package')")
|
||||||
),
|
),
|
||||||
responses(
|
responses(
|
||||||
(status = 200, description = "Package metadata (JSON)"),
|
(status = 200, description = "Package metadata (JSON)"),
|
||||||
@@ -396,6 +535,41 @@ pub async fn maven_artifact() {}
|
|||||||
)]
|
)]
|
||||||
pub async fn npm_package() {}
|
pub async fn npm_package() {}
|
||||||
|
|
||||||
|
// -------------------- Cargo --------------------
|
||||||
|
|
||||||
|
/// Get Cargo crate metadata
|
||||||
|
#[utoipa::path(
|
||||||
|
get,
|
||||||
|
path = "/cargo/api/v1/crates/{crate_name}",
|
||||||
|
tag = "cargo",
|
||||||
|
params(
|
||||||
|
("crate_name" = String, Path, description = "Crate name")
|
||||||
|
),
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "Crate metadata (JSON)"),
|
||||||
|
(status = 404, description = "Crate not found")
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
pub async fn cargo_metadata() {}
|
||||||
|
|
||||||
|
/// Download Cargo crate
|
||||||
|
#[utoipa::path(
|
||||||
|
get,
|
||||||
|
path = "/cargo/api/v1/crates/{crate_name}/{version}/download",
|
||||||
|
tag = "cargo",
|
||||||
|
params(
|
||||||
|
("crate_name" = String, Path, description = "Crate name"),
|
||||||
|
("version" = String, Path, description = "Crate version")
|
||||||
|
),
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "Crate file (.crate)"),
|
||||||
|
(status = 404, description = "Crate version not found")
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
pub async fn cargo_download() {}
|
||||||
|
|
||||||
|
// -------------------- PyPI --------------------
|
||||||
|
|
||||||
/// PyPI Simple index
|
/// PyPI Simple index
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
get,
|
get,
|
||||||
@@ -422,6 +596,8 @@ pub async fn pypi_simple() {}
|
|||||||
)]
|
)]
|
||||||
pub async fn pypi_package() {}
|
pub async fn pypi_package() {}
|
||||||
|
|
||||||
|
// -------------------- Auth / Tokens --------------------
|
||||||
|
|
||||||
/// Create API token
|
/// Create API token
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
post,
|
post,
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ static UPLOAD_SESSIONS: std::sync::LazyLock<RwLock<HashMap<String, Vec<u8>>>> =
|
|||||||
pub fn routes() -> Router<Arc<AppState>> {
|
pub fn routes() -> Router<Arc<AppState>> {
|
||||||
Router::new()
|
Router::new()
|
||||||
.route("/v2/", get(check))
|
.route("/v2/", get(check))
|
||||||
|
.route("/v2/_catalog", get(catalog))
|
||||||
// Single-segment name routes (e.g., /v2/alpine/...)
|
// Single-segment name routes (e.g., /v2/alpine/...)
|
||||||
.route("/v2/{name}/blobs/{digest}", head(check_blob))
|
.route("/v2/{name}/blobs/{digest}", head(check_blob))
|
||||||
.route("/v2/{name}/blobs/{digest}", get(download_blob))
|
.route("/v2/{name}/blobs/{digest}", get(download_blob))
|
||||||
@@ -87,6 +88,26 @@ async fn check() -> (StatusCode, Json<Value>) {
|
|||||||
(StatusCode::OK, Json(json!({})))
|
(StatusCode::OK, Json(json!({})))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// List all repositories in the registry
|
||||||
|
async fn catalog(State(state): State<Arc<AppState>>) -> Json<Value> {
|
||||||
|
let keys = state.storage.list("docker/").await;
|
||||||
|
|
||||||
|
// Extract unique repository names from paths like "docker/{name}/manifests/..."
|
||||||
|
let mut repos: Vec<String> = keys
|
||||||
|
.iter()
|
||||||
|
.filter_map(|k| {
|
||||||
|
k.strip_prefix("docker/")
|
||||||
|
.and_then(|rest| rest.split('/').next())
|
||||||
|
.map(String::from)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
repos.sort();
|
||||||
|
repos.dedup();
|
||||||
|
|
||||||
|
Json(json!({ "repositories": repos }))
|
||||||
|
}
|
||||||
|
|
||||||
async fn check_blob(
|
async fn check_blob(
|
||||||
State(state): State<Arc<AppState>>,
|
State(state): State<Arc<AppState>>,
|
||||||
Path((name, digest)): Path<(String, String)>,
|
Path((name, digest)): Path<(String, String)>,
|
||||||
|
|||||||
Reference in New Issue
Block a user