Compare commits
18 commits
d3ef4d2030
...
55e3f97946
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
55e3f97946 | ||
|
|
2b4f066234 | ||
|
|
7b37f88fb5 | ||
|
|
84c36c464d | ||
|
|
868bfbc365 | ||
|
|
4ac5700ac5 | ||
|
|
3afdc66ec2 | ||
|
|
f9eacd03be | ||
|
|
99ab28d3be | ||
|
|
e80c3dc9a4 | ||
|
|
8561ee3e74 | ||
|
|
4ef77bf255 | ||
|
|
fa12b80638 | ||
|
|
c7af43ab33 | ||
|
|
0fb3a6bfe1 | ||
|
|
b7430cbb65 | ||
|
|
84ac8f3b9f | ||
|
|
e8d303f184 |
13 changed files with 957 additions and 46 deletions
366
Cargo.lock
generated
366
Cargo.lock
generated
|
|
@ -294,6 +294,12 @@ version = "1.0.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg_aliases"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chrono"
|
name = "chrono"
|
||||||
version = "0.4.44"
|
version = "0.4.44"
|
||||||
|
|
@ -376,7 +382,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
|
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"generic-array",
|
"generic-array",
|
||||||
"rand_core",
|
"rand_core 0.6.4",
|
||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -625,8 +631,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
|
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
"js-sys",
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi",
|
||||||
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -636,9 +644,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
"js-sys",
|
||||||
"libc",
|
"libc",
|
||||||
"r-efi 5.3.0",
|
"r-efi 5.3.0",
|
||||||
"wasip2",
|
"wasip2",
|
||||||
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -734,12 +744,13 @@ dependencies = [
|
||||||
"futures",
|
"futures",
|
||||||
"hex",
|
"hex",
|
||||||
"hmac",
|
"hmac",
|
||||||
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2",
|
"sha2",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower-http",
|
"tower-http 0.5.2",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
"uuid",
|
"uuid",
|
||||||
|
|
@ -836,6 +847,24 @@ dependencies = [
|
||||||
"pin-utils",
|
"pin-utils",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"want",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hyper-rustls"
|
||||||
|
version = "0.27.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58"
|
||||||
|
dependencies = [
|
||||||
|
"http",
|
||||||
|
"hyper",
|
||||||
|
"hyper-util",
|
||||||
|
"rustls 0.23.37",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"tokio",
|
||||||
|
"tokio-rustls",
|
||||||
|
"tower-service",
|
||||||
|
"webpki-roots 1.0.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -844,13 +873,21 @@ version = "0.1.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0"
|
checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"base64 0.22.1",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
"futures-channel",
|
||||||
|
"futures-util",
|
||||||
"http",
|
"http",
|
||||||
"http-body",
|
"http-body",
|
||||||
"hyper",
|
"hyper",
|
||||||
|
"ipnet",
|
||||||
|
"libc",
|
||||||
|
"percent-encoding",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
|
"socket2",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1006,6 +1043,22 @@ dependencies = [
|
||||||
"generic-array",
|
"generic-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ipnet"
|
||||||
|
version = "2.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iri-string"
|
||||||
|
version = "0.7.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d8e7418f59cc01c88316161279a7f665217ae316b388e58a0d10e29f54f1e5eb"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.17"
|
version = "1.0.17"
|
||||||
|
|
@ -1099,6 +1152,12 @@ version = "0.4.29"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lru-slab"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "matchers"
|
name = "matchers"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
|
@ -1183,7 +1242,7 @@ dependencies = [
|
||||||
"num-integer",
|
"num-integer",
|
||||||
"num-iter",
|
"num-iter",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"rand",
|
"rand 0.8.5",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
@ -1368,6 +1427,61 @@ dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quinn"
|
||||||
|
version = "0.11.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"cfg_aliases",
|
||||||
|
"pin-project-lite",
|
||||||
|
"quinn-proto",
|
||||||
|
"quinn-udp",
|
||||||
|
"rustc-hash",
|
||||||
|
"rustls 0.23.37",
|
||||||
|
"socket2",
|
||||||
|
"thiserror 2.0.18",
|
||||||
|
"tokio",
|
||||||
|
"tracing",
|
||||||
|
"web-time",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quinn-proto"
|
||||||
|
version = "0.11.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"getrandom 0.3.4",
|
||||||
|
"lru-slab",
|
||||||
|
"rand 0.9.2",
|
||||||
|
"ring",
|
||||||
|
"rustc-hash",
|
||||||
|
"rustls 0.23.37",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"slab",
|
||||||
|
"thiserror 2.0.18",
|
||||||
|
"tinyvec",
|
||||||
|
"tracing",
|
||||||
|
"web-time",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quinn-udp"
|
||||||
|
version = "0.5.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd"
|
||||||
|
dependencies = [
|
||||||
|
"cfg_aliases",
|
||||||
|
"libc",
|
||||||
|
"once_cell",
|
||||||
|
"socket2",
|
||||||
|
"tracing",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.45"
|
version = "1.0.45"
|
||||||
|
|
@ -1396,8 +1510,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"rand_chacha",
|
"rand_chacha 0.3.1",
|
||||||
"rand_core",
|
"rand_core 0.6.4",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.9.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
||||||
|
dependencies = [
|
||||||
|
"rand_chacha 0.9.0",
|
||||||
|
"rand_core 0.9.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1407,7 +1531,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ppv-lite86",
|
"ppv-lite86",
|
||||||
"rand_core",
|
"rand_core 0.6.4",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core 0.9.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1419,6 +1553,15 @@ dependencies = [
|
||||||
"getrandom 0.2.17",
|
"getrandom 0.2.17",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.9.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.3.4",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.5.18"
|
version = "0.5.18"
|
||||||
|
|
@ -1454,6 +1597,44 @@ version = "0.8.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
|
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "reqwest"
|
||||||
|
version = "0.12.28"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.22.1",
|
||||||
|
"bytes",
|
||||||
|
"futures-core",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"http-body-util",
|
||||||
|
"hyper",
|
||||||
|
"hyper-rustls",
|
||||||
|
"hyper-util",
|
||||||
|
"js-sys",
|
||||||
|
"log",
|
||||||
|
"percent-encoding",
|
||||||
|
"pin-project-lite",
|
||||||
|
"quinn",
|
||||||
|
"rustls 0.23.37",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"serde_urlencoded",
|
||||||
|
"sync_wrapper",
|
||||||
|
"tokio",
|
||||||
|
"tokio-rustls",
|
||||||
|
"tower",
|
||||||
|
"tower-http 0.6.8",
|
||||||
|
"tower-service",
|
||||||
|
"url",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"wasm-bindgen-futures",
|
||||||
|
"web-sys",
|
||||||
|
"webpki-roots 1.0.6",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ring"
|
name = "ring"
|
||||||
version = "0.17.14"
|
version = "0.17.14"
|
||||||
|
|
@ -1481,13 +1662,19 @@ dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"pkcs1",
|
"pkcs1",
|
||||||
"pkcs8",
|
"pkcs8",
|
||||||
"rand_core",
|
"rand_core 0.6.4",
|
||||||
"signature",
|
"signature",
|
||||||
"spki",
|
"spki",
|
||||||
"subtle",
|
"subtle",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-hash"
|
||||||
|
version = "2.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "1.1.4"
|
version = "1.1.4"
|
||||||
|
|
@ -1508,10 +1695,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e"
|
checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ring",
|
"ring",
|
||||||
"rustls-webpki",
|
"rustls-webpki 0.101.7",
|
||||||
"sct",
|
"sct",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls"
|
||||||
|
version = "0.23.37"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"ring",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"rustls-webpki 0.103.10",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-pemfile"
|
name = "rustls-pemfile"
|
||||||
version = "1.0.4"
|
version = "1.0.4"
|
||||||
|
|
@ -1521,6 +1722,16 @@ dependencies = [
|
||||||
"base64 0.21.7",
|
"base64 0.21.7",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-pki-types"
|
||||||
|
version = "1.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd"
|
||||||
|
dependencies = [
|
||||||
|
"web-time",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-webpki"
|
name = "rustls-webpki"
|
||||||
version = "0.101.7"
|
version = "0.101.7"
|
||||||
|
|
@ -1531,6 +1742,17 @@ dependencies = [
|
||||||
"untrusted",
|
"untrusted",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-webpki"
|
||||||
|
version = "0.103.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef"
|
||||||
|
dependencies = [
|
||||||
|
"ring",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"untrusted",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustversion"
|
name = "rustversion"
|
||||||
version = "1.0.22"
|
version = "1.0.22"
|
||||||
|
|
@ -1685,7 +1907,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
|
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"digest",
|
"digest",
|
||||||
"rand_core",
|
"rand_core 0.6.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1780,19 +2002,19 @@ dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"paste",
|
"paste",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"rustls",
|
"rustls 0.21.12",
|
||||||
"rustls-pemfile",
|
"rustls-pemfile",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2",
|
"sha2",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"sqlformat",
|
"sqlformat",
|
||||||
"thiserror",
|
"thiserror 1.0.69",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
"tracing",
|
"tracing",
|
||||||
"url",
|
"url",
|
||||||
"webpki-roots",
|
"webpki-roots 0.25.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1864,7 +2086,7 @@ dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"rand",
|
"rand 0.8.5",
|
||||||
"rsa",
|
"rsa",
|
||||||
"serde",
|
"serde",
|
||||||
"sha1",
|
"sha1",
|
||||||
|
|
@ -1872,7 +2094,7 @@ dependencies = [
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"sqlx-core",
|
"sqlx-core",
|
||||||
"stringprep",
|
"stringprep",
|
||||||
"thiserror",
|
"thiserror 1.0.69",
|
||||||
"tracing",
|
"tracing",
|
||||||
"whoami",
|
"whoami",
|
||||||
]
|
]
|
||||||
|
|
@ -1904,14 +2126,14 @@ dependencies = [
|
||||||
"md-5",
|
"md-5",
|
||||||
"memchr",
|
"memchr",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rand",
|
"rand 0.8.5",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2",
|
"sha2",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"sqlx-core",
|
"sqlx-core",
|
||||||
"stringprep",
|
"stringprep",
|
||||||
"thiserror",
|
"thiserror 1.0.69",
|
||||||
"tracing",
|
"tracing",
|
||||||
"whoami",
|
"whoami",
|
||||||
]
|
]
|
||||||
|
|
@ -1990,6 +2212,9 @@ name = "sync_wrapper"
|
||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
|
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "synstructure"
|
name = "synstructure"
|
||||||
|
|
@ -2021,7 +2246,16 @@ version = "1.0.69"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl 1.0.69",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "2.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl 2.0.18",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -2035,6 +2269,17 @@ dependencies = [
|
||||||
"syn 2.0.117",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "2.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.117",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thread_local"
|
name = "thread_local"
|
||||||
version = "1.1.9"
|
version = "1.1.9"
|
||||||
|
|
@ -2097,6 +2342,16 @@ dependencies = [
|
||||||
"syn 2.0.117",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-rustls"
|
||||||
|
version = "0.26.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61"
|
||||||
|
dependencies = [
|
||||||
|
"rustls 0.23.37",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-stream"
|
name = "tokio-stream"
|
||||||
version = "0.1.18"
|
version = "0.1.18"
|
||||||
|
|
@ -2141,6 +2396,24 @@ dependencies = [
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tower-http"
|
||||||
|
version = "0.6.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"bytes",
|
||||||
|
"futures-util",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"iri-string",
|
||||||
|
"pin-project-lite",
|
||||||
|
"tower",
|
||||||
|
"tower-layer",
|
||||||
|
"tower-service",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tower-layer"
|
name = "tower-layer"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
|
|
@ -2215,6 +2488,12 @@ dependencies = [
|
||||||
"tracing-log",
|
"tracing-log",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "try-lock"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.19.0"
|
version = "1.19.0"
|
||||||
|
|
@ -2291,6 +2570,7 @@ dependencies = [
|
||||||
"form_urlencoded",
|
"form_urlencoded",
|
||||||
"idna",
|
"idna",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -2334,6 +2614,15 @@ version = "0.9.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "want"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
|
||||||
|
dependencies = [
|
||||||
|
"try-lock",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.11.1+wasi-snapshot-preview1"
|
version = "0.11.1+wasi-snapshot-preview1"
|
||||||
|
|
@ -2377,6 +2666,20 @@ dependencies = [
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-futures"
|
||||||
|
version = "0.4.64"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"futures-util",
|
||||||
|
"js-sys",
|
||||||
|
"once_cell",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro"
|
name = "wasm-bindgen-macro"
|
||||||
version = "0.2.114"
|
version = "0.2.114"
|
||||||
|
|
@ -2443,12 +2746,41 @@ dependencies = [
|
||||||
"semver",
|
"semver",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "web-sys"
|
||||||
|
version = "0.3.91"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9"
|
||||||
|
dependencies = [
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "web-time"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
|
||||||
|
dependencies = [
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webpki-roots"
|
name = "webpki-roots"
|
||||||
version = "0.25.4"
|
version = "0.25.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
|
checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "webpki-roots"
|
||||||
|
version = "1.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed"
|
||||||
|
dependencies = [
|
||||||
|
"rustls-pki-types",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "whoami"
|
name = "whoami"
|
||||||
version = "1.6.1"
|
version = "1.6.1"
|
||||||
|
|
|
||||||
|
|
@ -9,3 +9,4 @@
|
||||||
* Integrate with git using github webhooks or add your own git remote
|
* Integrate with git using github webhooks or add your own git remote
|
||||||
* automatic redeployment after git push
|
* automatic redeployment after git push
|
||||||
* Builtin ssl. Automatically provisioned using let's encrypt.
|
* Builtin ssl. Automatically provisioned using let's encrypt.
|
||||||
|
* Caddy reverse proxy
|
||||||
|
|
@ -45,7 +45,9 @@ ssh pi@hiypi.local
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo apt update && sudo apt full-upgrade -y
|
sudo apt update && sudo apt full-upgrade -y
|
||||||
sudo apt install -y git curl ufw fail2ban unattended-upgrades
|
sudo apt install -y git curl ufw fail2ban unattended-upgrades podman python3 pipx aardvark-dns sqlite3
|
||||||
|
pipx install podman-compose
|
||||||
|
pipx ensurepath
|
||||||
```
|
```
|
||||||
|
|
||||||
### Static IP (optional but recommended)
|
### Static IP (optional but recommended)
|
||||||
|
|
|
||||||
|
|
@ -15,3 +15,15 @@ POSTGRES_PASSWORD=changeme
|
||||||
# Forgejo (optional — only needed if you add the forgejo service to docker-compose.yml).
|
# Forgejo (optional — only needed if you add the forgejo service to docker-compose.yml).
|
||||||
FORGEJO_DB_PASSWORD=changeme
|
FORGEJO_DB_PASSWORD=changeme
|
||||||
FORGEJO_DOMAIN=git.yourdomain.com
|
FORGEJO_DOMAIN=git.yourdomain.com
|
||||||
|
# Actions runner registration token — obtain from Forgejo:
|
||||||
|
# Site Administration → Actions → Runners → Create new runner
|
||||||
|
FORGEJO_RUNNER_TOKEN=
|
||||||
|
|
||||||
|
# ── Backup (infra/backup.sh) ──────────────────────────────────────────────────
|
||||||
|
# Local directory to store backup archives.
|
||||||
|
HIY_BACKUP_DIR=/mnt/usb/hiy-backups
|
||||||
|
# Optional rclone remote (e.g. "b2:mybucket/hiy", "s3:mybucket/hiy").
|
||||||
|
# Requires rclone installed and configured. Leave blank to skip remote upload.
|
||||||
|
HIY_BACKUP_REMOTE=
|
||||||
|
# How many days to keep local archives (default 30).
|
||||||
|
HIY_BACKUP_RETAIN_DAYS=30
|
||||||
|
|
|
||||||
51
infra/auto-update.sh
Executable file
51
infra/auto-update.sh
Executable file
|
|
@ -0,0 +1,51 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# auto-update.sh — pull latest changes and restart affected services.
|
||||||
|
# Run by the hiy-update.timer systemd user unit every 5 minutes.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||||
|
|
||||||
|
log() { echo "[hiy-update] $(date '+%H:%M:%S') $*"; }
|
||||||
|
|
||||||
|
cd "$REPO_ROOT"
|
||||||
|
|
||||||
|
# Fetch without touching the working tree.
|
||||||
|
git fetch origin 2>&1 | sed 's/^/[git] /' || { log "git fetch failed — skipping"; exit 0; }
|
||||||
|
|
||||||
|
LOCAL=$(git rev-parse HEAD)
|
||||||
|
REMOTE=$(git rev-parse "@{u}" 2>/dev/null || echo "$LOCAL")
|
||||||
|
|
||||||
|
if [ "$LOCAL" = "$REMOTE" ]; then
|
||||||
|
log "Already up to date ($LOCAL)."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "New commits detected — pulling ($LOCAL → $REMOTE)…"
|
||||||
|
git pull 2>&1 | sed 's/^/[git] /'
|
||||||
|
|
||||||
|
# Determine which services need restarting based on what changed.
|
||||||
|
CHANGED=$(git diff --name-only "$LOCAL" "$REMOTE")
|
||||||
|
log "Changed files: $(echo "$CHANGED" | tr '\n' ' ')"
|
||||||
|
|
||||||
|
# Always rebuild the server if any server-side code changed.
|
||||||
|
SERVER_CHANGED=$(echo "$CHANGED" | grep -E '^server/|^Cargo' || true)
|
||||||
|
COMPOSE_CHANGED=$(echo "$CHANGED" | grep '^infra/docker-compose' || true)
|
||||||
|
CADDY_CHANGED=$(echo "$CHANGED" | grep '^proxy/Caddyfile' || true)
|
||||||
|
|
||||||
|
COMPOSE_CMD="podman compose --env-file $REPO_ROOT/.env -f $SCRIPT_DIR/docker-compose.yml"
|
||||||
|
|
||||||
|
if [ -n "$COMPOSE_CHANGED" ]; then
|
||||||
|
log "docker-compose.yml changed — restarting full stack…"
|
||||||
|
$COMPOSE_CMD up -d
|
||||||
|
elif [ -n "$SERVER_CHANGED" ]; then
|
||||||
|
log "Server code changed — rebuilding server…"
|
||||||
|
$COMPOSE_CMD up -d --build server
|
||||||
|
elif [ -n "$CADDY_CHANGED" ]; then
|
||||||
|
log "Caddyfile changed — reloading Caddy…"
|
||||||
|
$COMPOSE_CMD exec caddy caddy reload --config /etc/caddy/Caddyfile --adapter caddyfile
|
||||||
|
else
|
||||||
|
log "No service restart needed for these changes."
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Done."
|
||||||
118
infra/backup.sh
118
infra/backup.sh
|
|
@ -3,21 +3,32 @@
|
||||||
#
|
#
|
||||||
# What is backed up:
|
# What is backed up:
|
||||||
# 1. SQLite database (hiy.db) — apps, deploys, env vars, users
|
# 1. SQLite database (hiy.db) — apps, deploys, env vars, users
|
||||||
# 2. Env files directory — decrypted env files written per deploy
|
# 2. Env files — per-deploy decrypted env files
|
||||||
# 3. Git repos — bare repos for git-push deploys
|
# 3. Git repos — bare repos for git-push deploys
|
||||||
|
# 4. Postgres — pg_dumpall (hiy + forgejo databases)
|
||||||
|
# 5. Forgejo data volume — repositories, avatars, LFS objects
|
||||||
|
# 6. Caddy TLS certificates — caddy-data volume
|
||||||
|
# 7. .env file — secrets (handle the archive with care)
|
||||||
#
|
#
|
||||||
# Destination options (mutually exclusive; set one):
|
# Destination options (mutually exclusive; set one):
|
||||||
# HIY_BACKUP_DIR — local path (e.g. /mnt/usb/hiy-backups, default /tmp/hiy-backups)
|
# HIY_BACKUP_DIR — local path (e.g. /mnt/usb/hiy-backups, default /tmp/hiy-backups)
|
||||||
# HIY_BACKUP_REMOTE — rclone remote:path (e.g. "b2:mybucket/hiy")
|
# HIY_BACKUP_REMOTE — rclone remote:path (e.g. "b2:mybucket/hiy")
|
||||||
# requires rclone installed and configured
|
# requires rclone installed and configured
|
||||||
#
|
#
|
||||||
# Retention: 30 days (local only; remote retention is managed by the storage provider)
|
# Retention: 30 days local (remote retention managed by the storage provider).
|
||||||
#
|
#
|
||||||
# Suggested cron (run as the same user as hiy-server):
|
# Suggested cron (run as the same user that owns the containers):
|
||||||
# 0 3 * * * /path/to/infra/backup.sh >> /var/log/hiy-backup.log 2>&1
|
# 0 3 * * * /path/to/infra/backup.sh >> /var/log/hiy-backup.log 2>&1
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
# ── Load .env ──────────────────────────────────────────────────────────────────
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
ENV_FILE="${SCRIPT_DIR}/../.env"
|
||||||
|
if [ -f "$ENV_FILE" ]; then
|
||||||
|
set -a; source "$ENV_FILE"; set +a
|
||||||
|
fi
|
||||||
|
|
||||||
# ── Config ─────────────────────────────────────────────────────────────────────
|
# ── Config ─────────────────────────────────────────────────────────────────────
|
||||||
HIY_DATA_DIR="${HIY_DATA_DIR:-/data}"
|
HIY_DATA_DIR="${HIY_DATA_DIR:-/data}"
|
||||||
BACKUP_DIR="${HIY_BACKUP_DIR:-/tmp/hiy-backups}"
|
BACKUP_DIR="${HIY_BACKUP_DIR:-/tmp/hiy-backups}"
|
||||||
|
|
@ -34,30 +45,90 @@ log "=== HIY Backup ==="
|
||||||
log "Data dir : ${HIY_DATA_DIR}"
|
log "Data dir : ${HIY_DATA_DIR}"
|
||||||
log "Staging : ${STAGING}"
|
log "Staging : ${STAGING}"
|
||||||
|
|
||||||
# ── 1. Stage files ─────────────────────────────────────────────────────────────
|
|
||||||
mkdir -p "${STAGING}"
|
mkdir -p "${STAGING}"
|
||||||
|
|
||||||
# SQLite: use the .dump command to produce a portable SQL text dump.
|
# ── Helper: find a running container by compose service label ──────────────────
|
||||||
if [ -f "${HIY_DATA_DIR}/hiy.db" ]; then
|
find_container() {
|
||||||
log "Dumping SQLite database…"
|
local service="$1"
|
||||||
|
podman ps --filter "label=com.docker.compose.service=${service}" \
|
||||||
|
--format '{{.Names}}' | head -1
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── 1. SQLite ──────────────────────────────────────────────────────────────────
|
||||||
|
log "--- SQLite ---"
|
||||||
|
SERVER_CTR=$(find_container server)
|
||||||
|
if [ -n "${SERVER_CTR}" ]; then
|
||||||
|
log "Copying hiy.db from container ${SERVER_CTR}…"
|
||||||
|
podman cp "${SERVER_CTR}:${HIY_DATA_DIR}/hiy.db" "${STAGING}/hiy.db"
|
||||||
|
log "Dumping hiy.db…"
|
||||||
|
sqlite3 "${STAGING}/hiy.db" .dump > "${STAGING}/hiy.sql"
|
||||||
|
rm "${STAGING}/hiy.db"
|
||||||
|
elif [ -f "${HIY_DATA_DIR}/hiy.db" ]; then
|
||||||
|
log "Server container not running — dumping from host path…"
|
||||||
sqlite3 "${HIY_DATA_DIR}/hiy.db" .dump > "${STAGING}/hiy.sql"
|
sqlite3 "${HIY_DATA_DIR}/hiy.db" .dump > "${STAGING}/hiy.sql"
|
||||||
else
|
else
|
||||||
log "WARNING: ${HIY_DATA_DIR}/hiy.db not found — skipping SQLite dump"
|
log "WARNING: hiy.db not found — skipping SQLite dump"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Env files (contain decrypted secrets — handle with care).
|
# ── 2. Env files ───────────────────────────────────────────────────────────────
|
||||||
if [ -d "${HIY_DATA_DIR}/envs" ]; then
|
log "--- Env files ---"
|
||||||
log "Copying env files…"
|
if [ -n "${SERVER_CTR}" ]; then
|
||||||
cp -r "${HIY_DATA_DIR}/envs" "${STAGING}/envs"
|
podman exec "${SERVER_CTR}" sh -c \
|
||||||
|
"[ -d ${HIY_DATA_DIR}/envs ] && tar -C ${HIY_DATA_DIR} -czf - envs" \
|
||||||
|
> "${STAGING}/envs.tar.gz" 2>/dev/null || true
|
||||||
|
elif [ -d "${HIY_DATA_DIR}/envs" ]; then
|
||||||
|
tar -czf "${STAGING}/envs.tar.gz" -C "${HIY_DATA_DIR}" envs
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Bare git repos.
|
# ── 3. Git repos ───────────────────────────────────────────────────────────────
|
||||||
if [ -d "${HIY_DATA_DIR}/repos" ]; then
|
log "--- Git repos ---"
|
||||||
log "Copying git repos…"
|
if [ -n "${SERVER_CTR}" ]; then
|
||||||
cp -r "${HIY_DATA_DIR}/repos" "${STAGING}/repos"
|
podman exec "${SERVER_CTR}" sh -c \
|
||||||
|
"[ -d ${HIY_DATA_DIR}/repos ] && tar -C ${HIY_DATA_DIR} -czf - repos" \
|
||||||
|
> "${STAGING}/repos.tar.gz" 2>/dev/null || true
|
||||||
|
elif [ -d "${HIY_DATA_DIR}/repos" ]; then
|
||||||
|
tar -czf "${STAGING}/repos.tar.gz" -C "${HIY_DATA_DIR}" repos
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── 2. Create archive ──────────────────────────────────────────────────────────
|
# ── 4. Postgres ────────────────────────────────────────────────────────────────
|
||||||
|
log "--- Postgres ---"
|
||||||
|
PG_CTR=$(find_container postgres)
|
||||||
|
if [ -n "${PG_CTR}" ]; then
|
||||||
|
log "Running pg_dumpall via container ${PG_CTR}…"
|
||||||
|
podman exec "${PG_CTR}" pg_dumpall -U hiy_admin \
|
||||||
|
> "${STAGING}/postgres.sql"
|
||||||
|
else
|
||||||
|
log "WARNING: postgres container not running — skipping Postgres dump"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 5. Forgejo data volume ─────────────────────────────────────────────────────
|
||||||
|
log "--- Forgejo volume ---"
|
||||||
|
if podman volume exists forgejo-data 2>/dev/null; then
|
||||||
|
log "Exporting forgejo-data volume…"
|
||||||
|
podman volume export forgejo-data > "${STAGING}/forgejo-data.tar"
|
||||||
|
else
|
||||||
|
log "forgejo-data volume not found — skipping"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 6. Caddy TLS certificates ──────────────────────────────────────────────────
|
||||||
|
log "--- Caddy volume ---"
|
||||||
|
if podman volume exists caddy-data 2>/dev/null; then
|
||||||
|
log "Exporting caddy-data volume…"
|
||||||
|
podman volume export caddy-data > "${STAGING}/caddy-data.tar"
|
||||||
|
else
|
||||||
|
log "caddy-data volume not found — skipping"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 7. .env file ───────────────────────────────────────────────────────────────
|
||||||
|
log "--- .env ---"
|
||||||
|
if [ -f "${ENV_FILE}" ]; then
|
||||||
|
cp "${ENV_FILE}" "${STAGING}/dot-env"
|
||||||
|
log "WARNING: archive contains plaintext secrets — store it securely"
|
||||||
|
else
|
||||||
|
log ".env not found at ${ENV_FILE} — skipping"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Create archive ─────────────────────────────────────────────────────────────
|
||||||
mkdir -p "${BACKUP_DIR}"
|
mkdir -p "${BACKUP_DIR}"
|
||||||
ARCHIVE_PATH="${BACKUP_DIR}/${ARCHIVE_NAME}"
|
ARCHIVE_PATH="${BACKUP_DIR}/${ARCHIVE_NAME}"
|
||||||
log "Creating archive: ${ARCHIVE_PATH}"
|
log "Creating archive: ${ARCHIVE_PATH}"
|
||||||
|
|
@ -67,19 +138,20 @@ rm -rf "${STAGING}"
|
||||||
ARCHIVE_SIZE=$(du -sh "${ARCHIVE_PATH}" | cut -f1)
|
ARCHIVE_SIZE=$(du -sh "${ARCHIVE_PATH}" | cut -f1)
|
||||||
log "Archive size: ${ARCHIVE_SIZE}"
|
log "Archive size: ${ARCHIVE_SIZE}"
|
||||||
|
|
||||||
# ── 3. Upload to remote (optional) ────────────────────────────────────────────
|
# ── Upload to remote (optional) ────────────────────────────────────────────────
|
||||||
if [ -n "${BACKUP_REMOTE}" ]; then
|
if [ -n "${BACKUP_REMOTE}" ]; then
|
||||||
if command -v rclone &>/dev/null; then
|
if command -v rclone &>/dev/null; then
|
||||||
log "Uploading to remote: ${BACKUP_REMOTE}"
|
log "Uploading to ${BACKUP_REMOTE}…"
|
||||||
rclone copy "${ARCHIVE_PATH}" "${BACKUP_REMOTE}/"
|
#use patched rclone for now
|
||||||
|
/home/sander/dev/rclone/rclone copy --transfers 1 --retries 5 "${ARCHIVE_PATH}" "${BACKUP_REMOTE}/"
|
||||||
log "Upload complete."
|
log "Upload complete."
|
||||||
else
|
else
|
||||||
log "WARNING: HIY_BACKUP_REMOTE is set but rclone is not installed — skipping upload"
|
log "WARNING: HIY_BACKUP_REMOTE is set but rclone is not installed — skipping"
|
||||||
log "Install rclone: https://rclone.org/install/"
|
log "Install: https://rclone.org/install/"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── 4. Rotate old local backups ────────────────────────────────────────────────
|
# ── Rotate old local backups ───────────────────────────────────────────────────
|
||||||
log "Removing local backups older than ${RETAIN_DAYS} days…"
|
log "Removing local backups older than ${RETAIN_DAYS} days…"
|
||||||
find "${BACKUP_DIR}" -maxdepth 1 -name 'hiy-backup-*.tar.gz' \
|
find "${BACKUP_DIR}" -maxdepth 1 -name 'hiy-backup-*.tar.gz' \
|
||||||
-mtime "+${RETAIN_DAYS}" -delete
|
-mtime "+${RETAIN_DAYS}" -delete
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,8 @@ services:
|
||||||
FORGEJO__server__SSH_DOMAIN: ${FORGEJO_DOMAIN}
|
FORGEJO__server__SSH_DOMAIN: ${FORGEJO_DOMAIN}
|
||||||
# Skip the first-run wizard — everything is configured via env vars above.
|
# Skip the first-run wizard — everything is configured via env vars above.
|
||||||
FORGEJO__security__INSTALL_LOCK: "true"
|
FORGEJO__security__INSTALL_LOCK: "true"
|
||||||
|
# Enable Actions.
|
||||||
|
FORGEJO__actions__ENABLED: "true"
|
||||||
volumes:
|
volumes:
|
||||||
- forgejo-data:/data
|
- forgejo-data:/data
|
||||||
depends_on:
|
depends_on:
|
||||||
|
|
@ -100,6 +102,28 @@ services:
|
||||||
networks:
|
networks:
|
||||||
- hiy-net
|
- hiy-net
|
||||||
|
|
||||||
|
# ── Forgejo Actions runner ─────────────────────────────────────────────────
|
||||||
|
# Obtain FORGEJO_RUNNER_TOKEN from Forgejo:
|
||||||
|
# Site Administration → Actions → Runners → Create new runner
|
||||||
|
act_runner:
|
||||||
|
image: data.forgejo.org/forgejo/runner:6
|
||||||
|
restart: unless-stopped
|
||||||
|
command: ["/entrypoint.sh"]
|
||||||
|
environment:
|
||||||
|
FORGEJO_INSTANCE_URL: http://forgejo:3000
|
||||||
|
FORGEJO_RUNNER_TOKEN: ${FORGEJO_RUNNER_TOKEN}
|
||||||
|
FORGEJO_RUNNER_NAME: hiy-runner
|
||||||
|
# Give the runner access to Podman so CI jobs can build/run containers.
|
||||||
|
DOCKER_HOST: tcp://podman-proxy:2375
|
||||||
|
volumes:
|
||||||
|
- act_runner_data:/data
|
||||||
|
- ./runner-entrypoint.sh:/entrypoint.sh:ro
|
||||||
|
depends_on:
|
||||||
|
- forgejo
|
||||||
|
- podman-proxy
|
||||||
|
networks:
|
||||||
|
- hiy-net
|
||||||
|
|
||||||
# ── Reverse proxy ─────────────────────────────────────────────────────────
|
# ── Reverse proxy ─────────────────────────────────────────────────────────
|
||||||
caddy:
|
caddy:
|
||||||
image: docker.io/library/caddy:2-alpine
|
image: docker.io/library/caddy:2-alpine
|
||||||
|
|
@ -170,6 +194,7 @@ networks:
|
||||||
volumes:
|
volumes:
|
||||||
hiy-data:
|
hiy-data:
|
||||||
forgejo-data:
|
forgejo-data:
|
||||||
|
act_runner_data:
|
||||||
caddy-data:
|
caddy-data:
|
||||||
caddy-config:
|
caddy-config:
|
||||||
hiy-pg-data:
|
hiy-pg-data:
|
||||||
|
|
|
||||||
142
infra/install.sh
Executable file
142
infra/install.sh
Executable file
|
|
@ -0,0 +1,142 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# install.sh — one-time setup for a fresh Raspberry Pi.
|
||||||
|
#
|
||||||
|
# Run this once after cloning the repo:
|
||||||
|
# cd ~/Hostityourself && ./infra/install.sh
|
||||||
|
#
|
||||||
|
# What it does:
|
||||||
|
# 1. Installs system packages (podman, aardvark-dns, sqlite3, git, uidmap)
|
||||||
|
# 2. Installs podman-compose (via pip, into ~/.local/bin)
|
||||||
|
# 3. Installs rclone (for off-site backups — optional)
|
||||||
|
# 4. Creates .env from the template and prompts for required values
|
||||||
|
# 5. Runs infra/start.sh to build and launch the stack
|
||||||
|
#
|
||||||
|
# Safe to re-run — all steps are idempotent.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||||
|
|
||||||
|
log() { echo; echo "▶ $*"; }
|
||||||
|
info() { echo " $*"; }
|
||||||
|
ok() { echo " ✓ $*"; }
|
||||||
|
|
||||||
|
echo "╔══════════════════════════════════════════╗"
|
||||||
|
echo "║ HostItYourself — installer ║"
|
||||||
|
echo "╚══════════════════════════════════════════╝"
|
||||||
|
|
||||||
|
# ── 1. System packages ─────────────────────────────────────────────────────────
|
||||||
|
log "Installing system packages…"
|
||||||
|
sudo apt-get update -qq
|
||||||
|
sudo apt-get install -y \
|
||||||
|
podman \
|
||||||
|
aardvark-dns \
|
||||||
|
sqlite3 \
|
||||||
|
git \
|
||||||
|
uidmap \
|
||||||
|
python3-pip \
|
||||||
|
python3-venv \
|
||||||
|
curl
|
||||||
|
ok "System packages installed."
|
||||||
|
|
||||||
|
# ── 2. podman-compose ──────────────────────────────────────────────────────────
|
||||||
|
log "Checking podman-compose…"
|
||||||
|
if command -v podman-compose &>/dev/null; then
|
||||||
|
ok "podman-compose already installed ($(podman-compose --version 2>&1 | head -1))."
|
||||||
|
else
|
||||||
|
info "Installing podman-compose via pip…"
|
||||||
|
pip3 install --user podman-compose
|
||||||
|
ok "podman-compose installed to ~/.local/bin"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ensure ~/.local/bin is in PATH for this session and future logins.
|
||||||
|
if ! echo "$PATH" | grep -q "$HOME/.local/bin"; then
|
||||||
|
export PATH="$HOME/.local/bin:$PATH"
|
||||||
|
fi
|
||||||
|
PROFILE="$HOME/.bashrc"
|
||||||
|
if ! grep -q '\.local/bin' "$PROFILE" 2>/dev/null; then
|
||||||
|
echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$PROFILE"
|
||||||
|
info "Added ~/.local/bin to PATH in $PROFILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 3. rclone (optional) ───────────────────────────────────────────────────────
|
||||||
|
log "rclone (used for off-site backups)…"
|
||||||
|
if command -v rclone &>/dev/null; then
|
||||||
|
ok "rclone already installed ($(rclone --version 2>&1 | head -1))."
|
||||||
|
else
|
||||||
|
read -r -p " Install rclone? [y/N] " _RCLONE
|
||||||
|
if [[ "${_RCLONE,,}" == "y" ]]; then
|
||||||
|
curl -fsSL https://rclone.org/install.sh | sudo bash
|
||||||
|
ok "rclone installed."
|
||||||
|
info "Configure a remote later with: rclone config"
|
||||||
|
info "Then set HIY_BACKUP_REMOTE in .env"
|
||||||
|
else
|
||||||
|
info "Skipped. Install later with: curl https://rclone.org/install.sh | sudo bash"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 4. .env setup ──────────────────────────────────────────────────────────────
|
||||||
|
log "Setting up .env…"
|
||||||
|
ENV_FILE="$REPO_ROOT/.env"
|
||||||
|
ENV_EXAMPLE="$SCRIPT_DIR/.env.example"
|
||||||
|
|
||||||
|
if [ -f "$ENV_FILE" ]; then
|
||||||
|
ok ".env already exists — skipping (edit manually if needed)."
|
||||||
|
else
|
||||||
|
cp "$ENV_EXAMPLE" "$ENV_FILE"
|
||||||
|
info "Created .env from template. Filling in required values…"
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Helper: prompt for a value and write it into .env.
|
||||||
|
set_env() {
|
||||||
|
local key="$1" prompt="$2" default="$3" secret="${4:-}"
|
||||||
|
local current
|
||||||
|
current=$(grep "^${key}=" "$ENV_FILE" | cut -d= -f2- || echo "")
|
||||||
|
if [ -z "$current" ] || [ "$current" = "changeme" ] || [ "$current" = "" ]; then
|
||||||
|
if [ -n "$secret" ]; then
|
||||||
|
read -r -s -p " ${prompt} [${default}]: " _VAL; echo
|
||||||
|
else
|
||||||
|
read -r -p " ${prompt} [${default}]: " _VAL
|
||||||
|
fi
|
||||||
|
_VAL="${_VAL:-$default}"
|
||||||
|
# Replace the line in .env (works on both macOS and Linux).
|
||||||
|
sed -i "s|^${key}=.*|${key}=${_VAL}|" "$ENV_FILE"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
set_env "DOMAIN_SUFFIX" "Your domain (e.g. example.com)" "yourdomain.com"
|
||||||
|
set_env "ACME_EMAIL" "Email for Let's Encrypt notices" ""
|
||||||
|
set_env "HIY_ADMIN_USER" "Dashboard admin username" "admin"
|
||||||
|
set_env "HIY_ADMIN_PASS" "Dashboard admin password" "$(openssl rand -hex 12)" "secret"
|
||||||
|
set_env "POSTGRES_PASSWORD" "Postgres admin password" "$(openssl rand -hex 16)" "secret"
|
||||||
|
set_env "FORGEJO_DB_PASSWORD" "Forgejo DB password" "$(openssl rand -hex 16)" "secret"
|
||||||
|
set_env "FORGEJO_DOMAIN" "Forgejo domain (e.g. git.example.com)" "git.yourdomain.com"
|
||||||
|
|
||||||
|
echo
|
||||||
|
ok ".env written to $ENV_FILE"
|
||||||
|
info "Review it with: cat $ENV_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 5. Git remote check ────────────────────────────────────────────────────────
|
||||||
|
log "Checking git remote…"
|
||||||
|
cd "$REPO_ROOT"
|
||||||
|
REMOTE_URL=$(git remote get-url origin 2>/dev/null || echo "")
|
||||||
|
if [ -n "$REMOTE_URL" ]; then
|
||||||
|
ok "Git remote: $REMOTE_URL"
|
||||||
|
else
|
||||||
|
info "No git remote configured — auto-update will not work."
|
||||||
|
info "Set one with: git remote add origin <url>"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ensure the tracking branch is set so auto-update.sh can compare commits.
|
||||||
|
BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
||||||
|
if ! git rev-parse --abbrev-ref --symbolic-full-name '@{u}' &>/dev/null; then
|
||||||
|
info "Setting upstream tracking branch…"
|
||||||
|
git branch --set-upstream-to="origin/$BRANCH" "$BRANCH" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 6. Launch the stack ────────────────────────────────────────────────────────
|
||||||
|
log "Running start.sh to build and launch the stack…"
|
||||||
|
echo
|
||||||
|
exec "$SCRIPT_DIR/start.sh"
|
||||||
143
infra/restore.sh
Executable file
143
infra/restore.sh
Executable file
|
|
@ -0,0 +1,143 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# HIY restore script
|
||||||
|
#
|
||||||
|
# Restores a backup archive produced by infra/backup.sh.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./infra/restore.sh /path/to/hiy-backup-20260101-030000.tar.gz
|
||||||
|
#
|
||||||
|
# What is restored:
|
||||||
|
# 1. SQLite database (hiy.db)
|
||||||
|
# 2. Env files and git repos
|
||||||
|
# 3. Postgres databases (pg_dumpall dump)
|
||||||
|
# 4. Forgejo data volume
|
||||||
|
# 5. Caddy TLS certificates
|
||||||
|
# 6. .env file (optional — skipped if already present unless --force is passed)
|
||||||
|
#
|
||||||
|
# ⚠ Run this with the stack STOPPED, then bring it back up afterwards:
|
||||||
|
# podman compose -f infra/docker-compose.yml down
|
||||||
|
# ./infra/restore.sh hiy-backup-*.tar.gz
|
||||||
|
# podman compose -f infra/docker-compose.yml up -d
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ARCHIVE="${1:-}"
|
||||||
|
FORCE="${2:-}"
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
ENV_FILE="${SCRIPT_DIR}/../.env"
|
||||||
|
HIY_DATA_DIR="${HIY_DATA_DIR:-/data}"
|
||||||
|
|
||||||
|
log() { echo "[hiy-restore] $(date '+%H:%M:%S') $*"; }
|
||||||
|
die() { log "ERROR: $*"; exit 1; }
|
||||||
|
|
||||||
|
# ── Validate ───────────────────────────────────────────────────────────────────
|
||||||
|
[ -z "${ARCHIVE}" ] && die "Usage: $0 <archive.tar.gz> [--force]"
|
||||||
|
[ -f "${ARCHIVE}" ] || die "Archive not found: ${ARCHIVE}"
|
||||||
|
|
||||||
|
WORK_DIR=$(mktemp -d)
|
||||||
|
trap 'rm -rf "${WORK_DIR}"' EXIT
|
||||||
|
|
||||||
|
log "=== HIY Restore ==="
|
||||||
|
log "Archive : ${ARCHIVE}"
|
||||||
|
log "Work dir: ${WORK_DIR}"
|
||||||
|
|
||||||
|
log "Extracting archive…"
|
||||||
|
tar -xzf "${ARCHIVE}" -C "${WORK_DIR}"
|
||||||
|
|
||||||
|
# ── Helper: find a running container by compose service label ──────────────────
|
||||||
|
find_container() {
|
||||||
|
local service="$1"
|
||||||
|
podman ps --filter "label=com.docker.compose.service=${service}" \
|
||||||
|
--format '{{.Names}}' | head -1
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── 1. .env file ───────────────────────────────────────────────────────────────
|
||||||
|
log "--- .env ---"
|
||||||
|
if [ -f "${WORK_DIR}/dot-env" ]; then
|
||||||
|
if [ -f "${ENV_FILE}" ] && [ "${FORCE}" != "--force" ]; then
|
||||||
|
log "SKIP: ${ENV_FILE} already exists (pass --force to overwrite)"
|
||||||
|
else
|
||||||
|
cp "${WORK_DIR}/dot-env" "${ENV_FILE}"
|
||||||
|
log "Restored .env to ${ENV_FILE}"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log "No .env in archive — skipping"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 2. SQLite ──────────────────────────────────────────────────────────────────
|
||||||
|
log "--- SQLite ---"
|
||||||
|
if [ -f "${WORK_DIR}/hiy.sql" ]; then
|
||||||
|
DB_PATH="${HIY_DATA_DIR}/hiy.db"
|
||||||
|
mkdir -p "$(dirname "${DB_PATH}")"
|
||||||
|
if [ -f "${DB_PATH}" ]; then
|
||||||
|
log "Moving existing hiy.db to hiy.db.bak…"
|
||||||
|
mv "${DB_PATH}" "${DB_PATH}.bak"
|
||||||
|
fi
|
||||||
|
log "Restoring hiy.db…"
|
||||||
|
sqlite3 "${DB_PATH}" < "${WORK_DIR}/hiy.sql"
|
||||||
|
log "SQLite restored."
|
||||||
|
else
|
||||||
|
log "No hiy.sql in archive — skipping"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 3. Env files & git repos ───────────────────────────────────────────────────
|
||||||
|
log "--- Env files ---"
|
||||||
|
if [ -f "${WORK_DIR}/envs.tar.gz" ]; then
|
||||||
|
log "Restoring envs/…"
|
||||||
|
tar -xzf "${WORK_DIR}/envs.tar.gz" -C "${HIY_DATA_DIR}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "--- Git repos ---"
|
||||||
|
if [ -f "${WORK_DIR}/repos.tar.gz" ]; then
|
||||||
|
log "Restoring repos/…"
|
||||||
|
tar -xzf "${WORK_DIR}/repos.tar.gz" -C "${HIY_DATA_DIR}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 4. Postgres ────────────────────────────────────────────────────────────────
|
||||||
|
log "--- Postgres ---"
|
||||||
|
if [ -f "${WORK_DIR}/postgres.sql" ]; then
|
||||||
|
PG_CTR=$(find_container postgres)
|
||||||
|
if [ -n "${PG_CTR}" ]; then
|
||||||
|
log "Restoring Postgres via container ${PG_CTR}…"
|
||||||
|
# Drop existing connections then restore.
|
||||||
|
podman exec -i "${PG_CTR}" psql -U hiy_admin -d postgres \
|
||||||
|
-c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname IN ('hiy','forgejo') AND pid <> pg_backend_pid();" \
|
||||||
|
> /dev/null 2>&1 || true
|
||||||
|
podman exec -i "${PG_CTR}" psql -U hiy_admin -d postgres \
|
||||||
|
< "${WORK_DIR}/postgres.sql"
|
||||||
|
log "Postgres restored."
|
||||||
|
else
|
||||||
|
log "WARNING: postgres container not running"
|
||||||
|
log " Start Postgres first, then run:"
|
||||||
|
log " podman exec -i <postgres_container> psql -U hiy_admin -d postgres < ${WORK_DIR}/postgres.sql"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log "No postgres.sql in archive — skipping"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 5. Forgejo data volume ─────────────────────────────────────────────────────
|
||||||
|
log "--- Forgejo volume ---"
|
||||||
|
if [ -f "${WORK_DIR}/forgejo-data.tar" ]; then
|
||||||
|
log "Importing forgejo-data volume…"
|
||||||
|
podman volume exists forgejo-data 2>/dev/null || podman volume create forgejo-data
|
||||||
|
podman volume import forgejo-data "${WORK_DIR}/forgejo-data.tar"
|
||||||
|
log "forgejo-data restored."
|
||||||
|
else
|
||||||
|
log "No forgejo-data.tar in archive — skipping"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 6. Caddy TLS certificates ──────────────────────────────────────────────────
|
||||||
|
log "--- Caddy volume ---"
|
||||||
|
if [ -f "${WORK_DIR}/caddy-data.tar" ]; then
|
||||||
|
log "Importing caddy-data volume…"
|
||||||
|
podman volume exists caddy-data 2>/dev/null || podman volume create caddy-data
|
||||||
|
podman volume import caddy-data "${WORK_DIR}/caddy-data.tar"
|
||||||
|
log "caddy-data restored."
|
||||||
|
else
|
||||||
|
log "No caddy-data.tar in archive — skipping"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "=== Restore complete ==="
|
||||||
|
log "Bring the stack back up with:"
|
||||||
|
log " podman compose -f ${SCRIPT_DIR}/docker-compose.yml up -d"
|
||||||
23
infra/runner-entrypoint.sh
Executable file
23
infra/runner-entrypoint.sh
Executable file
|
|
@ -0,0 +1,23 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# runner-entrypoint.sh — register the Forgejo runner on first start, then run the daemon.
|
||||||
|
#
|
||||||
|
# On first run (no /data/.runner file) it calls create-runner-file to register
|
||||||
|
# with the Forgejo instance using FORGEJO_RUNNER_TOKEN. On subsequent starts it
|
||||||
|
# goes straight to the daemon.
|
||||||
|
set -e
|
||||||
|
|
||||||
|
CONFIG=/data/.runner
|
||||||
|
|
||||||
|
if [ ! -f "$CONFIG" ]; then
|
||||||
|
echo "[runner] No registration found — registering with Forgejo…"
|
||||||
|
forgejo-runner register \
|
||||||
|
--instance "${FORGEJO_INSTANCE_URL}" \
|
||||||
|
--token "${FORGEJO_RUNNER_TOKEN}" \
|
||||||
|
--name "${FORGEJO_RUNNER_NAME:-hiy-runner}" \
|
||||||
|
--labels "ubuntu-latest:docker://node:20-bookworm,ubuntu-22.04:docker://node:20-bookworm" \
|
||||||
|
--no-interactive
|
||||||
|
echo "[runner] Registration complete."
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[runner] Starting daemon…"
|
||||||
|
exec forgejo-runner daemon --config "$CONFIG"
|
||||||
|
|
@ -171,6 +171,7 @@ Wants=network-online.target
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
RemainAfterExit=yes
|
RemainAfterExit=yes
|
||||||
|
Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/home/$(id -un)/.local/bin
|
||||||
ExecStart=${SCRIPT_DIR}/boot.sh
|
ExecStart=${SCRIPT_DIR}/boot.sh
|
||||||
ExecStop=podman compose --env-file ${REPO_ROOT}/.env -f ${SCRIPT_DIR}/docker-compose.yml down
|
ExecStop=podman compose --env-file ${REPO_ROOT}/.env -f ${SCRIPT_DIR}/docker-compose.yml down
|
||||||
|
|
||||||
|
|
@ -182,3 +183,37 @@ systemctl --user daemon-reload
|
||||||
systemctl --user enable hiy.service
|
systemctl --user enable hiy.service
|
||||||
loginctl enable-linger "$(id -un)" 2>/dev/null || true
|
loginctl enable-linger "$(id -un)" 2>/dev/null || true
|
||||||
echo "[hiy] Boot service installed: systemctl --user status hiy.service"
|
echo "[hiy] Boot service installed: systemctl --user status hiy.service"
|
||||||
|
|
||||||
|
# ── Install systemd timer for auto-update ─────────────────────────────────────
|
||||||
|
UPDATE_SERVICE="$SERVICE_DIR/hiy-update.service"
|
||||||
|
UPDATE_TIMER="$SERVICE_DIR/hiy-update.timer"
|
||||||
|
|
||||||
|
cat > "$UPDATE_SERVICE" <<UNIT
|
||||||
|
[Unit]
|
||||||
|
Description=HIY auto-update (git pull + restart changed services)
|
||||||
|
After=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/home/$(id -un)/.local/bin
|
||||||
|
ExecStart=${SCRIPT_DIR}/auto-update.sh
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=journal
|
||||||
|
UNIT
|
||||||
|
|
||||||
|
cat > "$UPDATE_TIMER" <<UNIT
|
||||||
|
[Unit]
|
||||||
|
Description=HIY auto-update every 5 minutes
|
||||||
|
|
||||||
|
[Timer]
|
||||||
|
OnBootSec=2min
|
||||||
|
OnUnitActiveSec=5min
|
||||||
|
Unit=hiy-update.service
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=timers.target
|
||||||
|
UNIT
|
||||||
|
|
||||||
|
systemctl --user daemon-reload
|
||||||
|
systemctl --user enable --now hiy-update.timer
|
||||||
|
echo "[hiy] Auto-update timer installed: systemctl --user status hiy-update.timer"
|
||||||
|
|
|
||||||
|
|
@ -162,6 +162,12 @@ async fn main() -> anyhow::Result<()> {
|
||||||
routes::apps::restore_caddy_routes(&restore_db).await;
|
routes::apps::restore_caddy_routes(&restore_db).await;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Restart any app containers that are stopped (e.g. after a host reboot).
|
||||||
|
let containers_db = state.db.clone();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
routes::apps::restore_app_containers(&containers_db).await;
|
||||||
|
});
|
||||||
|
|
||||||
// ── Protected routes (admin login required) ───────────────────────────────
|
// ── Protected routes (admin login required) ───────────────────────────────
|
||||||
let protected = Router::new()
|
let protected = Router::new()
|
||||||
.route("/", get(routes::ui::index))
|
.route("/", get(routes::ui::index))
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,73 @@ pub async fn restore_caddy_routes(db: &crate::DbPool) {
|
||||||
tracing::info!("restore_caddy_routes: registered {} app routes", apps.len());
|
tracing::info!("restore_caddy_routes: registered {} app routes", apps.len());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// On startup, ensure every app that had a successful deploy is actually running.
|
||||||
|
/// If the host rebooted, containers will be in "exited" state — start them.
|
||||||
|
/// If a container is missing entirely, log a warning (we don't rebuild automatically).
|
||||||
|
pub async fn restore_app_containers(db: &crate::DbPool) {
|
||||||
|
let apps = match sqlx::query_as::<_, crate::models::App>("SELECT * FROM apps")
|
||||||
|
.fetch_all(db)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(a) => a,
|
||||||
|
Err(e) => { tracing::error!("restore_app_containers: DB error: {}", e); return; }
|
||||||
|
};
|
||||||
|
|
||||||
|
for app in &apps {
|
||||||
|
// Only care about apps that have at least one successful deploy.
|
||||||
|
let has_deploy: bool = sqlx::query_scalar(
|
||||||
|
"SELECT COUNT(*) > 0 FROM deploys WHERE app_id = ? AND status = 'success'"
|
||||||
|
)
|
||||||
|
.bind(&app.id)
|
||||||
|
.fetch_one(db)
|
||||||
|
.await
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
if !has_deploy {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let container = format!("hiy-{}", app.id);
|
||||||
|
|
||||||
|
// Check container state via `podman inspect`.
|
||||||
|
let inspect = tokio::process::Command::new("podman")
|
||||||
|
.args(["inspect", "--format", "{{.State.Status}}", &container])
|
||||||
|
.output()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
match inspect {
|
||||||
|
Ok(out) if out.status.success() => {
|
||||||
|
let status = String::from_utf8_lossy(&out.stdout).trim().to_string();
|
||||||
|
if status == "running" {
|
||||||
|
tracing::debug!("restore_app_containers: {} already running", container);
|
||||||
|
} else {
|
||||||
|
tracing::info!("restore_app_containers: starting {} (was {})", container, status);
|
||||||
|
let start = tokio::process::Command::new("podman")
|
||||||
|
.args(["start", &container])
|
||||||
|
.output()
|
||||||
|
.await;
|
||||||
|
match start {
|
||||||
|
Ok(o) if o.status.success() =>
|
||||||
|
tracing::info!("restore_app_containers: {} started", container),
|
||||||
|
Ok(o) =>
|
||||||
|
tracing::warn!("restore_app_containers: failed to start {}: {}",
|
||||||
|
container, String::from_utf8_lossy(&o.stderr).trim()),
|
||||||
|
Err(e) =>
|
||||||
|
tracing::warn!("restore_app_containers: error starting {}: {}", container, e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
tracing::warn!(
|
||||||
|
"restore_app_containers: container {} not found — redeploy needed",
|
||||||
|
container
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tracing::info!("restore_app_containers: done");
|
||||||
|
}
|
||||||
|
|
||||||
/// Push a visibility change to Caddy without requiring a full redeploy.
|
/// Push a visibility change to Caddy without requiring a full redeploy.
|
||||||
/// Best-effort: logs a warning on failure but does not surface an error to the caller.
|
/// Best-effort: logs a warning on failure but does not surface an error to the caller.
|
||||||
async fn push_visibility_to_caddy(app_id: &str, port: i64, is_public: bool) {
|
async fn push_visibility_to_caddy(app_id: &str, port: i64, is_public: bool) {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue