Compare commits

..

5 Commits

Author SHA1 Message Date
Cadey Ratio 89fbb37e7c add blogpost
Signed-off-by: Christine Dodrill <me@christine.website>
2021-01-14 22:25:10 -05:00
Cadey Ratio 4ba4a83417 add rss ttl
Signed-off-by: Christine Dodrill <me@christine.website>
2021-01-14 21:50:13 -05:00
Cadey Ratio ef3b80ede3
Merge branch 'main' into cache-better 2021-01-14 21:41:12 -05:00
Cadey Ratio 52779efa04 expire cache quicker for dynamic pages
Signed-off-by: Christine Dodrill <me@christine.website>
2021-01-14 21:37:47 -05:00
Cadey Ratio 31cad90e0a Many improvements around bandwidth use
- Use ETags for RSS/Atom feeds
- Use cache-control headers
- Update to rust nightly (for rust-analyzer and faster builds)
- Limit feeds to the last 20 posts:
  https://twitter.com/theprincessxena/status/1349891678857998339
- Use if-none-match to limit bandwidth further

Also does this:

- bump go_vanity to 0.3.0 and lets users customize the branch name
- fix formatting on jsonfeed
- remove last vestige of kubernetes/docker support

Signed-off-by: Christine Dodrill <me@christine.website>
2021-01-14 21:36:46 -05:00
27 changed files with 269 additions and 1093 deletions

464
Cargo.lock generated
View File

@ -36,6 +36,12 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"
[[package]]
name = "adler32"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
[[package]]
name = "aho-corasick"
version = "0.7.15"
@ -47,12 +53,9 @@ dependencies = [
[[package]]
name = "annotate-snippets"
version = "0.9.0"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c96c3d1062ea7101741480185a6a1275eab01cbe8b20e378d1311bc056d2e08"
dependencies = [
"unicode-width",
]
checksum = "aba2d96b8c8b5e656ad7ffb0d09f57772f10a1db74c8d23fca0ec695b38a4047"
[[package]]
name = "ansi_term"
@ -78,27 +81,6 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
[[package]]
name = "async-stream"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3670df70cbc01729f901f94c887814b3c68db038aad1329a418bae178bc5295c"
dependencies = [
"async-stream-impl",
"futures-core",
]
[[package]]
name = "async-stream-impl"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3548b8efc9f8e8a5a0a2808c5bd8451a9031b9e5b879a79590304ae928b0a70"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "atty"
version = "0.2.14"
@ -230,21 +212,6 @@ version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48"
[[package]]
name = "cfcache"
version = "0.1.0"
dependencies = [
"eyre",
"kankyo",
"reqwest 0.11.0",
"serde",
"serde_json",
"thiserror",
"tokio 1.1.0",
"tracing",
"tracing-futures",
]
[[package]]
name = "cfg-if"
version = "0.1.10"
@ -374,9 +341,9 @@ dependencies = [
[[package]]
name = "dhall"
version = "0.9.0"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7930c7ac2b3989a07a2a3400bf9f4bc1c65074f330e3ff22b372a4d386fabd0"
checksum = "a8e2aa2abb16c2ef064fbc45f5163be5c5ff990e8584e54fb1c8f6e318107289"
dependencies = [
"abnf_to_pest",
"annotate-snippets",
@ -389,11 +356,12 @@ dependencies = [
"pest_consume",
"pest_generator",
"quote",
"reqwest 0.10.10",
"reqwest",
"serde",
"serde_cbor",
"sha2",
"url",
"walkdir",
]
[[package]]
@ -507,18 +475,6 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
[[package]]
name = "flate2"
version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7411863d55df97a419aa64cb4d2f167103ea9d767e2c54a1868b7ac3f6b47129"
dependencies = [
"cfg-if 1.0.0",
"crc32fast",
"libc",
"miniz_oxide",
]
[[package]]
name = "fnv"
version = "1.0.7"
@ -709,28 +665,8 @@ dependencies = [
"http",
"indexmap",
"slab",
"tokio 0.2.24",
"tokio-util 0.3.1",
"tracing",
"tracing-futures",
]
[[package]]
name = "h2"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b67e66362108efccd8ac053abafc8b7a8d86a37e6e48fc4f6f7485eb5e9e6a5"
dependencies = [
"bytes 1.0.0",
"fnv",
"futures-core",
"futures-sink",
"futures-util",
"http",
"indexmap",
"slab",
"tokio 1.1.0",
"tokio-util 0.6.2",
"tokio",
"tokio-util",
"tracing",
"tracing-futures",
]
@ -808,16 +744,6 @@ dependencies = [
"http",
]
[[package]]
name = "http-body"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2861bd27ee074e5ee891e8b539837a9430012e249d7f0ca2d795650f579c1994"
dependencies = [
"bytes 1.0.0",
"http",
]
[[package]]
name = "httparse"
version = "1.3.4"
@ -849,39 +775,15 @@ dependencies = [
"futures-channel",
"futures-core",
"futures-util",
"h2 0.2.7",
"h2",
"http",
"http-body 0.3.1",
"http-body",
"httparse",
"httpdate",
"itoa",
"pin-project 1.0.4",
"socket2",
"tokio 0.2.24",
"tower-service",
"tracing",
"want",
]
[[package]]
name = "hyper"
version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12219dc884514cb4a6a03737f4413c0e01c23a1b059b0156004b23f1e19dccbe"
dependencies = [
"bytes 1.0.0",
"futures-channel",
"futures-core",
"futures-util",
"h2 0.3.0",
"http",
"http-body 0.4.0",
"httparse",
"httpdate",
"itoa",
"pin-project 1.0.4",
"socket2",
"tokio 1.1.0",
"tokio",
"tower-service",
"tracing",
"want",
@ -894,25 +796,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d979acc56dcb5b8dddba3917601745e877576475aa046df3226eabdecef78eed"
dependencies = [
"bytes 0.5.6",
"hyper 0.13.9",
"hyper",
"native-tls",
"tokio 0.2.24",
"tokio",
"tokio-tls",
]
[[package]]
name = "hyper-tls"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
dependencies = [
"bytes 1.0.0",
"hyper 0.14.2",
"native-tls",
"tokio 1.1.0",
"tokio-native-tls",
]
[[package]]
name = "idna"
version = "0.2.0"
@ -942,11 +831,11 @@ dependencies = [
[[package]]
name = "input_buffer"
version = "0.4.0"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f97967975f448f1a7ddb12b0bc41069d09ed6a1c161a92687e057325db35d413"
checksum = "19a8a95243d5a0398cae618ec29477c6e3cb631152be5c19481f80bc71559754"
dependencies = [
"bytes 1.0.0",
"bytes 0.5.6",
]
[[package]]
@ -1048,6 +937,24 @@ version = "0.2.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89203f3fba0a3795506acaad8ebce3c80c0af93f994d5a1d7a0b1eeb23271929"
[[package]]
name = "libflate"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "389de7875e06476365974da3e7ff85d55f1972188ccd9f6020dd7c8156e17914"
dependencies = [
"adler32",
"crc32fast",
"libflate_lz77",
"rle-decode-fast",
]
[[package]]
name = "libflate_lz77"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3286f09f7d4926fc486334f28d8d2e6ebe4f7f9994494b6dab27ddfad2c9b11b"
[[package]]
name = "linked-hash-map"
version = "0.5.4"
@ -1065,9 +972,9 @@ dependencies = [
[[package]]
name = "log"
version = "0.4.13"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcf3805d4480bb5b86070dcfeb9e2cb2ebc148adb753c5cca5f884d1d65a42b2"
checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
dependencies = [
"cfg-if 0.1.10",
]
@ -1113,11 +1020,11 @@ dependencies = [
"color-eyre",
"envy",
"pretty_env_logger",
"reqwest 0.11.0",
"reqwest",
"serde",
"serde_json",
"thiserror",
"tokio 1.1.0",
"tokio",
"tracing",
"tracing-futures",
]
@ -1161,25 +1068,12 @@ dependencies = [
"kernel32-sys",
"libc",
"log",
"miow 0.2.2",
"miow",
"net2",
"slab",
"winapi 0.2.8",
]
[[package]]
name = "mio"
version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e50ae3f04d169fcc9bde0b547d1c205219b7157e07ded9c5aff03e0637cb3ed7"
dependencies = [
"libc",
"log",
"miow 0.3.6",
"ntapi",
"winapi 0.3.9",
]
[[package]]
name = "miow"
version = "0.2.2"
@ -1192,16 +1086,6 @@ dependencies = [
"ws2_32-sys",
]
[[package]]
name = "miow"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897"
dependencies = [
"socket2",
"winapi 0.3.9",
]
[[package]]
name = "multipart"
version = "0.17.1"
@ -1260,15 +1144,6 @@ dependencies = [
"version_check",
]
[[package]]
name = "ntapi"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44"
dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "num-integer"
version = "0.1.44"
@ -1393,11 +1268,11 @@ dependencies = [
"chrono",
"envy",
"pretty_env_logger",
"reqwest 0.11.0",
"reqwest",
"serde",
"serde_json",
"thiserror",
"tokio 1.1.0",
"tokio",
"tracing",
"tracing-futures",
]
@ -1481,7 +1356,7 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83abebdb324c30f176d449513f0134bafbf976d5279c6554742599e3996d1629"
dependencies = [
"rand 0.8.2",
"rand 0.8.1",
"serde",
"serde_derive",
"serde_json",
@ -1593,25 +1468,25 @@ dependencies = [
[[package]]
name = "procfs"
version = "0.9.1"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab8809e0c18450a2db0f236d2a44ec0b4c1412d0eb936233579f0990faa5d5cd"
checksum = "c4a336c8310f4955f343935b9c11a30254d1ad8fad98ec257a4407a061a6fd49"
dependencies = [
"bitflags",
"byteorder",
"flate2",
"hex",
"lazy_static",
"libc",
"libflate",
]
[[package]]
name = "prometheus"
version = "0.11.0"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8425533e7122f0c3cc7a37e6244b16ad3a2cc32ae7ac6276e2a75da0d9c200d"
checksum = "30d70cf4412832bcac9cffe27906f4a66e450d323525e977168c70d1b36120ae"
dependencies = [
"cfg-if 1.0.0",
"cfg-if 0.1.10",
"fnv",
"lazy_static",
"libc",
@ -1651,9 +1526,9 @@ dependencies = [
[[package]]
name = "rand"
version = "0.8.2"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18519b42a40024d661e1714153e9ad0c3de27cd495760ceb09710920f1098b1e"
checksum = "c24fcd450d3fa2b592732565aa4f17a27a61c65ece4726353e000939b0edee34"
dependencies = [
"libc",
"rand_chacha 0.3.0",
@ -1772,9 +1647,9 @@ dependencies = [
"futures-core",
"futures-util",
"http",
"http-body 0.3.1",
"hyper 0.13.9",
"hyper-tls 0.4.3",
"http-body",
"hyper",
"hyper-tls",
"ipnet",
"js-sys",
"lazy_static",
@ -1786,8 +1661,8 @@ dependencies = [
"pin-project-lite 0.2.3",
"serde",
"serde_json",
"serde_urlencoded",
"tokio 0.2.24",
"serde_urlencoded 0.7.0",
"tokio",
"tokio-tls",
"url",
"wasm-bindgen",
@ -1797,45 +1672,16 @@ dependencies = [
]
[[package]]
name = "reqwest"
version = "0.11.0"
name = "rle-decode-fast"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd281b1030aa675fb90aa994d07187645bb3c8fc756ca766e7c3070b439de9de"
dependencies = [
"base64 0.13.0",
"bytes 1.0.0",
"encoding_rs",
"futures-core",
"futures-util",
"http",
"http-body 0.4.0",
"hyper 0.14.2",
"hyper-tls 0.5.0",
"ipnet",
"js-sys",
"lazy_static",
"log",
"mime",
"native-tls",
"percent-encoding",
"pin-project-lite 0.2.3",
"serde",
"serde_json",
"serde_urlencoded",
"tokio 1.1.0",
"tokio-native-tls",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"winreg",
]
checksum = "cabe4fa914dec5870285fa7f71f602645da47c486e68486d2b4ceb4a343e90ac"
[[package]]
name = "ructe"
version = "0.13.0"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3507c22423c8be907293f0aa684b08ac62efb20e0768639fdfbce833481fd30"
checksum = "1b8ffc645837eab09b6cbb6772d18897c6309753254592e149b40ff7fb113c01"
dependencies = [
"base64 0.12.3",
"bytecount",
@ -1863,6 +1709,15 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "schannel"
version = "0.1.19"
@ -1916,28 +1771,29 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.120"
version = "1.0.118"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "166b2349061381baf54a58e4b13c89369feb0ef2eaa57198899e2312aac30aab"
checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_cbor"
version = "0.11.1"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e18acfa2f90e8b735b2836ab8d538de304cbb6729a7360729ea5a895d15a622"
checksum = "45cd6d95391b16cd57e88b68be41d504183b7faae22030c0cc3b3f73dd57b2fd"
dependencies = [
"byteorder",
"half",
"serde",
]
[[package]]
name = "serde_derive"
version = "1.0.120"
version = "1.0.118"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ca2a8cb5805ce9e3b95435e3765b7b553cecc762d938d409434338386cb5775"
checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df"
dependencies = [
"proc-macro2",
"quote",
@ -1946,9 +1802,9 @@ dependencies = [
[[package]]
name = "serde_dhall"
version = "0.9.0"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f80d945a711c648e559c4d39832379f36a303d393fca4719080de51701266f38"
checksum = "313ac1225c35cd2760b4124d39fa4df19b4a5e29edd55a3eb3fc0b01940f8707"
dependencies = [
"dhall",
"dhall_proc_macros",
@ -1968,6 +1824,18 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_urlencoded"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97"
dependencies = [
"dtoa",
"itoa",
"serde",
"url",
]
[[package]]
name = "serde_urlencoded"
version = "0.7.0"
@ -2045,15 +1913,6 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6fa3938c99da4914afedd13bf3d79bcb6c277d1b2c398d23257a304d9e1b074"
[[package]]
name = "signal-hook-registry"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6"
dependencies = [
"libc",
]
[[package]]
name = "sitemap"
version = "0.4.1"
@ -2211,64 +2070,24 @@ dependencies = [
"iovec",
"lazy_static",
"memchr",
"mio 0.6.23",
"mio",
"num_cpus",
"pin-project-lite 0.1.11",
"slab",
]
[[package]]
name = "tokio"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8efab2086f17abcddb8f756117665c958feee6b2e39974c2f1600592ab3a4195"
dependencies = [
"autocfg",
"bytes 1.0.0",
"libc",
"memchr",
"mio 0.7.7",
"num_cpus",
"once_cell",
"parking_lot",
"pin-project-lite 0.2.3",
"signal-hook-registry",
"tokio-macros",
"winapi 0.3.9",
]
[[package]]
name = "tokio-macros"
version = "1.0.0"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42517d2975ca3114b22a16192634e8241dc5cc1f130be194645970cc1c371494"
checksum = "e44da00bfc73a25f814cd8d7e57a68a5c31b74b3152a0a1d1f590c97ed06265a"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tokio-native-tls"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b"
dependencies = [
"native-tls",
"tokio 1.1.0",
]
[[package]]
name = "tokio-stream"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76066865172052eb8796c686f0b441a93df8b08d40a950b062ffb9a426f00edd"
dependencies = [
"futures-core",
"pin-project-lite 0.2.3",
"tokio 1.1.0",
]
[[package]]
name = "tokio-tls"
version = "0.3.1"
@ -2276,19 +2095,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a70f4fcd7b3b24fb194f837560168208f669ca8cb70d0c4b862944452396343"
dependencies = [
"native-tls",
"tokio 0.2.24",
"tokio",
]
[[package]]
name = "tokio-tungstenite"
version = "0.13.0"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1a5f475f1b9d077ea1017ecbc60890fda8e54942d680ca0b1d2b47cfa2d861b"
checksum = "6d9e878ad426ca286e4dcae09cbd4e1973a7f8987d97570e2469703dd7f5720c"
dependencies = [
"futures-util",
"log",
"pin-project 1.0.4",
"tokio 1.1.0",
"pin-project 0.4.27",
"tokio",
"tungstenite",
]
@ -2303,23 +2122,7 @@ dependencies = [
"futures-sink",
"log",
"pin-project-lite 0.1.11",
"tokio 0.2.24",
]
[[package]]
name = "tokio-util"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "feb971a26599ffd28066d387f109746df178eff14d5ea1e235015c5601967a4b"
dependencies = [
"async-stream",
"bytes 1.0.0",
"futures-core",
"futures-sink",
"log",
"pin-project-lite 0.2.3",
"tokio 1.1.0",
"tokio-stream",
"tokio",
]
[[package]]
@ -2432,18 +2235,18 @@ checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
[[package]]
name = "tungstenite"
version = "0.12.0"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ada8297e8d70872fa9a551d93250a9f407beb9f37ef86494eb20012a2ff7c24"
checksum = "f0308d80d86700c5878b9ef6321f020f29b1bb9d5ff3cab25e75e23f3a492a23"
dependencies = [
"base64 0.13.0",
"base64 0.12.3",
"byteorder",
"bytes 1.0.0",
"bytes 0.5.6",
"http",
"httparse",
"input_buffer",
"log",
"rand 0.8.2",
"rand 0.7.3",
"sha-1 0.9.2",
"url",
"utf-8",
@ -2549,6 +2352,12 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "urlencoding"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9232eb53352b4442e40d7900465dfc534e8cb2dc8f18656fcb2ac16112b5593"
[[package]]
name = "utf-8"
version = "0.7.5"
@ -2583,6 +2392,17 @@ version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
[[package]]
name = "walkdir"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d"
dependencies = [
"same-file",
"winapi 0.3.9",
"winapi-util",
]
[[package]]
name = "want"
version = "0.3.0"
@ -2595,32 +2415,30 @@ dependencies = [
[[package]]
name = "warp"
version = "0.3.0"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dafd0aac2818a94a34df0df1100a7356c493d8ede4393875fd0b5c51bb6bc80"
checksum = "f41be6df54c97904af01aa23e613d4521eed7ab23537cede692d4058f6449407"
dependencies = [
"bytes 1.0.0",
"bytes 0.5.6",
"futures",
"headers",
"http",
"hyper 0.14.2",
"hyper",
"log",
"mime",
"mime_guess",
"multipart",
"percent-encoding",
"pin-project 1.0.4",
"pin-project 0.4.27",
"scoped-tls",
"serde",
"serde_json",
"serde_urlencoded",
"tokio 1.1.0",
"tokio-stream",
"serde_urlencoded 0.6.1",
"tokio",
"tokio-tungstenite",
"tokio-util 0.6.2",
"tower-service",
"tracing",
"tracing-futures",
"urlencoding",
]
[[package]]
@ -2783,9 +2601,8 @@ checksum = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57"
[[package]]
name = "xesite"
version = "2.2.0"
version = "2.1.0"
dependencies = [
"cfcache",
"chrono",
"color-eyre",
"comrak",
@ -2793,7 +2610,7 @@ dependencies = [
"eyre",
"glob",
"go_vanity",
"hyper 0.14.2",
"hyper",
"jsonfeed",
"kankyo",
"lazy_static",
@ -2804,9 +2621,8 @@ dependencies = [
"pfacts",
"pretty_env_logger",
"prometheus",
"rand 0.8.2",
"reqwest 0.10.10",
"reqwest 0.11.0",
"rand 0.8.1",
"reqwest",
"ructe",
"sdnotify",
"serde",
@ -2815,7 +2631,7 @@ dependencies = [
"serde_yaml",
"sitemap",
"thiserror",
"tokio 1.1.0",
"tokio",
"tracing",
"tracing-futures",
"tracing-subscriber",

View File

@ -14,43 +14,42 @@ chrono = "0.4"
comrak = "0.9"
envy = "0.4"
glob = "0.3"
hyper = "0.14"
hyper = "0.13"
kankyo = "0.3"
lazy_static = "1.4"
log = "0.4"
mime = "0.3.0"
prometheus = { version = "0.11", default-features = false, features = ["process"] }
prometheus = { version = "0.10", default-features = false, features = ["process"] }
rand = "0"
reqwest = { version = "0.11", features = ["json"] }
sdnotify = { version = "0.1", default-features = false }
serde_dhall = "0.9.0"
serde_dhall = "0.8.0"
serde = { version = "1", features = ["derive"] }
serde_yaml = "0.8"
sitemap = "0.4"
thiserror = "1"
tokio = { version = "1", features = ["full"] }
tokio = { version = "0.2", features = ["macros"] }
tracing = "0.1"
tracing-futures = "0.2"
tracing-subscriber = { version = "0.2", features = ["fmt"] }
warp = "0.3"
warp = "0.2"
xml-rs = "0.8"
url = "2"
uuid = { version = "0.8", features = ["serde", "v4"] }
# workspace dependencies
cfcache = { path = "./lib/cfcache" }
go_vanity = { path = "./lib/go_vanity" }
jsonfeed = { path = "./lib/jsonfeed" }
mi = { path = "./lib/mi" }
patreon = { path = "./lib/patreon" }
[build-dependencies]
ructe = { version = "0.13", features = ["warp02"] }
ructe = { version = "0.12", features = ["warp02"] }
[dev-dependencies]
pfacts = "0"
serde_json = "1"
eyre = "0.6"
reqwest = { version = "0.10", features = ["json"] }
pretty_env_logger = "0"
[workspace]

View File

@ -1,38 +0,0 @@
---
title: New PGP Key Fingerprint
date: 2021-01-15
---
# New PGP Key Fingerprint
This morning I got an encrypted email, and in the process of trying to decrypt
it I discovered that I had _lost_ my PGP key. I have no idea how I lost it. As
such, I have created a new PGP key and replaced the one on my website with it.
I did the replacement in [this
commit](https://github.com/Xe/site/commit/66233bcd40155cf71e221edf08851db39dbd421c),
which you can see is verified with a subkey of my new key.
My new PGP key ID is `803C 935A E118 A224`. The key with the ID `799F 9134 8118
1111` should not be used anymore. Here are all the subkey fingerprints:
```
Signature key ....: 378E BFC6 3D79 B49D 8C36 448C 803C 935A E118 A224
created ....: 2021-01-15 13:04:28
Encryption key....: 8C61 7F30 F331 D21B 5517 6478 8C5C 9BC7 0FC2 511E
created ....: 2021-01-15 13:04:28
Authentication key: 7BF7 E531 ABA3 7F77 FD17 8F72 CE17 781B F55D E945
created ....: 2021-01-15 13:06:20
General key info..: pub rsa2048/803C935AE118A224 2021-01-15 Christine Dodrill (Yubikey) <me@christine.website>
sec> rsa2048/803C935AE118A224 created: 2021-01-15 expires: 2031-01-13
card-no: 0006 03646872
ssb> rsa2048/8C5C9BC70FC2511E created: 2021-01-15 expires: 2031-01-13
card-no: 0006 03646872
ssb> rsa2048/CE17781BF55DE945 created: 2021-01-15 expires: 2031-01-13
card-no: 0006 03646872
```
I don't really know what the proper way is to go about revoking an old PGP key.
It probably doesn't help that I don't use PGP very often. I think this is the
first encrypted email I've gotten in a year.
Let's hope that I don't lose this key as easily!

View File

@ -1,332 +0,0 @@
---
title: Encrypted Secrets with NixOS
date: 2021-01-20
series: nixos
tags:
- age
- ed25519
---
# Encrypted Secrets with NixOS
One of the best things about NixOS is the fact that it's so easy to do
configuration management using it. The Nix store (where all your packages live)
has a huge flaw for secret management though: everything in the Nix store is
globally readable. This means that anyone logged into or running code on the
system could read any secret in the Nix store without any limits. This is
sub-optimal if your goal is to keep secret values secret. There have been a few
approaches to this over the years, but I want to describe how I'm doing it.
Here are my goals and implementation for this setup and how a few other secret
management strategies don't quite pan out.
At a high level I have these goals:
* It should be trivial to declare new secrets
* Secrets should never be globally readable in any useful form
* If I restart the machine, I should not need to take manual human action to
ensure all of the services come back online
* GPG should be avoided at all costs
As a side goal being able to roll back secret changes would also be nice.
The two biggest tools that offer a way to help with secret management on NixOS
that come to mind are NixOps and Morph.
[NixOps](https://github.com/NixOS/nixops) is a tool that helps administrators
operate NixOS across multiple servers at once. I use NixOps extensively in my
own setup. It calls deployment secrets "keys" and they are documented
[here](https://hydra.nixos.org/build/115931128/download/1/manual/manual.html#idm140737322649152).
At a high level they are declared like this:
```nix
deployment.keys.example = {
text = "this is a super sekrit value :)";
user = "example";
group = "keys";
permissions = "0400";
};
```
This will create a new secret in `/run/keys` that will contain our super secret
value.
[Wait, isn't `/run` an ephemeral filesystem? What happens when the system
reboots?](conversation://Mara/hmm)
Let's make an example system and find out! So let's say we have that `example`
secret from earlier and want to use it in a job. The job definition could look
something like this:
```nix
# create a service-specific user
users.users.example.isSystemUser = true;
# without this group the secret can't be read
users.users.example.extraGroups = [ "keys" ];
systemd.services.example = {
wantedBy = [ "multi-user.target" ];
after = [ "example-key.service" ];
wants = [ "example-key.service" ];
serviceConfig.User = "example";
serviceConfig.Type = "oneshot";
script = ''
stat /run/keys/example
'';
};
```
This creates a user called `example` and gives it permission to read deployment
keys. It also creates a systemd service called `example.service` and runs
[`stat(1)`](https://linux.die.net/man/1/stat) to show the permissions of the
service and the key file. It also runs as our `example` user. To avoid systemd
thinking our service failed, we're also going to mark it as a
[oneshot](https://www.digitalocean.com/community/tutorials/understanding-systemd-units-and-unit-files#the-service-section).
Altogether it could look something like
[this](https://gist.github.com/Xe/4a71d7741e508d9002be91b62248144a). Let's see
what `systemctl` has to report:
```console
$ nixops ssh -d blog-example pa -- systemctl status example
● example.service
Loaded: loaded (/nix/store/j4a8f6mnaw3v4sz7dqlnz95psh72xglw-unit-example.service/example.service; enabled; vendor preset: enabled)
Active: inactive (dead) since Wed 2021-01-20 20:53:54 UTC; 37s ago
Process: 2230 ExecStart=/nix/store/1yg89z4dsdp1axacqk07iq5jqv58q169-unit-script-example-start/bin/example-start (code=exited, status=0/SUCCESS)
Main PID: 2230 (code=exited, status=0/SUCCESS)
IP: 0B in, 0B out
CPU: 3ms
Jan 20 20:53:54 pa example-start[2235]: File: /run/keys/example
Jan 20 20:53:54 pa example-start[2235]: Size: 31 Blocks: 8 IO Block: 4096 regular file
Jan 20 20:53:54 pa example-start[2235]: Device: 18h/24d Inode: 37428 Links: 1
Jan 20 20:53:54 pa example-start[2235]: Access: (0400/-r--------) Uid: ( 998/ example) Gid: ( 96/ keys)
Jan 20 20:53:54 pa example-start[2235]: Access: 2021-01-20 20:53:54.010554201 +0000
Jan 20 20:53:54 pa example-start[2235]: Modify: 2021-01-20 20:53:54.010554201 +0000
Jan 20 20:53:54 pa example-start[2235]: Change: 2021-01-20 20:53:54.398103181 +0000
Jan 20 20:53:54 pa example-start[2235]: Birth: -
Jan 20 20:53:54 pa systemd[1]: example.service: Succeeded.
Jan 20 20:53:54 pa systemd[1]: Finished example.service.
```
So what happens when we reboot? I'll force a reboot in my hypervisor and we'll
find out:
```console
$ nixops ssh -d blog-example pa -- systemctl status example
● example.service
Loaded: loaded (/nix/store/j4a8f6mnaw3v4sz7dqlnz95psh72xglw-unit-example.service/example.service; enabled; vendor preset: enabled)
Active: inactive (dead)
```
The service is inactive. Let's see what the status of `example-key.service` is:
```console
$ nixops ssh -d blog-example pa -- systemctl status example-key
● example-key.service
Loaded: loaded (/nix/store/ikqn64cjq8pspkf3ma1jmx8qzpyrckpb-unit-example-key.service/example-key.service; linked; vendor preset: enabled)
Active: activating (start-pre) since Wed 2021-01-20 20:56:05 UTC; 3min 1s ago
Cntrl PID: 610 (example-key-pre)
IP: 0B in, 0B out
IO: 116.0K read, 0B written
Tasks: 4 (limit: 2374)
Memory: 1.6M
CPU: 3ms
CGroup: /system.slice/example-key.service
├─610 /nix/store/kl6lr3czkbnr6m5crcy8ffwfzbj8a22i-bash-4.4-p23/bin/bash -e /nix/store/awx1zrics3cal8kd9c5d05xzp5ikazlk-unit-script-example-key-pre-start/bin/example-key-pre-start
├─619 /nix/store/kl6lr3czkbnr6m5crcy8ffwfzbj8a22i-bash-4.4-p23/bin/bash -e /nix/store/awx1zrics3cal8kd9c5d05xzp5ikazlk-unit-script-example-key-pre-start/bin/example-key-pre-start
├─620 /nix/store/kl6lr3czkbnr6m5crcy8ffwfzbj8a22i-bash-4.4-p23/bin/bash -e /nix/store/awx1zrics3cal8kd9c5d05xzp5ikazlk-unit-script-example-key-pre-start/bin/example-key-pre-start
└─621 inotifywait -qm --format %f -e create,move /run/keys
Jan 20 20:56:05 pa systemd[1]: Starting example-key.service...
```
The service is blocked waiting for the keys to exist. We have to populate the
keys with `nixops send-keys`:
```console
$ nixops send-keys -d blog-example
pa> uploading key example...
```
Now when we check on `example.service`, we get the following:
```console
$ nixops ssh -d blog-example pa -- systemctl status example
● example.service
Loaded: loaded (/nix/store/j4a8f6mnaw3v4sz7dqlnz95psh72xglw-unit-example.service/example.service; enabled; vendor preset: enabled)
Active: inactive (dead) since Wed 2021-01-20 21:00:24 UTC; 32s ago
Process: 954 ExecStart=/nix/store/1yg89z4dsdp1axacqk07iq5jqv58q169-unit-script-example-start/bin/example-start (code=exited, status=0/SUCCESS)
Main PID: 954 (code=exited, status=0/SUCCESS)
IP: 0B in, 0B out
CPU: 3ms
Jan 20 21:00:24 pa example-start[957]: File: /run/keys/example
Jan 20 21:00:24 pa example-start[957]: Size: 31 Blocks: 8 IO Block: 4096 regular file
Jan 20 21:00:24 pa example-start[957]: Device: 18h/24d Inode: 27774 Links: 1
Jan 20 21:00:24 pa example-start[957]: Access: (0400/-r--------) Uid: ( 998/ example) Gid: ( 96/ keys)
Jan 20 21:00:24 pa example-start[957]: Access: 2021-01-20 21:00:24.588494730 +0000
Jan 20 21:00:24 pa example-start[957]: Modify: 2021-01-20 21:00:24.588494730 +0000
Jan 20 21:00:24 pa example-start[957]: Change: 2021-01-20 21:00:24.606495751 +0000
Jan 20 21:00:24 pa example-start[957]: Birth: -
Jan 20 21:00:24 pa systemd[1]: example.service: Succeeded.
Jan 20 21:00:24 pa systemd[1]: Finished example.service.
```
This means that NixOps secrets require _manual human intervention_ in order to
repopulate them on server boot. If your server went offline overnight due to an
unexpected issue, your services using those keys could be stuck offline until
morning. This is undesirable for a number of reasons. This plus the requirement
for the `keys` group (which at time of writing was undocumented) to be added to
service user accounts means that while they do work, they are not very
ergonomic.
[You can read secrets from files using something like
`deployment.keys.example.text = "${builtins.readFile ./secrets/example.env}"`,
but it is kind of a pain to have to do that. It would be better to just
reference the secrets by filesystem paths in the first
place.](conversation://Mara/hacker)
On the other hand [Morph](https://github.com/DBCDK/morph) gets this a bit
better. It is sadly even less documented than NixOps is, but it offers a similar
experience via [deployment
secrets](https://github.com/DBCDK/morph/blob/master/examples/secrets.nix). The
main differences that Morph brings to the table are taking paths to secrets and
allowing you to run an arbitrary command on the secret being uploaded. Secrets
are also able to be put anywhere on the disk, meaning that when a host reboots it
will come back up with the most recent secrets uploaded to it.
However, like NixOps, Morph secrets don't have the ability to be rolled back.
This means that if you mess up a secret value you better hope you have the old
information somewhere. This violates what you'd expect from a NixOS machine.
So given these examples, I thought it would be interesting to explore what the
middle path could look like. I chose to use
[age](https://github.com/FiloSottile/age) for encrypting secrets in the Nix
store as well as using SSH host keys to ensure that every secret is decryptable
at runtime by _that machine only_. If you get your hands on the secret
cyphertext, it should be unusable to you.
One of the harder things here will be keeping a list of all of the server host
keys. Recently I added a
[hosts.toml](https://github.com/Xe/nixos-configs/blob/master/ops/metadata/hosts.toml)
file to my config repo for autoconfiguring my WireGuard overlay network. It was
easy enough to add all the SSH host keys for each machine using a command like
this to get them:
[We will cover how this WireGuard overlay works in a future post.](conversation://Mara/hacker)
```console
$ nixops ssh-for-each -d hexagone -- cat /etc/ssh/ssh_host_ed25519_key.pub
firgu....> ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB8+mCR+MEsv0XYi7ohvdKLbDecBtb3uKGQOPfIhdj3C root@nixos
chrysalis> ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGDA5iXvkKyvAiMEd/5IruwKwoymC8WxH4tLcLWOSYJ1 root@chrysalis
lufta....> ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMADhGV0hKt3ZY+uBjgOXX08txBS6MmHZcSL61KAd3df root@lufta
keanu....> ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGDZUmuhfjEIROo2hog2c8J53taRuPJLNOtdaT8Nt69W root@nixos
```
age lets you use SSH keys for decryption, so I added these keys to my
`hosts.toml` and ended up with something like
[this](https://github.com/Xe/nixos-configs/commit/14726e982001e794cd72afa1ece209eed58d3f38#diff-61d1d8dddd71be624c0d718be22072c950ec31c72fded8a25094ea53d94c8185).
Now we can encrypt secrets on the host machine and safely put them in the Nix
store because they will be readable to each target machine with a command like
this:
```shell
age -d -i /etc/ssh/ssh_host_ed25519_key -o $dest $src
```
From here it's easy to make a function that we can use for generating new
encrypted secrets in the Nix store. First we need to import the host metadata
from the toml file:
```nix
let
cfg = config.within.secrets;
metadata = lib.importTOML ../../ops/metadata/hosts.toml;
mkSecretOnDisk = name:
{ source, ... }:
pkgs.stdenv.mkDerivation {
name = "${name}-secret";
phases = "installPhase";
buildInputs = [ pkgs.age ];
installPhase =
let key = metadata.hosts."${config.networking.hostName}".ssh_pubkey;
in ''
age -a -r "${key}" -o $out ${source}
'';
};
```
And then we can generate systemd oneshot jobs with something like this:
```nix
mkService = name:
{ source, dest, owner, group, permissions, ... }: {
description = "decrypt secret for ${name}";
wantedBy = [ "multi-user.target" ];
serviceConfig.Type = "oneshot";
script = with pkgs; ''
rm -rf ${dest}
${age}/bin/age -d -i /etc/ssh/ssh_host_ed25519_key -o ${dest} ${
mkSecretOnDisk name { inherit source; }
}
chown ${owner}:${group} ${dest}
chmod ${permissions} ${dest}
'';
};
```
And from there we just need some [boring
boilerplate](https://github.com/Xe/nixos-configs/blob/master/common/crypto/default.nix#L8-L38)
to define a secret type. Then we declare the secret type and its invocation:
```nix
in {
options.within.secrets = mkOption {
type = types.attrsOf secret;
description = "secret configuration";
default = { };
};
config.systemd.services = let
units = mapAttrs' (name: info: {
name = "${name}-key";
value = (mkService name info);
}) cfg;
in units;
}
```
And we have ourself a NixOS module that allows us to:
* Trivially declare new secrets
* Make secrets in the Nix store useless without the key
* Make every secret be transparently decrypted on startup
* Avoid the use of GPG
* Roll back secrets like any other configuration change
Declaring new secrets works like this (as stolen from [the service definition
for the website you are reading right now](https://github.com/Xe/nixos-configs/blob/master/common/services/xesite.nix#L35-L41)):
```nix
within.secrets.example = {
source = ./secrets/example.env;
dest = "/var/lib/example/.env";
owner = "example";
group = "nogroup";
permissions = "0400";
};
```
Barring some kind of cryptographic attack against age, this should allow the
secrets to be stored securely. I am working on a way to make this more generic.
This overall approach was inspired by [agenix](https://github.com/ryantm/agenix)
but made more specific for my needs. I hope this approach will make it easy for
me to manage these secrets in the future.

View File

@ -1,12 +0,0 @@
---
title: "Tailscale on NixOS: A New Minecraft Server in Ten Minutes"
date: 2021-01-19
tags:
- link
redirect_to: https://tailscale.com/blog/nixos-minecraft/
---
# Tailscale on NixOS: A New Minecraft Server in Ten Minutes
Check out this post [on the Tailscale
blog](https://tailscale.com/blog/nixos-minecraft/)!

View File

@ -1,20 +0,0 @@
[package]
name = "cfcache"
version = "0.1.0"
authors = ["Christine Dodrill <me@christine.website>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
reqwest = { version = "0.11", features = ["json"] }
serde_json = "1"
serde = { version = "1", features = ["derive"] }
thiserror = "1"
tracing = "0.1"
tracing-futures = "0.2"
[dev-dependencies]
eyre = "0.6.5"
kankyo = "0.3"
tokio = { version = "1", features = ["full"] }

View File

@ -1,15 +0,0 @@
use eyre::Result;
#[tokio::main]
async fn main() -> Result<()> {
kankyo::init()?;
let key = std::env::var("CF_TOKEN")?;
let zone_id = std::env::var("CF_ZONE_ID")?;
let cli = cfcache::Client::new(key, zone_id)?;
cli.purge(vec!["https://christine.website/.within/health".to_string()])
.await?;
Ok(())
}

View File

@ -1,64 +0,0 @@
use reqwest::header;
use tracing::instrument;
pub type Result<T = ()> = std::result::Result<T, Error>;
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("json error: {0}")]
Json(#[from] serde_json::Error),
#[error("request error: {0}")]
Request(#[from] reqwest::Error),
#[error("invalid header value: {0}")]
InvalidHeaderValue(#[from] reqwest::header::InvalidHeaderValue),
}
pub struct Client {
zone_id: String,
cli: reqwest::Client,
}
static USER_AGENT: &str = concat!(
"xesite ",
env!("CARGO_PKG_NAME"),
"/",
env!("CARGO_PKG_VERSION")
);
impl Client {
pub fn new(api_key: String, zone_id: String) -> Result<Self> {
let mut headers = header::HeaderMap::new();
headers.insert(
header::AUTHORIZATION,
header::HeaderValue::from_str(&format!("Bearer {}", api_key))?,
);
let cli = reqwest::Client::builder()
.user_agent(USER_AGENT)
.default_headers(headers)
.build()?;
Ok(Self { zone_id, cli })
}
#[instrument(skip(self), err)]
pub async fn purge(&self, urls: Vec<String>) -> Result {
#[derive(serde::Serialize)]
struct Files {
files: Vec<String>,
}
self.cli
.post(&format!(
"https://api.cloudflare.com/client/v4/zones/{}/purge_cache",
self.zone_id
))
.json(&Files { files: urls })
.send()
.await?
.error_for_status()?;
Ok(())
}
}

View File

@ -8,8 +8,8 @@ build = "src/build.rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
mime = "0.3"
warp = "0.3"
mime = "0.3.0"
warp = "0.2"
[build-dependencies]
ructe = { version = "0.13", features = ["warp02"] }
ructe = { version = "0.12", features = ["warp02"] }

View File

@ -9,7 +9,7 @@ edition = "2018"
[dependencies]
chrono = { version = "0.4", features = ["serde"] }
color-eyre = "0.5"
reqwest = { version = "0.11", features = ["json"] }
reqwest = { version = "0.10", features = ["json"] }
serde_json = "1.0"
serde = { version = "1", features = ["derive"] }
thiserror = "1"
@ -17,6 +17,6 @@ tracing = "0.1"
tracing-futures = "0.2"
[dev-dependencies]
tokio = { version = "1", features = ["macros"] }
tokio = { version = "0.2", features = ["macros"] }
envy = "0.4"
pretty_env_logger = "0"

View File

@ -34,7 +34,7 @@ impl Client {
})
}
#[instrument(skip(self), err)]
#[instrument(skip(self))]
pub async fn mentioners(&self, url: String) -> Result<Vec<WebMention>> {
Ok(self
.cli
@ -46,16 +46,6 @@ impl Client {
.json()
.await?)
}
#[instrument(skip(self), err)]
pub async fn refresh(&self) -> Result<()> {
self.cli
.post("https://mi.within.website/api/blog/refresh")
.send()
.await?
.error_for_status()?;
Ok(())
}
}
#[derive(Debug, Deserialize, Eq, PartialEq, Clone)]

View File

@ -8,7 +8,7 @@ edition = "2018"
[dependencies]
chrono = { version = "0.4", features = ["serde"] }
reqwest = { version = "0.11", features = ["json"] }
reqwest = { version = "0.10", features = ["json"] }
serde_json = "1.0"
serde = { version = "1", features = ["derive"] }
thiserror = "1"
@ -16,6 +16,6 @@ tracing = "0.1"
tracing-futures = "0.2"
[dev-dependencies]
tokio = { version = "1", features = ["macros"] }
tokio = { version = "0.2", features = ["macros"] }
envy = "0.4"
pretty_env_logger = "0"

View File

@ -244,34 +244,4 @@ in [ Person::{
, gitLink = "https://github.com/nasirhm"
, twitter = "https://twitter.com/_nasirhm_"
}
, Person::{
, name = "Eliot Partridge"
, tags =
[ "python"
, "linux"
, "typescript"
, "javascript"
, "docker"
, "c#"
, "dotnet"
, "php"
]
, gitLink = "https://github.com/BytewaveMLP"
}
, Person::{
, name = "İlteriş Eroğlu"
, tags =
[ "linux"
, "javascript"
, "node.js"
, "bash"
, "nfc"
, "python"
, "devops"
, "networking"
, "bgp"
]
, gitLink = "https://github.com/linuxgemini"
, twitter = "https://twitter.com/linuxgemini"
}
]

View File

@ -5,7 +5,6 @@ use std::{fs, path::PathBuf};
use tracing::{error, instrument};
pub mod markdown;
pub mod poke;
#[derive(Clone, Deserialize)]
pub struct Config {

View File

@ -1,86 +0,0 @@
use color_eyre::eyre::Result;
use std::{env, time::Duration};
use tokio::time::sleep as delay_for;
#[instrument(err)]
pub async fn the_cloud() -> Result<()> {
info!("waiting for things to settle");
delay_for(Duration::from_secs(10)).await;
info!("purging cloudflare cache");
cloudflare().await?;
info!("waiting for the cloudflare cache to purge globally");
delay_for(Duration::from_secs(45)).await;
info!("poking mi");
mi().await?;
info!("poking bing");
bing().await?;
info!("poking google");
google().await?;
Ok(())
}
#[instrument(err)]
async fn bing() -> Result<()> {
let cli = reqwest::Client::new();
cli.get("https://www.bing.com/ping")
.query(&[("sitemap", "https://christine.website/sitemap.xml")])
.header("User-Agent", crate::APPLICATION_NAME)
.send()
.await?
.error_for_status()?;
Ok(())
}
#[instrument(err)]
async fn google() -> Result<()> {
let cli = reqwest::Client::new();
cli.get("https://www.google.com/ping")
.query(&[("sitemap", "https://christine.website/sitemap.xml")])
.header("User-Agent", crate::APPLICATION_NAME)
.send()
.await?
.error_for_status()?;
Ok(())
}
#[instrument(err)]
async fn cloudflare() -> Result<()> {
let cli = cfcache::Client::new(env::var("CF_TOKEN")?, env::var("CF_ZONE_ID")?)?;
cli.purge(
vec![
"https://christine.website/sitemap.xml",
"https://christine.website",
"https://christine.website/blog",
"https://christine.website/blog.atom",
"https://christine.website/blog.json",
"https://christine.website/blog.rss",
"https://christine.website/gallery",
"https://christine.website/talks",
"https://christine.website/resume",
"https://christine.website/signalboost",
"https://christine.website/feeds",
]
.into_iter()
.map(|i| i.to_string())
.collect(),
)
.await?;
Ok(())
}
#[instrument(err)]
async fn mi() -> Result<()> {
let cli = mi::Client::new(env::var("MI_TOKEN")?, crate::APPLICATION_NAME.to_string())?;
cli.refresh().await?;
Ok(())
}

View File

@ -5,11 +5,11 @@ use crate::{
use lazy_static::lazy_static;
use prometheus::{opts, register_int_counter_vec, IntCounterVec};
use std::{convert::Infallible, fmt, sync::Arc};
use tracing::instrument;
use warp::{
http::{Response, StatusCode},
Rejection, Reply,
};
use tracing::instrument;
lazy_static! {
static ref HIT_COUNTER: IntCounterVec =
@ -86,6 +86,12 @@ impl fmt::Display for PostNotFound {
impl warp::reject::Reject for PostNotFound {}
impl From<PostNotFound> for warp::reject::Rejection {
fn from(error: PostNotFound) -> Self {
warp::reject::custom(error)
}
}
#[derive(Debug, thiserror::Error)]
struct SeriesNotFound(String);
@ -97,6 +103,12 @@ impl fmt::Display for SeriesNotFound {
impl warp::reject::Reject for SeriesNotFound {}
impl From<SeriesNotFound> for warp::reject::Rejection {
fn from(error: SeriesNotFound) -> Self {
warp::reject::custom(error)
}
}
lazy_static! {
static ref REJECTION_COUNTER: IntCounterVec = register_int_counter_vec!(
opts!("rejections", "Number of rejections by kind"),

View File

@ -39,6 +39,21 @@ async fn main() -> Result<()> {
.await?,
);
match sdnotify::SdNotify::from_env() {
Ok(ref mut n) => {
n.notify_ready().map_err(|why| {
error!("can't signal readiness to systemd: {}", why);
why
})?;
n.set_status(format!("hosting {} posts", state.clone().everything.len()))
.map_err(|why| {
error!("can't signal status to systemd: {}", why);
why
})?;
}
Err(why) => error!("not running under systemd with Type=notify: {}", why),
}
let healthcheck = warp::get().and(warp::path(".within").and(warp::path("health")).map(|| "OK"));
let base = warp::path!("blog" / ..);
@ -207,28 +222,6 @@ async fn main() -> Result<()> {
.with(warp::log(APPLICATION_NAME))
.recover(handlers::rejection);
match sdnotify::SdNotify::from_env() {
Ok(ref mut n) => {
// shitty heuristic for detecting if we're running in prod
tokio::spawn(async {
if let Err(why) = app::poke::the_cloud().await {
error!("Unable to poke the cloud: {}", why);
}
});
n.notify_ready().map_err(|why| {
error!("can't signal readiness to systemd: {}", why);
why
})?;
n.set_status(format!("hosting {} posts", state.clone().everything.len()))
.map_err(|why| {
error!("can't signal status to systemd: {}", why);
why
})?;
}
Err(why) => error!("not running under systemd with Type=notify: {}", why),
}
warp::serve(site)
.run((
[0, 0, 0, 0],

View File

@ -12,7 +12,6 @@ pub struct Data {
pub image: Option<String>,
pub thumb: Option<String>,
pub show: Option<bool>,
pub redirect_to: Option<String>,
}
enum State {

View File

@ -1,49 +1,74 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
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
=O4RS
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
JHDVbdWMkg5jUeusy24SURK5WA==
=zxPx
-----END PGP PUBLIC KEY BLOCK-----

View File

@ -19,7 +19,7 @@
<entry>
<id>https://christine.website/@post.link</id>
<title>@post.front_matter.title</title>
<published>@post.date.to_rfc3339()</published>
<updated>@post.date.to_rfc3339()</updated>
<link href="https://christine.website/@post.link" rel="alternate"/>
</entry>
}

View File

@ -9,7 +9,7 @@
<meta name="twitter:card" content="summary" />
<meta name="twitter:site" content="@@theprincessxena" />
<meta name="twitter:title" content="@post.front_matter.title" />
<meta name="twitter:description" content="Posted on @post.date.format("%Y-%m-%d")" />
<meta name="twitter:description" content="Posted on @post.date" />
<!-- Facebook -->
<meta property="og:type" content="website" />
@ -20,9 +20,7 @@
<meta name="description" content="@post.front_matter.title - Christine Dodrill's Blog" />
<meta name="author" content="Christine Dodrill">
@if post.front_matter.redirect_to.is_none() {
<link rel="canonical" href="https://christine.website/@post.link">
}
<link rel="canonical" href="https://christine.website/@post.link">
<script type="application/ld+json">
@{
@ -31,7 +29,7 @@
"headline": "@post.front_matter.title",
"image": "https://christine.website/static/img/avatar.png",
"url": "https://christine.website/@post.link",
"datePublished": "@post.date.format("%Y-%m-%d")",
"datePublished": "@post.date",
"mainEntityOfPage": @{
"@@type": "WebPage",
"@@id": "https://christine.website/@post.link"
@ -47,12 +45,6 @@
@}
</script>
@if let Some(to) = post.front_matter.redirect_to.clone() {
<script>
window.location.replace("@to");
</script>
}
@body
<hr />

View File

@ -10,7 +10,7 @@
<h3>Email</h3>
<p>me@@christine.website</p>
<p>My GPG fingerprint is <code>803C 935A E118 A224</code>. If you get an email that appears to be from me and the signature does not match that fingerprint, it is not from me. You may download a copy of my public key <a href="/static/gpg.pub">here</a>.</p>
<p>My GPG fingerprint is <code>799F 9134 8118 1111</code>. If you get an email that appears to be from me and the signature does not match that fingerprint, it is not from me. You may download a copy of my public key <a href="/static/gpg.pub">here</a>.</p>
<h3>Social Media</h3>
<ul>

View File

@ -1,3 +1,5 @@
@use crate::APPLICATION_NAME as APP;
@()
</div>
<hr />

View File

@ -9,7 +9,7 @@
<meta name="twitter:card" content="summary" />
<meta name="twitter:site" content="@@theprincessxena" />
<meta name="twitter:title" content="@post.front_matter.title" />
<meta name="twitter:description" content="Posted on @post.date.format("%Y-%m-%d")" />
<meta name="twitter:description" content="Posted on @post.date" />
<!-- Facebook -->
<meta property="og:type" content="website" />
@ -29,7 +29,7 @@
"headline": "@post.front_matter.title",
"image": "https://christine.website/static/img/avatar.png",
"url": "https://christine.website/@post.link",
"datePublished": "@post.date.format("%Y-%m-%d")",
"datePublished": "@post.date",
"mainEntityOfPage": @{
"@@type": "WebPage",
"@@id": "https://christine.website/@post.link"

View File

@ -3,62 +3,8 @@
@(title: Option<&str>, styles: Option<&str>)
<!DOCTYPE html>
<!--
MMMMMMMMMMMMMMMMMMNmmNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNmmmd.:mmMM
MMMMMMMMMMMMMMMMMNmmmNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNmmydmmmmmNMM
MMMMMMMMMMMMMMMMNm/:mNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNmms /mmmmmMMM
MMMMMMMMMMMMMMMNmm:-dmMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNmmmmdsdmmmmNMMM
MMMMMMMMMMMMMMMmmmmmmmNMMMMMMMMMMMNmmdhhddhhmNNMMMMMMMMMMMMMMMMNmy:hmmmmmmmmMMMM
MMMMMMMMMMMMMMNm++mmmmNMMMMMMmdyo/::.........-:/sdNMMMMMMMMMMNmmms`smmmmmmmNMMMM
MMMMMMMMMMMMMMmd.-dmmmmMMmhs/-....................-+dNMMMMMMNmmmmmmmmmmmmmmMMMMM
MMMMMMMMMMMMMNmmmmmmmmho:-...........................:sNMMNmmmmmmmmmmmmmmmNMNmdd
MMMMMMMMMMMMNmd+ydhs/-.................................-sNmmmmmmmmmmmmmmmdhyssss
MMMMMMMMMMMNNh+`........................................:dmmmmmmmmmmmmmmmyssssss
MMMMNNdhy+:-...........................................+dmmmmmmmmmmmmmmmdsssssss
MMMN+-...............................................-smmmmmmmmmmmmmmmmmysyyhdmN
MMMMNho:::-.--::-.......................----------..:hmmmmmmmmmmmmmmmmmmmNMMMMMM
MMMMMMMMNNNmmdo:......................--------------:ymmmmmmmmmmmmmmmmmmmMMMMMMM
MMMMMMMMMMds+........................-----------------+dmmmmmmmmmmmmmmmmmMMMMMMM
MMMMMMMMMh+........................--------------------:smmmmmmmmmmmmmmNMMMMMMMM
MMMMMMMNy/........................-------------::--------/hmmmmmmmmmmmNMMMMMMNmd
MMMMMMMd/........................--------------so----------odmmmmmmmmMMNmdhhysss
MMMMMMm/........................--------------+mh-----------:ymmmmdhhyysssssssss
MMMMMMo.......................---------------:dmmo------------+dmdysssssssssssss
yhdmNh:......................---------------:dmmmm+------------:sssssssssssyhhdm
sssssy.......................--------------:hmmmmmmos++:---------/sssyyhdmNMMMMM
ssssso......................--------------:hmmmNNNMNdddysso:------:yNNMMMMMMMMMM
ysssss.....................--------------/dmNyy/mMMd``d/------------sNMMMMMMMMMM
MNmdhy-...................--------------ommmh`o/NM/. smh+-----------:yNMMMMMMMMM
MMMMMN+...................------------/hmmss: `-//-.smmmmd+----------:hMMMMMMMMM
MMMMMMd:..................----------:smmmmhy+oosyysdmmy+:. `.--------/dMMMMMMMM
MMMMMMMh-................---------:smmmmmmmmmmmmmmmh/` `/s:-------sMMMMMMMM
MMMMMMMms:...............-------/ymmmmmmmmmmmmmmmd/ :dMMNy/-----+mMMMMMMM
MMMMMMmyss/..............------ommmmmmmmmmmmmmmmd. :yMMMMMMNs:---+mMMMMMMM
MMMMNdssssso-............----..odmmmmmmmmmmmmmmh:.` .sNMMMMMMMMMd/--sMMMMMMMM
MMMmysssssssh/................` -odmmmmmmmmmh+. `omMMMMMMMMMMMMh/+mMMMMMMMM
MNdyssssssymMNy-.............. `/sssso+:. `+mMMMMMMMMMMMMMMMdNMMMMMMMMM
NhssssssshNMMMMNo:............/.` `+dMMMMMMMMMMMMMMMMMMMMMMMMMMMM
ysssssssdMMMMMMMMm+-..........+ddy/.` -omMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
ssssssymMMMMMMMMMMMh/.........-oNMMNmy+--` `-+dNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
ssssydNMMMMMMMMMMMMMNy:........-hMMMMMMMNmdmMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
sssymMMMMMMMMMMMMMMMMMm+....-..:hMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
symNMMMMMMMMMMMMMMMMMMMNo.../-/dMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
dNMMMMMMMMMMMMMMMMMMMMMMh:.:hyNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
la budza pu cusku lu
<<.i ko do snura .i ko do kanro
.i ko do panpi .i ko do gleki>> li'u
-->
<html lang="en">
<head>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XLJX94YGBV"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag()@{dataLayer.push(arguments);@}
gtag('js', new Date());
gtag('config', 'G-XLJX94YGBV');
</script>
@if title.is_some() {
<title>@title.unwrap() - Christine Dodrill</title>
} else {

View File

@ -10,7 +10,7 @@
<p>
<ul>
@for post in posts {
<li>@post.date.format("%Y-%m-%d") - <a href="/@post.link">@post.front_matter.title</a></li>
<li>@post.date - <a href="/@post.link">@post.front_matter.title</a></li>
}
</ul>
</p>

View File

@ -9,7 +9,7 @@
<meta name="twitter:card" content="summary" />
<meta name="twitter:site" content="@@theprincessxena" />
<meta name="twitter:title" content="@post.front_matter.title" />
<meta name="twitter:description" content="Posted on @post.date.format("%Y-%m-%d")" />
<meta name="twitter:description" content="Posted on @post.date" />
<!-- Facebook -->
<meta property="og:type" content="website" />
@ -29,7 +29,7 @@
"headline": "@post.front_matter.title",
"image": "https://christine.website/static/img/avatar.png",
"url": "https://christine.website/@post.link",
"datePublished": "@post.date.format("%Y-%m-%d")",
"datePublished": "@post.date",
"mainEntityOfPage": @{
"@@type": "WebPage",
"@@id": "https://christine.website/@post.link"