aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat
-rw-r--r--Cargo.lock310+70 −240
-rw-r--r--Cargo.toml61+19 −42
-rw-r--r--LICENSES/OFL-1.1.txt93+93 −0
-rw-r--r--REUSE.toml8+8 −0
-rw-r--r--src/assets/base.css77+77 −0
-rw-r--r--src/assets/jetbrainsmono/JetBrainsMono-Italic[wght].woff2bin0 -> 122452 bytes
-rw-r--r--src/assets/jetbrainsmono/JetBrainsMono[wght].woff2bin0 -> 113700 bytes
-rw-r--r--src/controller_web.rs289+129 −160
8 files changed, 396 insertions, 442 deletions
diff --git a/Cargo.lock b/Cargo.lock
index c3f41c1..7017aee 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -218,33 +218,17 @@ checksum = "fef252edff26ddba56bbcdf2ee3307b8129acb86f5749b68990c168a6fcc9c76"
dependencies = [
"axum",
"axum-core",
- "axum-macros",
"bytes",
- "fastrand",
- "form_urlencoded",
"futures-core",
"futures-util",
- "headers",
"http",
"http-body",
"http-body-util",
"mime",
- "multer",
- "percent-encoding",
"pin-project-lite",
- "prost",
- "rustversion",
- "serde_core",
- "serde_html_form",
- "serde_json",
- "serde_path_to_error",
- "tokio",
- "tokio-stream",
- "tokio-util",
"tower-layer",
"tower-service",
"tracing",
- "typed-json",
]
[[package]]
@@ -400,18 +384,6 @@ dependencies = [
]
[[package]]
-name = "cargo-config2"
-version = "0.1.39"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3795d3a48839a46854805f56c8fe9c558f10804bcf57df53925ca843d87c788f"
-dependencies = [
- "serde",
- "serde_derive",
- "toml",
- "windows-sys 0.61.2",
-]
-
-[[package]]
name = "cargo-deny"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -456,33 +428,6 @@ dependencies = [
]
[[package]]
-name = "cargo-llvm-cov"
-version = "0.8.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8132c5e1b6489a345e12bbc3deb7154cb8d8b51bd9d9b168505eff5e5ce88ee"
-dependencies = [
- "anyhow",
- "camino",
- "cargo-config2",
- "duct",
- "fs-err",
- "glob",
- "lcov2cobertura",
- "lexopt",
- "opener",
- "regex",
- "rustc-demangle",
- "ruzstd",
- "serde",
- "serde_derive",
- "serde_json",
- "shell-escape",
- "tar",
- "termcolor",
- "walkdir",
-]
-
-[[package]]
name = "cargo-lock"
version = "11.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -864,18 +809,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
[[package]]
-name = "duct"
-version = "1.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e66e9c0c03d094e1a0ba1be130b849034aa80c3a2ab8ee94316bc809f3fa684"
-dependencies = [
- "libc",
- "os_pipe",
- "shared_child",
- "shared_thread",
-]
-
-[[package]]
name = "dunce"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1961,12 +1894,6 @@ dependencies = [
]
[[package]]
-name = "glob"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
-
-[[package]]
name = "globset"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2089,30 +2016,6 @@ dependencies = [
]
[[package]]
-name = "headers"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b3314d5adb5d94bcdf56771f2e50dbbc80bb4bdf88967526706205ac9eff24eb"
-dependencies = [
- "base64",
- "bytes",
- "headers-core",
- "http",
- "httpdate",
- "mime",
- "sha1",
-]
-
-[[package]]
-name = "headers-core"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4"
-dependencies = [
- "http",
-]
-
-[[package]]
name = "heapless"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2606,24 +2509,6 @@ dependencies = [
]
[[package]]
-name = "lcov2cobertura"
-version = "1.0.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aaa0cf456e88a45378a5737f228c0800175d94be6856908dc4718b3a91c7c9f8"
-dependencies = [
- "anyhow",
- "quick-xml",
- "regex",
- "rustc-demangle",
-]
-
-[[package]]
-name = "lexopt"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9fa0e2a1fcbe2f6be6c42e342259976206b383122fc152e872795338b5a3f3a7"
-
-[[package]]
name = "libc"
version = "0.2.180"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2700,6 +2585,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
[[package]]
+name = "maud"
+version = "0.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8156733e27020ea5c684db5beac5d1d611e1272ab17901a49466294b84fc217e"
+dependencies = [
+ "axum-core",
+ "http",
+ "itoa",
+ "maud_macros",
+]
+
+[[package]]
+name = "maud_macros"
+version = "0.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7261b00f3952f617899bc012e3dbd56e4f0110a038175929fa5d18e5a19913ca"
+dependencies = [
+ "proc-macro2",
+ "proc-macro2-diagnostics",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "maybe-async"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2752,15 +2661,6 @@ dependencies = [
]
[[package]]
-name = "minijinja"
-version = "2.14.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "12ea9ac0a51fb5112607099560fdf0f90366ab088a2a9e6e8ae176794e9806aa"
-dependencies = [
- "serde",
-]
-
-[[package]]
name = "miniz_oxide"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2828,15 +2728,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21"
[[package]]
-name = "normpath"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf23ab2b905654b4cb177e30b629937b3868311d4e1cba859f899c041046e69b"
-dependencies = [
- "windows-sys 0.61.2",
-]
-
-[[package]]
name = "nu-ansi-term"
version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2910,17 +2801,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]]
-name = "opener"
-version = "0.8.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cb9024962ab91e00c89d2a14352a8d0fc1a64346bf96f1839b45c09149564e47"
-dependencies = [
- "bstr",
- "normpath",
- "windows-sys 0.60.2",
-]
-
-[[package]]
name = "openssl"
version = "0.10.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3065,16 +2945,6 @@ dependencies = [
]
[[package]]
-name = "os_pipe"
-version = "1.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967"
-dependencies = [
- "libc",
- "windows-sys 0.61.2",
-]
-
-[[package]]
name = "parking"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3252,6 +3122,18 @@ dependencies = [
]
[[package]]
+name = "proc-macro2-diagnostics"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "version_check",
+]
+
+[[package]]
name = "prodash"
version = "30.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3299,15 +3181,6 @@ dependencies = [
]
[[package]]
-name = "quick-xml"
-version = "0.37.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb"
-dependencies = [
- "memchr",
-]
-
-[[package]]
name = "quote"
version = "1.0.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3571,10 +3444,38 @@ dependencies = [
]
[[package]]
-name = "rustc-demangle"
-version = "0.1.27"
+name = "rust-embed"
+version = "8.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04113cb9355a377d83f06ef1f0a45b8ab8cd7d8b1288160717d66df5c7988d27"
+dependencies = [
+ "rust-embed-impl",
+ "rust-embed-utils",
+ "walkdir",
+]
+
+[[package]]
+name = "rust-embed-impl"
+version = "8.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da0902e4c7c8e997159ab384e6d0fc91c221375f6894346ae107f47dd0f3ccaa"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "rust-embed-utils",
+ "syn",
+ "walkdir",
+]
+
+[[package]]
+name = "rust-embed-utils"
+version = "8.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d"
+checksum = "5bcdef0be6fe7f6fa333b1073c949729274b05f123a0ad7efcb8efd878e5c3b1"
+dependencies = [
+ "sha2",
+ "walkdir",
+]
[[package]]
name = "rustc-stable-hash"
@@ -3693,12 +3594,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
-name = "ruzstd"
-version = "0.8.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5ff0cc5e135c8870a775d3320910cd9b564ec036b4dc0b8741629020be63f01"
-
-[[package]]
name = "ryu"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3834,19 +3729,6 @@ dependencies = [
]
[[package]]
-name = "serde_html_form"
-version = "0.2.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2f2d7ff8a2140333718bb329f5c40fc5f0865b84c426183ce14c97d2ab8154f"
-dependencies = [
- "form_urlencoded",
- "indexmap",
- "itoa",
- "ryu",
- "serde_core",
-]
-
-[[package]]
name = "serde_json"
version = "1.0.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3939,28 +3821,6 @@ dependencies = [
]
[[package]]
-name = "shared_child"
-version = "1.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e362d9935bc50f019969e2f9ecd66786612daae13e8f277be7bfb66e8bed3f7"
-dependencies = [
- "libc",
- "windows-sys 0.60.2",
-]
-
-[[package]]
-name = "shared_thread"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "52b86057fcb5423f5018e331ac04623e32d6b5ce85e33300f92c79a1973928b0"
-
-[[package]]
-name = "shell-escape"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f"
-
-[[package]]
name = "shell-words"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4428,17 +4288,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]]
-name = "tar"
-version = "0.4.44"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a"
-dependencies = [
- "filetime",
- "libc",
- "xattr",
-]
-
-[[package]]
name = "target-lexicon"
version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4918,16 +4767,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ea3136b675547379c4bd395ca6b938e5ad3c3d20fad76e7fe85f9e0d011419c"
[[package]]
-name = "typed-json"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6024a8d0025400b3f6b189366e9aa92012cf9c4fe1cd2620848dd61425c49eed"
-dependencies = [
- "serde",
- "serde_json",
-]
-
-[[package]]
name = "typenum"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -5597,16 +5436,6 @@ dependencies = [
]
[[package]]
-name = "xattr"
-version = "1.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156"
-dependencies = [
- "libc",
- "rustix",
-]
-
-[[package]]
name = "yoke"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -5731,7 +5560,6 @@ dependencies = [
"bytes",
"bytesize",
"cargo-deny",
- "cargo-llvm-cov",
"chrono",
"crc32fast",
"hex",
@@ -5739,12 +5567,14 @@ dependencies = [
"hyper",
"hyper-tls",
"hyper-util",
- "minijinja",
+ "maud",
+ "mime_guess",
"opentelemetry",
"opentelemetry-appender-tracing",
"opentelemetry-otlp",
"opentelemetry-semantic-conventions",
"opentelemetry_sdk",
+ "rust-embed",
"rustls",
"sd-notify",
"semver",
diff --git a/Cargo.toml b/Cargo.toml
index 3f2528a..52168f9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,37 +10,10 @@ repository = "https://github.com/mrdimidium/Zorian"
[dependencies]
axum = { version = "0.8", features = ["http2", "macros", "multipart"] }
-axum-server = { version = "0.8", default-features = false, features = [
- "tls-rustls-no-provider",
-] }
-rustls = { version = "0.23", default-features = false, features = [
- "ring",
- "std",
- "tls12",
-] }
-axum-extra = { version = "0.12", features = [
- "async-read-body",
- "attachment",
- "cached",
- "erased-json",
- "error-response",
- "file-stream",
- "form",
- "handler",
- "json-deserializer",
- "json-lines",
- "middleware",
- "multipart",
- "optional-path",
- "protobuf",
- "query",
- "routing",
- "tracing",
- "typed-header",
- "typed-routing",
- "with-rejection",
-] }
+axum-extra = "0.12"
+axum-server = { version = "0.8", features = ["tls-rustls-no-provider"] }
bytes = "1.11"
+bytesize = { version = "2.3", features = ["serde"] }
chrono = { version = "0.4", features = ["serde"] }
crc32fast = "1.5"
hex = "0.4"
@@ -48,7 +21,14 @@ http-body-util = "0.1"
hyper = "1.8"
hyper-tls = "0.6"
hyper-util = { version = "0.1", features = ["full"] }
-minijinja = "2.14"
+maud = { version = "0.27", features = ["axum"] }
+mime_guess = "2.0"
+rust-embed = "8.11"
+rustls = { version = "0.23", default-features = false, features = [
+ "ring",
+ "std",
+ "tls12",
+] }
semver = "1.0"
serde = { version = "1.0", features = ["derive"] }
sqlx = { version = "0.8", features = [
@@ -59,22 +39,17 @@ sqlx = { version = "0.8", features = [
"chrono",
"json",
] }
-bytesize = { version = "2.3", features = ["serde"] }
thiserror = "2.0"
tokio = { version = "1.49", features = ["full"] }
toml = "0.9"
+tonic = "0.14"
tower = { version = "0.5", features = ["full"] }
-tower_governor = { version = "0.8", features = ["axum", "tracing"] }
tower-http = { version = "0.6", features = ["full"] }
-tracing = "0.1"
-tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
+tower_governor = { version = "0.8", features = ["axum", "tracing"] }
url = "2.5"
uuid = { version = "1.19", features = ["v4", "v5"] }
-[target.'cfg(target_os = "linux")'.dependencies]
-sd-notify = { version = "0.4" }
-
-# OpenTelemetry
+# telemetry
opentelemetry = "0.31"
opentelemetry-appender-tracing = "0.31"
opentelemetry-otlp = { version = "0.31", features = [
@@ -86,7 +61,6 @@ opentelemetry-otlp = { version = "0.31", features = [
"tls",
"tls-roots",
] }
-tonic = "0.14"
opentelemetry-semantic-conventions = "0.31"
opentelemetry_sdk = { version = "0.31", features = [
"rt-tokio",
@@ -94,10 +68,13 @@ opentelemetry_sdk = { version = "0.31", features = [
"metrics",
"trace",
] }
+tracing = "0.1"
tracing-opentelemetry = "0.32"
+tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
+
+[target.'cfg(target_os = "linux")'.dependencies]
+sd-notify = { version = "0.4" }
[dev-dependencies]
cargo-deny = "0.19"
-cargo-llvm-cov = "0.8.2"
tempfile = "3.24"
-url = "2.5"
diff --git a/LICENSES/OFL-1.1.txt b/LICENSES/OFL-1.1.txt
new file mode 100644
--- /dev/null
+++ b/LICENSES/OFL-1.1.txt
@@ -0,0 +1,93 @@
+Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono)
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+https://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/REUSE.toml b/REUSE.toml
index dc89013..ec848e2 100644
--- a/REUSE.toml
+++ b/REUSE.toml
@@ -9,3 +9,11 @@ path = [
]
SPDX-FileCopyrightText = "2026 Nikolay Govorov <me@govorov.online>"
SPDX-License-Identifier = "AGPL-3.0-or-later"
+
+[[annotations]]
+path = [
+ "src/assets/jetbrainsmono/OFL.txt",
+ "src/assets/jetbrainsmono/*.woff2",
+]
+SPDX-FileCopyrightText = "Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono)"
+SPDX-License-Identifier = "OFL-1.1"
diff --git a/src/assets/base.css b/src/assets/base.css
new file mode 100644
--- /dev/null
+++ b/src/assets/base.css
@@ -0,0 +1,77 @@
+/* SPDX-FileCopyrightText: 2026 Nikolay Govorov <me@govorov.online> */
+/* SPDX-License-Identifier: AGPL-3.0-or-later */
+
+@font-face {
+ font-family: 'JetBrains Mono';
+ font-style: normal;
+ font-weight: 100 700;
+ font-stretch: 100%;
+ font-display: swap;
+ src: url(./jetbrainsmono/JetBrainsMono[wght].woff2) format('woff2');
+}
+
+@font-face {
+ font-family: 'JetBrains Mono';
+ font-style: italic;
+ font-weight: 100 700;
+ font-stretch: 100%;
+ font-display: swap;
+ src: url(./jetbrainsmono/JetBrainsMono-Italic[wght].woff2) format('woff2');
+}
+
+*, *::before, *::after {
+ box-sizing: border-box;
+}
+
+:root {
+ line-height: 1.4;
+ font-family: 'JetBrains Mono', monospace;
+}
+
+html {
+ margin: 0;
+ padding: 0;
+}
+
+body {
+ margin: 0 auto;
+ max-width: 680px;
+ padding: 1.5em 2em;
+}
+
+pre, code {
+ font-size: .75rem;
+ font-weight: 600;
+}
+
+h1, h2, h3, h4, h5, h6 {
+ font-weight: 700;
+ line-height: 1.2;
+ text-wrap: balance;
+ margin: 0;
+}
+
+h1 {
+ font-size: 2.75rem;
+}
+
+h1:first-child {
+ margin-top: 0;
+}
+
+p {
+ text-wrap: pretty;
+}
+
+th {
+ text-align: start;
+}
+
+h3 a {
+ color: inherit;
+ text-decoration: none;
+}
+
+h3 a:hover {
+ text-decoration: underline;
+}
diff --git a/src/assets/jetbrainsmono/JetBrainsMono-Italic[wght].woff2 b/src/assets/jetbrainsmono/JetBrainsMono-Italic[wght].woff2
new file mode 100644
--- /dev/null
+++ b/src/assets/jetbrainsmono/JetBrainsMono-Italic[wght].woff2
Binary files differ
diff --git a/src/assets/jetbrainsmono/JetBrainsMono[wght].woff2 b/src/assets/jetbrainsmono/JetBrainsMono[wght].woff2
new file mode 100644
--- /dev/null
+++ b/src/assets/jetbrainsmono/JetBrainsMono[wght].woff2
Binary files differ
diff --git a/src/controller_web.rs b/src/controller_web.rs
index 4866c15..072a6d2 100644
--- a/src/controller_web.rs
+++ b/src/controller_web.rs
@@ -7,40 +7,40 @@ use std::sync::Arc;
use std::task::{Context, Poll};
use axum::body::Body;
-use axum::extract;
use axum::http::{HeaderValue, Method, Request, Response, StatusCode, header};
-use axum::response;
+use axum::{extract, routing};
use chrono::{DateTime, Utc};
+use maud::html;
+use rust_embed::Embed;
use sqlx::types::chrono;
use tower::{Layer, Service};
use tracing::error;
+#[derive(Embed)]
+#[folder = "src/assets/"]
+struct Assets;
+
const CSP: &str = "default-src 'self'; base-uri 'none'; img-src 'self'; font-src 'self'; style-src 'self'; script-src 'self'; object-src 'none'; frame-ancestors 'none'";
/// 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 }
- }
-}
+#[derive(Default)]
+pub struct WebController {}
impl WebController {
pub fn router(self: Arc<Self>) -> axum::Router {
- axum::Router::new()
- .route("/index.css", axum::routing::get(Self::styles))
+ let pages = axum::Router::new()
.route("/", axum::routing::get(Self::index))
- .layer(LastModifiedLayer {})
.layer(tower_http::set_header::SetResponseHeaderLayer::overriding(
header::CACHE_CONTROL,
HeaderValue::from_static("no-cache"),
- ))
+ ));
+
+ let assets = axum::Router::new().route("/assets/{*path}", routing::get(Self::assets));
+
+ axum::Router::new()
+ .merge(pages)
+ .merge(assets)
+ .layer(LastModifiedLayer {})
.layer(tower_http::set_header::SetResponseHeaderLayer::overriding(
header::CONTENT_SECURITY_POLICY,
HeaderValue::from_static(CSP),
@@ -48,21 +48,119 @@ impl WebController {
.with_state(self)
}
- async fn index(
- extract::State(controller): extract::State<Arc<Self>>,
- ) -> Result<axum::response::Html<String>, StatusCode> {
- let tmpl = controller.jinja.get_template("index");
-
- tmpl.and_then(|template| template.render(minijinja::context! {}))
- .map(response::Html)
- .map_err(|err| {
- error!("html rendering failed: {err}");
- StatusCode::INTERNAL_SERVER_ERROR
- })
+ async fn assets(extract::Path(path): extract::Path<String>) -> Response<Body> {
+ match Assets::get(&path) {
+ Some(file) => {
+ let mime = mime_guess::from_path(&path).first_or_octet_stream();
+ let is_font = matches!(
+ path.rsplit('.').next(),
+ Some("ttf" | "otf" | "woff" | "woff2" | "eot")
+ );
+
+ let mut builder = Response::builder()
+ .status(StatusCode::OK)
+ .header(header::CONTENT_TYPE, mime.as_ref());
+
+ if is_font {
+ builder = builder
+ .header(header::CACHE_CONTROL, "public, max-age=31536000, immutable");
+ }
+
+ builder.body(Body::from(file.data.into_owned())).unwrap()
+ }
+ None => Response::builder()
+ .status(StatusCode::NOT_FOUND)
+ .body(Body::empty())
+ .unwrap(),
+ }
}
- async fn styles() -> axum_extra::response::Css<&'static str> {
- axum_extra::response::Css(CSS)
+ async fn index() -> maud::Markup {
+ html! {
+ (maud::DOCTYPE)
+
+ html lang="en" {
+ meta charset="UTF-8";
+ meta name="viewport" content="width=device-width,initial-scale=1";
+ meta http-equiv="X-UA-Compatible" content="ie=edge";
+
+ link rel="stylesheet" href="assets/base.css";
+ title { "Earth PKG — tiny & opinionated packages mirror" }
+ }
+
+ body {
+ h1 { "Zorian — tiny & opinionated packages mirror." }
+
+ main {
+ p {
+ r#"This site provides a caching proxy for downloading Zig and Go installation files.
+ It reduces load on upstream servers and makes your infrastructure more reliable by adding redundancy."#
+ }
+
+ p {
+ "Zorian is open source software licensed under " a href="https://www.gnu.org/licenses/agpl-3.0.html" { "AGPL-3.0" } ". "
+ "Source code is available on " a href="https://github.com/mrdimidium/zorian" { "GitHub" }
+ }
+
+ h2 { "Usage" }
+
+ p {
+ "Replace official download URLs with " code { "https://pkg.earth/{tool}/{filename}" } ". "
+ "Files are cached automatically after the first download."
+ }
+
+ h3 id="zig" {
+ a href="#zig" { "Zig" }
+ }
+
+ p {
+ "Read more about community mirrors in the " a href="https://ziglang.org/download/community-mirrors/" { "blog post" } ". "
+ "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" } "."
+ }
+
+ p {
+ "For simplicity, you can use tools like " a href="https://github.com/prantlf/zigup" { "prantlf/zigup" }
+ " and " a href="https://github.com/mlugg/setup-zig" { "mlugg/setup-zig" } "."
+ }
+
+ 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" } ";" }
+ li { "download zig minisig file:" br; code { "wget https://pkg.earth/zig/zig-x86_64-linux-0.15.1.tar.xz.minisig" } ";" }
+ li { "check archive integrity:" br; code { "minisign -Vm zig-x86_64-linux-0.15.1.tar.xz -P RWSGOq2NVecA2UPNdBUZykf1CCb147pkmdtYxgb3Ti+JO/wCYvhbAb/U" } ";" }
+ li { "unpack archive:" br; code { "tar -xf 'zig-x86_64-linux-0.15.1.tar.xz'" } ";" }
+ li { "check installed zig:" br; code { "./zig-x86_64-linux-0.15.1/zig --version" } ";" }
+ }
+ "You can take actual minisig public key at " a href="https://ziglang.org/download/" { "ziglang.org/download" } "."
+ }
+
+ h3 id="go" { a href="#go" { "Go" } }
+
+ p {
+ "To install manually:"
+ ol {
+ li { "download go dist file:" br; code { "wget https://pkg.earth/go/go1.23.0.linux-amd64.tar.gz" } ";" }
+ li { "download go sha256 file:" br; code { "wget https://pkg.earth/go/go1.23.0.linux-amd64.tar.gz.sha256" } ";" }
+ li { "check archive integrity:" br; code { "sha256sum -c go1.23.0.linux-amd64.tar.gz.sha256" } ";" }
+ li { "unpack archive:" br; code { "tar -xzf go1.23.0.linux-amd64.tar.gz" } ";" }
+ li { "check installed go:" br; code { "./go/bin/go version" } ";" }
+ }
+ "You can find available versions at " a href="https://go.dev/dl/" { "go.dev/dl" } "."
+ }
+
+ h2 { "Privacy policy" }
+
+ p { "This mirror is a non-profit project available on a voluntary basis. The author has no plans to fund it." }
+
+ p { r#"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 { "Third-party analytics systems are not used, same as client-side trackers." }
+ }
+ }
+ }
}
}
@@ -147,132 +245,3 @@ fn parse_http_date(value: &str) -> Option<DateTime<Utc>> {
.map(|d| d.with_timezone(&Utc))
.ok()
}
-
-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>Zorian — tiny & opinionated packages mirror.</h1>
-
- <main>
- <p>This site provides a caching proxy for downloading Zig and Go installation files.
- It reduces load on upstream servers and makes your infrastructure more reliable by adding redundancy.</p>
-
- <p>Zorian is open source software licensed under <a href="https://www.gnu.org/licenses/agpl-3.0.html">AGPL-3.0</a>.
- Source code is available on <a href="https://github.com/mrdimidium/zorian">GitHub</a>.</p>
-
- <h2>Usage</h2>
-
- <p>Replace official download URLs with <code>https://pkg.earth/{tool}/{filename}</code>.
- Files are cached automatically after the first download.</p>
-
- <h3 id="zig"><a href="#zig">Zig</a></h3>
-
- <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>
-
- <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 minisig 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 minisig public key at <a href="https://ziglang.org/download/">ziglang.org/download</a>.
- </p>
-
- <h3 id="go"><a href="#go">Go</a></h3>
-
- <p>
- To install manually:
- <ol>
- <li>download go dist file:<br><code>wget https://pkg.earth/go/go1.23.0.linux-amd64.tar.gz</code>;</li>
- <li>download go sha256 file:<br><code>wget https://pkg.earth/go/go1.23.0.linux-amd64.tar.gz.sha256</code>;</li>
- <li>check archive integrity:<br><code>sha256sum -c go1.23.0.linux-amd64.tar.gz.sha256</code>;</li>
- <li>unpack archive:<br><code>tar -xzf go1.23.0.linux-amd64.tar.gz</code>;</li>
- <li>check installed go:<br><code>./go/bin/go version</code>.</li>
- </ol>
- You can find available versions at <a href="https://go.dev/dl/">go.dev/dl</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 = r##"
-: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;
-}
-
-h3 a {
- color: inherit;
- text-decoration: none;
-}
-
-h3 a:hover {
- text-decoration: underline;
-}
-"##;