Update
This commit is contained in:
parent
584c07ff23
commit
53e144d9a7
24 changed files with 1236 additions and 254 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
blog/
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2023 Dominic Grimm <dominic@dergrimm.net>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -6,3 +6,4 @@ Dockerfile
|
||||||
vendor/
|
vendor/
|
||||||
example/
|
example/
|
||||||
static/
|
static/
|
||||||
|
LICENSE
|
||||||
|
|
477
backend/Cargo.lock
generated
477
backend/Cargo.lock
generated
|
@ -30,7 +30,7 @@ dependencies = [
|
||||||
"actix-service",
|
"actix-service",
|
||||||
"actix-utils",
|
"actix-utils",
|
||||||
"ahash",
|
"ahash",
|
||||||
"base64",
|
"base64 0.21.0",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"brotli",
|
"brotli",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
@ -43,14 +43,14 @@ dependencies = [
|
||||||
"http",
|
"http",
|
||||||
"httparse",
|
"httparse",
|
||||||
"httpdate",
|
"httpdate",
|
||||||
"itoa",
|
"itoa 1.0.5",
|
||||||
"language-tags",
|
"language-tags",
|
||||||
"local-channel",
|
"local-channel",
|
||||||
"mime",
|
"mime",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"rand",
|
"rand",
|
||||||
"sha1",
|
"sha1 0.10.5",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
|
@ -155,7 +155,7 @@ dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http",
|
"http",
|
||||||
"itoa",
|
"itoa 1.0.5",
|
||||||
"language-tags",
|
"language-tags",
|
||||||
"log",
|
"log",
|
||||||
"mime",
|
"mime",
|
||||||
|
@ -322,6 +322,17 @@ dependencies = [
|
||||||
"toml",
|
"toml",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-trait"
|
||||||
|
version = "0.1.64"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
@ -339,13 +350,14 @@ dependencies = [
|
||||||
"askama_actix",
|
"askama_actix",
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
|
"comrak",
|
||||||
"diesel",
|
"diesel",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"envconfig",
|
"envconfig",
|
||||||
"fronma",
|
"fronma",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
"pulldown-cmark",
|
"r2d2_redis",
|
||||||
"scan_dir",
|
"scan_dir",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_yaml 0.9.17",
|
"serde_yaml 0.9.17",
|
||||||
|
@ -368,12 +380,42 @@ dependencies = [
|
||||||
"rustc-demangle",
|
"rustc-demangle",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64"
|
||||||
|
version = "0.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
version = "0.21.0"
|
version = "0.21.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
|
checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bincode"
|
||||||
|
version = "1.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bit-set"
|
||||||
|
version = "0.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
|
||||||
|
dependencies = [
|
||||||
|
"bit-vec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bit-vec"
|
||||||
|
version = "0.6.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.3.2"
|
version = "1.3.2"
|
||||||
|
@ -491,6 +533,7 @@ dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"strsim",
|
"strsim",
|
||||||
"termcolor",
|
"termcolor",
|
||||||
|
"terminal_size",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -525,6 +568,38 @@ dependencies = [
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "combine"
|
||||||
|
version = "4.6.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "comrak"
|
||||||
|
version = "0.16.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "784836d0812dade01579cc0cc9b1684847044e716fd7aa6bffbc172e42199500"
|
||||||
|
dependencies = [
|
||||||
|
"clap",
|
||||||
|
"emojis",
|
||||||
|
"entities",
|
||||||
|
"memchr",
|
||||||
|
"once_cell",
|
||||||
|
"pest",
|
||||||
|
"pest_derive",
|
||||||
|
"regex",
|
||||||
|
"shell-words",
|
||||||
|
"slug",
|
||||||
|
"syntect",
|
||||||
|
"typed-arena",
|
||||||
|
"unicode_categories",
|
||||||
|
"xdg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "convert_case"
|
name = "convert_case"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
@ -633,6 +708,12 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "deunicode"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "850878694b7933ca4c9569d30a34b55031b9b139ee1fc7b94a527c4ef960d690"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "diesel"
|
name = "diesel"
|
||||||
version = "2.0.3"
|
version = "2.0.3"
|
||||||
|
@ -643,7 +724,7 @@ dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"chrono",
|
"chrono",
|
||||||
"diesel_derives",
|
"diesel_derives",
|
||||||
"itoa",
|
"itoa 1.0.5",
|
||||||
"pq-sys",
|
"pq-sys",
|
||||||
"r2d2",
|
"r2d2",
|
||||||
]
|
]
|
||||||
|
@ -670,6 +751,41 @@ dependencies = [
|
||||||
"crypto-common",
|
"crypto-common",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dirs"
|
||||||
|
version = "4.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
|
||||||
|
dependencies = [
|
||||||
|
"dirs-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dirs-sys"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"redox_users",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dtoa"
|
||||||
|
version = "0.4.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "emojis"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "44fe60b864b6544ad211d4053ced474a9b9d2c8d66b77f01d6c6bcfed10c6bf0"
|
||||||
|
dependencies = [
|
||||||
|
"phf",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encoding_rs"
|
name = "encoding_rs"
|
||||||
version = "0.8.32"
|
version = "0.8.32"
|
||||||
|
@ -679,6 +795,12 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "entities"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5320ae4c3782150d900b79807611a59a99fc9a1d61d686faafc24b93fc8d7ca"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "env_logger"
|
name = "env_logger"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
|
@ -733,6 +855,16 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fancy-regex"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9d6b8560a05112eb52f04b00e5d3790c0dd75d9d980eb8a122fb23b92a623ccf"
|
||||||
|
dependencies = [
|
||||||
|
"bit-set",
|
||||||
|
"regex",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flate2"
|
name = "flate2"
|
||||||
version = "1.0.25"
|
version = "1.0.25"
|
||||||
|
@ -886,7 +1018,7 @@ checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"fnv",
|
"fnv",
|
||||||
"itoa",
|
"itoa 1.0.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -979,6 +1111,12 @@ dependencies = [
|
||||||
"windows-sys 0.45.0",
|
"windows-sys 0.45.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "0.4.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.5"
|
version = "1.0.5"
|
||||||
|
@ -1021,6 +1159,15 @@ version = "0.2.139"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
|
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "line-wrap"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9"
|
||||||
|
dependencies = [
|
||||||
|
"safemem",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "link-cplusplus"
|
name = "link-cplusplus"
|
||||||
version = "1.0.8"
|
version = "1.0.8"
|
||||||
|
@ -1182,6 +1329,28 @@ version = "1.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
|
checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "onig"
|
||||||
|
version = "6.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8c4b31c8722ad9171c6d77d3557db078cab2bd50afcc9d09c8b315c59df8ca4f"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"libc",
|
||||||
|
"once_cell",
|
||||||
|
"onig_sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "onig_sys"
|
||||||
|
version = "69.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7b829e3d7e9cc74c7e315ee8edb185bf4190da5acde74afd7fc59c35b1f086e7"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "os_str_bytes"
|
name = "os_str_bytes"
|
||||||
version = "6.4.1"
|
version = "6.4.1"
|
||||||
|
@ -1238,6 +1407,68 @@ version = "2.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
|
checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pest"
|
||||||
|
version = "2.5.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "028accff104c4e513bad663bbcd2ad7cfd5304144404c31ed0a77ac103d00660"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror",
|
||||||
|
"ucd-trie",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pest_derive"
|
||||||
|
version = "2.5.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2ac3922aac69a40733080f53c1ce7f91dcf57e1a5f6c52f421fadec7fbdc4b69"
|
||||||
|
dependencies = [
|
||||||
|
"pest",
|
||||||
|
"pest_generator",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pest_generator"
|
||||||
|
version = "2.5.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d06646e185566b5961b4058dd107e0a7f56e77c3f484549fb119867773c0f202"
|
||||||
|
dependencies = [
|
||||||
|
"pest",
|
||||||
|
"pest_meta",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pest_meta"
|
||||||
|
version = "2.5.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6f60b2ba541577e2a0c307c8f39d1439108120eb7903adeb6497fa880c59616"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"pest",
|
||||||
|
"sha2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "phf"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c"
|
||||||
|
dependencies = [
|
||||||
|
"phf_shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "phf_shared"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676"
|
||||||
|
dependencies = [
|
||||||
|
"siphasher",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-lite"
|
name = "pin-project-lite"
|
||||||
version = "0.2.9"
|
version = "0.2.9"
|
||||||
|
@ -1256,6 +1487,20 @@ version = "0.3.26"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
|
checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "plist"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5329b8f106a176ab0dce4aae5da86bfcb139bb74fb00882859e03745011f3635"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.13.1",
|
||||||
|
"indexmap",
|
||||||
|
"line-wrap",
|
||||||
|
"quick-xml",
|
||||||
|
"serde",
|
||||||
|
"time 0.3.17",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.17"
|
version = "0.2.17"
|
||||||
|
@ -1304,23 +1549,21 @@ dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pulldown-cmark"
|
|
||||||
version = "0.9.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2d9cc634bc78768157b5cbfe988ffcd1dcba95cd2b2f03a88316c08c6d00ed63"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags",
|
|
||||||
"memchr",
|
|
||||||
"unicase",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quick-error"
|
name = "quick-error"
|
||||||
version = "1.2.3"
|
version = "1.2.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quick-xml"
|
||||||
|
version = "0.26.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f50b1c63b38611e7d4d7f68b82d3ad0cc71a2ad2e7f61fc10f1328d917c93cd"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.23"
|
version = "1.0.23"
|
||||||
|
@ -1341,6 +1584,16 @@ dependencies = [
|
||||||
"scheduled-thread-pool",
|
"scheduled-thread-pool",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "r2d2_redis"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "182473b876b0b93e353682ec58e207dd1cb4a62278bbe0045fe52b86b74363bb"
|
||||||
|
dependencies = [
|
||||||
|
"r2d2",
|
||||||
|
"redis",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.8.5"
|
version = "0.8.5"
|
||||||
|
@ -1371,6 +1624,21 @@ dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redis"
|
||||||
|
version = "0.20.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d4f0ceb2ec0dd769483ecd283f6615aa83dcd0be556d5294c6e659caefe7cc54"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"combine",
|
||||||
|
"dtoa",
|
||||||
|
"itoa 0.4.8",
|
||||||
|
"percent-encoding",
|
||||||
|
"sha1 0.6.1",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.2.16"
|
version = "0.2.16"
|
||||||
|
@ -1380,6 +1648,17 @@ dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_users"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
"redox_syscall",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.7.1"
|
version = "1.7.1"
|
||||||
|
@ -1432,6 +1711,21 @@ version = "1.0.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
|
checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "safemem"
|
||||||
|
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]]
|
[[package]]
|
||||||
name = "scan_dir"
|
name = "scan_dir"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
|
@ -1494,7 +1788,7 @@ version = "1.0.92"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7434af0dc1cbd59268aa98b4c22c131c0584d2232f6fb166efb993e2832e896a"
|
checksum = "7434af0dc1cbd59268aa98b4c22c131c0584d2232f6fb166efb993e2832e896a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa 1.0.5",
|
||||||
"ryu",
|
"ryu",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
@ -1506,7 +1800,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
|
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"form_urlencoded",
|
"form_urlencoded",
|
||||||
"itoa",
|
"itoa 1.0.5",
|
||||||
"ryu",
|
"ryu",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
@ -1530,12 +1824,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8fb06d4b6cdaef0e0c51fa881acb721bed3c924cfaa71d9c94a3b771dfdf6567"
|
checksum = "8fb06d4b6cdaef0e0c51fa881acb721bed3c924cfaa71d9c94a3b771dfdf6567"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"itoa",
|
"itoa 1.0.5",
|
||||||
"ryu",
|
"ryu",
|
||||||
"serde",
|
"serde",
|
||||||
"unsafe-libyaml",
|
"unsafe-libyaml",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha1"
|
||||||
|
version = "0.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770"
|
||||||
|
dependencies = [
|
||||||
|
"sha1_smol",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha1"
|
name = "sha1"
|
||||||
version = "0.10.5"
|
version = "0.10.5"
|
||||||
|
@ -1547,6 +1850,29 @@ dependencies = [
|
||||||
"digest",
|
"digest",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha1_smol"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha2"
|
||||||
|
version = "0.10.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shell-words"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signal-hook-registry"
|
name = "signal-hook-registry"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
|
@ -1556,6 +1882,12 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "siphasher"
|
||||||
|
version = "0.3.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.4.7"
|
version = "0.4.7"
|
||||||
|
@ -1565,6 +1897,15 @@ dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "slug"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b3bc762e6a4b6c6fcaade73e77f9ebc6991b676f88bb2358bddb56560f073373"
|
||||||
|
dependencies = [
|
||||||
|
"deunicode",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smallvec"
|
name = "smallvec"
|
||||||
version = "1.10.0"
|
version = "1.10.0"
|
||||||
|
@ -1609,6 +1950,30 @@ dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syntect"
|
||||||
|
version = "5.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c6c454c27d9d7d9a84c7803aaa3c50cd088d2906fe3c6e42da3209aa623576a8"
|
||||||
|
dependencies = [
|
||||||
|
"bincode",
|
||||||
|
"bitflags",
|
||||||
|
"fancy-regex",
|
||||||
|
"flate2",
|
||||||
|
"fnv",
|
||||||
|
"lazy_static",
|
||||||
|
"once_cell",
|
||||||
|
"onig",
|
||||||
|
"plist",
|
||||||
|
"regex-syntax",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
|
"walkdir",
|
||||||
|
"yaml-rust",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "termcolor"
|
name = "termcolor"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
|
@ -1618,6 +1983,36 @@ dependencies = [
|
||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "terminal_size"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cb20089a8ba2b69debd491f8d2d023761cbf196e999218c591fa1e7e15a21907"
|
||||||
|
dependencies = [
|
||||||
|
"rustix",
|
||||||
|
"windows-sys 0.42.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.38"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.38"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tikv-jemalloc-sys"
|
name = "tikv-jemalloc-sys"
|
||||||
version = "0.5.3+5.3.0-patched"
|
version = "0.5.3+5.3.0-patched"
|
||||||
|
@ -1655,7 +2050,7 @@ version = "0.3.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376"
|
checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa 1.0.5",
|
||||||
"serde",
|
"serde",
|
||||||
"time-core",
|
"time-core",
|
||||||
"time-macros",
|
"time-macros",
|
||||||
|
@ -1753,12 +2148,24 @@ dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typed-arena"
|
||||||
|
version = "2.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.16.0"
|
version = "1.16.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
|
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ucd-trie"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicase"
|
name = "unicase"
|
||||||
version = "2.6.0"
|
version = "2.6.0"
|
||||||
|
@ -1795,6 +2202,12 @@ version = "0.1.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode_categories"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unsafe-libyaml"
|
name = "unsafe-libyaml"
|
||||||
version = "0.2.5"
|
version = "0.2.5"
|
||||||
|
@ -1824,6 +2237,17 @@ version = "0.9.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "walkdir"
|
||||||
|
version = "2.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
|
||||||
|
dependencies = [
|
||||||
|
"same-file",
|
||||||
|
"winapi",
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.10.0+wasi-snapshot-preview1"
|
version = "0.10.0+wasi-snapshot-preview1"
|
||||||
|
@ -2002,6 +2426,15 @@ version = "0.42.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
|
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xdg"
|
||||||
|
version = "2.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0c4583db5cbd4c4c0303df2d15af80f0539db703fa1c68802d4cbbd2dd0f88f6"
|
||||||
|
dependencies = [
|
||||||
|
"dirs",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yaml-rust"
|
name = "yaml-rust"
|
||||||
version = "0.4.5"
|
version = "0.4.5"
|
||||||
|
|
|
@ -3,6 +3,7 @@ name = "backend"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["Dominic Grimm <dominic@dergrimm.net>"]
|
authors = ["Dominic Grimm <dominic@dergrimm.net>"]
|
||||||
|
publish = false
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "backend"
|
name = "backend"
|
||||||
|
@ -11,11 +12,11 @@ build = "build.rs"
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "blogctl"
|
name = "blogctl"
|
||||||
|
|
||||||
# [profile.release]
|
[profile.release]
|
||||||
# codegen-units = 1
|
codegen-units = 1
|
||||||
# lto = "fat"
|
lto = "fat"
|
||||||
# strip = true
|
strip = true
|
||||||
# panic = "abort"
|
panic = "abort"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
@ -27,13 +28,14 @@ askama = "0.11.1"
|
||||||
askama_actix = "0.13.0"
|
askama_actix = "0.13.0"
|
||||||
chrono = { version = "0.4.23", features = ["serde"] }
|
chrono = { version = "0.4.23", features = ["serde"] }
|
||||||
clap = { version = "4.1.4", features = ["derive"] }
|
clap = { version = "4.1.4", features = ["derive"] }
|
||||||
|
comrak = { version = "0.16.0", features = ["shortcodes"] }
|
||||||
diesel = { version = "2.0.2", features = ["i-implement-a-third-party-backend-and-opt-into-breaking-changes", "postgres", "chrono", "r2d2"] }
|
diesel = { version = "2.0.2", features = ["i-implement-a-third-party-backend-and-opt-into-breaking-changes", "postgres", "chrono", "r2d2"] }
|
||||||
env_logger = "0.10.0"
|
env_logger = "0.10.0"
|
||||||
envconfig = "0.10.0"
|
envconfig = "0.10.0"
|
||||||
fronma = "0.1.1"
|
fronma = "0.1.1"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
pulldown-cmark = { version = "0.9.2", default-features = false, features = ["simd"] }
|
r2d2_redis = "0.14.0"
|
||||||
scan_dir = "0.3.3"
|
scan_dir = "0.3.3"
|
||||||
serde = { version = "1.0.152", features = ["derive"] }
|
serde = { version = "1.0.152", features = ["derive"] }
|
||||||
serde_yaml = "0.9.17"
|
serde_yaml = "0.9.17"
|
||||||
|
|
|
@ -10,6 +10,11 @@ WORKDIR /usr/src/static
|
||||||
COPY --from=css /usr/src/scss/dist ./css
|
COPY --from=css /usr/src/scss/dist ./css
|
||||||
RUN minify . -r -o .
|
RUN minify . -r -o .
|
||||||
|
|
||||||
|
FROM tdewolff/minify:latest as templates
|
||||||
|
WORKDIR /usr/src/templates
|
||||||
|
COPY ./templates .
|
||||||
|
RUN minify . -r -o .
|
||||||
|
|
||||||
FROM docker.io/lukemathwalker/cargo-chef:latest-rust-1.67.0 as chef
|
FROM docker.io/lukemathwalker/cargo-chef:latest-rust-1.67.0 as chef
|
||||||
|
|
||||||
FROM chef as diesel
|
FROM chef as diesel
|
||||||
|
@ -28,11 +33,16 @@ RUN cargo chef cook --recipe-path recipe.json
|
||||||
RUN rm -rf ./src
|
RUN rm -rf ./src
|
||||||
COPY ./build.rs .
|
COPY ./build.rs .
|
||||||
COPY --from=static /usr/src/static ./static
|
COPY --from=static /usr/src/static ./static
|
||||||
COPY ./templates ./templates
|
COPY --from=templates /usr/src/templates ./templates
|
||||||
COPY ./src ./src
|
COPY ./src ./src
|
||||||
RUN cargo build
|
RUN cargo build
|
||||||
|
|
||||||
FROM docker.io/debian:bullseye-slim as runner
|
FROM docker.io/debian:bullseye-slim as runner
|
||||||
|
LABEL maintainer="Dominic Grimm <dominic@dergrimm.net>" \
|
||||||
|
org.opencontainers.image.description="Personal blog backend" \
|
||||||
|
org.opencontainers.image.licenses="MIT" \
|
||||||
|
org.opencontainers.image.source="https://git.dergrimm.net/dergrimm/blog" \
|
||||||
|
org.opencontainers.image.url="https://git.dergrimm.net/dergrimm/blog"
|
||||||
RUN apt update
|
RUN apt update
|
||||||
RUN apt install -y libpq5
|
RUN apt install -y libpq5
|
||||||
RUN apt install -y ca-certificates
|
RUN apt install -y ca-certificates
|
||||||
|
@ -40,12 +50,10 @@ RUN apt-get clean
|
||||||
RUN apt-get autoremove -y
|
RUN apt-get autoremove -y
|
||||||
RUN rm -rf /var/lib/{apt,dpkg,cache,log}/
|
RUN rm -rf /var/lib/{apt,dpkg,cache,log}/
|
||||||
WORKDIR /usr/local/bin
|
WORKDIR /usr/local/bin
|
||||||
ENV RUST_BACKTRACE=full
|
|
||||||
COPY --from=diesel /usr/local/cargo/bin/diesel .
|
COPY --from=diesel /usr/local/cargo/bin/diesel .
|
||||||
WORKDIR /usr/src/backend
|
WORKDIR /usr/src/backend
|
||||||
COPY ./run.sh .
|
COPY ./run.sh .
|
||||||
RUN chmod +x ./run.sh
|
RUN chmod +x ./run.sh
|
||||||
COPY ./migrations ./migrations
|
COPY ./migrations ./migrations
|
||||||
COPY --from=builder /usr/src/backend/target/debug/backend /usr/src/backend/target/debug/blogctl ./bin/
|
COPY --from=builder /usr/src/backend/target/debug/backend /usr/src/backend/target/debug/blogctl ./bin/
|
||||||
EXPOSE 80
|
|
||||||
ENTRYPOINT [ "./run.sh" ]
|
ENTRYPOINT [ "./run.sh" ]
|
||||||
|
|
|
@ -1,42 +1,88 @@
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
width: 100vw;
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
* {
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
|
width: 100vw;
|
||||||
|
min-height: 100vh;
|
||||||
|
|
||||||
font-family: "JetBrains Mono", monospace;
|
font-family: "JetBrains Mono", monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// * {
|
||||||
|
// font-family: "JetBrains Mono", monospace;
|
||||||
|
// }
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
border: none;
|
||||||
|
border-top: medium solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.dashed {
|
||||||
|
list-style-type: none;
|
||||||
|
|
||||||
|
li:before {
|
||||||
|
content: "-";
|
||||||
|
position: absolute;
|
||||||
|
// margin-left: -20px;
|
||||||
|
margin-left: -1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table.border-rows {
|
||||||
|
border-collapse: collapse;
|
||||||
|
|
||||||
|
tr:first-child,
|
||||||
|
tr:not(:last-child) {
|
||||||
|
border-bottom: thin solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
th,
|
||||||
|
td {
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
th:not(:last-child),
|
||||||
|
td:not(:last-child) {
|
||||||
|
// border: thin solid black;
|
||||||
|
border-right: thin solid black;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#wrapper {
|
#wrapper {
|
||||||
width: 75%;
|
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
overflow: auto;
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
#navbar {
|
#navbar {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-bottom: 1%;
|
|
||||||
|
|
||||||
ul {
|
|
||||||
list-style-type: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
display: block;
|
|
||||||
text-align: center;
|
|
||||||
padding: 1em;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#navbar-brand {
|
#navbar-brand {
|
||||||
|
@ -48,18 +94,138 @@ body {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.navbar-link a,
|
||||||
|
#navbar-links ul li a {
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
#navbar-links {
|
#navbar-links {
|
||||||
width: 50%;
|
width: 50%;
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
|
list-style-type: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
float: right;
|
float: right;
|
||||||
|
|
||||||
|
li {
|
||||||
|
float: left;
|
||||||
|
|
||||||
|
&:not(:first-of-type) a {
|
||||||
|
padding-left: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#content {
|
#main-wrapper {
|
||||||
padding: 3%;
|
flex: 1;
|
||||||
|
padding: 1.5% 3%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#footer {
|
||||||
|
font-size: small;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#status-code {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#status-code-message {
|
||||||
|
font-size: small;
|
||||||
|
}
|
||||||
|
|
||||||
|
#post-index {
|
||||||
|
li:not(:last-child) {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.double-border {
|
.double-border {
|
||||||
border: 5px double black;
|
border: 5px double black;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.section-margin {
|
||||||
|
margin-top: 1%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-light-padding {
|
||||||
|
padding: 1%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb {
|
||||||
|
padding: 1% 0;
|
||||||
|
|
||||||
|
ul {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
li:not(:last-child)::after {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 0.5rem;
|
||||||
|
content: ">";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.wysiwyg {
|
||||||
|
hr {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
@extend ul.dashed;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
@extend table.border-rows;
|
||||||
|
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
p > code,
|
||||||
|
li > code,
|
||||||
|
dd > code,
|
||||||
|
td > code {
|
||||||
|
word-wrap: break-word;
|
||||||
|
box-decoration-break: clone;
|
||||||
|
padding: 0.1rem 0.3rem 0.2rem;
|
||||||
|
border: thin solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
padding: 0 1rem;
|
||||||
|
border: thin solid black;
|
||||||
|
overflow-x: scroll;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 600px) {
|
||||||
|
#wrapper {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (min-width: 600px) {
|
||||||
|
#wrapper {
|
||||||
|
width: 75%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (min-width: 768px) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (min-width: 992px) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (min-width: 1200px) {
|
||||||
|
#wrapper {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use diesel::QueryDsl;
|
|
||||||
#[cfg(not(target_env = "msvc"))]
|
#[cfg(not(target_env = "msvc"))]
|
||||||
use tikv_jemallocator::Jemalloc;
|
use tikv_jemallocator::Jemalloc;
|
||||||
|
|
||||||
|
@ -6,104 +5,23 @@ use tikv_jemallocator::Jemalloc;
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
static GLOBAL: Jemalloc = Jemalloc;
|
static GLOBAL: Jemalloc = Jemalloc;
|
||||||
|
|
||||||
use actix_web::{get, http::StatusCode, middleware, web, App, HttpResponse, HttpServer};
|
use actix_web::{middleware, App, HttpServer};
|
||||||
use actix_web_static_files::ResourceFiles;
|
|
||||||
use askama_actix::{Template, TemplateToResponse};
|
|
||||||
use diesel::prelude::*;
|
|
||||||
|
|
||||||
use backend::*;
|
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/generated.rs"));
|
|
||||||
|
|
||||||
mod filters {
|
|
||||||
pub fn cmark<T: std::fmt::Display>(s: T) -> ::askama::Result<String> {
|
|
||||||
let s = s.to_string();
|
|
||||||
|
|
||||||
let options = pulldown_cmark::Options::empty();
|
|
||||||
let parser = pulldown_cmark::Parser::new_ext(&s, options);
|
|
||||||
|
|
||||||
let mut html_output = String::with_capacity(s.len() * 3 / 2);
|
|
||||||
pulldown_cmark::html::push_html(&mut html_output, parser);
|
|
||||||
|
|
||||||
Ok(html_output)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Template)]
|
|
||||||
#[template(path = "status_code.html")]
|
|
||||||
struct StatusCodeTemplate {
|
|
||||||
status_code: StatusCode,
|
|
||||||
message: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Template)]
|
|
||||||
#[template(path = "posts/{slug}.html")]
|
|
||||||
struct PostBySlugTemplate {
|
|
||||||
post: db::models::Post,
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn not_found() -> HttpResponse {
|
|
||||||
StatusCodeTemplate {
|
|
||||||
status_code: StatusCode::NOT_FOUND,
|
|
||||||
message: None,
|
|
||||||
}
|
|
||||||
.to_response()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/posts/{slug}")]
|
|
||||||
async fn post_by_slug(db_pool: web::Data<db::DbPool>, path: web::Path<String>) -> HttpResponse {
|
|
||||||
let slug = path.into_inner();
|
|
||||||
|
|
||||||
let conn = &mut match db_pool.get() {
|
|
||||||
Ok(x) => x,
|
|
||||||
Err(e) => return HttpResponse::InternalServerError().body(format!("{:?}", e)),
|
|
||||||
};
|
|
||||||
|
|
||||||
let post = match db::schema::posts::table
|
|
||||||
.filter(db::schema::posts::slug.eq(&slug))
|
|
||||||
.filter(db::schema::posts::active)
|
|
||||||
.first::<db::models::Post>(conn)
|
|
||||||
.optional()
|
|
||||||
{
|
|
||||||
Ok(x) => x,
|
|
||||||
Err(e) => {
|
|
||||||
dbg!(&e);
|
|
||||||
return HttpResponse::InternalServerError().body(format!("{:?}", e));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
match post {
|
|
||||||
Some(x) => PostBySlugTemplate { post: x }.to_response(),
|
|
||||||
None => {
|
|
||||||
let mut resp = StatusCodeTemplate {
|
|
||||||
status_code: StatusCode::NOT_FOUND,
|
|
||||||
message: None,
|
|
||||||
}
|
|
||||||
.to_response();
|
|
||||||
*resp.status_mut() = StatusCode::NOT_FOUND;
|
|
||||||
|
|
||||||
resp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[actix_web::main]
|
#[actix_web::main]
|
||||||
async fn main() -> std::io::Result<()> {
|
async fn main() -> std::io::Result<()> {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
log::info!("Listening to requests at http://0.0.0.0:80");
|
log::info!(
|
||||||
|
"Listening to requests at {}",
|
||||||
|
backend::config::CONFIG.bind_url
|
||||||
|
);
|
||||||
|
|
||||||
HttpServer::new(move || {
|
HttpServer::new(move || {
|
||||||
let generated = generate();
|
|
||||||
|
|
||||||
App::new()
|
App::new()
|
||||||
.wrap(middleware::Compress::default())
|
.wrap(middleware::Compress::default())
|
||||||
.wrap(middleware::Logger::default())
|
.wrap(middleware::Logger::default())
|
||||||
.app_data(web::Data::new(db::pool().unwrap()))
|
.configure(backend::web::init)
|
||||||
.service(post_by_slug)
|
|
||||||
.service(ResourceFiles::new("/static", generated))
|
|
||||||
.default_service(web::route().to(not_found))
|
|
||||||
})
|
})
|
||||||
.bind("0.0.0.0:80")
|
.bind(&backend::config::CONFIG.bind_url)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.run()
|
.run()
|
||||||
.await
|
.await
|
||||||
|
|
|
@ -9,6 +9,7 @@ use anyhow::{bail, Result};
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
|
use r2d2_redis::redis;
|
||||||
use scan_dir::ScanDir;
|
use scan_dir::ScanDir;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
@ -27,6 +28,9 @@ struct Cli {
|
||||||
enum Commands {
|
enum Commands {
|
||||||
#[clap(about = "Imports new posts")]
|
#[clap(about = "Imports new posts")]
|
||||||
Import,
|
Import,
|
||||||
|
|
||||||
|
#[clap(about = "Clears redis cache")]
|
||||||
|
Clear,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug)]
|
#[derive(Deserialize, Serialize, Debug)]
|
||||||
|
@ -50,88 +54,137 @@ struct Post {
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
match Cli::parse().commands {
|
match Cli::parse().commands {
|
||||||
Commands::Import => {
|
Commands::Import => {
|
||||||
let conn = &mut db::establish_connection()?;
|
let db_conn = &mut db::establish_connection()?;
|
||||||
|
let redis_conn = &mut cache::establish_connection()?;
|
||||||
|
|
||||||
for post in ScanDir::dirs().read("/blog", |iter| {
|
let posts = ScanDir::dirs()
|
||||||
iter.map(|(entry, _)| {
|
.read("/blog", |iter| {
|
||||||
let path = entry.path().join("post.md");
|
iter.map(|(entry, _)| {
|
||||||
let src = fs::read_to_string(&path)?;
|
let path = entry.path().join("post.md");
|
||||||
let frontmatter = match fronma::parser::parse::<PostFrontmatter>(&src) {
|
let src = fs::read_to_string(&path)?;
|
||||||
Ok(x) => x,
|
let frontmatter = match fronma::parser::parse::<PostFrontmatter>(&src) {
|
||||||
Err(x) => bail!("Error parsing frontmatter: {:?}", x),
|
Ok(x) => x,
|
||||||
};
|
Err(x) => bail!("Error parsing frontmatter: {:?}", x),
|
||||||
|
};
|
||||||
|
|
||||||
Ok(Post {
|
Ok(Post {
|
||||||
path,
|
path,
|
||||||
frontmatter: frontmatter.headers,
|
frontmatter: frontmatter.headers,
|
||||||
content: frontmatter.body.to_string(),
|
content: frontmatter.body.to_string(),
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect::<Result<Vec<_>>>()
|
|
||||||
})?? {
|
|
||||||
let content = post.content.trim();
|
|
||||||
|
|
||||||
if let Some(id) = post.frontmatter.id {
|
|
||||||
diesel::update(db::schema::posts::table)
|
|
||||||
.filter(db::schema::posts::id.eq(id))
|
|
||||||
.set(db::models::UpdatePost {
|
|
||||||
name: Some(&post.frontmatter.name),
|
|
||||||
slug: Some(&post.frontmatter.slug),
|
|
||||||
description: Some(&post.frontmatter.description),
|
|
||||||
content: Some(content),
|
|
||||||
published_at: Some(post.frontmatter.published_at),
|
|
||||||
edited_at: Some(post.frontmatter.edited_at),
|
|
||||||
active: Some(post.frontmatter.active),
|
|
||||||
})
|
})
|
||||||
.execute(conn)?;
|
})
|
||||||
} else {
|
.collect::<Result<Vec<_>>>()
|
||||||
let id = if let Some(id) = db::schema::posts::table
|
})??
|
||||||
.select(db::schema::posts::id)
|
.into_iter()
|
||||||
.filter(db::schema::posts::slug.eq(&post.frontmatter.slug))
|
.map(|post| -> Result<_> {
|
||||||
.first::<i32>(conn)
|
let trimmed = PostFrontmatter {
|
||||||
.optional()?
|
id: post.frontmatter.id,
|
||||||
{
|
name: post.frontmatter.name.trim().to_string(),
|
||||||
|
slug: post.frontmatter.slug.trim().to_string(),
|
||||||
|
description: post.frontmatter.description.trim().to_string(),
|
||||||
|
published_at: post.frontmatter.published_at,
|
||||||
|
edited_at: post.frontmatter.edited_at,
|
||||||
|
active: post.frontmatter.active,
|
||||||
|
};
|
||||||
|
let content = post.content.trim();
|
||||||
|
|
||||||
|
if let Some(id) = trimmed.id {
|
||||||
diesel::update(db::schema::posts::table)
|
diesel::update(db::schema::posts::table)
|
||||||
.filter(db::schema::posts::id.eq(id))
|
.filter(db::schema::posts::id.eq(id))
|
||||||
.set(db::models::UpdatePost {
|
.set(db::models::UpdatePost {
|
||||||
name: Some(&post.frontmatter.name),
|
name: Some(&trimmed.name),
|
||||||
slug: None,
|
slug: Some(&trimmed.slug),
|
||||||
description: Some(&post.frontmatter.description),
|
description: Some(&trimmed.description),
|
||||||
content: Some(content),
|
content: Some(content),
|
||||||
published_at: Some(post.frontmatter.published_at),
|
published_at: Some(trimmed.published_at),
|
||||||
edited_at: Some(post.frontmatter.edited_at),
|
edited_at: Some(trimmed.edited_at),
|
||||||
active: Some(post.frontmatter.active),
|
active: Some(trimmed.active),
|
||||||
})
|
})
|
||||||
.execute(conn)?;
|
.execute(db_conn)?;
|
||||||
|
|
||||||
id
|
Ok(id)
|
||||||
} else {
|
} else {
|
||||||
diesel::insert_into(db::schema::posts::table)
|
let id = if let Some(id) = db::schema::posts::table
|
||||||
.values(db::models::NewPost {
|
.select(db::schema::posts::id)
|
||||||
name: &post.frontmatter.name,
|
.filter(db::schema::posts::slug.eq(&trimmed.slug))
|
||||||
slug: &post.frontmatter.slug,
|
.first::<i32>(db_conn)
|
||||||
description: &post.frontmatter.description,
|
.optional()?
|
||||||
content: content,
|
{
|
||||||
published_at: post.frontmatter.published_at,
|
diesel::update(db::schema::posts::table)
|
||||||
edited_at: post.frontmatter.edited_at,
|
.filter(db::schema::posts::id.eq(id))
|
||||||
active: post.frontmatter.active,
|
.set(db::models::UpdatePost {
|
||||||
})
|
name: Some(&trimmed.name),
|
||||||
.returning(db::schema::posts::id)
|
slug: None,
|
||||||
.get_result::<i32>(conn)?
|
description: Some(&trimmed.description),
|
||||||
};
|
content: Some(content),
|
||||||
|
published_at: Some(trimmed.published_at),
|
||||||
|
edited_at: Some(trimmed.edited_at),
|
||||||
|
active: Some(trimmed.active),
|
||||||
|
})
|
||||||
|
.execute(db_conn)?;
|
||||||
|
|
||||||
fs::write(
|
id
|
||||||
post.path,
|
} else {
|
||||||
format!(
|
diesel::insert_into(db::schema::posts::table)
|
||||||
"---\n{}---\n{}",
|
.values(db::models::NewPost {
|
||||||
serde_yaml::to_string(&PostFrontmatter {
|
name: &trimmed.name,
|
||||||
id: Some(id),
|
slug: &trimmed.slug,
|
||||||
..post.frontmatter
|
description: &trimmed.description,
|
||||||
})?,
|
content: content,
|
||||||
post.content
|
published_at: trimmed.published_at,
|
||||||
),
|
edited_at: trimmed.edited_at,
|
||||||
)?;
|
active: trimmed.active,
|
||||||
}
|
})
|
||||||
|
.returning(db::schema::posts::id)
|
||||||
|
.get_result::<i32>(db_conn)?
|
||||||
|
};
|
||||||
|
|
||||||
|
fs::write(
|
||||||
|
post.path,
|
||||||
|
format!(
|
||||||
|
"---\n{}---\n\n{}\n",
|
||||||
|
serde_yaml::to_string(&PostFrontmatter {
|
||||||
|
id: Some(id),
|
||||||
|
..trimmed
|
||||||
|
})?,
|
||||||
|
content
|
||||||
|
),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(id)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>>>()?;
|
||||||
|
|
||||||
|
let ids = db::schema::posts::table
|
||||||
|
.select(db::schema::posts::id)
|
||||||
|
.load::<i32>(db_conn)?;
|
||||||
|
|
||||||
|
diesel::delete(
|
||||||
|
db::schema::posts::table
|
||||||
|
.filter(diesel::dsl::not(db::schema::posts::id.eq_any(posts))),
|
||||||
|
)
|
||||||
|
.execute(db_conn)?;
|
||||||
|
|
||||||
|
for id in ids {
|
||||||
|
redis::cmd("DEL")
|
||||||
|
.arg(cache::keys::post_content(id))
|
||||||
|
.query::<()>(redis_conn)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Commands::Clear => {
|
||||||
|
let db_conn = &mut db::establish_connection()?;
|
||||||
|
let redis_conn = &mut cache::establish_connection()?;
|
||||||
|
|
||||||
|
for id in db::schema::posts::table
|
||||||
|
.select(db::schema::posts::id)
|
||||||
|
.load::<i32>(db_conn)?
|
||||||
|
{
|
||||||
|
redis::cmd("DEL")
|
||||||
|
.arg(cache::keys::post_content(id))
|
||||||
|
.query::<()>(redis_conn)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
28
backend/src/cache.rs
Normal file
28
backend/src/cache.rs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use r2d2_redis::{r2d2, redis, RedisConnectionManager};
|
||||||
|
|
||||||
|
use crate::config;
|
||||||
|
|
||||||
|
pub type RedisPool = r2d2::Pool<RedisConnectionManager>;
|
||||||
|
pub type ConnectionPool = r2d2::PooledConnection<RedisConnectionManager>;
|
||||||
|
|
||||||
|
pub fn establish_connection() -> Result<redis::Connection> {
|
||||||
|
Ok(redis::Client::open(config::CONFIG.redis_url.as_str())?.get_connection()?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pool() -> Result<RedisPool> {
|
||||||
|
Ok(r2d2::Pool::builder().build(RedisConnectionManager::new(
|
||||||
|
config::CONFIG.redis_url.as_str(),
|
||||||
|
)?)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref POOL: RedisPool = pool().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod keys {
|
||||||
|
pub fn post_content(id: i32) -> String {
|
||||||
|
format!("post_content:{}", id)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,19 @@
|
||||||
|
|
||||||
use envconfig::Envconfig;
|
use envconfig::Envconfig;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
#[derive(Envconfig, Debug)]
|
#[derive(Envconfig, Debug)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
|
#[envconfig(from = "BACKEND_BIND_URL")]
|
||||||
|
pub bind_url: String,
|
||||||
|
|
||||||
#[envconfig(from = "BACKEND_DB_URL")]
|
#[envconfig(from = "BACKEND_DB_URL")]
|
||||||
pub db_url: String,
|
pub db_url: String,
|
||||||
|
|
||||||
|
#[envconfig(from = "BACKEND_REDIS_URL")]
|
||||||
|
pub redis_url: String,
|
||||||
|
|
||||||
|
#[envconfig(from = "BACKEND_CACHE_POST_CONTENT_TTL")]
|
||||||
|
pub cache_post_content_ttl: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
|
|
|
@ -1,2 +1,5 @@
|
||||||
|
pub mod cache;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod db;
|
pub mod db;
|
||||||
|
pub mod markdown;
|
||||||
|
pub mod web;
|
||||||
|
|
28
backend/src/markdown.rs
Normal file
28
backend/src/markdown.rs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
pub fn to_html(src: &str) -> String {
|
||||||
|
let adapter = comrak::plugins::syntect::SyntectAdapter::new("InspiredGitHub");
|
||||||
|
let options = comrak::ComrakOptions {
|
||||||
|
extension: comrak::ComrakExtensionOptions {
|
||||||
|
strikethrough: true,
|
||||||
|
tagfilter: true,
|
||||||
|
table: true,
|
||||||
|
autolink: true,
|
||||||
|
tasklist: true,
|
||||||
|
superscript: true,
|
||||||
|
header_ids: Some("__blog-content_".to_string()),
|
||||||
|
footnotes: true,
|
||||||
|
description_lists: true,
|
||||||
|
front_matter_delimiter: None,
|
||||||
|
shortcodes: true,
|
||||||
|
},
|
||||||
|
parse: comrak::ComrakParseOptions {
|
||||||
|
smart: true,
|
||||||
|
..comrak::ComrakParseOptions::default()
|
||||||
|
},
|
||||||
|
..comrak::ComrakOptions::default()
|
||||||
|
};
|
||||||
|
let mut plugins = comrak::ComrakPlugins::default();
|
||||||
|
|
||||||
|
plugins.render.codefence_syntax_highlighter = Some(&adapter);
|
||||||
|
|
||||||
|
comrak::markdown_to_html_with_plugins(src, &options, &plugins)
|
||||||
|
}
|
176
backend/src/web/mod.rs
Normal file
176
backend/src/web/mod.rs
Normal file
|
@ -0,0 +1,176 @@
|
||||||
|
use actix_web::{get, http, web, HttpResponse};
|
||||||
|
use actix_web_static_files::ResourceFiles;
|
||||||
|
use askama_actix::TemplateToResponse;
|
||||||
|
use diesel::prelude::*;
|
||||||
|
use r2d2_redis::redis;
|
||||||
|
use std::ops::DerefMut;
|
||||||
|
|
||||||
|
use crate::{cache, config, db, markdown};
|
||||||
|
|
||||||
|
pub mod templates;
|
||||||
|
|
||||||
|
pub mod static_dir {
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/generated.rs"));
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn not_found() -> HttpResponse {
|
||||||
|
let mut resp = templates::StatusCode {
|
||||||
|
status_code: http::StatusCode::NOT_FOUND,
|
||||||
|
message: Some("maybe try a correct url?".to_string()),
|
||||||
|
}
|
||||||
|
.to_response();
|
||||||
|
*resp.status_mut() = http::StatusCode::NOT_FOUND;
|
||||||
|
|
||||||
|
resp
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/posts")]
|
||||||
|
async fn posts(db_pool: web::Data<db::DbPool>) -> HttpResponse {
|
||||||
|
let db_conn = &mut match db_pool.get() {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(e) => return HttpResponse::InternalServerError().body(format!("{:?}", e)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let posts = match db::schema::posts::table
|
||||||
|
.filter(db::schema::posts::active)
|
||||||
|
.order(db::schema::posts::published_at.desc())
|
||||||
|
.load::<db::models::Post>(db_conn)
|
||||||
|
{
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(e) => {
|
||||||
|
return HttpResponse::InternalServerError().body(format!("{:?}", e));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
templates::Posts { posts }.to_response()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/")]
|
||||||
|
async fn index() -> HttpResponse {
|
||||||
|
templates::Index.to_response()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/about")]
|
||||||
|
async fn about() -> HttpResponse {
|
||||||
|
templates::About.to_response()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/posts/{slug}")]
|
||||||
|
async fn post_by_slug(
|
||||||
|
db_pool: web::Data<db::DbPool>,
|
||||||
|
redis_pool: web::Data<cache::RedisPool>,
|
||||||
|
path: web::Path<String>,
|
||||||
|
) -> HttpResponse {
|
||||||
|
let slug = path.into_inner();
|
||||||
|
|
||||||
|
let db_conn = &mut match db_pool.get() {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(e) => return HttpResponse::InternalServerError().body(format!("{:?}", e)),
|
||||||
|
};
|
||||||
|
let redis_conn = &mut match redis_pool.get() {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(e) => return HttpResponse::InternalServerError().body(format!("{:?}", e)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let post_stripped: Option<(i32, String)> = match db::schema::posts::table
|
||||||
|
.select((db::schema::posts::id, db::schema::posts::name))
|
||||||
|
.filter(db::schema::posts::slug.eq(&slug))
|
||||||
|
.filter(db::schema::posts::active)
|
||||||
|
.get_result::<(i32, String)>(db_conn)
|
||||||
|
.optional()
|
||||||
|
{
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(e) => {
|
||||||
|
return HttpResponse::InternalServerError().body(format!("{:?}", e));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match post_stripped {
|
||||||
|
Some(stripped) => {
|
||||||
|
let (stripped_id, stripped_name) = stripped;
|
||||||
|
|
||||||
|
let key = cache::keys::post_content(stripped_id);
|
||||||
|
|
||||||
|
match match redis::cmd("GET")
|
||||||
|
.arg(&key)
|
||||||
|
.query::<Option<String>>(redis_conn.deref_mut())
|
||||||
|
{
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(e) => return HttpResponse::InternalServerError().body(format!("{:?}", e)),
|
||||||
|
} {
|
||||||
|
Some(s) => templates::PostBySlug {
|
||||||
|
name: stripped_name,
|
||||||
|
slug,
|
||||||
|
content: s,
|
||||||
|
}
|
||||||
|
.to_response(),
|
||||||
|
None => {
|
||||||
|
let post = match db::schema::posts::table
|
||||||
|
.filter(db::schema::posts::id.eq(stripped_id))
|
||||||
|
.first::<db::models::Post>(db_conn)
|
||||||
|
{
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(e) => {
|
||||||
|
return HttpResponse::InternalServerError().body(format!("{:?}", e));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let html = markdown::to_html(&post.content);
|
||||||
|
|
||||||
|
match redis::cmd("SET")
|
||||||
|
.arg(&key)
|
||||||
|
.arg(&html)
|
||||||
|
.query::<Option<String>>(redis_conn.deref_mut())
|
||||||
|
{
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(e) => {
|
||||||
|
return HttpResponse::InternalServerError().body(format!("{:?}", e))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if let Err(e) = redis::cmd("EXPIRE")
|
||||||
|
.arg(key)
|
||||||
|
.arg(config::CONFIG.cache_post_content_ttl)
|
||||||
|
.query::<()>(redis_conn.deref_mut())
|
||||||
|
{
|
||||||
|
return HttpResponse::InternalServerError().body(format!("{:?}", e));
|
||||||
|
}
|
||||||
|
|
||||||
|
templates::PostBySlug {
|
||||||
|
name: post.name,
|
||||||
|
slug: post.slug,
|
||||||
|
content: html,
|
||||||
|
}
|
||||||
|
.to_response()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let mut resp = templates::StatusCode {
|
||||||
|
status_code: http::StatusCode::NOT_FOUND,
|
||||||
|
message: Some("this post does not exists... yet".to_string()),
|
||||||
|
}
|
||||||
|
.to_response();
|
||||||
|
*resp.status_mut() = http::StatusCode::NOT_FOUND;
|
||||||
|
|
||||||
|
resp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_routes(cfg: &mut web::ServiceConfig) {
|
||||||
|
let generated = static_dir::generate();
|
||||||
|
|
||||||
|
cfg.service(index)
|
||||||
|
.service(about)
|
||||||
|
.service(posts)
|
||||||
|
.service(ResourceFiles::new("/static", generated))
|
||||||
|
.service(post_by_slug)
|
||||||
|
.default_service(web::route().to(not_found));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(cfg: &mut web::ServiceConfig) {
|
||||||
|
cfg.app_data(web::Data::new(db::pool().unwrap()))
|
||||||
|
.app_data(web::Data::new(cache::pool().unwrap()));
|
||||||
|
|
||||||
|
setup_routes(cfg);
|
||||||
|
}
|
33
backend/src/web/templates.rs
Normal file
33
backend/src/web/templates.rs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
use actix_web::http;
|
||||||
|
use askama_actix::Template;
|
||||||
|
|
||||||
|
use crate::db;
|
||||||
|
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "status_code.html")]
|
||||||
|
pub struct StatusCode {
|
||||||
|
pub status_code: http::StatusCode,
|
||||||
|
pub message: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "web/index.html")]
|
||||||
|
pub struct Index;
|
||||||
|
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "web/about.html")]
|
||||||
|
pub struct About;
|
||||||
|
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "web/posts/index.html")]
|
||||||
|
pub struct Posts {
|
||||||
|
pub posts: Vec<db::models::Post>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "web/posts/{slug}.html")]
|
||||||
|
pub struct PostBySlug {
|
||||||
|
pub name: String,
|
||||||
|
pub slug: String,
|
||||||
|
pub content: String,
|
||||||
|
}
|
|
@ -7,7 +7,7 @@
|
||||||
<title>{% block title %}{% endblock %} | dergrimm's blog</title>
|
<title>{% block title %}{% endblock %} | dergrimm's blog</title>
|
||||||
|
|
||||||
<link rel="preconnect" href="https://fonts.bunny.net" />
|
<link rel="preconnect" href="https://fonts.bunny.net" />
|
||||||
<link href="https://fonts.bunny.net/css?family=jetbrains-mono:400" rel="stylesheet" />
|
<link href="https://fonts.bunny.net/css?family=jetbrains-mono:400,400i,700,700i" rel="stylesheet" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="/static/css/styles.css" />
|
<link rel="stylesheet" href="/static/css/styles.css" />
|
||||||
|
|
||||||
|
@ -15,19 +15,39 @@
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="wrapper">
|
<div id="wrapper">
|
||||||
<nav id="navbar" class="double-border">
|
<header class="double-border section-margin section-light-padding">
|
||||||
<div id="navbar-brand"><a href="/">dergrimm's blog</a></div>
|
<nav id="navbar">
|
||||||
<div id="navbar-links">
|
<div id="navbar-brand" class="navbar-link"><a href="/">dergrimm's blog</a></div>
|
||||||
<ul>
|
<div id="navbar-links">
|
||||||
<li><a href="/posts">posts</a></li>
|
<ul>
|
||||||
<li><a href="/about">about</a></li>
|
<li><a href="/posts">posts</a></li>
|
||||||
</ul>
|
<li><a href="/about">about</a></li>
|
||||||
</div>
|
</ul>
|
||||||
</nav>
|
</div>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
<div id="content" class="double-border">
|
<div id="main-wrapper" class="section-margin">
|
||||||
{% block content %}{% endblock %}
|
<nav class="breadcrumb">
|
||||||
|
<ul>
|
||||||
|
<li><a href="/">/</a></li>
|
||||||
|
{% block breadcrumb %}{% endblock %}
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<main>
|
||||||
|
{% block content %}{% endblock %}
|
||||||
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<footer id="footer" class="double-border section-margin section-light-padding">
|
||||||
|
(C) 2023 Dominic Grimm
|
||||||
|
<<a href='mailto:%64o%6Dinic@%64ergr%69mm%2Enet'>dominic@dergrimm.net</a>>
|
||||||
|
| <a href="https://git.dergrimm.net/dergrimm/blog">repo</a>
|
||||||
|
| <a href="/">dergrimm.net</a>
|
||||||
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block title %}{{ post.name }}{% endblock %}
|
|
||||||
|
|
||||||
{% block head %}{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="blog">
|
|
||||||
{{ post.content|cmark|safe }}
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
|
@ -1,14 +1,19 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block title %}{{ status_code }}{% endblock %}
|
{% block title %}{{ status_code|lower }}{% endblock %}
|
||||||
|
|
||||||
{% block head %}{% endblock %}
|
{% block head %}{% endblock %}
|
||||||
|
|
||||||
|
{% block breadcrumb %}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>{{ status_code }}!</h1>
|
<div id="status-code">
|
||||||
{% match message %}
|
<p><b>{{ status_code }}!</b></p>
|
||||||
{% when Some with (x) %}
|
{% match message %}
|
||||||
<p>{{ x }}</p>
|
{% when Some with (x) %}
|
||||||
{% when None %}
|
<p id="status-code-message"><i>{{ x }}</i></p>
|
||||||
{% endmatch %}
|
{% when None %}
|
||||||
|
{% endmatch %}
|
||||||
|
<p>:(</p>
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
17
backend/templates/web/about.html
Normal file
17
backend/templates/web/about.html
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}index{% endblock %}
|
||||||
|
|
||||||
|
{% block head %}{% endblock %}
|
||||||
|
|
||||||
|
{% block breadcrumb %}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<p><i>...fearlessly conquering the world of backend</i></p>
|
||||||
|
<h1>About</h1>
|
||||||
|
<p>Hey, I'm Dominic (aka. dergrimm) and am currently a student going to school in Germany.</p>
|
||||||
|
<p>
|
||||||
|
You can contact me at:
|
||||||
|
<a href='mailto:%64o%6Dinic@%64ergr%69mm%2Enet'>dominic@dergrimm.net</a>
|
||||||
|
</p>
|
||||||
|
{% endblock %}
|
14
backend/templates/web/index.html
Normal file
14
backend/templates/web/index.html
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}index{% endblock %}
|
||||||
|
|
||||||
|
{% block head %}{% endblock %}
|
||||||
|
|
||||||
|
{% block breadcrumb %}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1>This is my site</h1>
|
||||||
|
<p>Hey, I'm Dominic (aka. dergrimm) and this is my blog!</p>
|
||||||
|
<p>I mostly plan to talk about modern tech and server side stuff like Rust, Crystal, C, databases and APIs.</p>
|
||||||
|
<p>Keep posted by following my feed.</p>
|
||||||
|
{% endblock %}
|
24
backend/templates/web/posts/index.html
Normal file
24
backend/templates/web/posts/index.html
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}posts{% endblock %}
|
||||||
|
|
||||||
|
{% block head %}{% endblock %}
|
||||||
|
|
||||||
|
{% block breadcrumb %}
|
||||||
|
<li><a href="/posts">posts</a></li>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<ul id="post-index" class="dashed">
|
||||||
|
{% for post in posts %}
|
||||||
|
<li>
|
||||||
|
<span>
|
||||||
|
<a href="/posts/{{ post.slug }}">{{ post.name }}</a>
|
||||||
|
(<i>{{ post.published_at }}{% match post.edited_at %}{% when Some with (x) %} -> {{ x }}{% when None %}{% endmatch %}</i>)
|
||||||
|
</span>
|
||||||
|
<br />
|
||||||
|
<span>{{ post.description }}</span>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endblock %}
|
16
backend/templates/web/posts/{slug}.html
Normal file
16
backend/templates/web/posts/{slug}.html
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}{{ name }}{% endblock %}
|
||||||
|
|
||||||
|
{% block head %}{% endblock %}
|
||||||
|
|
||||||
|
{% block breadcrumb %}
|
||||||
|
<li><a href="/posts">posts</a></li>
|
||||||
|
<li><a href="/posts/{{ slug }}">{{ slug }}</a></li>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<article class="wysiwyg">
|
||||||
|
{{content|safe }}
|
||||||
|
</article>
|
||||||
|
{% endblock %}
|
|
@ -4,7 +4,7 @@ name: Hello world!
|
||||||
slug: hello_world
|
slug: hello_world
|
||||||
description: Hello world to the internet. Set up my first blog!
|
description: Hello world to the internet. Set up my first blog!
|
||||||
published_at: 2023-02-06
|
published_at: 2023-02-06
|
||||||
edited_at: null
|
edited_at: 2023-02-09
|
||||||
active: true
|
active: true
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ version: "3"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
db:
|
db:
|
||||||
image: docker.io/postgres:alpine
|
image: docker.io/postgres:15-alpine
|
||||||
restart: always
|
restart: always
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_USER: ${POSTGRES_USER}
|
POSTGRES_USER: ${POSTGRES_USER}
|
||||||
|
@ -11,27 +11,45 @@ services:
|
||||||
- db:/var/lib/postgresql/data
|
- db:/var/lib/postgresql/data
|
||||||
|
|
||||||
adminer:
|
adminer:
|
||||||
image: docker.io/adminer:standalone
|
image: docker.io/adminer:4-standalone
|
||||||
restart: always
|
restart: always
|
||||||
ports:
|
ports:
|
||||||
- 8080:8080
|
- 8080:8080
|
||||||
depends_on:
|
depends_on:
|
||||||
- db
|
- db
|
||||||
|
|
||||||
backend:
|
redis:
|
||||||
image: git.dergrimm.net/dergrimm/blog_backend:latest
|
image: docker.io/redis:7-alpine
|
||||||
|
restart: always
|
||||||
|
|
||||||
|
redis-commander:
|
||||||
|
image: rediscommander/redis-commander:latest
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
REDIS_HOSTS: local:redis:6379
|
||||||
|
ports:
|
||||||
|
- 8081:8081
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
|
||||||
|
blog:
|
||||||
|
image: git.dergrimm.net/dergrimm/blog:latest
|
||||||
build:
|
build:
|
||||||
context: ./backend
|
context: ./backend
|
||||||
restart: always
|
restart: always
|
||||||
command: worker
|
command: worker
|
||||||
environment:
|
environment:
|
||||||
|
BACKEND_BIND_URL: 0.0.0.0:80
|
||||||
BACKEND_DB_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_USER}
|
BACKEND_DB_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_USER}
|
||||||
|
BACKEND_REDIS_URL: redis://redis
|
||||||
|
BACKEND_CACHE_POST_CONTENT_TTL: 3600
|
||||||
volumes:
|
volumes:
|
||||||
- ./blog:/blog
|
- ./blog:/blog
|
||||||
ports:
|
ports:
|
||||||
- 80:80
|
- 80:80
|
||||||
depends_on:
|
depends_on:
|
||||||
- db
|
- db
|
||||||
|
- redis
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
db:
|
db:
|
||||||
|
|
Loading…
Reference in a new issue