First commit

This commit is contained in:
2025-05-27 19:44:12 +02:00
commit 13a664981d
6 changed files with 2341 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/target

2233
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

13
Cargo.toml Normal file
View File

@@ -0,0 +1,13 @@
[package]
name = "rdockup"
version = "0.1.0"
edition = "2024"
[dependencies]
oci-client = "0.15.0"
tokio = { version = "1", features = ["full"] }
axum = "0.8"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tower-http = { version = "0.6", features = ["trace"] }
tracing-subscriber = "0.3"

52
src/api.rs Normal file
View File

@@ -0,0 +1,52 @@
use axum::{
Router,
extract::Json,
http::{HeaderMap, StatusCode},
response::IntoResponse,
routing::post,
};
use serde::Deserialize;
use crate::registry_client;
#[derive(Deserialize)]
struct TagRequest {
image: String,
}
fn validate_token(token: &str) -> bool {
token == "my-secret-token"
}
pub fn create_router() -> Router {
Router::new().route("/tags", post(get_tags_handler))
}
async fn get_tags_handler(
headers: HeaderMap,
Json(payload): Json<TagRequest>,
) -> impl IntoResponse {
// Extract Bearer token from Authorization header
let token = headers
.get("authorization")
.and_then(|h| h.to_str().ok())
.and_then(|s| s.strip_prefix("Bearer "))
.unwrap_or("");
if !validate_token(token) {
return (
StatusCode::UNAUTHORIZED,
[("content-type", "text/plain")],
"Invalid or missing token".to_string(),
);
}
match registry_client::get_tags(&payload.image).await {
Ok(json) => (StatusCode::OK, [("content-type", "application/json")], json),
Err(e) => (
StatusCode::BAD_REQUEST,
[("content-type", "text/plain")],
format!("Error: {}", e),
),
}
}

21
src/main.rs Normal file
View File

@@ -0,0 +1,21 @@
mod api;
mod registry_client;
// use axum::Router;
use tower_http::trace::TraceLayer;
use tracing_subscriber::{filter::LevelFilter, layer::SubscriberExt, util::SubscriberInitExt};
#[tokio::main]
async fn main() {
// Initialize logging for access logs
tracing_subscriber::registry()
.with(tracing_subscriber::fmt::layer())
.with(LevelFilter::INFO)
.init();
// Build app with routes from api module and add logging middleware
let app = api::create_router().layer(TraceLayer::new_for_http());
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}

21
src/registry_client.rs Normal file
View File

@@ -0,0 +1,21 @@
use oci_client::client::ClientConfig;
use oci_client::{Client, Reference, secrets::RegistryAuth};
use serde::Serialize;
#[derive(Serialize)]
struct SerializableTags<'a> {
name: &'a str,
tags: &'a [String],
}
pub async fn get_tags(image: &str) -> Result<String, Box<dyn std::error::Error>> {
let client = Client::new(ClientConfig::default());
let img_ref: Reference = image.parse()?;
let auth = RegistryAuth::Anonymous;
let tags = client.list_tags(&img_ref, &auth, None, None).await?;
let serialized_tags = SerializableTags {
name: &tags.name,
tags: &tags.tags,
};
Ok(serde_json::to_string_pretty(&serialized_tags)?)
}