summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam T. Carpenter <atc@53hor.net>2024-11-14 21:49:47 -0500
committerAdam T. Carpenter <atc@53hor.net>2024-11-14 21:49:47 -0500
commit4aa45ef3e7798ee18bea8b49af75e383afce02a1 (patch)
tree7a1353753d9300929b43a561ff2f1aae59b6434f
parent014e34fa4a8cd4e3cdb3573a7748696c68873523 (diff)
parentfc0e8296178ca779a270d91b681777f50b3b626d (diff)
downloadcarpentertutoring-4aa45ef3e7798ee18bea8b49af75e383afce02a1.tar.xz
carpentertutoring-4aa45ef3e7798ee18bea8b49af75e383afce02a1.zip
Merge branch 'release'HEADmaster
-rw-r--r--.gitignore5
-rw-r--r--Cargo.lock1296
-rw-r--r--Cargo.toml17
-rw-r--r--about/blurb.php4
-rw-r--r--about/figure.php8
-rw-r--r--about/team/adamc/adamc.pngbin385645 -> 0 bytes
-rw-r--r--about/team/adamc/adamc.webpbin44888 -> 0 bytes
-rw-r--r--about/team/adamc/blurb.php1
-rw-r--r--about/team/adamc/name.php1
-rw-r--r--about/team/alexp/alexp.pngbin367859 -> 0 bytes
-rw-r--r--about/team/alexp/alexp.webpbin18202 -> 0 bytes
-rw-r--r--about/team/alexp/blurb.php3
-rw-r--r--about/team/alexp/name.php1
-rw-r--r--about/team/genevievea/blurb.php14
-rw-r--r--about/team/genevievea/genevievea.pngbin337781 -> 0 bytes
-rw-r--r--about/team/genevievea/genevievea.webpbin70202 -> 0 bytes
-rw-r--r--about/team/genevievea/name.php1
-rw-r--r--about/team/jaydea/blurb.php1
-rw-r--r--about/team/jaydea/jaydea.pngbin465599 -> 0 bytes
-rw-r--r--about/team/jaydea/jaydea.webpbin23712 -> 0 bytes
-rw-r--r--about/team/jaydea/name.php1
-rw-r--r--about/team/joelm/blurb.php1
-rw-r--r--about/team/joelm/joelm.pngbin491308 -> 0 bytes
-rw-r--r--about/team/joelm/joelm.webpbin33982 -> 0 bytes
-rw-r--r--about/team/joelm/name.php1
-rw-r--r--about/team/katherinev/blurb.php1
-rw-r--r--about/team/katherinev/katherinev.pngbin382099 -> 0 bytes
-rw-r--r--about/team/katherinev/katherinev.webpbin21316 -> 0 bytes
-rw-r--r--about/team/katherinev/name.php1
-rw-r--r--about/team/zackh/blurb.php1
-rw-r--r--about/team/zackh/name.php1
-rw-r--r--about/team/zackh/zackh.pngbin532626 -> 0 bytes
-rw-r--r--about/team/zackh/zackh.webpbin48908 -> 0 bytes
-rw-r--r--assets/amy-sunny.pngbin511942 -> 0 bytes
-rw-r--r--assets/amy-sunny.webpbin58450 -> 0 bytes
-rw-r--r--assets/amy.pngbin346630 -> 0 bytes
-rw-r--r--assets/amy.webpbin44532 -> 0 bytes
-rw-r--r--assets/bg.pngbin150111 -> 0 bytes
-rw-r--r--assets/brochure/brochure-back-center.pngbin332534 -> 0 bytes
-rw-r--r--assets/brochure/brochure-back-left.pngbin316496 -> 0 bytes
-rw-r--r--assets/brochure/brochure-back-right.pngbin304208 -> 0 bytes
-rw-r--r--assets/brochure/brochure-front-center.pngbin334345 -> 0 bytes
-rw-r--r--assets/brochure/brochure-front-left.pngbin402732 -> 0 bytes
-rw-r--r--assets/brochure/brochure-front-right.pngbin295297 -> 0 bytes
-rw-r--r--assets/icons/close-box.svg1
-rw-r--r--assets/icons/dots-horizontal-circle.svg1
-rw-r--r--assets/icons/email-variant.svg1
-rw-r--r--assets/icons/phone-classic.svg1
-rw-r--r--assets/icons/send-circle.svg1
-rw-r--r--assets/icons/star-box.svg5
-rw-r--r--assets/logo-simple.pngbin9332 -> 0 bytes
-rw-r--r--assets/logo-small.pngbin46158 -> 0 bytes
-rw-r--r--assets/logo.pngbin89452 -> 0 bytes
-rw-r--r--assets/logo.webpbin10338 -> 0 bytes
-rw-r--r--assets/signature.pngbin17196 -> 0 bytes
-rw-r--r--assets/signature.webpbin11102 -> 0 bytes
-rw-r--r--favicon.icobin327262 -> 0 bytes
-rw-r--r--footer.php26
-rw-r--r--header.php315
-rwxr-xr-xscripts/serve.sh2
-rw-r--r--src/handlers.rs40
-rw-r--r--src/helpers.rs5
-rw-r--r--src/main.rs61
-rw-r--r--src/posts.rs3
-rw-r--r--src/posts/abstractions.rs2
-rw-r--r--src/posts/abstractions/post.rs6
-rw-r--r--src/posts/abstractions/repo.rs6
-rw-r--r--src/posts/fs_post.rs47
-rw-r--r--src/posts/fs_post_repo.rs32
-rw-r--r--src/tutors.rs3
-rw-r--r--src/tutors/abstractions.rs2
-rw-r--r--src/tutors/abstractions/tutor.rs7
-rw-r--r--src/tutors/abstractions/tutor_repo.rs5
-rw-r--r--src/tutors/fs_tutor.rs48
-rw-r--r--src/tutors/fs_tutor_repo.rs29
-rw-r--r--src/views.rs6
-rw-r--r--src/views/about.rs17
-rw-r--r--src/views/brochure.rs6
-rw-r--r--src/views/index.rs6
-rw-r--r--src/views/policies.rs6
-rw-r--r--src/views/post.rs15
-rw-r--r--src/views/posts.rs18
-rw-r--r--static/desktop.css (renamed from desktop.css)0
-rw-r--r--static/favicon.ico (renamed from assets/favicon.ico)bin327262 -> 327262 bytes
-rw-r--r--static/widescreen.css (renamed from widescreen.css)0
-rw-r--r--templates/about/blurb.html3
-rw-r--r--templates/about/figure.html7
-rw-r--r--templates/about/index.html (renamed from about/index.php)42
-rw-r--r--templates/about/team.html13
-rw-r--r--templates/base.html68
-rw-r--r--templates/brochure/brochure.css (renamed from brochure/styles.css)0
-rw-r--r--templates/brochure/brochure.html (renamed from brochure/brochure.html)1
-rw-r--r--templates/brochure/brochure.js (renamed from brochure/brochure.js)0
-rw-r--r--templates/brochure/index.html (renamed from brochure/index.php)16
-rw-r--r--templates/index.html (renamed from index.php)70
-rw-r--r--templates/policies.html (renamed from policies/index.php)8
-rw-r--r--templates/post.html13
-rw-r--r--templates/posts.html15
-rw-r--r--templates/styles.css272
-rw-r--r--tutors/email/index.php150
100 files changed, 2113 insertions, 647 deletions
diff --git a/.gitignore b/.gitignore
index 5958712..bf4e644 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,8 @@
_site/
*.core
PHPMailer-master
+
+
+# Added by cargo
+
+/target
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..759f7c4
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,1296 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "addr2line"
+version = "0.22.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "askama"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b79091df18a97caea757e28cd2d5fda49c6cd4bd01ddffd7ff01ace0c0ad2c28"
+dependencies = [
+ "askama_derive",
+ "askama_escape",
+ "comrak",
+ "humansize",
+ "num-traits",
+ "percent-encoding",
+]
+
+[[package]]
+name = "askama_axum"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a41603f7cdbf5ac4af60760f17253eb6adf6ec5b6f14a7ed830cf687d375f163"
+dependencies = [
+ "askama",
+ "axum-core",
+ "http",
+]
+
+[[package]]
+name = "askama_derive"
+version = "0.12.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19fe8d6cb13c4714962c072ea496f3392015f0989b1a2847bb4b2d9effd71d83"
+dependencies = [
+ "askama_parser",
+ "basic-toml",
+ "mime",
+ "mime_guess",
+ "proc-macro2",
+ "quote",
+ "serde",
+ "syn",
+]
+
+[[package]]
+name = "askama_escape"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341"
+
+[[package]]
+name = "askama_parser"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acb1161c6b64d1c3d83108213c2a2533a342ac225aabd0bda218278c2ddb00c0"
+dependencies = [
+ "nom",
+]
+
+[[package]]
+name = "async-trait"
+version = "0.1.82"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
+
+[[package]]
+name = "axum"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf"
+dependencies = [
+ "async-trait",
+ "axum-core",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "http-body-util",
+ "hyper",
+ "hyper-util",
+ "itoa",
+ "matchit",
+ "memchr",
+ "mime",
+ "percent-encoding",
+ "pin-project-lite",
+ "rustversion",
+ "serde",
+ "serde_json",
+ "serde_path_to_error",
+ "serde_urlencoded",
+ "sync_wrapper 1.0.1",
+ "tokio",
+ "tower 0.4.13",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "axum-core"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3"
+dependencies = [
+ "async-trait",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "http-body-util",
+ "mime",
+ "pin-project-lite",
+ "rustversion",
+ "sync_wrapper 0.1.2",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "backtrace"
+version = "0.3.73"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a"
+dependencies = [
+ "addr2line",
+ "cc",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+]
+
+[[package]]
+name = "basic-toml"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "823388e228f614e9558c6804262db37960ec8821856535f5c3f59913140558f8"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
+
+[[package]]
+name = "bumpalo"
+version = "3.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
+
+[[package]]
+name = "bytes"
+version = "1.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
+
+[[package]]
+name = "carpentertutoring"
+version = "0.1.0"
+dependencies = [
+ "askama",
+ "askama_axum",
+ "axum",
+ "chrono",
+ "tokio",
+ "tower 0.5.0",
+ "tower-http",
+ "tracing",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "cc"
+version = "1.1.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b62ac837cdb5cb22e10a256099b4fc502b1dfe560cb282963a974d7abd80e476"
+dependencies = [
+ "shlex",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "chrono"
+version = "0.4.38"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "comrak"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "482aa5695bca086022be453c700a40c02893f1ba7098a2c88351de55341ae894"
+dependencies = [
+ "entities",
+ "memchr",
+ "once_cell",
+ "regex",
+ "slug",
+ "typed-arena",
+ "unicode_categories",
+]
+
+[[package]]
+name = "deunicode"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "339544cc9e2c4dc3fc7149fd630c5f22263a4fdf18a98afd0075784968b5cf00"
+
+[[package]]
+name = "entities"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5320ae4c3782150d900b79807611a59a99fc9a1d61d686faafc24b93fc8d7ca"
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
+dependencies = [
+ "futures-core",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
+
+[[package]]
+name = "futures-sink"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
+
+[[package]]
+name = "futures-task"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
+
+[[package]]
+name = "futures-util"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "pin-project-lite",
+ "pin-utils",
+]
+
+[[package]]
+name = "gimli"
+version = "0.29.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
+
+[[package]]
+name = "http"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "http-body"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
+dependencies = [
+ "bytes",
+ "http",
+]
+
+[[package]]
+name = "http-body-util"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f"
+dependencies = [
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "http-range-header"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08a397c49fec283e3d6211adbe480be95aae5f304cfb923e9970e08956d5168a"
+
+[[package]]
+name = "httparse"
+version = "1.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9"
+
+[[package]]
+name = "httpdate"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
+
+[[package]]
+name = "humansize"
+version = "2.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7"
+dependencies = [
+ "libm",
+]
+
+[[package]]
+name = "hyper"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-util",
+ "http",
+ "http-body",
+ "httparse",
+ "httpdate",
+ "itoa",
+ "pin-project-lite",
+ "smallvec",
+ "tokio",
+]
+
+[[package]]
+name = "hyper-util"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9"
+dependencies = [
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "hyper",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
+
+[[package]]
+name = "lazy_static"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+
+[[package]]
+name = "libc"
+version = "0.2.158"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
+
+[[package]]
+name = "libm"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
+
+[[package]]
+name = "lock_api"
+version = "0.4.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
+
+[[package]]
+name = "matchers"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
+dependencies = [
+ "regex-automata 0.1.10",
+]
+
+[[package]]
+name = "matchit"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
+
+[[package]]
+name = "memchr"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+[[package]]
+name = "mime"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
+
+[[package]]
+name = "mime_guess"
+version = "2.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e"
+dependencies = [
+ "mime",
+ "unicase",
+]
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
+dependencies = [
+ "adler",
+]
+
+[[package]]
+name = "mio"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "wasi",
+ "windows-sys",
+]
+
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
+[[package]]
+name = "nu-ansi-term"
+version = "0.46.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
+dependencies = [
+ "overload",
+ "winapi",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "object"
+version = "0.36.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+
+[[package]]
+name = "overload"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
+
+[[package]]
+name = "parking_lot"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+
+[[package]]
+name = "pin-project"
+version = "1.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3"
+dependencies = [
+ "pin-project-internal",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "1.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
+name = "regex"
+version = "1.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata 0.4.6",
+ "regex-syntax 0.8.2",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
+dependencies = [
+ "regex-syntax 0.6.29",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax 0.8.2",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
+
+[[package]]
+name = "rustversion"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
+
+[[package]]
+name = "ryu"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "serde"
+version = "1.0.210"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.210"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.128"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8"
+dependencies = [
+ "itoa",
+ "memchr",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "serde_path_to_error"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6"
+dependencies = [
+ "itoa",
+ "serde",
+]
+
+[[package]]
+name = "serde_urlencoded"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
+dependencies = [
+ "form_urlencoded",
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "sharded-slab"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
+dependencies = [
+ "lazy_static",
+]
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "slug"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "882a80f72ee45de3cc9a5afeb2da0331d58df69e4e7d8eeb5d3c7784ae67e724"
+dependencies = [
+ "deunicode",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
+
+[[package]]
+name = "socket2"
+version = "0.5.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c"
+dependencies = [
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.77"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "sync_wrapper"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
+
+[[package]]
+name = "sync_wrapper"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394"
+
+[[package]]
+name = "thread_local"
+version = "1.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+]
+
+[[package]]
+name = "tokio"
+version = "1.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998"
+dependencies = [
+ "backtrace",
+ "bytes",
+ "libc",
+ "mio",
+ "parking_lot",
+ "pin-project-lite",
+ "signal-hook-registry",
+ "socket2",
+ "tokio-macros",
+ "windows-sys",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tokio-util"
+version = "0.7.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-sink",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "tower"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
+dependencies = [
+ "futures-core",
+ "futures-util",
+ "pin-project",
+ "pin-project-lite",
+ "tokio",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "tower"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36b837f86b25d7c0d7988f00a54e74739be6477f2aac6201b8f429a7569991b7"
+dependencies = [
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "tower-http"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5"
+dependencies = [
+ "bitflags 2.6.0",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "http-body-util",
+ "http-range-header",
+ "httpdate",
+ "mime",
+ "mime_guess",
+ "percent-encoding",
+ "pin-project-lite",
+ "tokio",
+ "tokio-util",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "tower-layer"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
+
+[[package]]
+name = "tower-service"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
+
+[[package]]
+name = "tracing"
+version = "0.1.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
+dependencies = [
+ "log",
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
+dependencies = [
+ "once_cell",
+ "valuable",
+]
+
+[[package]]
+name = "tracing-log"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
+dependencies = [
+ "log",
+ "once_cell",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.3.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
+dependencies = [
+ "matchers",
+ "nu-ansi-term",
+ "once_cell",
+ "regex",
+ "sharded-slab",
+ "smallvec",
+ "thread_local",
+ "tracing",
+ "tracing-core",
+ "tracing-log",
+]
+
+[[package]]
+name = "typed-arena"
+version = "2.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a"
+
+[[package]]
+name = "unicase"
+version = "2.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
+dependencies = [
+ "version_check",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "unicode_categories"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
+
+[[package]]
+name = "valuable"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
+
+[[package]]
+name = "version_check"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.93"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.93"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.93"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.93"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.93"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..30fd4f8
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,17 @@
+[package]
+name = "carpentertutoring"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+askama = { version = "0.12", features = ["markdown", "with-axum"], default-features = false }
+askama_axum = "0.4.0"
+axum = "0.7.5"
+chrono = { version = "0.4.38", default-features = false, features = ["now"] }
+tokio = { version = "1.40.0", features = ["full"] }
+tower = "0.5.0"
+tower-http = { version = "0.5.2", features = ["fs", "trace", "normalize-path"] }
+tracing = "0.1.40"
+tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
diff --git a/about/blurb.php b/about/blurb.php
deleted file mode 100644
index 5b4e5db..0000000
--- a/about/blurb.php
+++ /dev/null
@@ -1,4 +0,0 @@
- <div>
- <h2><?php include("./team/$tutor/name.php") ?></h2>
- <p><?php include("./team/$tutor/blurb.php") ?></p>
- </div>
diff --git a/about/figure.php b/about/figure.php
deleted file mode 100644
index c6cee51..0000000
--- a/about/figure.php
+++ /dev/null
@@ -1,8 +0,0 @@
- <figure class="shadowy">
- <picture>
- <source srcset="<?php printf('/about/team/%s/%s.webp', $tutor, $tutor) ?>" type="image/webp" />
- <source srcset="<?php printf('/about/team/%s/%s.png', $tutor, $tutor) ?>" type="image/png" />
- <img alt="<?php include("./team/$tutor/name.php") ?>" src="<?php printf('/about/team/%s/%s.png', $tutor, $tutor) ?>" />
- </picture>
- <figcaption><?php include("./team/$tutor/name.php") ?></figcaption>
- </figure>
diff --git a/about/team/adamc/adamc.png b/about/team/adamc/adamc.png
deleted file mode 100644
index e0af5e7..0000000
--- a/about/team/adamc/adamc.png
+++ /dev/null
Binary files differ
diff --git a/about/team/adamc/adamc.webp b/about/team/adamc/adamc.webp
deleted file mode 100644
index 1884f87..0000000
--- a/about/team/adamc/adamc.webp
+++ /dev/null
Binary files differ
diff --git a/about/team/adamc/blurb.php b/about/team/adamc/blurb.php
deleted file mode 100644
index 08eca54..0000000
--- a/about/team/adamc/blurb.php
+++ /dev/null
@@ -1 +0,0 @@
- Adam earned a Bachelor's in Computer Science from The College of William &amp; Mary in 2018. Since graduating, he has accumulated over four years of experience as a software engineer specializing in server-side automation and front end self-service software. Adam's favorite part of software development is learning and teaching new technology. He loves simplifying complicated concepts in computing and giving others the hints and tricks they need to realize their ideas. In his spare time Adam is a hobbyist mechanic, constantly tinkering with his classic Hudson Hornet. He is eager to contribute to Carpenter Tutoring with all levels of high school and college computer science and programming, information technology fundamentals, and software development practices.
diff --git a/about/team/adamc/name.php b/about/team/adamc/name.php
deleted file mode 100644
index 6884f41..0000000
--- a/about/team/adamc/name.php
+++ /dev/null
@@ -1 +0,0 @@
-Adam Carpenter
diff --git a/about/team/alexp/alexp.png b/about/team/alexp/alexp.png
deleted file mode 100644
index e524184..0000000
--- a/about/team/alexp/alexp.png
+++ /dev/null
Binary files differ
diff --git a/about/team/alexp/alexp.webp b/about/team/alexp/alexp.webp
deleted file mode 100644
index 5c67439..0000000
--- a/about/team/alexp/alexp.webp
+++ /dev/null
Binary files differ
diff --git a/about/team/alexp/blurb.php b/about/team/alexp/blurb.php
deleted file mode 100644
index 94830ec..0000000
--- a/about/team/alexp/blurb.php
+++ /dev/null
@@ -1,3 +0,0 @@
-Alex graduated from Christopher Newport University with a Bachelor’s of Science in Mathematics with a minor in Leadership Studies in 2023. She is currently pursuing her Master of Arts in Secondary Education in hopes of becoming a high school math teacher. Alex has always loved helping others in her classes throughout her time in school, whether that is helping her friends in their math classes or through formal tutoring positions. One of her favorite parts of teaching is taking concepts that may be difficult for students, explaining it in a way that makes sense to them, and seeing the light bulb go off once they understand. She has worked with students of varying abilities and ages ranging from kindergarten to college. She has experience mainly in math and science but also loves to help with standardized test prep and writing papers at a college level.
-
-In her free time, Alex enjoys spending time with her friends and her cat, Finley, and reading books at the nearest coffee shop.
diff --git a/about/team/alexp/name.php b/about/team/alexp/name.php
deleted file mode 100644
index 6488d59..0000000
--- a/about/team/alexp/name.php
+++ /dev/null
@@ -1 +0,0 @@
-Alex Patterson
diff --git a/about/team/genevievea/blurb.php b/about/team/genevievea/blurb.php
deleted file mode 100644
index d555b46..0000000
--- a/about/team/genevievea/blurb.php
+++ /dev/null
@@ -1,14 +0,0 @@
- Genevieve is currently pursuing a master’s degree in Clinical Mental Health Counseling
- at the College of William &amp; Mary. Previously, Genevieve earned a master’s in Equality Studies
- from University College Dublin and bachelors’ degrees in Political Science and Women and
- Gender Studies from St. Olaf College. In high school, Genevieve began tutoring elementary and
- middle school children in piano and violin. While in college, Genevieve worked as tutor in a local
- elementary school, focusing on language arts, reading, and math. Genevieve continued to seek
- tutoring opportunities while living in Dublin and gained experience working with middle school
- aged children in reading and language arts. Through her work as a tutor and additional
- employment experiences, Genevieve has worked with individuals of variety of ages, academic
- levels, and children with additional support needs. Genevieve loves supporting students and
- working with them to foster a love of learning and overcome any challenges. As a Carpenter
- Tutor, Genevieve welcomes students seeking assistance with all levels of reading, writing, and
- language arts; elementary and middle school math; piano and violin; and additional skills such
- as time management and study skills.
diff --git a/about/team/genevievea/genevievea.png b/about/team/genevievea/genevievea.png
deleted file mode 100644
index 4b7f99a..0000000
--- a/about/team/genevievea/genevievea.png
+++ /dev/null
Binary files differ
diff --git a/about/team/genevievea/genevievea.webp b/about/team/genevievea/genevievea.webp
deleted file mode 100644
index 56197c5..0000000
--- a/about/team/genevievea/genevievea.webp
+++ /dev/null
Binary files differ
diff --git a/about/team/genevievea/name.php b/about/team/genevievea/name.php
deleted file mode 100644
index 56a0214..0000000
--- a/about/team/genevievea/name.php
+++ /dev/null
@@ -1 +0,0 @@
-Genevieve Akins
diff --git a/about/team/jaydea/blurb.php b/about/team/jaydea/blurb.php
deleted file mode 100644
index ab7cbda..0000000
--- a/about/team/jaydea/blurb.php
+++ /dev/null
@@ -1 +0,0 @@
-Jayde graduated from Christopher Newport University in 2019 with a Bachelor's of Arts in Spanish and a minor in Dance. She then went on to receive her Master's of Arts in TESOL (Teaching English as a Second Language) in 2020. For the past 3 years Jayde worked as a Middle School Newcomer teacher in Newport News, supporting students with very limited English in all subject areas. She tutored and taught students ranging from lower elementary to upper middle school and has a specific love for tutoring in Spanish and English. Outside of the classroom, she enjoys reading, spending time with her husband and son, and catching up on a good TV show.
diff --git a/about/team/jaydea/jaydea.png b/about/team/jaydea/jaydea.png
deleted file mode 100644
index 11ae1f7..0000000
--- a/about/team/jaydea/jaydea.png
+++ /dev/null
Binary files differ
diff --git a/about/team/jaydea/jaydea.webp b/about/team/jaydea/jaydea.webp
deleted file mode 100644
index d261530..0000000
--- a/about/team/jaydea/jaydea.webp
+++ /dev/null
Binary files differ
diff --git a/about/team/jaydea/name.php b/about/team/jaydea/name.php
deleted file mode 100644
index 39c83d7..0000000
--- a/about/team/jaydea/name.php
+++ /dev/null
@@ -1 +0,0 @@
-Jayde Adams
diff --git a/about/team/joelm/blurb.php b/about/team/joelm/blurb.php
deleted file mode 100644
index 843605f..0000000
--- a/about/team/joelm/blurb.php
+++ /dev/null
@@ -1 +0,0 @@
- Joel has been a math nerd since he first discovered the number 11. Since then, he has worked to develop his skills in both studying and teaching math. Joel earned his Bachelor's in Mathematics and his Master's in Computational Operations Research from the College of William &amp; Mary. Throughout his education, he has consistently sought opportunities to tutor, whether it was helping friends in high school, peers in college, or other students during his Master's program. Joel has a passion for seeing students break through the barriers which can form when studying math and discover the amazing language hidden within. As a part of Carpenter Tutoring, Joel is happy to offer assistance to students in high school and college math and introductory physics.
diff --git a/about/team/joelm/joelm.png b/about/team/joelm/joelm.png
deleted file mode 100644
index fcf53ac..0000000
--- a/about/team/joelm/joelm.png
+++ /dev/null
Binary files differ
diff --git a/about/team/joelm/joelm.webp b/about/team/joelm/joelm.webp
deleted file mode 100644
index fdd9c0b..0000000
--- a/about/team/joelm/joelm.webp
+++ /dev/null
Binary files differ
diff --git a/about/team/joelm/name.php b/about/team/joelm/name.php
deleted file mode 100644
index 370f5ce..0000000
--- a/about/team/joelm/name.php
+++ /dev/null
@@ -1 +0,0 @@
-Joel Monroe
diff --git a/about/team/katherinev/blurb.php b/about/team/katherinev/blurb.php
deleted file mode 100644
index 4987d48..0000000
--- a/about/team/katherinev/blurb.php
+++ /dev/null
@@ -1 +0,0 @@
-Katherine is currently working as an English As A Second Language teacher in Newport News. Previously, she earned a Bachelor of Arts degree in Psychology and Leadership Studies with a minor in Linguistics from Christopher Newport University in 2022, and a Masters in Teaching in 2023. Katherine is passionate about all things education! She has been an independent tutor to students grades K-12 in math, science, English, French, and general study skills, while also working in middle schools for the past 3 years. Outside of the classroom, she enjoys being outdoors, reading, and learning new languages. Katherine loves getting to know students and breaking down difficult ideas in a way that each student will understand. She is excited to contribute to Carpenter Tutoring by working with students in French, English, and K-8 math, as well as academic coaching.
diff --git a/about/team/katherinev/katherinev.png b/about/team/katherinev/katherinev.png
deleted file mode 100644
index de82f40..0000000
--- a/about/team/katherinev/katherinev.png
+++ /dev/null
Binary files differ
diff --git a/about/team/katherinev/katherinev.webp b/about/team/katherinev/katherinev.webp
deleted file mode 100644
index 856dfca..0000000
--- a/about/team/katherinev/katherinev.webp
+++ /dev/null
Binary files differ
diff --git a/about/team/katherinev/name.php b/about/team/katherinev/name.php
deleted file mode 100644
index ea4ec1a..0000000
--- a/about/team/katherinev/name.php
+++ /dev/null
@@ -1 +0,0 @@
-Katherine Vander Vennet
diff --git a/about/team/zackh/blurb.php b/about/team/zackh/blurb.php
deleted file mode 100644
index eb49046..0000000
--- a/about/team/zackh/blurb.php
+++ /dev/null
@@ -1 +0,0 @@
- Zack graduated from William &amp; Mary in 2021 with a Bachelors of Science in Computer Science and Applied Mathematics. There, he spent three years tutoring his peers in math and computer science to help them reach a deeper understanding and appreciation of both subjects. Here he gained a passion for teaching and is excited to offer his assistance again at any levels of math and computer science through Carpenter Tutoring. In addition to tutoring, he currently works as an application developer for ADP in Norfolk, VA. In his spare time he loves to bike and spend time with his cat, Hans.
diff --git a/about/team/zackh/name.php b/about/team/zackh/name.php
deleted file mode 100644
index 5d0283a..0000000
--- a/about/team/zackh/name.php
+++ /dev/null
@@ -1 +0,0 @@
-Zack H'Doubler
diff --git a/about/team/zackh/zackh.png b/about/team/zackh/zackh.png
deleted file mode 100644
index c666802..0000000
--- a/about/team/zackh/zackh.png
+++ /dev/null
Binary files differ
diff --git a/about/team/zackh/zackh.webp b/about/team/zackh/zackh.webp
deleted file mode 100644
index 9281421..0000000
--- a/about/team/zackh/zackh.webp
+++ /dev/null
Binary files differ
diff --git a/assets/amy-sunny.png b/assets/amy-sunny.png
deleted file mode 100644
index 003c700..0000000
--- a/assets/amy-sunny.png
+++ /dev/null
Binary files differ
diff --git a/assets/amy-sunny.webp b/assets/amy-sunny.webp
deleted file mode 100644
index 680335f..0000000
--- a/assets/amy-sunny.webp
+++ /dev/null
Binary files differ
diff --git a/assets/amy.png b/assets/amy.png
deleted file mode 100644
index ea2f165..0000000
--- a/assets/amy.png
+++ /dev/null
Binary files differ
diff --git a/assets/amy.webp b/assets/amy.webp
deleted file mode 100644
index 6ca2282..0000000
--- a/assets/amy.webp
+++ /dev/null
Binary files differ
diff --git a/assets/bg.png b/assets/bg.png
deleted file mode 100644
index a0c9ab8..0000000
--- a/assets/bg.png
+++ /dev/null
Binary files differ
diff --git a/assets/brochure/brochure-back-center.png b/assets/brochure/brochure-back-center.png
deleted file mode 100644
index 48ee7e2..0000000
--- a/assets/brochure/brochure-back-center.png
+++ /dev/null
Binary files differ
diff --git a/assets/brochure/brochure-back-left.png b/assets/brochure/brochure-back-left.png
deleted file mode 100644
index 5a2cc4a..0000000
--- a/assets/brochure/brochure-back-left.png
+++ /dev/null
Binary files differ
diff --git a/assets/brochure/brochure-back-right.png b/assets/brochure/brochure-back-right.png
deleted file mode 100644
index 73a5b8d..0000000
--- a/assets/brochure/brochure-back-right.png
+++ /dev/null
Binary files differ
diff --git a/assets/brochure/brochure-front-center.png b/assets/brochure/brochure-front-center.png
deleted file mode 100644
index 59f9f82..0000000
--- a/assets/brochure/brochure-front-center.png
+++ /dev/null
Binary files differ
diff --git a/assets/brochure/brochure-front-left.png b/assets/brochure/brochure-front-left.png
deleted file mode 100644
index 2ed6c56..0000000
--- a/assets/brochure/brochure-front-left.png
+++ /dev/null
Binary files differ
diff --git a/assets/brochure/brochure-front-right.png b/assets/brochure/brochure-front-right.png
deleted file mode 100644
index 1fd53c7..0000000
--- a/assets/brochure/brochure-front-right.png
+++ /dev/null
Binary files differ
diff --git a/assets/icons/close-box.svg b/assets/icons/close-box.svg
deleted file mode 100644
index 717756e..0000000
--- a/assets/icons/close-box.svg
+++ /dev/null
@@ -1 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg fill="#ffffff" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M19,3H16.3H7.7H5A2,2 0 0,0 3,5V7.7V16.4V19A2,2 0 0,0 5,21H7.7H16.4H19A2,2 0 0,0 21,19V16.3V7.7V5A2,2 0 0,0 19,3M15.6,17L12,13.4L8.4,17L7,15.6L10.6,12L7,8.4L8.4,7L12,10.6L15.6,7L17,8.4L13.4,12L17,15.6L15.6,17Z" /></svg>
diff --git a/assets/icons/dots-horizontal-circle.svg b/assets/icons/dots-horizontal-circle.svg
deleted file mode 100644
index ca1de94..0000000
--- a/assets/icons/dots-horizontal-circle.svg
+++ /dev/null
@@ -1 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path fill="#ffffff" d="M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,10.5A1.5,1.5 0 0,0 10.5,12A1.5,1.5 0 0,0 12,13.5A1.5,1.5 0 0,0 13.5,12A1.5,1.5 0 0,0 12,10.5M6.5,10.5A1.5,1.5 0 0,0 5,12A1.5,1.5 0 0,0 6.5,13.5A1.5,1.5 0 0,0 8,12A1.5,1.5 0 0,0 6.5,10.5M17.5,10.5A1.5,1.5 0 0,0 16,12A1.5,1.5 0 0,0 17.5,13.5A1.5,1.5 0 0,0 19,12A1.5,1.5 0 0,0 17.5,10.5Z" /></svg>
diff --git a/assets/icons/email-variant.svg b/assets/icons/email-variant.svg
deleted file mode 100644
index e8ef2dd..0000000
--- a/assets/icons/email-variant.svg
+++ /dev/null
@@ -1 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg fill="#ffffff" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M12,13L2,6.76V6C2,4.89 2.89,4 4,4H20A2,2 0 0,1 22,6V6.75L12,13M22,18A2,2 0 0,1 20,20H4C2.89,20 2,19.1 2,18V9.11L4,10.36V18H20V10.36L22,9.11V18Z" /></svg>
diff --git a/assets/icons/phone-classic.svg b/assets/icons/phone-classic.svg
deleted file mode 100644
index 814990b..0000000
--- a/assets/icons/phone-classic.svg
+++ /dev/null
@@ -1 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg fill="#ffffff" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M12,3C7.46,3 3.34,4.78 0.29,7.67C0.11,7.85 0,8.1 0,8.38C0,8.66 0.11,8.91 0.29,9.09L2.77,11.57C2.95,11.75 3.2,11.86 3.5,11.86C3.75,11.86 4,11.75 4.18,11.58C4.97,10.84 5.87,10.22 6.84,9.73C7.17,9.57 7.4,9.23 7.4,8.83V5.73C8.85,5.25 10.39,5 12,5C13.59,5 15.14,5.25 16.59,5.72V8.82C16.59,9.21 16.82,9.56 17.15,9.72C18.13,10.21 19,10.84 19.82,11.57C20,11.75 20.25,11.85 20.5,11.85C20.8,11.85 21.05,11.74 21.23,11.56L23.71,9.08C23.89,8.9 24,8.65 24,8.37C24,8.09 23.88,7.85 23.7,7.67C20.65,4.78 16.53,3 12,3M9,7V10C9,10 3,15 3,18V22H21V18C21,15 15,10 15,10V7H13V9H11V7H9M12,12A4,4 0 0,1 16,16A4,4 0 0,1 12,20A4,4 0 0,1 8,16A4,4 0 0,1 12,12M12,13.5A2.5,2.5 0 0,0 9.5,16A2.5,2.5 0 0,0 12,18.5A2.5,2.5 0 0,0 14.5,16A2.5,2.5 0 0,0 12,13.5Z" /></svg>
diff --git a/assets/icons/send-circle.svg b/assets/icons/send-circle.svg
deleted file mode 100644
index 1b7e5c3..0000000
--- a/assets/icons/send-circle.svg
+++ /dev/null
@@ -1 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg fill="#ffffff" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M8,7.71V11.05L15.14,12L8,12.95V16.29L18,12L8,7.71Z" /></svg>
diff --git a/assets/icons/star-box.svg b/assets/icons/star-box.svg
deleted file mode 100644
index 2973ca8..0000000
--- a/assets/icons/star-box.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24">
- <path fill="#FFCA28" d="M19,3A2,2 0 0,1 21,5V19C21,20.11 20.1,21 19,21H5A2,2 0 0,1 3,19V5A2,2 0 0,1 5,3H19M15.58,17L14.63,12.92L17.79,10.19L13.62,9.83L12,6L10.38,9.84L6.21,10.2L9.37,12.93L8.42,17L12,14.84L15.58,17Z" />
-</svg>
diff --git a/assets/logo-simple.png b/assets/logo-simple.png
deleted file mode 100644
index b299e66..0000000
--- a/assets/logo-simple.png
+++ /dev/null
Binary files differ
diff --git a/assets/logo-small.png b/assets/logo-small.png
deleted file mode 100644
index a128e24..0000000
--- a/assets/logo-small.png
+++ /dev/null
Binary files differ
diff --git a/assets/logo.png b/assets/logo.png
deleted file mode 100644
index 81e0776..0000000
--- a/assets/logo.png
+++ /dev/null
Binary files differ
diff --git a/assets/logo.webp b/assets/logo.webp
deleted file mode 100644
index 16ab275..0000000
--- a/assets/logo.webp
+++ /dev/null
Binary files differ
diff --git a/assets/signature.png b/assets/signature.png
deleted file mode 100644
index 7d115de..0000000
--- a/assets/signature.png
+++ /dev/null
Binary files differ
diff --git a/assets/signature.webp b/assets/signature.webp
deleted file mode 100644
index 8ce6e28..0000000
--- a/assets/signature.webp
+++ /dev/null
Binary files differ
diff --git a/favicon.ico b/favicon.ico
deleted file mode 100644
index 8ba4c15..0000000
--- a/favicon.ico
+++ /dev/null
Binary files differ
diff --git a/footer.php b/footer.php
deleted file mode 100644
index 3a89f89..0000000
--- a/footer.php
+++ /dev/null
@@ -1,26 +0,0 @@
-</main>
-
-<footer>
- <figure class="logo">
- <picture>
- <source srcset="/assets/logo.webp" type="image/webp" />
- <source srcset="/assets/logo.png" type="image/png" />
- <img alt="logo" src="/assets/logo.png" />
- </picture>
- </figure>
- <p>
- &copy; 2019-<?php echo date('Y'); ?> Carpenter Tutoring, LLC. All rights reserved.
- </p>
- <p>
- <a href="https://g.page/carpenter-tutoring?share">Visit me on Google</a>
- |
- <a href="https://g.page/carpenter-tutoring/review?np">Submit a review</a>
- |
- <a href="mailto:webmaster@carpentertutoring.com">
- Having trouble with our site?
- </a>
- </p>
-</footer>
-</body>
-
-</html>
diff --git a/header.php b/header.php
deleted file mode 100644
index fc3ac25..0000000
--- a/header.php
+++ /dev/null
@@ -1,315 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-
-<head>
- <title>
- Tutoring Excellence Online and in Suffolk, VA - Carpenter Tutoring, LLC
- </title>
- <style>
- /* Colors */
- :root {
- --darkteal: rgb(0, 100, 108);
- --green: rgb(88, 169, 143);
- --darkerteal: rgb(29, 133, 137);
- --gray: rgb(157, 210, 211);
- --teal: rgb(58, 165, 166);
- --cyan: rgb(156, 210, 210);
- --darkgray: rgb(127, 177, 181);
- --darkgreen: rgb(59, 136, 114);
- --yellow: rgb(186, 214, 187);
- }
-
- /* Elements */
-
- html,
- body {
- margin: 0;
- padding: 0;
- font-family: "PT Sans", sans-serif;
- color: dimgray;
- }
-
- body {
- background-image: url("/assets/bg.png");
- }
-
- .banner {
- font-size: 1em;
- text-align: center;
- padding: 3em;
- background-color: white;
- }
-
- nav {
- display: flex;
- background-color: white;
- flex-direction: column-reverse;
- }
-
- nav img {
- height: 1.4em;
- }
-
- nav a {
- font-family: "PT Sans", sans-serif;
- color: dimgray;
- text-align: center;
- text-decoration: none;
- padding: 1em 1em;
- transition: 0.15s;
- }
-
- nav a:hover {
- color: var(--darkteal);
- background-color: lightgray;
- }
-
- div.buttons {
- display: flex;
- flex-direction: column;
- }
-
- h1 {
- font-size: 3em;
- color: var(--darkteal);
- font-family: "Architects Daughter", cursive;
- }
-
- h2 {
- font-size: 2em;
- color: var(--darkteal);
- font-family: "Indie Flower", cursive;
- }
-
- em {
-
- color: var(--darkteal);
- }
-
- section {
- font-size: 1.5em;
- margin: 0;
- padding: 1em;
- }
-
- section.quiet {
- background-color: white;
- }
-
- section.squarshed {
- padding-bottom: 1em;
- padding-top: 1em;
- }
-
- .button img {
- margin-right: 1em;
- }
-
- button.button,
- a.button {
- display: flex;
- align-items: center;
- background-color: var(--teal);
- padding-left: 1em;
- padding-right: 1em;
- padding-top: 0.5em;
- padding-bottom: 0.5em;
- margin-right: 0.5em;
- margin-bottom: 0.5em;
- text-decoration: none;
- color: white;
- border-radius: 3em;
- transition: 0.15s;
- }
-
- button.button:hover,
- a.button:hover {
- box-shadow: 0 4px 8px 0 lightgray, 0 6px 20px 0 lightgray;
- }
-
- button {
- font-family: "PT Sans", sans-serif;
- border: none;
- font-size: 0.75em;
- }
-
- a.primary {
- background-color: var(--darkteal);
- }
-
- footer {
- background-color: white;
- text-align: center;
- padding: 3em;
- font-size: 1.5em;
- }
-
- footer a,
- section a {
- color: var(--darkteal);
- text-decoration: underline;
- }
-
- .card {
- font-size: 0.9em;
- background-color: white;
- padding: 1em;
- border-radius: 1em;
- margin-left: 0;
- margin-right: 0;
- }
-
- form input,
- form textarea {
- font-family: "PT Sans", sans-serif;
- margin-bottom: 1.5em;
- box-sizing: border-box;
- width: 100%;
- border: 1px solid lightgray;
- padding: 1em;
- }
-
- form input {
- border-radius: 3em;
- }
-
- textarea {
- border-radius: 1em;
- resize: none;
- }
-
- /* honeypot */
- textarea#beehive {
- display: none;
- }
-
- table {
- width: 100%;
- border-collapse: collapse;
- }
-
- table caption {
- text-align: left;
- }
-
- table th {
- text-align: right;
- color: var(--darkteal);
- }
-
- table td,
- table th {
- vertical-align: bottom;
- }
-
- tr:nth-child(even) {
- background-color: lightgray;
- }
-
- picture img {
- width: 100%;
- }
-
- .shadowy img {
- box-shadow: 0 4px 8px 0 dimgray, 0 6px 20px 0 dimgray;
- }
-
- figcaption {
- text-align: right;
- color: var(--darkteal);
- font-family: "Architects Daughter", cursive;
- }
-
- section.flexible {
- display: flex;
- flex-direction: column;
- }
-
- #reviews .card,
- #offerings .card {
- margin: 1em;
- text-align: center;
- }
-
- #reviews,
- #offerings {
- padding-left: 0;
- padding-right: 0;
- display: flex;
- flex-wrap: wrap;
- justify-content: center;
- }
-
- .modal {
- position: fixed;
- z-index: 1;
- left: 0;
- top: 0;
- width: 100%;
- height: 100%;
- background-color: white;
- background-color: rgba(1, 1, 1, 0.4);
- overflow: auto;
- display: none;
- font-size: 1.5em;
- }
-
- .modal .card {
- max-height: 90%;
- max-width: 60%;
- }
-
- .modal a,
- .card a.button {
- float: right;
- }
-
- :target.modal {
- display: flex;
- justify-content: center;
- align-items: center;
- }
-
- :target.modal .card {
- overflow: auto;
- }
-
- .centered {
- text-align: center;
-justify-content: center;
- }
-
- <?php
- if (str_contains($_SERVER['REQUEST_URI'], '/brochure')) {
- include('../brochure/styles.css');
- }
- ?>
- </style>
- <link rel="preconnect" href="https://fonts.gstatic.com" />
- <link href="https://fonts.googleapis.com/css2?family=Architects+Daughter&family=Indie+Flower&family=PT+Sans&display=swap" rel="stylesheet" />
- <link rel="stylesheet" href="/desktop.css" />
- <link rel="stylesheet" href="/widescreen.css" />
- <meta charset="utf-8" />
- <meta name="viewport" content="width=device-width, initial-scale=1" />
- <meta name="description" content="Tutoring Excellence Online and in Suffolk, VA" />
- <meta property="og:description" content="Tutoring excellence online and in-person" />
- <meta property="og:image" content="http://carpentertutoring.com/assets/logo.webp" />
- <meta property="og:image:secure_url" content="https://carpentertutoring.com/assets/logo.webp" />
- <meta property="og:site_name" content="Carpenter Tutoring" />
- <meta property="og:title" content="Tutoring Excellence Online and in Suffolk, VA - Carpenter Tutoring, LLC" />
- <meta property="og:type" content="website" />
- <meta property="og:url" content="https://carpentertutoring.com" />
-</head>
-
-<body>
- <nav>
- <a href="/#reviews">Reviews</a>
- <a href="/about">Team</a>
- <a href="/policies">Policies</a>
- <a href="/#pricing">Pricing</a>
- <a href="/brochure">Brochure</a>
- <a href="/#offerings">Offerings</a>
- <a href="/#"><img alt="logo" src="/assets/logo-simple.png" /></a>
- </nav>
-
- <main>
diff --git a/scripts/serve.sh b/scripts/serve.sh
deleted file mode 100755
index 16a3410..0000000
--- a/scripts/serve.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/sh
-php83 -S localhost:8000
diff --git a/src/handlers.rs b/src/handlers.rs
new file mode 100644
index 0000000..800d8f8
--- /dev/null
+++ b/src/handlers.rs
@@ -0,0 +1,40 @@
+use askama::Template;
+use crate::views::post::PostView;
+use crate::views::posts::PostsView;
+use crate::posts::abstractions::repo::PostRepo;
+use crate::views::policies::PoliciesTemplate;
+use crate::views::index::IndexTemplate;
+use crate::views::brochure::BrochureTemplate;
+use crate::views::about::AboutView;
+use crate::tutors::abstractions::tutor_repo::TutorRepo;
+use std::sync::Arc;
+use axum::response::Html;
+use axum::extract::{State, Path};
+
+pub async fn about_handler(State(repo): State<Arc<impl TutorRepo>>) -> Html<String> {
+ let view = AboutView::with_tutors(repo.load());
+ Html(view.render().unwrap())
+}
+
+pub async fn brochure_handler() -> Html<String> {
+ Html(BrochureTemplate{}.render().unwrap())
+}
+
+pub async fn index_handler() -> Html<String> {
+ Html(IndexTemplate {}.render().unwrap())
+}
+
+pub async fn policies_handler() -> Html<String> {
+ Html(PoliciesTemplate{}.render().unwrap())
+}
+
+pub async fn posts_handler(State(repo): State<Arc<impl PostRepo>>) -> Html<String> {
+ let view = PostsView::with_posts(repo.load());
+ Html(view.render().unwrap())
+}
+
+pub async fn post_handler(Path(post_id): Path<String>, State(repo): State<Arc<impl PostRepo>>) -> Html<String> {
+ let view = PostView::with_post(repo.by_id(&post_id));
+ Html(view.render().unwrap())
+}
+
diff --git a/src/helpers.rs b/src/helpers.rs
new file mode 100644
index 0000000..c7509de
--- /dev/null
+++ b/src/helpers.rs
@@ -0,0 +1,5 @@
+use chrono::{prelude::*, Utc};
+
+pub fn current_year() -> i32 {
+ Utc::now().year()
+}
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..252588d
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,61 @@
+use tower::Layer;
+use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
+use tower_http::{trace::{self, TraceLayer}, normalize_path::NormalizePathLayer};
+use tracing::{info, Level};
+use axum::{routing::get, Router, ServiceExt, extract::Request};
+use tutors::fs_tutor_repo::FsTutorRepo;
+use std::{sync::Arc, env};
+use tower_http::services::ServeDir;
+use posts::fs_post_repo::FsPostRepo;
+
+mod helpers;
+mod posts;
+mod tutors;
+mod views;
+mod handlers;
+
+#[tokio::main]
+async fn main() {
+ let tracing_filter = EnvFilter::builder()
+ .with_env_var("CT_LOG")
+ .try_from_env()
+ .unwrap_or("carpentertutoring=debug,tower_http=debug,axum::rejection=trace".into());
+ tracing_subscriber::registry()
+ .with(tracing_filter)
+ .with(tracing_subscriber::fmt::layer())
+ .init();
+
+ info!("loading state...");
+ let blog_dir = env::var("CT_POSTS").unwrap();
+ let tutor_dir = env::var("CT_TEAM").unwrap();
+ let assets_dir = env::var("CT_ASSETS").unwrap();
+
+ let posts = Arc::new(FsPostRepo::with_dir(blog_dir));
+ let tutors = Arc::new(FsTutorRepo::with_dir(tutor_dir.clone()));
+
+ info!("initializing router...");
+ let app = Router::new()
+ .route("/", get(handlers::index_handler))
+ .route("/posts", get(handlers::posts_handler))
+ .route("/posts/:post_id", get(handlers::post_handler))
+ .with_state(posts)
+ .route("/policies", get(handlers::policies_handler))
+ .route("/brochure", get(handlers::brochure_handler))
+ .route("/about", get(handlers::about_handler))
+ .with_state(tutors)
+ .nest_service("/assets", ServeDir::new(assets_dir))
+ .nest_service("/team", ServeDir::new(tutor_dir))
+ .fallback_service(ServeDir::new("static"))
+ .layer(
+ TraceLayer::new_for_http()
+ .make_span_with(trace::DefaultMakeSpan::new()
+ .level(Level::INFO))
+ .on_response(trace::DefaultOnResponse::new()
+ .level(Level::INFO))
+ );
+ let app = NormalizePathLayer::trim_trailing_slash().layer(app);
+
+ let addr = env::var("CT_BIND").unwrap_or("0.0.0.0:8000".into());
+ let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
+ axum::serve(listener, ServiceExt::<Request>::into_make_service(app)).await.unwrap();
+}
diff --git a/src/posts.rs b/src/posts.rs
new file mode 100644
index 0000000..7f2c217
--- /dev/null
+++ b/src/posts.rs
@@ -0,0 +1,3 @@
+pub mod abstractions;
+pub mod fs_post;
+pub mod fs_post_repo;
diff --git a/src/posts/abstractions.rs b/src/posts/abstractions.rs
new file mode 100644
index 0000000..96c8ced
--- /dev/null
+++ b/src/posts/abstractions.rs
@@ -0,0 +1,2 @@
+pub mod post;
+pub mod repo;
diff --git a/src/posts/abstractions/post.rs b/src/posts/abstractions/post.rs
new file mode 100644
index 0000000..a941281
--- /dev/null
+++ b/src/posts/abstractions/post.rs
@@ -0,0 +1,6 @@
+use std::{borrow::Cow, fmt};
+
+pub trait Post: fmt::Debug + Ord {
+ fn get_title(&self) -> &str;
+ fn get_article(&self) -> Cow<str>;
+}
diff --git a/src/posts/abstractions/repo.rs b/src/posts/abstractions/repo.rs
new file mode 100644
index 0000000..6fd5d08
--- /dev/null
+++ b/src/posts/abstractions/repo.rs
@@ -0,0 +1,6 @@
+use crate::posts::abstractions::post::Post;
+
+pub trait PostRepo {
+ fn load(&self) -> impl IntoIterator<Item = impl Post>;
+ fn by_id(&self, post_id: &str) -> impl Post;
+}
diff --git a/src/posts/fs_post.rs b/src/posts/fs_post.rs
new file mode 100644
index 0000000..8b8e725
--- /dev/null
+++ b/src/posts/fs_post.rs
@@ -0,0 +1,47 @@
+use crate::posts::abstractions::post::Post;
+use std::{borrow::Cow, fs, path::PathBuf};
+
+#[derive(Debug, Eq)]
+pub struct FsPost {
+ file: PathBuf,
+}
+
+impl FsPost {
+ pub fn with_path(path: PathBuf) -> Self {
+ Self { file: path }
+ }
+}
+
+impl Post for FsPost {
+ fn get_title(&self) -> &str {
+ self.file.file_stem().unwrap().to_str().unwrap()
+ }
+
+ fn get_article(&self) -> Cow<str> {
+ let article = fs::read_to_string(&self.file).unwrap();
+ Cow::Owned(article)
+ }
+}
+
+impl Ord for FsPost {
+ fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+ self.file
+ .metadata()
+ .unwrap()
+ .modified()
+ .unwrap()
+ .cmp(&other.file.metadata().unwrap().modified().unwrap())
+ }
+}
+
+impl PartialOrd for FsPost {
+ fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl PartialEq for FsPost {
+ fn eq(&self, other: &Self) -> bool {
+ self.get_title() == other.get_title()
+ }
+}
diff --git a/src/posts/fs_post_repo.rs b/src/posts/fs_post_repo.rs
new file mode 100644
index 0000000..13f797b
--- /dev/null
+++ b/src/posts/fs_post_repo.rs
@@ -0,0 +1,32 @@
+use crate::posts::abstractions::post::Post;
+use crate::posts::abstractions::repo::PostRepo;
+use crate::posts::fs_post::FsPost;
+use std::{fs, path::PathBuf};
+
+pub struct FsPostRepo {
+ dir: PathBuf,
+}
+
+impl FsPostRepo {
+ pub fn with_dir(path: impl Into<PathBuf>) -> Self {
+ Self { dir: path.into() }
+ }
+}
+
+impl PostRepo for FsPostRepo {
+ fn load(&self) -> impl IntoIterator<Item = FsPost> {
+ let files = fs::read_dir(&self.dir).unwrap();
+ files
+ .flatten()
+ .filter(|d| !d.file_name().to_string_lossy().starts_with('.'))
+ .map(|d| FsPost::with_path(d.path()))
+ }
+
+ fn by_id(&self, post_id: &str) -> FsPost {
+ let posts = self.load();
+ posts
+ .into_iter()
+ .find(|p| p.get_title() == post_id)
+ .unwrap()
+ }
+}
diff --git a/src/tutors.rs b/src/tutors.rs
new file mode 100644
index 0000000..d90ef99
--- /dev/null
+++ b/src/tutors.rs
@@ -0,0 +1,3 @@
+pub mod abstractions;
+pub mod fs_tutor;
+pub mod fs_tutor_repo;
diff --git a/src/tutors/abstractions.rs b/src/tutors/abstractions.rs
new file mode 100644
index 0000000..a93c25d
--- /dev/null
+++ b/src/tutors/abstractions.rs
@@ -0,0 +1,2 @@
+pub mod tutor;
+pub mod tutor_repo;
diff --git a/src/tutors/abstractions/tutor.rs b/src/tutors/abstractions/tutor.rs
new file mode 100644
index 0000000..f36da3d
--- /dev/null
+++ b/src/tutors/abstractions/tutor.rs
@@ -0,0 +1,7 @@
+use std::{borrow::Cow, fmt};
+
+pub trait Tutor: fmt::Debug + Ord {
+ fn get_name(&self) -> &str;
+ fn get_id(&self) -> &str;
+ fn get_blurb(&self) -> Cow<str>;
+}
diff --git a/src/tutors/abstractions/tutor_repo.rs b/src/tutors/abstractions/tutor_repo.rs
new file mode 100644
index 0000000..e017806
--- /dev/null
+++ b/src/tutors/abstractions/tutor_repo.rs
@@ -0,0 +1,5 @@
+use crate::tutors::abstractions::tutor::Tutor;
+
+pub trait TutorRepo {
+ fn load(&self) -> impl IntoIterator<Item = impl Tutor>;
+}
diff --git a/src/tutors/fs_tutor.rs b/src/tutors/fs_tutor.rs
new file mode 100644
index 0000000..dc8a635
--- /dev/null
+++ b/src/tutors/fs_tutor.rs
@@ -0,0 +1,48 @@
+use crate::tutors::abstractions::tutor::Tutor;
+use std::{borrow::Cow, cmp::Ordering, fs, path::PathBuf};
+
+#[derive(Debug, Eq)]
+pub struct FsTutor {
+ dir: PathBuf,
+}
+
+impl FsTutor {
+ pub fn with_dir(path: PathBuf) -> Self {
+ Self { dir: path }
+ }
+}
+
+impl Tutor for FsTutor {
+ fn get_id(&self) -> &str {
+ self.dir.file_name().unwrap().to_str().unwrap()
+ }
+
+ fn get_name(&self) -> &str {
+ self.get_id()
+ }
+
+ fn get_blurb(&self) -> Cow<str> {
+ let mut path = self.dir.to_owned();
+ path.push(format!("{}.md", self.get_id()));
+ let blurb = fs::read_to_string(path).unwrap();
+ Cow::Owned(blurb)
+ }
+}
+
+impl Ord for FsTutor {
+ fn cmp(&self, other: &Self) -> Ordering {
+ self.get_id().cmp(other.get_id())
+ }
+}
+
+impl PartialOrd for FsTutor {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl PartialEq for FsTutor {
+ fn eq(&self, other: &Self) -> bool {
+ self.get_id() == other.get_id()
+ }
+}
diff --git a/src/tutors/fs_tutor_repo.rs b/src/tutors/fs_tutor_repo.rs
new file mode 100644
index 0000000..9b9a8d9
--- /dev/null
+++ b/src/tutors/fs_tutor_repo.rs
@@ -0,0 +1,29 @@
+use crate::tutors::abstractions::tutor_repo::TutorRepo;
+use crate::tutors::fs_tutor::FsTutor;
+use std::{fs, path::PathBuf};
+
+pub struct FsTutorRepo {
+ dir: PathBuf,
+}
+
+impl FsTutorRepo {
+ pub fn with_dir(path: impl Into<PathBuf>) -> Self {
+ Self { dir: path.into() }
+ }
+}
+
+impl TutorRepo for FsTutorRepo {
+ fn load(&self) -> impl IntoIterator<Item = FsTutor> {
+ let dirs = fs::read_dir(&self.dir).unwrap();
+ dirs.flatten()
+ .filter(|d| {
+ !d.path()
+ .file_stem()
+ .unwrap()
+ .to_str()
+ .unwrap_or_default()
+ .starts_with('.')
+ })
+ .map(|d| FsTutor::with_dir(d.path()))
+ }
+}
diff --git a/src/views.rs b/src/views.rs
new file mode 100644
index 0000000..cb58813
--- /dev/null
+++ b/src/views.rs
@@ -0,0 +1,6 @@
+pub mod about;
+pub mod brochure;
+pub mod index;
+pub mod policies;
+pub mod post;
+pub mod posts;
diff --git a/src/views/about.rs b/src/views/about.rs
new file mode 100644
index 0000000..349c9de
--- /dev/null
+++ b/src/views/about.rs
@@ -0,0 +1,17 @@
+use crate::helpers::*;
+use crate::tutors::abstractions::tutor::Tutor;
+use askama::Template;
+
+#[derive(Template)]
+#[template(path = "about/index.html")]
+pub struct AboutView<T: Tutor> {
+ tutors: Vec<T>,
+}
+
+impl<T: Tutor> AboutView<T> {
+ pub fn with_tutors(tutors: impl IntoIterator<Item = T>) -> Self {
+ let mut tutors: Vec<T> = tutors.into_iter().collect();
+ tutors.sort();
+ Self { tutors }
+ }
+}
diff --git a/src/views/brochure.rs b/src/views/brochure.rs
new file mode 100644
index 0000000..0d2f8fc
--- /dev/null
+++ b/src/views/brochure.rs
@@ -0,0 +1,6 @@
+use crate::helpers::*;
+use askama::Template;
+
+#[derive(Template)]
+#[template(path = "brochure/index.html")]
+pub struct BrochureTemplate;
diff --git a/src/views/index.rs b/src/views/index.rs
new file mode 100644
index 0000000..3ced24d
--- /dev/null
+++ b/src/views/index.rs
@@ -0,0 +1,6 @@
+use crate::helpers::*;
+use askama::Template;
+
+#[derive(Template)]
+#[template(path = "index.html")]
+pub struct IndexTemplate;
diff --git a/src/views/policies.rs b/src/views/policies.rs
new file mode 100644
index 0000000..3d9787d
--- /dev/null
+++ b/src/views/policies.rs
@@ -0,0 +1,6 @@
+use crate::helpers::*;
+use askama::Template;
+
+#[derive(Template)]
+#[template(path = "policies.html")]
+pub struct PoliciesTemplate;
diff --git a/src/views/post.rs b/src/views/post.rs
new file mode 100644
index 0000000..4f0554b
--- /dev/null
+++ b/src/views/post.rs
@@ -0,0 +1,15 @@
+use crate::helpers::*;
+use crate::posts::abstractions::post::Post;
+use askama::Template;
+
+#[derive(Template)]
+#[template(path = "post.html")]
+pub struct PostView<P: Post> {
+ post: P,
+}
+
+impl<P: Post> PostView<P> {
+ pub fn with_post(post: P) -> Self {
+ Self { post }
+ }
+}
diff --git a/src/views/posts.rs b/src/views/posts.rs
new file mode 100644
index 0000000..82b5996
--- /dev/null
+++ b/src/views/posts.rs
@@ -0,0 +1,18 @@
+use crate::helpers::*;
+use crate::posts::abstractions::post::Post;
+use askama::Template;
+
+#[derive(Template)]
+#[template(path = "posts.html")]
+pub struct PostsView<P: Post> {
+ posts: Vec<P>,
+}
+
+impl<P: Post> PostsView<P> {
+ pub fn with_posts(posts: impl IntoIterator<Item = P>) -> Self {
+ let mut posts: Vec<P> = posts.into_iter().collect();
+ posts.sort();
+ posts.reverse();
+ Self { posts }
+ }
+}
diff --git a/desktop.css b/static/desktop.css
index 1b0eb43..1b0eb43 100644
--- a/desktop.css
+++ b/static/desktop.css
diff --git a/assets/favicon.ico b/static/favicon.ico
index 8ba4c15..8ba4c15 100644
--- a/assets/favicon.ico
+++ b/static/favicon.ico
Binary files differ
diff --git a/widescreen.css b/static/widescreen.css
index 8231129..8231129 100644
--- a/widescreen.css
+++ b/static/widescreen.css
diff --git a/templates/about/blurb.html b/templates/about/blurb.html
new file mode 100644
index 0000000..4389d47
--- /dev/null
+++ b/templates/about/blurb.html
@@ -0,0 +1,3 @@
+<div id="{{ tutor.get_id() }}">
+ {{ tutor.get_blurb()|markdown }}
+</div>
diff --git a/templates/about/figure.html b/templates/about/figure.html
new file mode 100644
index 0000000..168aa4e
--- /dev/null
+++ b/templates/about/figure.html
@@ -0,0 +1,7 @@
+<figure class="shadowy">
+ <picture>
+ <source srcset="/team/{{ tutor.get_id() }}/{{ tutor.get_id() }}.webp" type="image/webp" />
+ <source srcset="/team/{{ tutor.get_id() }}/{{ tutor.get_id() }}.png" type="image/png" />
+ <img alt="{{ tutor.get_name() }}" src="/team/{{ tutor.get_id() }}/{{ tutor.get_id() }}.png" />
+ </picture>
+</figure>
diff --git a/about/index.php b/templates/about/index.html
index b821726..0d2434a 100644
--- a/about/index.php
+++ b/templates/about/index.html
@@ -1,4 +1,6 @@
-<?php include('../header.php'); ?>
+{% extends "base.html" %}
+
+{% block main %}
<section class="banner">
<h1>About Us</h1>
</section>
@@ -6,8 +8,8 @@
<section class="quiet squarshed">
<p>
<h2 style="font-size: 1.5em; text-align: center;">
- School is tricky, and there is no universal pathway to success. I started
- Carpenter Tutoring for those seeking a little help finding their way.
+ There are many pathways to success. I started Carpenter Tutoring for
+ those seeking a little help finding their way.
</h2>
</p>
</section>
@@ -64,36 +66,6 @@
<section></section>
-<?php
-// Using scandir because directory will remain sufficiently small for memory
-// constraints. Also skipping error handling while team list is a highly
-// controlled directory.
-$flip = true;
-$tutors = scandir('./team');
-
-foreach ($tutors as &$tutor) {
- if (str_starts_with($tutor, '.')) {
- continue;
- }
-?>
- <section class="quiet flexible">
-
- <?php
- if ($flip) {
- include('./blurb.php');
- include('./figure.php');
- } else {
- include('./figure.php');
- include('./blurb.php');
- }
- ?>
-
- </section>
-
- <section></section>
-<?php
- $flip = !$flip;
-}
-?>
+{% include "team.html" %}
-<?php include('../footer.php'); ?>
+{% endblock %}
diff --git a/templates/about/team.html b/templates/about/team.html
new file mode 100644
index 0000000..2857efe
--- /dev/null
+++ b/templates/about/team.html
@@ -0,0 +1,13 @@
+{% for tutor in tutors %}
+<section class="quiet flexible">
+ {% if loop.index0 % 2 == 0 %}
+ {% include "blurb.html" %}
+ {% include "figure.html" %}
+ {% else %}
+ {% include "figure.html" %}
+ {% include "blurb.html" %}
+ {% endif %}
+</section>
+
+<section></section>
+{% endfor %}
diff --git a/templates/base.html b/templates/base.html
new file mode 100644
index 0000000..48b49de
--- /dev/null
+++ b/templates/base.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+ <title>
+ Tutoring Excellence Online and in Suffolk, VA - Carpenter Tutoring, LLC
+ </title>
+ <style>
+ {% block style %}
+ {% include "styles.css" %}
+ {% endblock %}
+ </style>
+ <link rel="preconnect" href="https://fonts.gstatic.com" />
+ <link href="https://fonts.googleapis.com/css2?family=Architects+Daughter&family=Indie+Flower&family=PT+Sans&display=swap" rel="stylesheet" />
+ <link rel="stylesheet" href="/desktop.css" />
+ <link rel="stylesheet" href="/widescreen.css" />
+ <meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
+ <meta name="description" content="Tutoring Excellence Online and in Suffolk, VA" />
+ <meta property="og:description" content="Tutoring excellence online and in-person" />
+ <meta property="og:image" content="https://carpentertutoring.com/assets/logo.webp" />
+ <meta property="og:image:secure_url" content="https://carpentertutoring.com/assets/logo.webp" />
+ <meta property="og:site_name" content="Carpenter Tutoring" />
+ <meta property="og:title" content="Tutoring Excellence Online and in Suffolk, VA - Carpenter Tutoring, LLC" />
+ <meta property="og:type" content="website" />
+ <meta property="og:url" content="https://carpentertutoring.com" />
+</head>
+
+<body>
+ <nav>
+ <a href="/#reviews">Reviews</a>
+ <a href="/about">Team</a>
+ <a href="/policies">Policies</a>
+ <a href="/posts">Posts</a>
+ <a href="/#help">Helpful Links</a>
+ <a href="/brochure">Brochure</a>
+ <a href="/#offerings">Services</a>
+ <a href="/#"><img alt="logo" src="/assets/logo-simple.png" /></a>
+ </nav>
+
+ <main>
+ {% block main %}<p>Placeholder content</p>{% endblock %}
+ </main>
+
+ <footer>
+ <figure class="logo">
+ <picture>
+ <source srcset="/assets/logo.webp" type="image/webp" />
+ <source srcset="/assets/logo.png" type="image/png" />
+ <img alt="logo" src="/assets/logo.png" />
+ </picture>
+ </figure>
+ <p>
+ &copy; 2019-{{ self::current_year() }} Carpenter Tutoring, LLC. All rights reserved.
+ </p>
+ <p>
+ <a href="https://g.page/carpenter-tutoring?share">Visit me on Google</a>
+ |
+ <a href="https://g.page/carpenter-tutoring/review?np">Submit a review</a>
+ |
+ <a href="mailto:webmaster@carpentertutoring.com">
+ Having trouble with our site?
+ </a>
+ </p>
+ </footer>
+</body>
+
+</html>
diff --git a/brochure/styles.css b/templates/brochure/brochure.css
index 2abb118..2abb118 100644
--- a/brochure/styles.css
+++ b/templates/brochure/brochure.css
diff --git a/brochure/brochure.html b/templates/brochure/brochure.html
index 6690f4d..b5a3cd3 100644
--- a/brochure/brochure.html
+++ b/templates/brochure/brochure.html
@@ -8,7 +8,6 @@ The above copyright notice and this permission notice shall be included in all c
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.
-->
-
<button class="button">Flip</button>
<div class="paper">
<div class="page left">
diff --git a/brochure/brochure.js b/templates/brochure/brochure.js
index 4872373..4872373 100644
--- a/brochure/brochure.js
+++ b/templates/brochure/brochure.js
diff --git a/brochure/index.php b/templates/brochure/index.html
index 6dd3621..41a62f4 100644
--- a/brochure/index.php
+++ b/templates/brochure/index.html
@@ -1,4 +1,11 @@
-<?php include('../header.php'); ?>
+{% extends "base.html" %}
+
+{% block style %}
+ {% include "../styles.css" %}
+ {% include "brochure.css" %}
+{% endblock %}
+
+{% block main %}
<section class="banner">
<h1>Interactive Brochure</h1>
</section>
@@ -12,13 +19,14 @@
Click on pages to fold and unfold and use <em>Flip</em> to toggle front and back. This works best on a tablet or desktop screen.
</p>
-<?php include('./brochure.html')?>
+ {% include "brochure.html" %}
</section>
<section></section>
<script>
-<?php include('./brochure.js')?>
+ {% include "brochure.js" %}
</script>
-<?php include('../footer.php'); ?>
+
+{% endblock %}
diff --git a/index.php b/templates/index.html
index d3d83c1..dc4231b 100644
--- a/index.php
+++ b/templates/index.html
@@ -1,16 +1,6 @@
-<?php
-// honeypot handling for form bots
-session_start();
-
-if ($_SERVER['REQUEST_METHOD'] == 'GET') {
- $_SESSION['beehive'] = true;
-} else if (empty($_SESSION['beehive']) || !$_SESSION['beehive'] || !empty($_POST['beehive'])) {
- header('location: http://0.0.0.0/');
- die;
-}
-
-include('header.php');
-?>
+{% extends "base.html" %}
+
+{% block main %}
<!--banner-->
<section class="banner">
<h1>CARPENTER TUTORING</h1>
@@ -309,53 +299,11 @@ include('header.php');
</section>
-<section class="squarshed flexible centered">
- <a class="button primary" href="/brochure">Click here to view an interactive brochure of our offerings</a>
-</section>
-
-<!-- pricing -->
-<section class="quiet" id="pricing">
- <table>
- <caption>
- <h2>Pricing Guide - Remote*</h2>
- </caption>
- <tbody>
- <tr>
- <th scope="col">
- <a href="#offering-subject-tutoring">Single Subject</a>, <a href="#offering-time-management">Time Management</a>, <a href="#offering-study-skills">Study Skills Tutoring</a>, <a href="#offering-music-lessons">Music Lessons</a>
- </th>
- <td>$60/hr</td>
- </tr>
-
- <tr>
- <th scope="col">
- <a href="#offering-subject-tutoring">Multi-Subject</a>, <a href="#offering-academic-coaching">Academic Coaching</a>, <a href="#offering-dissertation-coaching">Dissertation Coaching</a>, <a href="#offering-test-prep">Test Prep</a>, <a href="#offering-college-prep">College
- Application Assistance</a>
- </th>
- <td>$70/hr</td>
- </tr>
-
- <tr>
- <th scope="col">Small Group Sessions, Group Lessons</th>
- <td>$40/hr/student</td>
- </tr>
- <tr>
- <th scope="col"><a href="#offering-evals">Simplified Homeschool Evaluations</a></th>
- <td>$45</td>
- </tr>
- <tr>
- <th scope="col"><a href="#offering-evals">Detailed Homeschool Evaluations</a></th>
- <td>$80+</td>
- </tr>
- </tbody>
- </table>
-
- <h3>
- <p><em>*In-person pricing for students in the Harbour View region of Suffolk is $10/hr more in each category.</em></p>
- <a href="/policies">See a list of our policies and procedures regarding scheduling,
- payment, and booking.</a>
- </h3>
-
+<!-- help -->
+<section class="quiet squarshed centered" id="help">
+ <h2>Helpful Links</h2>
+ <a class="button primary centered" href="/brochure">View an interactive brochure of our offerings</a>
+ <a class="button centered" href="/policies">See policies and procedures regarding scheduling, payment, and booking</a>
</section>
<!-- reviews -->
@@ -413,4 +361,4 @@ include('header.php');
</div>
</section>
-<?php include('footer.php'); ?>
+{% endblock %}
diff --git a/policies/index.php b/templates/policies.html
index 38dc2d2..0d39b38 100644
--- a/policies/index.php
+++ b/templates/policies.html
@@ -1,4 +1,7 @@
-<?php include('../header.php'); ?>
+{% extends "base.html" %}
+
+{% block main %}
+
<section class="banner">
<h1>Policies &amp; Procedures</h1>
</section>
@@ -58,4 +61,5 @@
</p>
</section>
<section></section>
-<?php include('../footer.php'); ?>
+
+{% endblock %}
diff --git a/templates/post.html b/templates/post.html
new file mode 100644
index 0000000..3155c7a
--- /dev/null
+++ b/templates/post.html
@@ -0,0 +1,13 @@
+{% extends "base.html" %}
+
+{% block main %}
+<section class="banner">
+ <h1>{{ post.get_title() }}</h1>
+</section>
+
+<section class="quiet">
+ <article>
+ {{ post.get_article()|markdown }}
+ </article>
+</section>
+{% endblock %}
diff --git a/templates/posts.html b/templates/posts.html
new file mode 100644
index 0000000..052cd42
--- /dev/null
+++ b/templates/posts.html
@@ -0,0 +1,15 @@
+{% extends "base.html" %}
+
+{% block main %}
+<section class="banner">
+ <h1>Posts</h1>
+</section>
+
+<section class="quiet">
+ <p>
+ {% for post in posts %}
+ <h2><a href="/posts/{{ post.get_title()|e }}">{{ post.get_title() }}</a></h2>
+ {% endfor %}
+ </p>
+<section>
+{% endblock %}
diff --git a/templates/styles.css b/templates/styles.css
new file mode 100644
index 0000000..68eae3b
--- /dev/null
+++ b/templates/styles.css
@@ -0,0 +1,272 @@
+/* Colors */
+:root {
+ --darkteal: rgb(0, 100, 108);
+ --green: rgb(88, 169, 143);
+ --darkerteal: rgb(29, 133, 137);
+ --gray: rgb(157, 210, 211);
+ --teal: rgb(58, 165, 166);
+ --cyan: rgb(156, 210, 210);
+ --darkgray: rgb(127, 177, 181);
+ --darkgreen: rgb(59, 136, 114);
+ --yellow: rgb(186, 214, 187);
+}
+
+/* Elements */
+
+html,
+body {
+ margin: 0;
+ padding: 0;
+ font-family: "PT Sans", sans-serif;
+ color: dimgray;
+}
+
+body {
+ background-image: url("/assets/bg.png");
+}
+
+.banner {
+ font-size: 1em;
+ text-align: center;
+ padding: 3em;
+ background-color: white;
+}
+
+nav {
+ display: flex;
+ background-color: white;
+ flex-direction: column-reverse;
+}
+
+nav img {
+ height: 1.4em;
+}
+
+nav a {
+ font-family: "PT Sans", sans-serif;
+ color: dimgray;
+ text-align: center;
+ text-decoration: none;
+ padding: 1em 1em;
+ transition: 0.15s;
+}
+
+nav a:hover {
+ color: var(--darkteal);
+ background-color: lightgray;
+}
+
+div.buttons {
+ display: flex;
+ flex-direction: column;
+}
+
+h1 {
+ font-size: 3em;
+ color: var(--darkteal);
+ font-family: "Architects Daughter", cursive;
+}
+
+h2 {
+ font-size: 2em;
+ color: var(--darkteal);
+ font-family: "Indie Flower", cursive;
+}
+
+em {
+
+ color: var(--darkteal);
+}
+
+section {
+ font-size: 1.5em;
+ margin: 0;
+ padding: 1em;
+}
+
+section.quiet {
+ background-color: white;
+}
+
+section.squarshed {
+ padding-bottom: 1em;
+ padding-top: 1em;
+}
+
+.button img {
+ margin-right: 1em;
+}
+
+button.button,
+a.button {
+ display: flex;
+ align-items: center;
+ background-color: var(--teal);
+ padding-left: 1em;
+ padding-right: 1em;
+ padding-top: 0.5em;
+ padding-bottom: 0.5em;
+ margin-right: 0.5em;
+ margin-bottom: 0.5em;
+ text-decoration: none;
+ color: white;
+ border-radius: 3em;
+ transition: 0.15s;
+}
+
+button.button:hover,
+a.button:hover {
+ box-shadow: 0 4px 8px 0 lightgray, 0 6px 20px 0 lightgray;
+}
+
+button {
+ font-family: "PT Sans", sans-serif;
+ border: none;
+ font-size: 0.75em;
+}
+
+a.primary {
+ background-color: var(--darkteal);
+}
+
+footer {
+ background-color: white;
+ text-align: center;
+ padding: 3em;
+ font-size: 1.5em;
+}
+
+footer a,
+section a {
+ color: var(--darkteal);
+ text-decoration: underline;
+}
+
+.card {
+ font-size: 0.9em;
+ background-color: white;
+ padding: 1em;
+ border-radius: 1em;
+ margin-left: 0;
+ margin-right: 0;
+}
+
+form input,
+form textarea {
+ font-family: "PT Sans", sans-serif;
+ margin-bottom: 1.5em;
+ box-sizing: border-box;
+ width: 100%;
+ border: 1px solid lightgray;
+ padding: 1em;
+}
+
+form input {
+ border-radius: 3em;
+}
+
+textarea {
+ border-radius: 1em;
+ resize: none;
+}
+
+/* honeypot */
+textarea#beehive {
+ display: none;
+}
+
+table {
+ width: 100%;
+ border-collapse: collapse;
+}
+
+table caption {
+ text-align: left;
+}
+
+table th {
+ text-align: right;
+ color: var(--darkteal);
+}
+
+table td,
+table th {
+ vertical-align: bottom;
+}
+
+tr:nth-child(even) {
+ background-color: lightgray;
+}
+
+picture img {
+ width: 100%;
+}
+
+.shadowy img {
+ box-shadow: 0 4px 8px 0 dimgray, 0 6px 20px 0 dimgray;
+}
+
+figcaption {
+ text-align: right;
+ color: var(--darkteal);
+ font-family: "Architects Daughter", cursive;
+}
+
+section.flexible {
+ display: flex;
+ flex-direction: column;
+}
+
+#reviews .card,
+#offerings .card {
+ margin: 1em;
+ text-align: center;
+}
+
+#reviews,
+#offerings {
+ padding-left: 0;
+ padding-right: 0;
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+}
+
+.modal {
+ position: fixed;
+ z-index: 1;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ background-color: white;
+ background-color: rgba(1, 1, 1, 0.4);
+ overflow: auto;
+ display: none;
+ font-size: 1.5em;
+}
+
+.modal .card {
+ max-height: 90%;
+ max-width: 60%;
+}
+
+.modal a,
+.card a.button {
+ float: right;
+}
+
+:target.modal {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+:target.modal .card {
+ overflow: auto;
+}
+
+.centered {
+ text-align: center;
+ justify-content: center;
+}
diff --git a/tutors/email/index.php b/tutors/email/index.php
deleted file mode 100644
index 7ea8211..0000000
--- a/tutors/email/index.php
+++ /dev/null
@@ -1,150 +0,0 @@
-<?php include('../../header.php'); ?>
-
-<section class="banner">
- <h1>Email setup</h1>
-</section>
-
-<section class="quiet flexible">
- <div>
- <p>
- Every tutor is expected to use a <em>@carpentertutoring.com</em> email address to facilitate client communication. This guide will walk you through setting up your email from scratch and configuring your mail client to use it.
- </p>
-
- <p>
- You have two options for using your new email address. The first is <em>standalone</em> which allows you to use the mail client of your choice to send and receive mail. Some popular desktop clients are Apple's Mail app, Mozilla Thunderbird, and Microsoft Outlook. Popular mobile mail clients include the iOS Mail app and K-9 Mail for Android. Your mailbox will remain on the Carpenter Tutoring mail server and in no way interact with any personal or private mail accounts you have on other systems.
- </p>
-
- <p>
- Your second choice is <em>forwarding</em>, where all of your Carpenter Tutoring mail will forward to a personal email address of your choice. Your personal email provider will need to be configured to send mail as your Carpenter Tutoring address for any client communication. The amount of setup is the same, but this method allows you to use a client such as <em>GMail</em> for managing mail.
- </p>
-
- <p>
- Remember you can change your decision anytime. See "Getting help" below.
- </p>
-
- <?php
- if (empty($_POST['password'])) {
- ?>
- <h2>Create a mail account</h2>
- <form method="post" action="/tutors/email/index.php#submit">
- <label for="name">Your full name
- <input type="text" name="name" placeholder="Amy Carpenter" required />
- </label>
-
- <label for="password">Your new Carpenter Tutoring email password
- <input type="password" name="password" minlength="8" placeholder="8 character minimum" required />
- </label>
-
- <label for="forward">Personal email address for forwarding
- <input type="email" name="forward" placeholder="leave blank to use a standalone mail client" />
- </label>
-
- <button type="submit" class="button" alt="submit">Set me up</button>
- </form>
- <?
- }
- ?>
-
- <?php
- if (!empty($_POST['password']) && !empty($_POST['name'])) {
- // encrypt email and add to logins queue
- $descriptorspec = array(
- 0 => array('pipe', 'r'),
- 1 => array('file', '/var/ct_logins', 'a'),
- 2 => array('file', '/var/log/ct_mail_submit.log', 'a')
- );
- $process = proc_open('smtpctl encrypt', $descriptorspec, $pipes);
- fwrite($pipes[0], $_POST['password']);
- fclose($pipes[0]);
-
- // include name and generated email in logins queue
- $logins_queue = fopen('/var/ct_logins', 'a');
- fprintf($logins_queue, '\n%s\n', $_POST['name']);
-
- $names = explode(' ', strtolower($_POST['name']));
-
- $last = $names[1][0];
- $first = $names[0];
- $email = "$first$last@carpentertutoring.com";
- fprintf($logins_queue, '%s\n\n', $email);
-
- // close queue
- fclose($logins_queue);
- }
- ?>
- <h2 id="submit">Configuring your mail client</h2>
- <p>
- You may choose to use a third-party mail app to check your mail, forward your mail to a GMail address, or use another third-party webmail client. Follow the instructions below for the option you chose when you created your password. The connection settings remain the same either way.
- </p>
-
- <h3>Connection details</h2>
- <pre>
-SMTP server: mail.53hor.net
-Connection security: STARTTLS
-Authentication method: normal password
-SMTP port: 587
-SMTP password: (use the password you just submitted)
-SMTP username: (full carpenter tutoring email address)
-
-
-IMAP server: mail.53hor.net
-Connection security: STARTTLS
-Authentication method: normal password
-IMAP port: 143
-IMAP password: (use the password you just submitted)
-IMAP username: (full carpenter tutoring email address)
-</pre>
- <h3>Gmail forwarding</h3>
-
- <p>
- Once your <em>@carpentertutoring.com</em> email address is created, you will begin receiving forwarded email in your Gmail inbox. Sending mail from your <em>@carpentertutoring.com</em> address via Gmail requires a small amount of setup.
- </p>
-
- <p>
-
- <ol>
- <li>On your computer, open <a href="https://mail.google.com" target="_blank">Gmail</a>.</li>
- <li>In the top right, click settings (⚙️) and then <em>See all settings</em>.</li>
- <li>Click the <em>Accounts and import</em> or <em>Accounts</em> tab.</li>
- <li>In the "Send mail as" section, click <em>Add another email address</em>.</li>
- <li>Enter your name and the address you want to send from. This will be your <em>@carpentertutoring.com</em> address. Leave the "Treat as an alias" box checked.</li>
- <li>Click <em>Next Step</em>.</li>
- <li>For the "SMTP server" enter <em>mail.53hor.net</em>. Select <em>587</em> for the port.</li>
- <li>For the username and password you will enter your new Carpenter Tutoring address (with "@carpentertutoring.com" suffix) and the password you submitted in the previous step.</li>
- <li>Check "Secured connection using TLS (recommended)" and click <em>Add Account</em>.</li>
- <li>Gmail will prompt you to verify your new address. You should receive a confirmation code in your inbox, forwarded from your <em>@carpentertutoring.com</em> address. Use it to complete the alias setup.</li>
- <li>Return to the "Send mail as" section on the settings page and look for "When replying to a message." Check "Reply from the same address the message was sent to." This will ensure you respond to tutoring inquiries with your Carpenter Tutoring address and personal mail with your Gmail address.</li>
-
- </ol>
- </p>
-
- <h3>Native mail clients</h3>
- <p>
- <ul>
- <li><a href="https://support.apple.com/en-us/HT201320">iOS Mail app</a></li>
- <li><a href="https://support.microsoft.com/en-us/office/set-up-email-in-the-mail-app-7ff79e8b-439b-4b47-8ff9-3f9a33166c60">Mail app for Windows 10 or Windows 11</a></li>
- <li><a href="https://support.mozilla.org/en-US/kb/manual-account-configuration">Mozilla Thunderbird</a></li>
- </ul>
- </p>
-
- <h3>Other webmail clients</h3>
-
- <p>
- Setup instructions for other popular webmail clients are largely the same. The configuration options for IMAP and SMTP should be identical. Below is a list of setup instructions for common providers.
- </p>
- <p>
- <ul>
- <li><a href="https://support.microsoft.com/en-gb/office/add-or-remove-an-email-alias-in-outlook-com-459b1989-356d-40fa-a689-8f285b13f1f2">Outlook.com (MS Hotmail)</a></li>
- <li><a href="https://support.apple.com/guide/icloud/add-and-manage-email-aliases-mm6b1a490a/icloud">iCloud.com (Apple iCloud)</a></li>
-
- </ul>
- </p>
- <h2>Getting help</h2>
- <p>
- If you encounter difficulty or have questions throughout any part of this process, please reach out to <a href="webmaster@carpentertutoring.com">webmaster@carpentertutoring.com</a> and we'll get in touch to help.
- </p>
- </div>
-
-</section>
-
-<?php include('../../footer.php'); ?>