aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'src/web.rs')
-rw-r--r--src/web.rs154+154 −0
1 files changed, 154 insertions, 0 deletions
diff --git a/src/web.rs b/src/web.rs
new file mode 100644
--- /dev/null
+++ b/src/web.rs
@@ -0,0 +1,154 @@
+// SPDX-FileCopyrightText: 2026 Nikolay Govorov <me@govorov.online>
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+use std::sync::Arc;
+
+use axum::{
+ extract::{self},
+ http::{self},
+ response::{self, IntoResponse},
+};
+
+/// Handles html pages rendering and static files
+pub struct WebController {
+ jinja: minijinja::Environment<'static>,
+}
+
+impl Default for WebController {
+ fn default() -> Self {
+ let mut jinja = minijinja::Environment::new();
+ jinja.add_template("index", HTML).unwrap();
+
+ Self { jinja }
+ }
+}
+
+impl WebController {
+ pub fn router(self: Arc<Self>) -> axum::Router {
+ axum::Router::new()
+ .route("/index.css", axum::routing::get(Self::styles))
+ .route("/", axum::routing::get(Self::index))
+ .with_state(self)
+ }
+
+ async fn index(
+ extract::State(controller): extract::State<Arc<Self>>,
+ ) -> Result<response::Response, http::StatusCode> {
+ let template = controller.jinja.get_template("index").unwrap();
+ let rendered = template.render(minijinja::context! {}).unwrap();
+
+ let mut response = response::Html(rendered).into_response();
+ response.headers_mut().insert(
+ http::header::CONTENT_SECURITY_POLICY,
+ http::HeaderValue::from_static(
+ "default-src 'self'; base-uri 'none'; img-src 'self'; font-src 'self'; style-src 'self'; script-src 'self'; object-src 'none'; frame-ancestors 'none'",
+ ),
+ );
+
+ Ok(response)
+ }
+
+ async fn styles() -> Result<axum_extra::response::Css<&'static str>, http::StatusCode> {
+ Ok(axum_extra::response::Css(CSS))
+ }
+}
+
+const HTML: &str = r#"
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width,initial-scale=1">
+ <meta http-equiv="X-UA-Compatible" content="ie=edge">
+
+ <title>Earth PKG — tiny & opinionated packages mirror</title>
+ <link rel="stylesheet" href="index.css">
+ </head>
+
+ <body>
+ <h1>Earth PKG — tiny & opinionated packages mirror.</h1>
+
+ <main>
+ <p>This site provides a proxy for downloading zig installation files and dependencies.
+ On the one hand, this reduces the load on the original project's site
+ and makes your infrastructure more reliable by adding redundancy.</p>
+
+ <p>Read more about community mirrors in the <a href="https://ziglang.org/download/community-mirrors/">blog post</a>.
+ Information on how to deploy your own mirror is available
+ <a href="https://github.com/ziglang/www.ziglang.org/blob/main/MIRRORS.md">in the documentation</a>.</p>
+
+ <h2>Direct usage:</h2>
+
+ <p>For simplicity, you can use tools like <a href="https://github.com/prantlf/zigup">prantlf/zigup</a> and
+ <a href="https://github.com/mlugg/setup-zig">mlugg/setup-zig</a>.</p>
+
+ <p>
+ To install manually:
+ <ol>
+ <li>download zig dist file:<br><code>wget https://pkg.earth/zig/zig-x86_64-linux-0.15.1.tar.xz</code>;</li>
+ <li>download zig minisign file:<br><code>wget https://pkg.earth/zig/zig-x86_64-linux-0.15.1.tar.xz.minisig</code>;</li>
+ <li>check archive integrity:<br><code>minisign -Vm zig-x86_64-linux-0.15.1.tar.xz -P RWSGOq2NVecA2UPNdBUZykf1CCb147pkmdtYxgb3Ti+JO/wCYvhbAb/U</code>;</li>
+ <li>unpack archive:<br><code>tar -xf "zig-x86_64-linux-0.15.1.tar.xz"</code>;</li>
+ <li>check installed zig:<br><code>./zig-x86_64-linux-0.15.1/zig --version</code>.</li>
+ </ol>
+
+ You can take actual minisign public key in <a href="https://ziglang.org/download/">download page</a>.
+ </p>
+
+ <h2>Privacy policy</h2>
+
+ <p>This mirror is a non-profit project available on a voluntary basis. The author has no plans to fund it.</p>
+
+ <p>Since the mirror is hosted on hardware, we collect access logs to combat bots and brute-force attacks.
+ The logs are used for security purposes and load planning, are not shared with third parties,
+ and are deleted after 30 days.</p>
+
+ <p>Third-party analytics systems are not used, same as client-side trackers.</p>
+ </main>
+ </body>
+</html>
+"#;
+
+const CSS: &str = "
+:root {
+ font-size: 1.125rem;
+ line-height: 1.4;
+ font-family:
+ 'Alegreya Sans', -apple-system, BlinkMacSystemFont,
+ 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell,
+ 'Open Sans', 'Helvetica Neue', sans-serif;
+}
+
+html {
+ margin: 0;
+ padding: 0;
+}
+
+body {
+ margin: 0 auto;
+ padding: 1.5em 2em;
+ max-width: 680px;
+}
+
+code {
+ font-size: .75rem;
+}
+
+h1, h2, h3, h4, h5, h6 {
+ font-weight: 700;
+ line-height: 1.2;
+ margin: 0;
+}
+
+h1 {
+ font-size: 2.75rem;
+}
+
+h1:first-child {
+ margin-top: 0;
+}
+
+th {
+ text-align: start;
+}
+";