mirror of
https://github.com/getnora-io/nora.git
synced 2026-04-13 01:30:32 +00:00
feat: add Go module proxy (GOPROXY protocol) (#59)
* feat: add Go module proxy (GOPROXY protocol) (#47) Implements caching proxy for Go modules with 5 standard endpoints: - GET /go/{module}/@v/list — list versions - GET /go/{module}/@v/{version}.info — version metadata - GET /go/{module}/@v/{version}.mod — go.mod file - GET /go/{module}/@v/{version}.zip — module zip - GET /go/{module}/@latest — latest version info Features: - Module path encoding/decoding per Go spec (!x → X) - Immutable caching (.info/.mod/.zip never overwritten) - Mutable endpoints (@v/list, @latest) refreshed from upstream - Configurable upstream (default: proxy.golang.org) - Separate timeout for .zip downloads (default: 120s) - Size limit for zips (default: 100MB) - Path traversal protection - Dashboard integration (stats, mount points, index) - 25 unit tests (encoding, path splitting, safety, content-type) Closes #47 * style: cargo fmt * feat(ui): add Go pages, compact cards, fix icons - Go in sidebar + list/detail pages with go get command - Dashboard: fix fallback icon (was Docker whale for Go) - Compact registry cards: lg:grid-cols-6, all 6 in one row - Cargo icon: crate boxes instead of truck - Go icon: stylized Go text (sidebar + dashboard) * fix(go): URL-decode path + send encoded paths to upstream Go client sends %21 for ! in module paths. Axum wildcard does not auto-decode, so we percent-decode manually. Upstream proxy.golang.org expects encoded paths (with !), not decoded uppercase. Tested: full Pusk build (22 modules, 135MB cached) including SherClockHolmes/webpush-go with triple uppercase encoding. * style: cargo fmt
This commit is contained in:
@@ -87,6 +87,8 @@ pub fn routes() -> Router<Arc<AppState>> {
|
||||
.route("/ui/cargo/{name}", get(cargo_detail))
|
||||
.route("/ui/pypi", get(pypi_list))
|
||||
.route("/ui/pypi/{name}", get(pypi_detail))
|
||||
.route("/ui/go", get(go_list))
|
||||
.route("/ui/go/{*name}", get(go_detail))
|
||||
// API endpoints for HTMX
|
||||
.route("/api/ui/stats", get(api_stats))
|
||||
.route("/api/ui/dashboard", get(api_dashboard))
|
||||
@@ -298,3 +300,41 @@ async fn pypi_detail(
|
||||
let detail = get_pypi_detail(&state.storage, &name).await;
|
||||
Html(render_package_detail("pypi", &name, &detail, lang))
|
||||
}
|
||||
|
||||
// Go pages
|
||||
async fn go_list(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Query(query): Query<ListQuery>,
|
||||
headers: axum::http::HeaderMap,
|
||||
) -> impl IntoResponse {
|
||||
let lang = extract_lang_from_list(&query, headers.get("cookie").and_then(|v| v.to_str().ok()));
|
||||
let page = query.page.unwrap_or(1).max(1);
|
||||
let limit = query.limit.unwrap_or(DEFAULT_PAGE_SIZE).min(100);
|
||||
|
||||
let all_modules = state.repo_index.get("go", &state.storage).await;
|
||||
let (modules, total) = paginate(&all_modules, page, limit);
|
||||
|
||||
Html(render_registry_list_paginated(
|
||||
"go",
|
||||
"Go Modules",
|
||||
&modules,
|
||||
page,
|
||||
limit,
|
||||
total,
|
||||
lang,
|
||||
))
|
||||
}
|
||||
|
||||
async fn go_detail(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Path(name): Path<String>,
|
||||
Query(query): Query<LangQuery>,
|
||||
headers: axum::http::HeaderMap,
|
||||
) -> impl IntoResponse {
|
||||
let lang = extract_lang(
|
||||
&Query(query),
|
||||
headers.get("cookie").and_then(|v| v.to_str().ok()),
|
||||
);
|
||||
let detail = get_go_detail(&state.storage, &name).await;
|
||||
Html(render_package_detail("go", &name, &detail, lang))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user