diff --git a/.env.example b/.env.example index c6f6eba..f4395b4 100644 --- a/.env.example +++ b/.env.example @@ -10,3 +10,6 @@ BACKEND_UNTIS_CLIENT_NAME="bvplan" BACKEND_UNTIS_SCHOOL= BACKEND_UNTIS_USERNAME= BACKEND_UNTIS_PASSWORD= +BACKEND_UNTIS_VPLAN_URL= +BACKEND_UNTIS_VPLAN_USERNAME= +BACKEND_UNTIS_VPLAN_PASSWORD= diff --git a/Makefile b/Makefile index d6d7152..c7c6c67 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,6 @@ -.PHONY: all dev prod +.PHONY: all build -all: prod +all: build -dev: - docker-compose build --build-arg BUILD_ENV=development - -prod: - docker-compose build +build: + BUILDKIT_PROGRESS=plain docker compose build diff --git a/backend/.editorconfig b/backend/.editorconfig index 373508e..bfb95f2 100644 --- a/backend/.editorconfig +++ b/backend/.editorconfig @@ -7,3 +7,27 @@ insert_final_newline = true indent_style = space indent_size = 4 trim_trailing_whitespace = true + +[*.sql] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true + +[*.sql] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true + +[*.html] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true diff --git a/backend/Cargo.lock b/backend/Cargo.lock index 93f457c..47e0660 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -92,7 +92,7 @@ dependencies = [ "actix-service", "actix-utils", "ahash", - "base64", + "base64 0.13.1", "bitflags", "brotli", "bytes", @@ -105,7 +105,7 @@ dependencies = [ "http", "httparse", "httpdate", - "itoa", + "itoa 1.0.5", "language-tags", "local-channel", "mime", @@ -215,7 +215,7 @@ dependencies = [ "futures-core", "futures-util", "http", - "itoa", + "itoa 1.0.5", "language-tags", "log", "mime", @@ -389,9 +389,9 @@ dependencies = [ [[package]] name = "arc-swap" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "983cd8b9d4b02a6dc6ffa557262eb5858a27a0038ffffe21a0f133eaa819a164" +checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" [[package]] name = "ascii" @@ -467,7 +467,7 @@ dependencies = [ "slab", "socket2", "waker-fn", - "windows-sys 0.42.0", + "windows-sys", ] [[package]] @@ -500,9 +500,9 @@ checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" [[package]] name = "async-trait" -version = "0.1.60" +version = "0.1.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d1d8ab452a3936018a687b20e6f7cf5363d713b732b8884001317b0e48aa3" +checksum = "705339e0e4a9690e2908d2b3d049d85682cf19fbd5782494498fbf7003a6a282" dependencies = [ "proc-macro2", "quote", @@ -548,7 +548,11 @@ dependencies = [ "juniper_actix", "lazy_static", "log", + "r2d2_redis", + "reqwest", + "scraper", "serde", + "stdext", "tikv-jemallocator", "tokio", "untis", @@ -576,6 +580,12 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + [[package]] name = "bitflags" version = "1.3.2" @@ -641,7 +651,7 @@ version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de0aa578035b938855a710ba58d43cfb4d435f3619f99236fb35922a574d6cb1" dependencies = [ - "base64", + "base64 0.13.1", "chrono", "hex", "lazy_static", @@ -654,11 +664,12 @@ dependencies = [ [[package]] name = "bstr" -version = "0.2.17" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +checksum = "b45ea9b00a7b3f2988e9a65ad3917e62123c38dba709b666506207be96d1790b" dependencies = [ "memchr", + "serde", ] [[package]] @@ -709,10 +720,10 @@ dependencies = [ [[package]] name = "celery" version = "0.4.0-rcn.11" -source = "git+https://github.com/rusty-celery/rusty-celery.git?branch=main#1bf8729cd107e7e0919eda19224f4cd2be428f88" +source = "git+https://github.com/rusty-celery/rusty-celery.git?branch=main#a966d04188abfb0ea5af417c58c35ffa5af15978" dependencies = [ "async-trait", - "base64", + "base64 0.13.1", "celery-codegen", "chrono", "colored", @@ -723,7 +734,7 @@ dependencies = [ "log", "once_cell", "rand 0.8.5", - "redis", + "redis 0.21.7", "serde", "serde_json", "thiserror", @@ -737,7 +748,7 @@ dependencies = [ [[package]] name = "celery-codegen" version = "0.4.0-rcn.11" -source = "git+https://github.com/rusty-celery/rusty-celery.git?branch=main#1bf8729cd107e7e0919eda19224f4cd2be428f88" +source = "git+https://github.com/rusty-celery/rusty-celery.git?branch=main#a966d04188abfb0ea5af417c58c35ffa5af15978" dependencies = [ "proc-macro2", "quote", @@ -920,10 +931,37 @@ dependencies = [ ] [[package]] -name = "cxx" -version = "1.0.85" +name = "cssparser" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5add3fc1717409d029b20c5b6903fc0c0b02fa6741d820054f4a2efa5e5816fd" +checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa 0.4.8", + "matches", + "phf 0.8.0", + "proc-macro2", + "quote", + "smallvec", + "syn", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfae75de57f2b2e85e8768c3ea840fd159c8f33e2b6522c7835b7abac81be16e" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "cxx" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d1075c37807dcf850c379432f0df05ba52cc30f279c5cfc43cc221ce7f8579" dependencies = [ "cc", "cxxbridge-flags", @@ -933,9 +971,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.85" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c87959ba14bc6fbc61df77c3fcfe180fc32b93538c4f1031dd802ccb5f2ff0" +checksum = "5044281f61b27bc598f2f6647d480aed48d2bf52d6eb0b627d84c0361b17aa70" dependencies = [ "cc", "codespan-reporting", @@ -948,15 +986,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.85" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69a3e162fde4e594ed2b07d0f83c6c67b745e7f28ce58c6df5e6b6bef99dfb59" +checksum = "61b50bc93ba22c27b0d31128d2d130a0a6b3d267ae27ef7e4fae2167dfe8781c" [[package]] name = "cxxbridge-macro" -version = "1.0.85" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e7e2adeb6a0d4a282e581096b06e1791532b7d576dcde5ccd9382acf55db8e6" +checksum = "39e61fda7e62115119469c7b3591fd913ecca96fb766cfd3f2e2502ab7bc87a5" dependencies = [ "proc-macro2", "quote", @@ -1006,7 +1044,7 @@ dependencies = [ "byteorder", "chrono", "diesel_derives", - "itoa", + "itoa 1.0.5", "pq-sys", "r2d2", ] @@ -1040,6 +1078,27 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +[[package]] +name = "dtoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" + +[[package]] +name = "dtoa-short" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bde03329ae10e79ede66c9ce4dc930aa8599043b0743008548680f25b91502d6" +dependencies = [ + "dtoa", +] + +[[package]] +name = "ego-tree" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a68a4904193147e0a8dec3314640e6db742afd5f6e634f428a6af230d9b3591" + [[package]] name = "either" version = "1.8.0" @@ -1057,12 +1116,12 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.10.0" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" dependencies = [ + "atty", "humantime", - "is-terminal", "log", "regex", "termcolor", @@ -1088,27 +1147,6 @@ dependencies = [ "syn", ] -[[package]] -name = "errno" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" -dependencies = [ - "errno-dragonfly", - "libc", - "winapi", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "event-listener" version = "2.5.3" @@ -1191,6 +1229,16 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394" +[[package]] +name = "futf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" +dependencies = [ + "mac", + "new_debug_unreachable", +] + [[package]] name = "futures" version = "0.3.25" @@ -1306,6 +1354,15 @@ dependencies = [ "slab", ] +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + [[package]] name = "generic-array" version = "0.14.6" @@ -1316,6 +1373,15 @@ dependencies = [ "version_check", ] +[[package]] +name = "getopts" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +dependencies = [ + "unicode-width", +] + [[package]] name = "getrandom" version = "0.1.16" @@ -1346,9 +1412,9 @@ checksum = "dec7af912d60cdbd3677c1af9352ebae6fb8394d165568a2234df0fa00f87793" [[package]] name = "globset" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a1e17342619edbc21a964c2afbeb6c820c6a2560032872f397bb97ea127bd0a" +checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" dependencies = [ "aho-corasick", "bstr", @@ -1436,6 +1502,20 @@ dependencies = [ "winapi", ] +[[package]] +name = "html5ever" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" +dependencies = [ + "log", + "mac", + "markup5ever", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "http" version = "0.2.8" @@ -1444,7 +1524,7 @@ checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" dependencies = [ "bytes", "fnv", - "itoa", + "itoa 1.0.5", ] [[package]] @@ -1491,7 +1571,7 @@ dependencies = [ "http-body", "httparse", "httpdate", - "itoa", + "itoa 1.0.5", "pin-project-lite", "socket2", "tokio", @@ -1577,33 +1657,17 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "io-lifetimes" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c" -dependencies = [ - "libc", - "windows-sys 0.42.0", -] - [[package]] name = "ipnet" -version = "2.7.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11b0d96e660696543b251e58030cf9787df56da39dab19ad60eae7353040917e" +checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" [[package]] -name = "is-terminal" -version = "0.4.1" +name = "itoa" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927609f78c2913a6f6ac3c27a4fe87f43e2a35367c0c4b0f8265e8f49a104330" -dependencies = [ - "hermit-abi 0.2.6", - "io-lifetimes", - "rustix", - "windows-sys 0.42.0", -] +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" [[package]] name = "itoa" @@ -1718,9 +1782,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.138" +version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "link-cplusplus" @@ -1737,12 +1801,6 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" -[[package]] -name = "linux-raw-sys" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" - [[package]] name = "local-channel" version = "0.1.3" @@ -1780,12 +1838,38 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" + +[[package]] +name = "markup5ever" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" +dependencies = [ + "log", + "phf 0.10.1", + "phf_codegen 0.10.0", + "string_cache", + "string_cache_codegen", + "tendril", +] + [[package]] name = "match_cfg" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + [[package]] name = "memchr" version = "2.5.0" @@ -1822,7 +1906,7 @@ dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.42.0", + "windows-sys", ] [[package]] @@ -1844,10 +1928,22 @@ dependencies = [ ] [[package]] -name = "nom" -version = "7.1.1" +name = "new_debug_unreachable" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" +checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[package]] +name = "nom" +version = "7.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5507769c4919c998e69e49c839d9dc6e693ede4cc4290d6ad8b41d4f09c548c" dependencies = [ "memchr", "minimal-lexical", @@ -1884,18 +1980,18 @@ dependencies = [ [[package]] name = "object" -version = "0.30.0" +version = "0.30.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239da7f290cfa979f43f85a8efeee9a8a76d0827c356d37f9d3d7254d6b537fb" +checksum = "2b8c786513eb403643f2a88c244c2aaa270ef2153f55094587d0c48a3cf22a83" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" +checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" [[package]] name = "openssl" @@ -1983,7 +2079,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.5", + "parking_lot_core 0.9.6", ] [[package]] @@ -2002,15 +2098,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba" +checksum = "ba1ef8814b5c993410bb3adfad7a5ed269563e4a2f90c41f5d85be7fb47133bf" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-sys 0.42.0", + "windows-sys", ] [[package]] @@ -2025,6 +2121,98 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +[[package]] +name = "phf" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" +dependencies = [ + "phf_macros", + "phf_shared 0.8.0", + "proc-macro-hack", +] + +[[package]] +name = "phf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_shared 0.10.0", +] + +[[package]] +name = "phf_codegen" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", +] + +[[package]] +name = "phf_codegen" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", +] + +[[package]] +name = "phf_generator" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" +dependencies = [ + "phf_shared 0.8.0", + "rand 0.7.3", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared 0.10.0", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_shared" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" +dependencies = [ + "siphasher", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project" version = "1.0.12" @@ -2086,7 +2274,7 @@ dependencies = [ "libc", "log", "wepoll-ffi", - "windows-sys 0.42.0", + "windows-sys", ] [[package]] @@ -2104,6 +2292,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -2128,6 +2322,12 @@ dependencies = [ "version_check", ] +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + [[package]] name = "proc-macro2" version = "1.0.49" @@ -2157,6 +2357,16 @@ dependencies = [ "scheduled-thread-pool", ] +[[package]] +name = "r2d2_redis" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "182473b876b0b93e353682ec58e207dd1cb4a62278bbe0045fe52b86b74363bb" +dependencies = [ + "r2d2", + "redis 0.20.2", +] + [[package]] name = "rand" version = "0.7.3" @@ -2168,6 +2378,7 @@ dependencies = [ "rand_chacha 0.2.2", "rand_core 0.5.1", "rand_hc", + "rand_pcg", ] [[package]] @@ -2228,6 +2439,15 @@ dependencies = [ "rand_core 0.5.1", ] +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core 0.5.1", +] + [[package]] name = "rc2" version = "0.8.1" @@ -2248,6 +2468,21 @@ dependencies = [ "futures-io", ] +[[package]] +name = "redis" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4f0ceb2ec0dd769483ecd283f6615aa83dcd0be556d5294c6e659caefe7cc54" +dependencies = [ + "async-trait", + "combine 4.6.6", + "dtoa", + "itoa 0.4.8", + "percent-encoding", + "sha1 0.6.1", + "url", +] + [[package]] name = "redis" version = "0.21.7" @@ -2260,7 +2495,7 @@ dependencies = [ "combine 4.6.6", "futures", "futures-util", - "itoa", + "itoa 1.0.5", "percent-encoding", "pin-project-lite", "ryu", @@ -2281,9 +2516,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" dependencies = [ "aho-corasick", "memchr", @@ -2311,7 +2546,7 @@ version = "0.11.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68cc60575865c7831548863cc02356512e3f1dc2f3f82cb837d7fc4cc8f3c97c" dependencies = [ - "base64", + "base64 0.13.1", "bytes", "encoding_rs", "futures-core", @@ -2372,25 +2607,11 @@ dependencies = [ "semver", ] -[[package]] -name = "rustix" -version = "0.36.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3807b5d10909833d3e9acd1eb5fb988f79376ff10fce42937de71a449c4c588" -dependencies = [ - "bitflags", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys", - "windows-sys 0.42.0", -] - [[package]] name = "rustls" -version = "0.20.7" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "539a2bfe908f471bfa933876bd1eb6a19cf2176d375f82ef7f99530a40e48c2c" +checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" dependencies = [ "log", "ring", @@ -2424,11 +2645,11 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55" +checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" dependencies = [ - "base64", + "base64 0.21.0", ] [[package]] @@ -2439,12 +2660,11 @@ checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" [[package]] name = "schannel" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" dependencies = [ - "lazy_static", - "windows-sys 0.36.1", + "windows-sys", ] [[package]] @@ -2462,6 +2682,22 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "scraper" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7cb4dae083699a22a65aa9d2699c27f525e35dffaec38b10801e958ed4cf27" +dependencies = [ + "cssparser", + "ego-tree", + "getopts", + "html5ever", + "matches", + "selectors", + "smallvec", + "tendril", +] + [[package]] name = "scratch" version = "1.0.3" @@ -2501,6 +2737,26 @@ dependencies = [ "libc", ] +[[package]] +name = "selectors" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" +dependencies = [ + "bitflags", + "cssparser", + "derive_more", + "fxhash", + "log", + "matches", + "phf 0.8.0", + "phf_codegen 0.8.0", + "precomputed-hash", + "servo_arc", + "smallvec", + "thin-slice", +] + [[package]] name = "semver" version = "1.0.16" @@ -2509,18 +2765,18 @@ checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" [[package]] name = "serde" -version = "1.0.151" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fed41fc1a24994d044e6db6935e69511a1153b52c15eb42493b26fa87feba0" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.151" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "255abe9a125a985c05190d687b320c12f9b1f0b99445e608c21ba0782c719ad8" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" dependencies = [ "proc-macro2", "quote", @@ -2534,7 +2790,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" dependencies = [ "indexmap", - "itoa", + "itoa 1.0.5", "ryu", "serde", ] @@ -2546,11 +2802,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa", + "itoa 1.0.5", "ryu", "serde", ] +[[package]] +name = "servo_arc" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432" +dependencies = [ + "nodrop", + "stable_deref_trait", +] + [[package]] name = "sha1" version = "0.6.1" @@ -2586,6 +2852,12 @@ dependencies = [ "libc", ] +[[package]] +name = "siphasher" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" + [[package]] name = "slab" version = "0.4.7" @@ -2635,12 +2907,50 @@ dependencies = [ "lock_api", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "stdext" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f3b6b32ae82412fb897ef134867d53a294f57ba5b758f06d71e865352c3e207" + +[[package]] +name = "string_cache" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213494b7a2b503146286049378ce02b482200519accc31872ee8be91fa820a08" +dependencies = [ + "new_debug_unreachable", + "once_cell", + "parking_lot 0.12.1", + "phf_shared 0.10.0", + "precomputed-hash", + "serde", +] + +[[package]] +name = "string_cache_codegen" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", + "proc-macro2", + "quote", +] + [[package]] name = "subtle" version = "2.4.1" @@ -2684,6 +2994,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "tendril" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +dependencies = [ + "futf", + "mac", + "utf-8", +] + [[package]] name = "termcolor" version = "1.1.3" @@ -2693,6 +3014,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "thin-slice" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" + [[package]] name = "thiserror" version = "1.0.38" @@ -2751,7 +3078,7 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" dependencies = [ - "itoa", + "itoa 1.0.5", "serde", "time-core", "time-macros", @@ -2789,9 +3116,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.23.0" +version = "1.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eab6d665857cc6ca78d6e80303a02cea7a7851e85dfbd77cbdc09bd129f1ef46" +checksum = "1d9f76183f91ecfb55e1d7d5602bd1d979e38a3a522fe900241cf195624d67ae" dependencies = [ "autocfg", "bytes", @@ -2804,7 +3131,7 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.42.0", + "windows-sys", ] [[package]] @@ -2921,9 +3248,9 @@ dependencies = [ [[package]] name = "try-lock" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "typenum" @@ -2970,7 +3297,7 @@ dependencies = [ [[package]] name = "untis" version = "0.1.0" -source = "git+https://git.dergrimm.net/dergrimm/untis.rs.git?branch=main#197b4e2ecf94bd1765593e8680008162d41a3015" +source = "git+https://git.dergrimm.net/dergrimm/untis.rs.git?branch=main#0651a3929683ba9aace219b710951d555253ec26" dependencies = [ "anyhow", "chrono", @@ -2998,6 +3325,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "uuid" version = "0.8.2" @@ -3185,19 +3518,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" -dependencies = [ - "windows_aarch64_msvc 0.36.1", - "windows_i686_gnu 0.36.1", - "windows_i686_msvc 0.36.1", - "windows_x86_64_gnu 0.36.1", - "windows_x86_64_msvc 0.36.1", -] - [[package]] name = "windows-sys" version = "0.42.0" @@ -3205,85 +3525,55 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ "windows_aarch64_gnullvm", - "windows_aarch64_msvc 0.42.0", - "windows_i686_gnu 0.42.0", - "windows_i686_msvc 0.42.0", - "windows_x86_64_gnu 0.42.0", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", "windows_x86_64_gnullvm", - "windows_x86_64_msvc 0.42.0", + "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" [[package]] name = "windows_aarch64_msvc" -version = "0.36.1" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" [[package]] name = "windows_i686_gnu" -version = "0.36.1" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" [[package]] name = "windows_i686_msvc" -version = "0.36.1" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" [[package]] name = "windows_x86_64_gnu" -version = "0.36.1" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" [[package]] name = "windows_x86_64_msvc" -version = "0.36.1" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" [[package]] name = "winreg" @@ -3321,10 +3611,11 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.4+zstd.1.5.2" +version = "2.0.5+zstd.1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fa202f2ef00074143e219d15b62ffc317d17cc33909feac471c044087cad7b0" +checksum = "edc50ffce891ad571e9f9afe5039c4837bede781ac4bb13052ed7ae695518596" dependencies = [ "cc", "libc", + "pkg-config", ] diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 1f5d950..71a87ed 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -6,6 +6,9 @@ edition = "2021" [[bin]] name = "api" +[[bin]] +name = "worker" + [profile.release] codegen-units = 1 lto = "fat" @@ -19,13 +22,17 @@ anyhow = { version = "1.0.66", features = ["backtrace"] } celery = { git = "https://github.com/rusty-celery/rusty-celery.git", branch = "main" } chrono = "0.4.23" diesel = { version = "2.0.2", features = ["i-implement-a-third-party-backend-and-opt-into-breaking-changes", "postgres", "chrono", "r2d2"] } -env_logger = "0.10.0" +env_logger = "0.9.3" envconfig = "0.10.0" juniper = { version = "0.15.10", features = ["scalar-naivetime"] } juniper_actix = "0.4.0" lazy_static = "1.4.0" log = "0.4.17" +r2d2_redis = "0.14.0" +reqwest = "0.11.13" +scraper = "0.14.0" serde = "1.0.148" +stdext = "0.3.1" tokio = { version = "1.22.0", features = ["full"] } untis = { git = "https://git.dergrimm.net/dergrimm/untis.rs.git", branch = "main" } url = "2.3.1" diff --git a/backend/Dockerfile b/backend/Dockerfile index 3e56343..ee07834 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,26 +1,27 @@ -FROM lukemathwalker/cargo-chef:latest-rust-1.65.0 as chef -WORKDIR /usr/src/backend +FROM docker.io/lukemathwalker/cargo-chef:latest-rust-1.65.0 as chef + +FROM chef as diesel +RUN cargo install diesel_cli --no-default-features --features postgres FROM chef as planner +WORKDIR /usr/src/backend RUN mkdir src && touch src/main.rs COPY ./Cargo.toml ./Cargo.lock ./ RUN cargo chef prepare --recipe-path recipe.json FROM chef as builder +WORKDIR /usr/src/backend COPY --from=planner /usr/src/backend/recipe.json . RUN cargo chef cook --release --recipe-path recipe.json COPY ./src ./src RUN cargo build --release -FROM chef as diesel -RUN cargo install diesel_cli --no-default-features --features postgres - -FROM debian:buster-slim as runner +FROM docker.io/debian:bullseye-slim as runner RUN apt update RUN apt install -y libpq5 RUN apt install -y ca-certificates RUN apt-get clean -RUN apt-get autoremove --yes +RUN apt-get autoremove -y RUN rm -rf /var/lib/{apt,dpkg,cache,log}/ WORKDIR /usr/local/bin COPY --from=diesel /usr/local/cargo/bin/diesel . diff --git a/backend/migrations/2022-12-03-124501_init/down.sql b/backend/migrations/2022-12-03-124501_init/down.sql index 8d409b8..961c356 100644 --- a/backend/migrations/2022-12-03-124501_init/down.sql +++ b/backend/migrations/2022-12-03-124501_init/down.sql @@ -12,6 +12,8 @@ DROP TYPE substitution_type; DROP TABLE substitution_queries; +DROP TYPE week_type; + DROP TABLE timegrid_time_unit; DROP TABLE timegrid_days; @@ -34,4 +36,4 @@ DROP INDEX tenants_active; DROP TABLE tenants; -DROP TABLE schoolyears; \ No newline at end of file +DROP TABLE schoolyears; diff --git a/backend/migrations/2022-12-03-124501_init/up.sql b/backend/migrations/2022-12-03-124501_init/up.sql index a744228..4540d5b 100644 --- a/backend/migrations/2022-12-03-124501_init/up.sql +++ b/backend/migrations/2022-12-03-124501_init/up.sql @@ -1,196 +1,190 @@ -CREATE TABLE schoolyears( - id SERIAL PRIMARY KEY, - untis_id INTEGER NOT NULL UNIQUE, - name VARCHAR NOT NULL, - start_date DATE NOT NULL, - end_date DATE NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP +CREATE TABLE schoolyears ( + id SERIAL PRIMARY KEY, + untis_id INTEGER NOT NULL UNIQUE, + name VARCHAR NOT NULL, + start_date DATE NOT NULL, + end_date DATE NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP ); -CREATE TABLE tenants( - id SERIAL PRIMARY KEY, - untis_id INTEGER NOT NULL UNIQUE, - schoolyear_id INTEGER NOT NULL REFERENCES schoolyears(id), - name VARCHAR NOT NULL, - active BOOLEAN NOT NULL DEFAULT FALSE, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP +CREATE TABLE tenants ( + id SERIAL PRIMARY KEY, + untis_id INTEGER NOT NULL UNIQUE, + schoolyear_id INTEGER NOT NULL REFERENCES schoolyears (id), + name VARCHAR NOT NULL, + active BOOLEAN NOT NULL DEFAULT FALSE, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP ); -CREATE UNIQUE INDEX tenants_active ON tenants(active) +CREATE UNIQUE INDEX tenants_active ON tenants (active) WHERE - active; + active; -CREATE TABLE teachers( - id SERIAL PRIMARY KEY, - untis_id INTEGER NOT NULL UNIQUE, - schoolyear_id INTEGER NOT NULL REFERENCES schoolyears(id), - name VARCHAR NOT NULL, - forename VARCHAR, - display_name VARCHAR NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP +CREATE TABLE teachers ( + id SERIAL PRIMARY KEY, + untis_id INTEGER NOT NULL UNIQUE, + schoolyear_id INTEGER NOT NULL REFERENCES schoolyears (id), + name VARCHAR NOT NULL, + forename VARCHAR, + display_name VARCHAR NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP ); -CREATE TABLE classes( - id SERIAL PRIMARY KEY, - untis_id INTEGER NOT NULL UNIQUE, - schoolyear_id INTEGER NOT NULL REFERENCES schoolyears(id), - name VARCHAR NOT NULL, - long_name VARCHAR NOT NULL, - active BOOLEAN NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP +CREATE TABLE classes ( + id SERIAL PRIMARY KEY, + untis_id INTEGER NOT NULL UNIQUE, + schoolyear_id INTEGER NOT NULL REFERENCES schoolyears (id), + name VARCHAR NOT NULL, + long_name VARCHAR NOT NULL, + active BOOLEAN NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP ); -CREATE TABLE subjects( - id SERIAL PRIMARY KEY, - untis_id INTEGER NOT NULL UNIQUE, - schoolyear_id INTEGER NOT NULL REFERENCES schoolyears(id), - name VARCHAR NOT NULL, - long_name VARCHAR NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP +CREATE TABLE subjects ( + id SERIAL PRIMARY KEY, + untis_id INTEGER NOT NULL UNIQUE, + schoolyear_id INTEGER NOT NULL REFERENCES schoolyears (id), + name VARCHAR NOT NULL, + long_name VARCHAR NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP ); -CREATE TABLE rooms( - id SERIAL PRIMARY KEY, - untis_id INTEGER NOT NULL UNIQUE, - schoolyear_id INTEGER NOT NULL REFERENCES schoolyears(id), - name VARCHAR NOT NULL, - long_name VARCHAR NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP +CREATE TABLE rooms ( + id SERIAL PRIMARY KEY, + untis_id INTEGER NOT NULL UNIQUE, + schoolyear_id INTEGER NOT NULL REFERENCES schoolyears (id), + name VARCHAR NOT NULL, + long_name VARCHAR NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP ); -CREATE TABLE departments( - id SERIAL PRIMARY KEY, - untis_id INTEGER NOT NULL UNIQUE, - schoolyear_id INTEGER NOT NULL REFERENCES schoolyears(id), - name VARCHAR NOT NULL, - long_name VARCHAR NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP +CREATE TABLE departments ( + id SERIAL PRIMARY KEY, + untis_id INTEGER NOT NULL UNIQUE, + schoolyear_id INTEGER NOT NULL REFERENCES schoolyears (id), + name VARCHAR NOT NULL, + long_name VARCHAR NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP ); -CREATE TABLE holidays( - id SERIAL PRIMARY KEY, - untis_id INTEGER NOT NULL UNIQUE, - schoolyear_id INTEGER NOT NULL REFERENCES schoolyears(id), - name VARCHAR NOT NULL, - long_name VARCHAR NOT NULL, - start_date DATE NOT NULL, - end_date DATE NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP +CREATE TABLE holidays ( + id SERIAL PRIMARY KEY, + untis_id INTEGER NOT NULL UNIQUE, + schoolyear_id INTEGER NOT NULL REFERENCES schoolyears (id), + name VARCHAR NOT NULL, + long_name VARCHAR NOT NULL, + start_date DATE NOT NULL, + end_date DATE NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP ); -CREATE TABLE timegrids( - id SERIAL PRIMARY KEY, - schoolyear_id INTEGER NOT NULL REFERENCES schoolyears(id), - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP +CREATE TABLE timegrids ( + id SERIAL PRIMARY KEY, + schoolyear_id INTEGER NOT NULL REFERENCES schoolyears (id), + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP ); -CREATE TABLE timegrid_days( - id SERIAL PRIMARY KEY, - timegrid_id INTEGER NOT NULL REFERENCES timegrids(id), - day_index SMALLINT NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP +CREATE TABLE timegrid_days ( + id SERIAL PRIMARY KEY, + timegrid_id INTEGER NOT NULL REFERENCES timegrids (id), + day_index SMALLINT NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP ); -CREATE TABLE timegrid_time_unit( - id SERIAL PRIMARY KEY, - timegrid_day_id INTEGER NOT NULL REFERENCES timegrid_days(id), - start_time TIME NOT NULL, - end_time TIME NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP +CREATE TABLE timegrid_time_unit ( + id SERIAL PRIMARY KEY, + timegrid_day_id INTEGER NOT NULL REFERENCES timegrid_days (id), + start_time TIME NOT NULL, + end_time TIME NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP ); -CREATE TABLE substitution_queries( - id SERIAL PRIMARY KEY, - schoolyear_id INTEGER NOT NULL REFERENCES schoolyears(id), - date DATE NOT NULL UNIQUE, - active BOOLEAN NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP -); +CREATE TYPE week_type AS ENUM ('a', 'b'); -CREATE INDEX substitution_queries_active ON substitution_queries(active); - -CREATE TABLE substitution_query_results( - id SERIAL PRIMARY KEY, - substitution_query_id INTEGER NOT NULL REFERENCES substitution_queries(id), - queried_at TIMESTAMP NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP +CREATE TABLE substitution_queries ( + id SERIAL PRIMARY KEY, + schoolyear_id INTEGER NOT NULL REFERENCES schoolyears (id), + date DATE NOT NULL, + week_type week_type NOT NULL, + queried_at TIMESTAMP NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP ); CREATE TYPE substitution_type AS ENUM ( - 'cancel', - 'subst', - 'add', - 'shift', - 'rmchg', - 'rmlk', - 'bs', - 'oh', - 'sb', - 'other', - 'free', - 'exam', - 'ac', - 'holi', - 'stxt' + 'cancel', + 'subst', + 'add', + 'shift', + 'rmchg', + 'rmlk', + 'bs', + 'oh', + 'sb', + 'other', + 'free', + 'exam', + 'ac', + 'holi', + 'stxt' ); CREATE TABLE substitutions( - id SERIAL PRIMARY KEY, - substitution_query_result_id INTEGER NOT NULL REFERENCES substitution_query_results(id), - subst_type substitution_type NOT NULL, - lesson_id INTEGER NOT NULL, - start_time TIME NOT NULL, - end_time TIME NOT NULL, - text VARCHAR, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP + id SERIAL PRIMARY KEY, + substitution_query_id INTEGER NOT NULL REFERENCES substitution_queries (id), + subst_type substitution_type NOT NULL, + lesson_id INTEGER NOT NULL, + start_time TIME NOT NULL, + end_time TIME NOT NULL, + text VARCHAR, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP ); -CREATE TABLE substitution_classes( - id SERIAL PRIMARY KEY, - substitution_id INTEGER NOT NULL REFERENCES substitutions(id), - position SMALLINT NOT NULL, - class_id INTEGER NOT NULL REFERENCES classes(id), - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP +CREATE TABLE substitution_classes ( + id SERIAL PRIMARY KEY, + substitution_id INTEGER NOT NULL REFERENCES substitutions (id), + position SMALLINT NOT NULL, + class_id INTEGER NOT NULL REFERENCES classes (id), + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP ); -CREATE TABLE substitution_teachers( - id SERIAL PRIMARY KEY, - substitution_id INTEGER NOT NULL REFERENCES substitutions(id), - position SMALLINT NOT NULL, - teacher_id INTEGER REFERENCES teachers(id), - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP +CREATE TABLE substitution_teachers ( + id SERIAL PRIMARY KEY, + substitution_id INTEGER NOT NULL REFERENCES substitutions (id), + position SMALLINT NOT NULL, + teacher_id INTEGER REFERENCES teachers (id), + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP ); -CREATE TABLE substitution_subjects( - id SERIAL PRIMARY KEY, - substitution_id INTEGER NOT NULL REFERENCES substitutions(id), - position SMALLINT NOT NULL, - subject_id INTEGER NOT NULL REFERENCES subjects(id), - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP +CREATE TABLE substitution_subjects ( + id SERIAL PRIMARY KEY, + substitution_id INTEGER NOT NULL REFERENCES substitutions (id), + position SMALLINT NOT NULL, + subject_id INTEGER NOT NULL REFERENCES subjects (id), + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP ); -CREATE TABLE substitution_rooms( - id SERIAL PRIMARY KEY, - substitution_id INTEGER NOT NULL REFERENCES substitutions(id), - position SMALLINT NOT NULL, - room_id INTEGER REFERENCES rooms(id), - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP -); \ No newline at end of file +CREATE TABLE substitution_rooms ( + id SERIAL PRIMARY KEY, + substitution_id INTEGER NOT NULL REFERENCES substitutions (id), + position SMALLINT NOT NULL, + room_id INTEGER REFERENCES rooms (id), + original_room_id INTEGER REFERENCES rooms (id), + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP +); diff --git a/backend/run.sh b/backend/run.sh index 5f48551..90758a2 100644 --- a/backend/run.sh +++ b/backend/run.sh @@ -1,4 +1,5 @@ -#!/bin/bash +#!/usr/bin/env bash +# -*- coding: utf-8 -*- DATABASE_URL="$BACKEND_DB_URL" diesel setup \ --migration-dir ./migrations \ diff --git a/backend/src/api.rs b/backend/src/api.rs deleted file mode 100644 index 9620578..0000000 --- a/backend/src/api.rs +++ /dev/null @@ -1,55 +0,0 @@ -// #[cfg(not(target_env = "msvc"))] -// use tikv_jemallocator::Jemalloc; - -// #[cfg(not(target_env = "msvc"))] -// #[global_allocator] -// static GLOBAL: Jemalloc = Jemalloc; - -// async fn graphql_route( -// req: actix_web::HttpRequest, -// payload: actix_web::web::Payload, -// schema: actix_web::web::Data, -// ) -> Result { -// juniper_actix::graphql_handler( -// &schema, -// &backend::graphql::Context { -// pool: backend::db::POOL.clone(), -// }, -// req, -// payload, -// ) -// .await -// } - -// #[actix_web::main] -// async fn main() -> std::io::Result<()> { -// std::env::set_var("RUST_LOG", "info"); -// env_logger::init(); - -// let server = actix_web::HttpServer::new(move || { -// actix_web::App::new() -// .app_data(actix_web::web::Data::new(backend::graphql::schema())) -// .wrap( -// actix_cors::Cors::default() -// .allow_any_origin() -// .allowed_methods(vec!["POST", "GET"]) -// .allowed_headers(vec![ -// actix_web::http::header::AUTHORIZATION, -// actix_web::http::header::ACCEPT, -// ]) -// .allowed_header(actix_web::http::header::CONTENT_TYPE) -// .supports_credentials() -// .max_age(3600), -// ) -// .wrap(actix_web::middleware::Compress::default()) -// .wrap(actix_web::middleware::Logger::default()) -// .service( -// actix_web::web::resource("/") -// .route(actix_web::web::post().to(graphql_route)) -// .route(actix_web::web::get().to(graphql_route)), -// ) -// }); -// println!("Starting server on port 80!"); - -// server.bind("0.0.0.0:80")?.run().await -// } diff --git a/backend/src/bin/api.rs b/backend/src/bin/api.rs index c79d8eb..44ded4c 100644 --- a/backend/src/bin/api.rs +++ b/backend/src/bin/api.rs @@ -1,3 +1,10 @@ +#[cfg(not(target_env = "msvc"))] +use tikv_jemallocator::Jemalloc; + +#[cfg(not(target_env = "msvc"))] +#[global_allocator] +static GLOBAL: Jemalloc = Jemalloc; + use actix_cors::Cors; use actix_web::{ http::header, diff --git a/backend/src/bin/worker.rs b/backend/src/bin/worker.rs index 52e7909..2911956 100644 --- a/backend/src/bin/worker.rs +++ b/backend/src/bin/worker.rs @@ -1,4 +1,3 @@ -use std::thread; #[cfg(not(target_env = "msvc"))] use tikv_jemallocator::Jemalloc; @@ -6,6 +5,8 @@ use tikv_jemallocator::Jemalloc; #[global_allocator] static GLOBAL: Jemalloc = Jemalloc; +use std::thread; + #[tokio::main] async fn main() { std::env::set_var("RUST_LOG", "info"); diff --git a/backend/src/cache.rs b/backend/src/cache.rs new file mode 100644 index 0000000..044dc03 --- /dev/null +++ b/backend/src/cache.rs @@ -0,0 +1,26 @@ +use anyhow::Result; +use lazy_static::lazy_static; +use r2d2_redis::{r2d2, RedisConnectionManager}; + +use crate::config; + +pub type CachePool = r2d2::Pool; +pub type ConnectionPool = r2d2::PooledConnection; + +pub fn establish_connection() -> Result { + Ok(RedisConnectionManager::new( + config::CONFIG.redis_url.as_str(), + )?) +} + +pub fn pool() -> Result { + Ok(r2d2::Pool::builder().build(establish_connection()?)?) +} + +lazy_static! { + pub static ref POOL: CachePool = pool().unwrap(); +} + +pub mod keys { + pub const LAST_SUBSTITUTION_QUERY_ID: &str = "last_subst_query_id"; +} diff --git a/backend/src/config.rs b/backend/src/config.rs index c250c7c..c75f961 100644 --- a/backend/src/config.rs +++ b/backend/src/config.rs @@ -11,6 +11,9 @@ pub struct Config { #[envconfig(from = "BACKEND_AMQP_URL")] pub amqp_url: String, + #[envconfig(from = "BACKEND_REDIS_URL")] + pub redis_url: String, + #[envconfig(from = "BACKEND_UNTIS_API_URL")] pub untis_api_url: String, @@ -28,6 +31,15 @@ pub struct Config { #[envconfig(from = "BACKEND_UNTIS_PASSWORD")] pub untis_password: String, + + #[envconfig(from = "BACKEND_UNTIS_VPLAN_URL")] + pub untis_vplan_url: String, + + #[envconfig(from = "BACKEND_UNTIS_VPLAN_USERNAME")] + pub untis_vplan_username: String, + + #[envconfig(from = "BACKEND_UNTIS_VPLAN_PASSWORD")] + pub untis_vplan_password: String, } lazy_static! { diff --git a/backend/src/db/models.rs b/backend/src/db/models.rs index 17f467d..5a31ca8 100644 --- a/backend/src/db/models.rs +++ b/backend/src/db/models.rs @@ -1,5 +1,6 @@ use chrono::prelude::*; use diesel::prelude::*; +use juniper::GraphQLEnum; use std::io::Write; use crate::db::schema; @@ -187,14 +188,48 @@ pub struct NewHoliday<'a> { pub end_date: NaiveDate, } -#[derive(Identifiable, Queryable, Associations, Debug)] +#[derive(GraphQLEnum, diesel::FromSqlRow, diesel::AsExpression, PartialEq, Eq, Clone, Debug)] +#[diesel(sql_type = schema::sql_types::WeekType)] +pub enum WeekType { + A, + B, +} + +impl diesel::serialize::ToSql for WeekType { + fn to_sql<'b>( + &'b self, + out: &mut diesel::serialize::Output<'b, '_, diesel::pg::Pg>, + ) -> diesel::serialize::Result { + match *self { + Self::A => out.write_all(b"a")?, + Self::B => out.write_all(b"b")?, + } + + Ok(diesel::serialize::IsNull::No) + } +} + +impl diesel::deserialize::FromSql for WeekType { + fn from_sql( + bytes: diesel::backend::RawValue<'_, diesel::pg::Pg>, + ) -> diesel::deserialize::Result { + match bytes.as_bytes() { + b"a" => Ok(Self::A), + b"b" => Ok(Self::B), + _ => Err("Unrecognized enum variant".into()), + } + } +} + +#[derive(Identifiable, Queryable, Associations, Clone, Debug)] #[diesel(table_name = schema::substitution_queries)] #[diesel(belongs_to(Schoolyear))] pub struct SubstitutionQuery { pub id: i32, pub schoolyear_id: i32, pub date: NaiveDate, - pub active: bool, + pub week_type: WeekType, + pub queried_at: NaiveDateTime, pub created_at: NaiveDateTime, pub updated_at: Option, } @@ -204,27 +239,11 @@ pub struct SubstitutionQuery { pub struct NewSubstitutionQuery { pub schoolyear_id: i32, pub date: NaiveDate, - pub active: bool, -} - -#[derive(Identifiable, Queryable, Associations, Debug)] -#[diesel(table_name = schema::substitution_query_results)] -#[diesel(belongs_to(SubstitutionQuery))] -pub struct SubstitutionQueryResult { - pub id: i32, - pub substitution_query_id: i32, - pub queried_at: NaiveDateTime, - pub created_at: NaiveDateTime, - pub updated_at: Option, -} - -#[derive(Insertable, Debug)] -#[diesel(table_name = schema::substitution_query_results)] -pub struct NewSubstitutionQueryResult { - pub substitution_query_id: i32, + pub week_type: WeekType, pub queried_at: NaiveDateTime, } -#[derive(diesel::FromSqlRow, diesel::AsExpression, PartialEq, Eq, Debug)] + +#[derive(GraphQLEnum, diesel::FromSqlRow, diesel::AsExpression, PartialEq, Eq, Clone, Debug)] #[diesel(sql_type = schema::sql_types::SubstitutionType)] pub enum SubstitutionType { Cancel, @@ -324,10 +343,10 @@ impl From for SubstitutionType { #[derive(Identifiable, Queryable, Associations, Debug)] #[diesel(table_name = schema::substitutions)] -#[diesel(belongs_to(SubstitutionQueryResult))] +#[diesel(belongs_to(SubstitutionQuery))] pub struct Substitution { pub id: i32, - pub substitution_query_result_id: i32, + pub substitution_query_id: i32, pub subst_type: SubstitutionType, pub lesson_id: i32, pub start_time: NaiveTime, @@ -340,7 +359,7 @@ pub struct Substitution { #[derive(Insertable, Debug)] #[diesel(table_name = schema::substitutions)] pub struct NewSubstitution<'a> { - pub substitution_query_result_id: i32, + pub substitution_query_id: i32, pub subst_type: SubstitutionType, pub lesson_id: i32, pub start_time: NaiveTime, @@ -421,6 +440,7 @@ pub struct SubstitutionRoom { pub position: i16, pub index: i16, pub room_id: Option, + pub original_room_id: Option, pub created_at: NaiveDateTime, pub updated_at: Option, } @@ -431,4 +451,5 @@ pub struct NewSubstitutionRoom { pub substitution_id: i32, pub position: i16, pub room_id: Option, + pub original_room_id: Option, } diff --git a/backend/src/db/schema.rs b/backend/src/db/schema.rs index 4b217b2..10ede01 100644 --- a/backend/src/db/schema.rs +++ b/backend/src/db/schema.rs @@ -2,6 +2,10 @@ pub mod sql_types { #[derive(diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "substitution_type"))] pub struct SubstitutionType; + + #[derive(diesel::sql_types::SqlType)] + #[diesel(postgres_type(name = "week_type"))] + pub struct WeekType; } diesel::table! { @@ -136,20 +140,15 @@ diesel::table! { } diesel::table! { + use diesel::sql_types::*; + + use super::sql_types::WeekType; + substitution_queries { id -> Integer, schoolyear_id -> Integer, date -> Date, - active -> Bool, - created_at -> Timestamp, - updated_at -> Nullable, - } -} - -diesel::table! { - substitution_query_results { - id -> Integer, - substitution_query_id -> Integer, + week_type -> WeekType, queried_at -> Timestamp, created_at -> Timestamp, updated_at -> Nullable, @@ -163,7 +162,7 @@ diesel::table! { substitutions { id -> Integer, - substitution_query_result_id -> Integer, + substitution_query_id -> Integer, subst_type -> SubstitutionType, lesson_id -> Integer, start_time -> Time, @@ -213,6 +212,7 @@ diesel::table! { substitution_id -> Integer, position -> SmallInt, room_id -> Nullable, + original_room_id -> Nullable, created_at -> Timestamp, updated_at -> Nullable, } diff --git a/backend/src/graphql.rs b/backend/src/graphql.rs index 87e4ff1..98b09ef 100644 --- a/backend/src/graphql.rs +++ b/backend/src/graphql.rs @@ -1,10 +1,286 @@ -use juniper::{graphql_object, EmptyMutation, EmptySubscription, GraphQLObject, RootNode}; +use diesel::prelude::*; +use juniper::{graphql_object, EmptyMutation, EmptySubscription, FieldResult, RootNode}; +use r2d2_redis::redis; +use std::ops::DerefMut; + +use crate::{cache, db}; #[derive(Clone)] pub struct Context; impl juniper::Context for Context {} +pub struct Schoolyear { + pub model: db::models::Schoolyear, +} + +#[graphql_object] +impl Schoolyear { + fn id(&self) -> i32 { + self.model.id + } + + fn untis_id(&self) -> i32 { + self.model.untis_id + } + + fn name(&self) -> &str { + self.model.name.as_str() + } + + fn start_date(&self) -> chrono::NaiveDate { + self.model.start_date + } + + fn end_date(&self) -> chrono::NaiveDate { + self.model.end_date + } +} + +pub struct Teacher { + pub model: db::models::Teacher, +} + +#[graphql_object] +impl Teacher { + fn id(&self) -> i32 { + self.model.id + } + + fn untis_id(&self) -> i32 { + self.model.untis_id + } + + fn schoolyear(&self) -> FieldResult { + let db_conn = &mut db::POOL.get()?; + + Ok(Schoolyear { + model: db::schema::schoolyears::table + .filter(db::schema::schoolyears::id.eq(self.model.schoolyear_id)) + .first::(db_conn)?, + }) + } + + fn name(&self) -> &str { + &self.model.name + } + + fn forename(&self) -> Option<&str> { + self.model.forename.as_deref() + } + + fn display_name(&self) -> &str { + &self.model.display_name + } +} + +pub struct Class { + pub model: db::models::Class, +} + +#[graphql_object] +impl Class { + fn id(&self) -> i32 { + self.model.id + } + + fn untis_id(&self) -> i32 { + self.model.untis_id + } + + fn schoolyear(&self) -> FieldResult { + let db_conn = &mut db::POOL.get()?; + + Ok(Schoolyear { + model: db::schema::schoolyears::table + .filter(db::schema::schoolyears::id.eq(self.model.schoolyear_id)) + .first::(db_conn)?, + }) + } + + fn name(&self) -> &str { + &self.model.name + } + + fn long_name(&self) -> &str { + &self.model.long_name + } + + fn active(&self) -> bool { + self.model.active + } +} + +pub struct SubstitutionClass { + pub model: db::models::SubstitutionClass, +} + +#[graphql_object] +impl SubstitutionClass { + fn id(&self) -> i32 { + self.model.id + } + + fn substitution(&self) -> FieldResult { + let db_conn = &mut db::POOL.get()?; + + Ok(Substitution { + model: db::schema::substitutions::table + .filter(db::schema::substitutions::id.eq(self.model.substitution_id)) + .first::(db_conn)?, + }) + } + + fn position(&self) -> i32 { + self.model.position as i32 + } + + fn class(&self) -> FieldResult { + let db_conn = &mut db::POOL.get()?; + + Ok(Class { + model: db::schema::classes::table + .filter(db::schema::classes::id.eq(self.model.class_id)) + .first::(db_conn)?, + }) + } +} + +pub struct SubstitutionTeacher { + pub model: db::models::SubstitutionTeacher, +} + +#[graphql_object] +impl SubstitutionTeacher { + fn id(&self) -> i32 { + self.model.id + } + + fn substitution(&self) -> FieldResult { + let db_conn = &mut db::POOL.get()?; + + Ok(Substitution { + model: db::schema::substitutions::table + .filter(db::schema::substitutions::id.eq(self.model.substitution_id)) + .first::(db_conn)?, + }) + } + + fn position(&self) -> i32 { + self.model.position as i32 + } + + fn teacher(&self) -> FieldResult> { + if let Some(teacher_id) = self.model.teacher_id { + let db_conn = &mut db::POOL.get()?; + + Ok(Some(Teacher { + model: db::schema::teachers::table + .filter(db::schema::teachers::id.eq(teacher_id)) + .first::(db_conn)?, + })) + } else { + Ok(None) + } + } +} + +pub struct Substitution { + pub model: db::models::Substitution, +} + +#[graphql_object] +impl Substitution { + fn id(&self) -> i32 { + self.model.id + } + + fn substitution_query(&self) -> FieldResult { + let db_conn = &mut db::POOL.get()?; + + Ok(SubstitutionQuery { + model: db::schema::substitution_queries::table + .filter(db::schema::substitution_queries::id.eq(self.model.substitution_query_id)) + .first::(db_conn)?, + }) + } + + fn subst_type(&self) -> db::models::SubstitutionType { + self.model.subst_type.to_owned() + } + + fn lesson_id(&self) -> i32 { + self.model.lesson_id + } + + fn start_time(&self) -> chrono::NaiveTime { + self.model.start_time + } + + fn end_time(&self) -> chrono::NaiveTime { + self.model.end_time + } + + fn text(&self) -> Option<&str> { + self.model.text.as_deref() + } + + fn classes(&self) -> FieldResult> { + let db_conn = &mut db::POOL.get()?; + + Ok(db::schema::substitution_classes::table + .filter(db::schema::substitution_classes::substitution_id.eq(self.model.id)) + .load::(db_conn)? + .into_iter() + .map(|x| SubstitutionClass { model: x }) + .collect()) + } +} + +pub struct SubstitutionQuery { + pub model: db::models::SubstitutionQuery, +} + +#[graphql_object] +impl SubstitutionQuery { + fn id(&self) -> i32 { + self.model.id + } + + fn schoolyear(&self) -> FieldResult { + let db_conn = &mut db::POOL.get()?; + + Ok(Schoolyear { + model: db::schema::schoolyears::table + .filter(db::schema::schoolyears::id.eq(self.model.schoolyear_id)) + .first::(db_conn)?, + }) + } + + fn date(&self) -> chrono::NaiveDate { + self.model.date + } + + fn week_type(&self) -> db::models::WeekType { + self.model.week_type.to_owned() + } + + fn queried_at(&self) -> chrono::NaiveDateTime { + self.model.queried_at + } + + fn substitutions(&self) -> FieldResult> { + let db_conn = &mut db::POOL.get()?; + + Ok(db::schema::substitutions::table + .filter(db::schema::substitutions::substitution_query_id.eq(self.model.id)) + .load::(db_conn)? + .into_iter() + .map(|x| Substitution { model: x }) + .collect()) + } +} + pub struct Query; #[graphql_object(context = Context)] @@ -12,6 +288,29 @@ impl Query { fn ping() -> &'static str { "pong" } + + fn last_substitution_query() -> FieldResult { + let db_conn = &mut db::POOL.get()?; + let redis_conn = &mut cache::POOL.get()?; + + let id = redis::cmd("GET") + .arg(cache::keys::LAST_SUBSTITUTION_QUERY_ID) + .query::>(redis_conn.deref_mut())? + .map_or_else( + || { + db::schema::substitution_queries::table + .select(db::schema::substitution_queries::id) + .order(db::schema::substitution_queries::queried_at.desc()) + .first::(db_conn) + }, + |x| Ok(x), + )?; + let last_query = db::schema::substitution_queries::table + .filter(db::schema::substitution_queries::id.eq(id)) + .first::(db_conn)?; + + Ok(SubstitutionQuery { model: last_query }) + } } pub type Schema = RootNode<'static, Query, EmptyMutation, EmptySubscription>; diff --git a/backend/src/lib.rs b/backend/src/lib.rs index 070916b..05224cc 100644 --- a/backend/src/lib.rs +++ b/backend/src/lib.rs @@ -1,3 +1,4 @@ +pub mod cache; pub mod config; pub mod db; pub mod graphql; diff --git a/backend/src/worker/mod.rs b/backend/src/worker/mod.rs index 1079404..50776f6 100644 --- a/backend/src/worker/mod.rs +++ b/backend/src/worker/mod.rs @@ -4,6 +4,7 @@ use lazy_static::lazy_static; use std::sync::{Arc, Mutex}; use std::thread; use std::time; +use stdext::duration::DurationExt; use crate::config; @@ -63,9 +64,9 @@ pub fn beat() -> impl std::future::Future< celery::beat!( broker = AMQPBroker { &config::CONFIG.amqp_url }, tasks = [ - "update_info_" => { + "update_info" => { update_info::update_info, - schedule = DeltaSchedule::new(time::Duration::from_secs(5 * 60)), + schedule = DeltaSchedule::new(time::Duration::from_minutes(5)), args = (), } ], diff --git a/backend/src/worker/update_info.rs b/backend/src/worker/update_info.rs index 04d891a..508a1e1 100644 --- a/backend/src/worker/update_info.rs +++ b/backend/src/worker/update_info.rs @@ -1,17 +1,20 @@ -use anyhow::Result; +use anyhow::{bail, Context, Result}; use celery::error::TaskError; use celery::task::TaskResult; use chrono::prelude::*; use diesel::prelude::*; +use lazy_static::lazy_static; +use r2d2_redis::redis; +use std::ops::DerefMut; use std::thread; use std::time::Duration; -use crate::{config, db}; +use crate::{cache, config, db}; -async fn fetch_schoolyears(client: &untis::Client, conn: &mut PgConnection) -> Result { +async fn fetch_schoolyears(client: &untis::Client, db_conn: &mut PgConnection) -> Result { let existing_schoolyears = db::schema::schoolyears::table .select(db::schema::schoolyears::untis_id) - .load::(conn)?; + .load::(db_conn)?; diesel::insert_into(db::schema::schoolyears::table) .values( &client @@ -27,29 +30,29 @@ async fn fetch_schoolyears(client: &untis::Client, conn: &mut PgConnection) -> R }) .collect::>(), ) - .execute(conn)?; + .execute(db_conn)?; Ok(db::schema::schoolyears::table .filter(db::schema::schoolyears::untis_id.eq(client.current_schoolyear().await?.id)) .select(db::schema::schoolyears::id) - .first(conn)?) + .first(db_conn)?) } async fn fetch_current_tenant( client: &untis::Client, - conn: &mut PgConnection, + db_conn: &mut PgConnection, schoolyear_id: i32, ) -> Result<()> { let tenant = client.current_tenant().await?; if diesel::select(diesel::dsl::not(diesel::dsl::exists( db::schema::tenants::table.filter(db::schema::tenants::untis_id.eq(tenant.id)), ))) - .get_result::(conn)? + .get_result::(db_conn)? { diesel::update(db::schema::tenants::table) .filter(db::schema::tenants::active) .set(db::schema::tenants::active.eq(false)) - .execute(conn)?; + .execute(db_conn)?; diesel::insert_into(db::schema::tenants::table) .values(db::models::NewTenant { untis_id: tenant.id, @@ -57,13 +60,13 @@ async fn fetch_current_tenant( name: &tenant.display_name, active: true, }) - .execute(conn)?; + .execute(db_conn)?; } else if diesel::select(diesel::dsl::exists( db::schema::tenants::table .filter(db::schema::tenants::untis_id.eq(tenant.id)) .filter(db::schema::tenants::active.eq(false)), )) - .get_result::(conn)? + .get_result::(db_conn)? { diesel::update(db::schema::tenants::table) .filter(db::schema::tenants::active) @@ -71,14 +74,14 @@ async fn fetch_current_tenant( db::schema::tenants::active.eq(false), db::schema::tenants::updated_at.eq(diesel::dsl::now), )) - .execute(conn)?; + .execute(db_conn)?; diesel::update(db::schema::tenants::table) .filter(db::schema::tenants::untis_id.eq(tenant.id)) .set(( db::schema::tenants::active.eq(true), db::schema::tenants::updated_at.eq(diesel::dsl::now), )) - .execute(conn)?; + .execute(db_conn)?; } Ok(()) @@ -86,13 +89,13 @@ async fn fetch_current_tenant( async fn fetch_teachers( client: &untis::Client, - conn: &mut PgConnection, + db_conn: &mut PgConnection, schoolyear_id: i32, ) -> Result<()> { let existing_teachers = db::schema::teachers::table .select(db::schema::teachers::untis_id) .filter(db::schema::teachers::schoolyear_id.eq(schoolyear_id)) - .load::(conn)?; + .load::(db_conn)?; diesel::insert_into(db::schema::teachers::table) .values( &client @@ -113,20 +116,20 @@ async fn fetch_teachers( }) .collect::>(), ) - .execute(conn)?; + .execute(db_conn)?; Ok(()) } async fn fetch_classes( client: &untis::Client, - conn: &mut PgConnection, + db_conn: &mut PgConnection, schoolyear_id: i32, ) -> Result<()> { let existing_classes = db::schema::classes::table .select(db::schema::classes::untis_id) .filter(db::schema::classes::schoolyear_id.eq(schoolyear_id)) - .load::(conn)?; + .load::(db_conn)?; diesel::insert_into(db::schema::classes::table) .values( &client @@ -143,20 +146,20 @@ async fn fetch_classes( }) .collect::>(), ) - .execute(conn)?; + .execute(db_conn)?; Ok(()) } async fn fetch_subjects( client: &untis::Client, - conn: &mut PgConnection, + db_conn: &mut PgConnection, schoolyear_id: i32, ) -> Result<()> { let existing_classes = db::schema::subjects::table .select(db::schema::subjects::untis_id) .filter(db::schema::subjects::schoolyear_id.eq(schoolyear_id)) - .load::(conn)?; + .load::(db_conn)?; diesel::insert_into(db::schema::subjects::table) .values( &client @@ -172,20 +175,20 @@ async fn fetch_subjects( }) .collect::>(), ) - .execute(conn)?; + .execute(db_conn)?; Ok(()) } async fn fetch_rooms( client: &untis::Client, - conn: &mut PgConnection, + db_conn: &mut PgConnection, schoolyear_id: i32, ) -> Result<()> { let existing_classes = db::schema::rooms::table .select(db::schema::rooms::untis_id) .filter(db::schema::rooms::schoolyear_id.eq(schoolyear_id)) - .load::(conn)?; + .load::(db_conn)?; diesel::insert_into(db::schema::rooms::table) .values( &client @@ -201,20 +204,20 @@ async fn fetch_rooms( }) .collect::>(), ) - .execute(conn)?; + .execute(db_conn)?; Ok(()) } async fn fetch_departments( client: &untis::Client, - conn: &mut PgConnection, + db_conn: &mut PgConnection, schoolyear_id: i32, ) -> Result<()> { let existing_classes = db::schema::departments::table .select(db::schema::departments::untis_id) .filter(db::schema::departments::schoolyear_id.eq(schoolyear_id)) - .load::(conn)?; + .load::(db_conn)?; diesel::insert_into(db::schema::departments::table) .values( &client @@ -230,20 +233,20 @@ async fn fetch_departments( }) .collect::>(), ) - .execute(conn)?; + .execute(db_conn)?; Ok(()) } async fn fetch_holidays( client: &untis::Client, - conn: &mut PgConnection, + db_conn: &mut PgConnection, schoolyear_id: i32, ) -> Result<()> { let existing_classes = db::schema::holidays::table .select(db::schema::holidays::untis_id) .filter(db::schema::holidays::schoolyear_id.eq(schoolyear_id)) - .load::(conn)?; + .load::(db_conn)?; diesel::insert_into(db::schema::holidays::table) .values( &client @@ -261,147 +264,198 @@ async fn fetch_holidays( }) .collect::>(), ) - .execute(conn)?; + .execute(db_conn)?; Ok(()) } +lazy_static! { + static ref TITLE_SELECTOR: scraper::Selector = scraper::Selector::parse(".mon_title").unwrap(); +} + async fn fetch_substitutions( client: &untis::Client, - conn: &mut PgConnection, + db_conn: &mut PgConnection, + redis_conn: &mut cache::ConnectionPool, schoolyear_id: i32, ) -> Result<()> { - let today = Utc::now().date_naive(); - if diesel::select(diesel::dsl::not(diesel::dsl::exists( - db::schema::substitution_queries::table - .filter(db::schema::substitution_queries::date.eq(today)), - ))) - .get_result::(conn)? - { - diesel::insert_into(db::schema::substitution_queries::table) - .values(db::models::NewSubstitutionQuery { - schoolyear_id, - date: today, - active: true, + let (date, week_type) = { + let html = reqwest::Client::new() + .get(&config::CONFIG.untis_vplan_url) + .header( + reqwest::header::USER_AGENT, + &config::CONFIG.untis_client_name, + ) + .header(reqwest::header::ACCEPT, "text/html") + .basic_auth( + &config::CONFIG.untis_vplan_username, + Some(&config::CONFIG.untis_vplan_password), + ) + .send() + .await? + .text() + .await?; + let document = scraper::Html::parse_document(&html); + let title = document + .select(&TITLE_SELECTOR) + .next() + .context("No element in vplan html which is selectable class \"mon_title\"")? + .text() + .next() + .context("\"mon_title\" element is empty")? + .split_once(',') + .context("Could not split \"mon_title\" string")?; + + ( + chrono::NaiveDate::parse_from_str( + title + .0 + .split_whitespace() + .next() + .context("Could not find date")?, + "%d.%m.%Y", + )?, + match title + .1 + .split_whitespace() + .last() + .context("Could not find week type indicator")? + { + "A" => db::models::WeekType::A, + "B" => db::models::WeekType::B, + x => bail!("Invalid week type: {:?}", x), + }, + ) + }; + + let substitution_query_id = diesel::insert_into(db::schema::substitution_queries::table) + .values(db::models::NewSubstitutionQuery { + schoolyear_id, + date, + week_type, + queried_at: Utc::now().naive_utc(), + }) + .returning(db::schema::substitution_queries::id) + .get_result::(db_conn)?; + redis::cmd("SET") + .arg(cache::keys::LAST_SUBSTITUTION_QUERY_ID) + .arg(substitution_query_id) + .query(redis_conn.deref_mut())?; + + for substitution in client.substitutions(&date, &date, None).await? { + let substitution_id = diesel::insert_into(db::schema::substitutions::table) + .values(db::models::NewSubstitution { + substitution_query_id, + subst_type: substitution.subst_type.into(), + lesson_id: substitution.lesson_id, + start_time: substitution.start_time, + end_time: substitution.end_time, + text: substitution.text.as_deref(), }) - .execute(conn)?; - } + .returning(db::schema::substitutions::id) + .get_result::(db_conn)?; - for query in db::schema::substitution_queries::table - .filter(db::schema::substitution_queries::active) - .load::(conn)? - { - let query_result_id = diesel::insert_into(db::schema::substitution_query_results::table) - .values(db::models::NewSubstitutionQueryResult { - substitution_query_id: query.id, - queried_at: Utc::now().naive_utc(), - }) - .returning(db::schema::substitution_query_results::id) - .get_result::(conn)?; + diesel::insert_into(db::schema::substitution_classes::table) + .values( + &substitution + .classes + .iter() + .enumerate() + .map(|(i, c)| { + Ok(db::models::NewSubstitutionClass { + substitution_id, + position: i as i16, + class_id: db::schema::classes::table + .filter(db::schema::classes::untis_id.eq(c.id)) + .select(db::schema::classes::id) + .get_result::(db_conn)?, + }) + }) + .collect::>>()?, + ) + .execute(db_conn)?; - let substs = client.substitutions(&query.date, &query.date, None).await?; - for substitution in substs { - let substitution_id = diesel::insert_into(db::schema::substitutions::table) - .values(db::models::NewSubstitution { - substitution_query_result_id: query_result_id, - subst_type: substitution.subst_type.into(), - lesson_id: substitution.lesson_id, - start_time: substitution.start_time, - end_time: substitution.end_time, - text: substitution.text.as_deref(), - }) - .returning(db::schema::substitutions::id) - .get_result::(conn)?; + diesel::insert_into(db::schema::substitution_teachers::table) + .values( + &substitution + .teachers + .iter() + .enumerate() + .map(|(i, t)| { + Ok(db::models::NewSubstitutionTeacher { + substitution_id, + position: i as i16, + teacher_id: if t.id == 0 { + None + } else { + Some( + db::schema::teachers::table + .filter(db::schema::teachers::untis_id.eq(t.id)) + .select(db::schema::teachers::id) + .get_result::(db_conn)?, + ) + }, + }) + }) + .collect::>>()?, + ) + .execute(db_conn)?; - diesel::insert_into(db::schema::substitution_classes::table) - .values( - &substitution - .classes - .iter() - .enumerate() - .map(|(i, c)| { - Ok(db::models::NewSubstitutionClass { - substitution_id, - position: i as i16, - class_id: db::schema::classes::table - .filter(db::schema::classes::untis_id.eq(c.id)) - .select(db::schema::classes::id) - .get_result::(conn)?, - }) + diesel::insert_into(db::schema::substitution_subjects::table) + .values( + &substitution + .subjects + .iter() + .enumerate() + .map(|(i, s)| { + Ok(db::models::NewSubstitutionSubject { + substitution_id, + position: i as i16, + subject_id: db::schema::subjects::table + .filter(db::schema::subjects::untis_id.eq(s.id)) + .select(db::schema::subjects::id) + .get_result::(db_conn)?, }) - .collect::>>()?, - ) - .execute(conn)?; - diesel::insert_into(db::schema::substitution_teachers::table) - .values( - &substitution - .teachers - .iter() - .enumerate() - .map(|(i, t)| { - Ok(db::models::NewSubstitutionTeacher { - substitution_id, - position: i as i16, - teacher_id: if t.id == 0 { - None - } else { - Some( - db::schema::teachers::table - .filter(db::schema::teachers::untis_id.eq(t.id)) - .select(db::schema::teachers::id) - .get_result::(conn)?, - ) - }, - }) + }) + .collect::>>()?, + ) + .execute(db_conn)?; + + diesel::insert_into(db::schema::substitution_rooms::table) + .values( + &substitution + .rooms + .iter() + .enumerate() + .map(|(i, r)| { + Ok(db::models::NewSubstitutionRoom { + substitution_id, + position: i as i16, + room_id: if r.id == 0 { + None + } else { + Some( + db::schema::rooms::table + .filter(db::schema::rooms::untis_id.eq(r.id)) + .select(db::schema::rooms::id) + .get_result::(db_conn)?, + ) + }, + original_room_id: if let Some(original_id) = r.original_id { + Some( + db::schema::rooms::table + .filter(db::schema::rooms::untis_id.eq(original_id)) + .select(db::schema::rooms::id) + .get_result::(db_conn)?, + ) + } else { + None + }, }) - .collect::>>()?, - ) - .execute(conn)?; - diesel::insert_into(db::schema::substitution_subjects::table) - .values( - &substitution - .subjects - .iter() - .enumerate() - .map(|(i, s)| { - Ok(db::models::NewSubstitutionSubject { - substitution_id, - position: i as i16, - subject_id: db::schema::subjects::table - .filter(db::schema::subjects::untis_id.eq(s.id)) - .select(db::schema::subjects::id) - .get_result::(conn)?, - }) - }) - .collect::>>()?, - ) - .execute(conn)?; - diesel::insert_into(db::schema::substitution_rooms::table) - .values( - &substitution - .rooms - .iter() - .enumerate() - .map(|(i, r)| { - Ok(db::models::NewSubstitutionRoom { - substitution_id, - position: i as i16, - room_id: if r.id == 0 { - None - } else { - Some( - db::schema::rooms::table - .filter(db::schema::rooms::untis_id.eq(r.id)) - .select(db::schema::rooms::id) - .get_result::(conn)?, - ) - }, - }) - }) - .collect::>>()?, - ) - .execute(conn)?; - } + }) + .collect::>>()?, + ) + .execute(db_conn)?; } Ok(()) @@ -409,10 +463,9 @@ async fn fetch_substitutions( #[celery::task] pub async fn update_info() -> TaskResult<()> { - let dur = Duration::from_secs(10); + let dur = Duration::from_secs(2); thread::sleep(dur); - dbg!("DONE!"); let mut client = match config::untis_from_env() { Ok(x) => x, Err(e) => return Err(TaskError::UnexpectedError(e.to_string())), @@ -421,65 +474,58 @@ pub async fn update_info() -> TaskResult<()> { return Err(TaskError::UnexpectedError(e.to_string())); } thread::sleep(dur); - dbg!("DONE!"); - let conn = &mut match db::POOL.get() { + let db_conn = &mut match db::POOL.get() { + Ok(x) => x, + Err(e) => return Err(TaskError::UnexpectedError(e.to_string())), + }; + let redis_conn = &mut match cache::POOL.get() { Ok(x) => x, Err(e) => return Err(TaskError::UnexpectedError(e.to_string())), }; - let schoolyear_id = match fetch_schoolyears(&client, conn).await { + let schoolyear_id = match fetch_schoolyears(&client, db_conn).await { Ok(x) => x, Err(e) => return Err(TaskError::UnexpectedError(e.to_string())), }; thread::sleep(dur); - dbg!("DONE!"); - if let Err(e) = fetch_current_tenant(&client, conn, schoolyear_id).await { + if let Err(e) = fetch_current_tenant(&client, db_conn, schoolyear_id).await { return Err(TaskError::UnexpectedError(e.to_string())); } thread::sleep(dur); - dbg!("DONE!"); - if let Err(e) = fetch_teachers(&client, conn, schoolyear_id).await { + if let Err(e) = fetch_teachers(&client, db_conn, schoolyear_id).await { return Err(TaskError::UnexpectedError(e.to_string())); } thread::sleep(dur); - dbg!("DONE!"); - if let Err(e) = fetch_classes(&client, conn, schoolyear_id).await { + if let Err(e) = fetch_classes(&client, db_conn, schoolyear_id).await { return Err(TaskError::UnexpectedError(e.to_string())); } thread::sleep(dur); - dbg!("DONE!"); - if let Err(e) = fetch_subjects(&client, conn, schoolyear_id).await { + if let Err(e) = fetch_subjects(&client, db_conn, schoolyear_id).await { return Err(TaskError::UnexpectedError(e.to_string())); } thread::sleep(dur); - dbg!("DONE!"); - if let Err(e) = fetch_rooms(&client, conn, schoolyear_id).await { + if let Err(e) = fetch_rooms(&client, db_conn, schoolyear_id).await { return Err(TaskError::UnexpectedError(e.to_string())); } thread::sleep(dur); - dbg!("DONE!"); - if let Err(e) = fetch_departments(&client, conn, schoolyear_id).await { + if let Err(e) = fetch_departments(&client, db_conn, schoolyear_id).await { return Err(TaskError::UnexpectedError(e.to_string())); } thread::sleep(dur); - dbg!("DONE!"); - if let Err(e) = fetch_holidays(&client, conn, schoolyear_id).await { + if let Err(e) = fetch_holidays(&client, db_conn, schoolyear_id).await { return Err(TaskError::UnexpectedError(e.to_string())); } thread::sleep(dur); - dbg!("DONE!"); - if let Err(e) = fetch_substitutions(&client, conn, schoolyear_id).await { + if let Err(e) = fetch_substitutions(&client, db_conn, redis_conn, schoolyear_id).await { return Err(TaskError::UnexpectedError(e.to_string())); } thread::sleep(dur); - dbg!("DONE!"); if let Err(e) = client.logout().await { return Err(TaskError::UnexpectedError(e.to_string())); } thread::sleep(dur); - dbg!("DONE!"); Ok(()) } diff --git a/config/nginx/nginx.conf b/config/nginx/nginx.conf index 2e75ee3..3a85118 100644 --- a/config/nginx/nginx.conf +++ b/config/nginx/nginx.conf @@ -32,24 +32,5 @@ http { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } - - location ~* /rabbitmq/api/(.*?)/(.*) { - proxy_pass http://rabbitmq:15672/api/$1/%2F/$2?$query_string; - proxy_buffering off; - proxy_set_header Host $http_host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - location ~* /rabbitmq/(.*) { - rewrite ^/rabbitmq/(.*)$ /$1 break; - proxy_pass http://rabbitmq:15672; - proxy_buffering off; - proxy_set_header Host $http_host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } } } diff --git a/docker-compose.yml b/docker-compose.yml index 85704b0..ed3d6ec 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,15 +10,20 @@ x-backend: depends_on: - postgres - rabbitmq + - redis environment: BACKEND_DB_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_USER} BACKEND_AMQP_URL: amqp://${RABBITMQ_USER}:${RABBITMQ_PASSWORD}@rabbitmq:5672 + BACKEND_REDIS_URL: redis://redis:6379 BACKEND_UNTIS_API_URL: ${BACKEND_UNTIS_API_URL} BACKEND_UNTIS_RPC_URL: ${BACKEND_UNTIS_RPC_URL} BACKEND_UNTIS_CLIENT_NAME: ${BACKEND_UNTIS_CLIENT_NAME} BACKEND_UNTIS_SCHOOL: ${BACKEND_UNTIS_SCHOOL} BACKEND_UNTIS_USERNAME: ${BACKEND_UNTIS_USERNAME} BACKEND_UNTIS_PASSWORD: ${BACKEND_UNTIS_PASSWORD} + BACKEND_UNTIS_VPLAN_URL: ${BACKEND_UNTIS_VPLAN_URL} + BACKEND_UNTIS_VPLAN_USERNAME: ${BACKEND_UNTIS_VPLAN_USERNAME} + BACKEND_UNTIS_VPLAN_PASSWORD: ${BACKEND_UNTIS_VPLAN_PASSWORD} services: nginx: @@ -49,7 +54,7 @@ services: - postgres rabbitmq: - image: docker.io/rabbitmq:3-management-alpine + image: docker.io/rabbitmq:3-alpine restart: always environment: RABBITMQ_DEFAULT_USER: ${RABBITMQ_USER} @@ -57,6 +62,12 @@ services: volumes: - rabbitmq:/var/lib/rabbitmq + redis: + image: docker.io/redis:alpine + restart: always + volumes: + - redis:/data + worker: <<: *backend command: worker @@ -72,5 +83,6 @@ services: volumes: postgres: rabbitmq: + redis: