Git hosting and a container registry in one Rust binary (axum + Astro)
Remove unused code and the blanket dead_code allowance
9 files changed, +8 -67
+2-16backend/src/auth.rs
| @@ -66,6 +66,8 @@ pub struct Viewer { | ||
| 66 | 66 | pub is_admin: bool, |
| 67 | 67 | pub avatar_key: Option<String>, |
| 68 | 68 | pub scopes: Scopes, |
| 69 | + /// How the request authenticated; shows up in Debug output and logs. | |
| 70 | + #[allow(dead_code)] | |
| 69 | 71 | pub via: AuthVia, |
| 70 | 72 | } |
| 71 | 73 | |
| @@ -380,22 +382,6 @@ impl OptionalFromRequestParts<AppState> for ApiViewer { | ||
| 380 | 382 | } |
| 381 | 383 | } |
| 382 | 384 | |
| 383 | -/// The site admin panel. 404s for everyone else so it is not discoverable. | |
| 384 | -pub struct AdminViewer(pub Viewer); | |
| 385 | - | |
| 386 | -impl FromRequestParts<AppState> for AdminViewer { | |
| 387 | - type Rejection = Response; | |
| 388 | - | |
| 389 | - async fn from_request_parts(parts: &mut Parts, state: &AppState) -> Result<Self, Self::Rejection> { | |
| 390 | - let RequireViewer(viewer) = RequireViewer::from_request_parts(parts, state).await?; | |
| 391 | - if viewer.site_admin() { | |
| 392 | - Ok(AdminViewer(viewer)) | |
| 393 | - } else { | |
| 394 | - Err(AppError::NotFound.into_response()) | |
| 395 | - } | |
| 396 | - } | |
| 397 | -} | |
| 398 | - | |
| 399 | 385 | /// Client IP as seen through a reverse proxy, for logs and the audit trail. |
| 400 | 386 | pub fn client_ip(headers: &HeaderMap) -> Option<String> { |
| 401 | 387 | for name in ["cf-connecting-ip", "x-real-ip"] { |
+1-3backend/src/error.rs
| @@ -17,7 +17,6 @@ pub enum AppError { | ||
| 17 | 17 | Forbidden(String), |
| 18 | 18 | BadRequest(String), |
| 19 | 19 | Conflict(String), |
| 20 | - TooLarge(String), | |
| 21 | 20 | Internal(anyhow::Error), |
| 22 | 21 | } |
| 23 | 22 | |
| @@ -43,7 +42,6 @@ impl AppError { | ||
| 43 | 42 | Self::Forbidden(_) => StatusCode::FORBIDDEN, |
| 44 | 43 | Self::BadRequest(_) => StatusCode::BAD_REQUEST, |
| 45 | 44 | Self::Conflict(_) => StatusCode::CONFLICT, |
| 46 | - Self::TooLarge(_) => StatusCode::PAYLOAD_TOO_LARGE, | |
| 47 | 45 | Self::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR, |
| 48 | 46 | } |
| 49 | 47 | } |
| @@ -53,7 +51,7 @@ impl AppError { | ||
| 53 | 51 | match self { |
| 54 | 52 | Self::NotFound => "Not found".into(), |
| 55 | 53 | Self::Unauthorized => "Sign in required".into(), |
| 56 | - Self::Forbidden(m) | Self::BadRequest(m) | Self::Conflict(m) | Self::TooLarge(m) => m.clone(), | |
| 54 | + Self::Forbidden(m) | Self::BadRequest(m) | Self::Conflict(m) => m.clone(), | |
| 57 | 55 | Self::Internal(_) => "Something went wrong on our side. It has been logged.".into(), |
| 58 | 56 | } |
| 59 | 57 | } |
+1-2backend/src/git.rs
| @@ -42,7 +42,6 @@ pub struct Git { | ||
| 42 | 42 | pub struct RefInfo { |
| 43 | 43 | /// Full name, e.g. "refs/heads/main". |
| 44 | 44 | pub name: String, |
| 45 | - pub sha: String, | |
| 46 | 45 | /// Peeled commit for annotated tags, otherwise the same as `sha`. |
| 47 | 46 | pub commit: String, |
| 48 | 47 | pub committed_at: Option<DateTime<Utc>>, |
| @@ -232,7 +231,7 @@ impl Git { | ||
| 232 | 231 | let commit = if peeled.is_empty() { sha.clone() } else { peeled }; |
| 233 | 232 | let date = if date.is_empty() { peeled_date } else { date }; |
| 234 | 233 | let committed_at = date.parse::<i64>().ok().and_then(|t| Utc.timestamp_opt(t, 0).single()); |
| 235 | - Some(RefInfo { name, sha, commit, committed_at, subject }) | |
| 234 | + Some(RefInfo { name, commit, committed_at, subject }) | |
| 236 | 235 | }) |
| 237 | 236 | .collect()) |
| 238 | 237 | } |
+0-2backend/src/main.rs
| @@ -5,8 +5,6 @@ | ||
| 5 | 5 | //! The same binary is also the git hook (`irongit hook pre-receive`) and the |
| 6 | 6 | //! admin tool (`irongit admin ...`). |
| 7 | 7 | |
| 8 | -// Scaffolding is wired up before every caller exists; revisit before release. | |
| 9 | -#![allow(dead_code)] | |
| 10 | 8 | |
| 11 | 9 | mod analytics; |
| 12 | 10 | mod api; |
+1-15backend/src/registry/mod.rs
| @@ -261,19 +261,6 @@ impl Actions { | ||
| 261 | 261 | } |
| 262 | 262 | } |
| 263 | 263 | |
| 264 | - pub fn list(&self) -> Vec<String> { | |
| 265 | - let mut out = Vec::new(); | |
| 266 | - if self.pull { | |
| 267 | - out.push("pull".to_string()); | |
| 268 | - } | |
| 269 | - if self.push { | |
| 270 | - out.push("push".to_string()); | |
| 271 | - } | |
| 272 | - if self.delete { | |
| 273 | - out.push("delete".to_string()); | |
| 274 | - } | |
| 275 | - out | |
| 276 | - } | |
| 277 | 264 | } |
| 278 | 265 | |
| 279 | 266 | /// The caller of a /v2 request. |
| @@ -355,7 +342,6 @@ pub struct Target { | ||
| 355 | 342 | pub owner: Account, |
| 356 | 343 | pub image: String, |
| 357 | 344 | pub package: Option<Package>, |
| 358 | - pub actions: Actions, | |
| 359 | 345 | } |
| 360 | 346 | |
| 361 | 347 | impl Target { |
| @@ -380,7 +366,7 @@ pub async fn authorize(state: &AppState, caller: &Caller, name: &str, action: &s | ||
| 380 | 366 | Caller::Anonymous => actions_for(state, None, &owner, package.as_ref()).await?, |
| 381 | 367 | }; |
| 382 | 368 | if actions.allows(action) { |
| 383 | - return Ok(Target { name: name.to_string(), owner, image, package, actions }); | |
| 369 | + return Ok(Target { name: name.to_string(), owner, image, package }); | |
| 384 | 370 | } |
| 385 | 371 | if caller.is_anonymous() { |
| 386 | 372 | return Err(RegError::unauthorized(Some(scope))); |
+0-5backend/src/storage.rs
| @@ -63,11 +63,6 @@ impl Storage { | ||
| 63 | 63 | action.sign(ttl) |
| 64 | 64 | } |
| 65 | 65 | |
| 66 | - /// A URL anyone can PUT a body to for `ttl`. | |
| 67 | - pub fn presign_put(&self, key: &str, ttl: Duration) -> Url { | |
| 68 | - self.bucket.put_object(Some(&self.credentials), key).sign(ttl) | |
| 69 | - } | |
| 70 | - | |
| 71 | 66 | /// A presigned PUT that only succeeds when the body's SHA-256 matches. |
| 72 | 67 | /// The client must send `x-amz-checksum-sha256: <sha256_base64>` and each |
| 73 | 68 | /// `x-amz-meta-<name>: <value>` in `metadata` exactly; R2 rejects any other |
+0-1backend/src/web/admin/audit.rs
| @@ -39,7 +39,6 @@ fn with_page(base: &str, page: i64) -> String { | ||
| 39 | 39 | |
| 40 | 40 | #[derive(sqlx::FromRow)] |
| 41 | 41 | pub struct AuditRow { |
| 42 | - pub id: i64, | |
| 43 | 42 | pub action: String, |
| 44 | 43 | pub target: String, |
| 45 | 44 | pub meta: serde_json::Value, |
+2-13backend/src/web/heatmap.rs
| @@ -3,7 +3,7 @@ | ||
| 3 | 3 | |
| 4 | 4 | use std::collections::HashMap; |
| 5 | 5 | |
| 6 | -use chrono::{Datelike, Duration, NaiveDate, Weekday}; | |
| 6 | +use chrono::{Datelike, Duration, NaiveDate}; | |
| 7 | 7 | use maud::{Markup, html}; |
| 8 | 8 | |
| 9 | 9 | const CELL: i64 = 11; |
| @@ -139,21 +139,10 @@ fn tooltip(count: i64, day: NaiveDate) -> String { | ||
| 139 | 139 | } |
| 140 | 140 | } |
| 141 | 141 | |
| 142 | -pub fn weekday_name(day: Weekday) -> &'static str { | |
| 143 | - match day { | |
| 144 | - Weekday::Mon => "Monday", | |
| 145 | - Weekday::Tue => "Tuesday", | |
| 146 | - Weekday::Wed => "Wednesday", | |
| 147 | - Weekday::Thu => "Thursday", | |
| 148 | - Weekday::Fri => "Friday", | |
| 149 | - Weekday::Sat => "Saturday", | |
| 150 | - Weekday::Sun => "Sunday", | |
| 151 | - } | |
| 152 | -} | |
| 153 | - | |
| 154 | 142 | #[cfg(test)] |
| 155 | 143 | mod tests { |
| 156 | 144 | use super::*; |
| 145 | + use chrono::Weekday; | |
| 157 | 146 | |
| 158 | 147 | #[test] |
| 159 | 148 | fn grid_starts_on_sunday_and_streaks_count() { |
+1-10backend/src/web/layout.rs
| @@ -9,7 +9,7 @@ use axum::{ | ||
| 9 | 9 | http::{StatusCode, header, request::Parts}, |
| 10 | 10 | response::{Html, IntoResponse, Response}, |
| 11 | 11 | }; |
| 12 | -use maud::{DOCTYPE, Markup, PreEscaped, html}; | |
| 12 | +use maud::{DOCTYPE, Markup, html}; | |
| 13 | 13 | |
| 14 | 14 | use crate::{ |
| 15 | 15 | auth::{MaybeViewer, Viewer}, |
| @@ -68,11 +68,6 @@ impl Page { | ||
| 68 | 68 | self |
| 69 | 69 | } |
| 70 | 70 | |
| 71 | - pub fn status(mut self, status: StatusCode) -> Self { | |
| 72 | - self.status = status; | |
| 73 | - self | |
| 74 | - } | |
| 75 | - | |
| 76 | 71 | /// Keep settings, forms and private pages out of search engines. |
| 77 | 72 | pub fn noindex(mut self) -> Self { |
| 78 | 73 | self.noindex = true; |
| @@ -228,7 +223,3 @@ pub fn fragment(markup: Markup) -> Response { | ||
| 228 | 223 | response.headers_mut().insert(header::CACHE_CONTROL, header::HeaderValue::from_static("no-store")); |
| 229 | 224 | response |
| 230 | 225 | } |
| 231 | - | |
| 232 | -pub fn raw_html(s: &str) -> PreEscaped<String> { | |
| 233 | - PreEscaped(s.to_string()) | |
| 234 | -} |