From f13c9c905f32f397d3c07ee7d650582b90a23910 Mon Sep 17 00:00:00 2001 From: Dominic Grimm Date: Wed, 14 Dec 2022 19:13:32 +0100 Subject: [PATCH] Update --- .env.example | 1 + backend/.dockerignore | 5 - backend/.gitignore | 11 +- backend/Cargo.lock | 59 +- backend/Cargo.toml | 2 +- backend/Dockerfile | 2 +- .../2022-12-03-124501_init/down.sql | 10 +- .../migrations/2022-12-03-124501_init/up.sql | 49 +- backend/src/config.rs | 20 + backend/src/db/models.rs | 100 +-- backend/src/db/schema.rs | 112 ++- backend/src/lib.rs | 1 - backend/src/untis.rs | 717 ------------------ backend/src/worker/update_info.rs | 161 +++- docker-compose.yml | 53 +- 15 files changed, 322 insertions(+), 981 deletions(-) delete mode 100644 backend/src/untis.rs diff --git a/.env.example b/.env.example index af95812..c6f6eba 100644 --- a/.env.example +++ b/.env.example @@ -6,6 +6,7 @@ RABBITMQ_PASSWORD="bvplan" BACKEND_UNTIS_API_URL="https://mese.webuntis.com/WebUntis/api/" BACKEND_UNTIS_RPC_URL="https://mese.webuntis.com/WebUntis/jsonrpc.do" +BACKEND_UNTIS_CLIENT_NAME="bvplan" BACKEND_UNTIS_SCHOOL= BACKEND_UNTIS_USERNAME= BACKEND_UNTIS_PASSWORD= diff --git a/backend/.dockerignore b/backend/.dockerignore index 1b321f5..b0b8dba 100644 --- a/backend/.dockerignore +++ b/backend/.dockerignore @@ -3,17 +3,12 @@ debug/ target/ -# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries -# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html -Cargo.lock - # These are backup files generated by rustfmt **/*.rs.bk # MSVC Windows builds of rustc generate these, which store debugging information *.pdb -dist/ Dockerfile .gitignore .dockerignore diff --git a/backend/.gitignore b/backend/.gitignore index ea8c4bf..73fab07 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -1 +1,10 @@ -/target +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb diff --git a/backend/Cargo.lock b/backend/Cargo.lock index c4eb9d7..9ce6e12 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -549,12 +549,12 @@ dependencies = [ "juniper_actix", "lazy_static", "log", - "now", "reqwest", "serde", "serde_json", "tikv-jemallocator", "tokio", + "untis", "url", ] @@ -1592,9 +1592,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec947b7a4ce12e3b87e353abae7ce124d025b6c7d6c5aea5cc0bcf92e9510ded" +checksum = "11b0d96e660696543b251e58030cf9787df56da39dab19ad60eae7353040917e" [[package]] name = "is-terminal" @@ -1742,9 +1742,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f9f08d8963a6c613f4b1a78f4f4a4dbfadf8e6545b2d72861731e4858b8b47f" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" [[package]] name = "local-channel" @@ -1865,15 +1865,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "now" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d89e9874397a1f0a52fc1f197a8effd9735223cb2390e9dcc83ac6cd02923d0" -dependencies = [ - "chrono", -] - [[package]] name = "num-integer" version = "0.1.45" @@ -1994,7 +1985,7 @@ checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" dependencies = [ "instant", "lock_api", - "parking_lot_core 0.8.5", + "parking_lot_core 0.8.6", ] [[package]] @@ -2009,9 +2000,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" dependencies = [ "cfg-if", "instant", @@ -2036,9 +2027,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" +checksum = "cf1c2c742266c2f1041c914ba65355a83ae8747b05f208319784083583494b4b" [[package]] name = "percent-encoding" @@ -2098,9 +2089,9 @@ checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" [[package]] name = "polling" -version = "2.5.1" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "166ca89eb77fd403230b9c156612965a81e094ec6ec3aa13663d4c8b113fa748" +checksum = "22122d5ec4f9fe1b3916419b76be1e80bcb93f618d071d2edf841b137b2a2bd6" dependencies = [ "autocfg", "cfg-if", @@ -2271,9 +2262,9 @@ dependencies = [ [[package]] name = "redis" -version = "0.21.6" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "571c252c68d09a2ad3e49edd14e9ee48932f3e0f27b06b4ea4c9b2a706d31103" +checksum = "152f3863635cbb76b73bc247845781098302c6c9ad2060e1a9a7de56840346b6" dependencies = [ "arc-swap", "async-trait", @@ -2530,18 +2521,18 @@ checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" [[package]] name = "serde" -version = "1.0.149" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "256b9932320c590e707b94576e3cc1f7c9024d0ee6612dfbcf1cb106cbe8e055" +checksum = "e326c9ec8042f1b5da33252c8a37e9ffbd2c9bef0155215b6e6c80c790e05f91" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.149" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4eae9b04cbffdfd550eb462ed33bc6a1b68c935127d008b27444d08380f94e4" +checksum = "42a3df25b0713732468deadad63ab9da1f1fd75a48a15024b50363f128db627e" dependencies = [ "proc-macro2", "quote", @@ -2988,6 +2979,20 @@ dependencies = [ "void", ] +[[package]] +name = "untis" +version = "0.1.0" +source = "git+https://git.dergrimm.net/dergrimm/untis.rs.git?branch=main#a6bc2bd2e947d73f18c7a118ba4836c4ea7e6531" +dependencies = [ + "anyhow", + "chrono", + "cookie", + "reqwest", + "serde", + "serde_json", + "url", +] + [[package]] name = "untrusted" version = "0.7.1" diff --git a/backend/Cargo.toml b/backend/Cargo.toml index e1f67be..ef2bddf 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -29,11 +29,11 @@ juniper = "0.15.10" juniper_actix = "0.4.0" lazy_static = "1.4.0" log = "0.4.17" -now = "0.1.3" reqwest = { version = "0.11.13", features = ["json"] } serde = "1.0.148" serde_json = "1.0.89" tokio = { version = "1.22.0", features = ["full"] } +untis = { git = "https://git.dergrimm.net/dergrimm/untis.rs.git", branch = "main" } url = "2.3.1" [target.'cfg(not(target_env = "msvc"))'.dependencies] diff --git a/backend/Dockerfile b/backend/Dockerfile index 42ac1e0..f99dba5 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -3,7 +3,7 @@ WORKDIR /usr/src/backend FROM chef as planner RUN mkdir src && touch src/main.rs -COPY ./Cargo.toml . +COPY ./Cargo.toml ./Cargo.lock ./ RUN cargo chef prepare --recipe-path recipe.json FROM chef as builder diff --git a/backend/migrations/2022-12-03-124501_init/down.sql b/backend/migrations/2022-12-03-124501_init/down.sql index 1f1c408..8d409b8 100644 --- a/backend/migrations/2022-12-03-124501_init/down.sql +++ b/backend/migrations/2022-12-03-124501_init/down.sql @@ -1,9 +1,3 @@ -DROP TABLE substitution_query_results; - -DROP TABLE substitution_queries; - -DROP TABLE substitution_planned_queries; - DROP TABLE substitution_room; DROP TABLE substitution_subject; @@ -16,6 +10,8 @@ DROP TABLE substitutions; DROP TYPE substitution_type; +DROP TABLE substitution_queries; + DROP TABLE timegrid_time_unit; DROP TABLE timegrid_days; @@ -34,6 +30,8 @@ DROP TABLE classes; DROP TABLE teachers; +DROP INDEX tenants_active; + DROP TABLE tenants; DROP TABLE schoolyears; \ No newline at end of file diff --git a/backend/migrations/2022-12-03-124501_init/up.sql b/backend/migrations/2022-12-03-124501_init/up.sql index 610af28..47364af 100644 --- a/backend/migrations/2022-12-03-124501_init/up.sql +++ b/backend/migrations/2022-12-03-124501_init/up.sql @@ -18,7 +18,7 @@ CREATE TABLE tenants( updated_at TIMESTAMP ); -CREATE UNIQUE INDEX ON tenants(active) +CREATE UNIQUE INDEX tenants_active ON tenants(active) WHERE active; @@ -110,6 +110,17 @@ CREATE TABLE timegrid_time_unit( 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 INDEX substitution_queries_active ON substitution_queries(active); + CREATE TYPE substitution_type AS ENUM ( 'cancel', 'subst', @@ -130,6 +141,7 @@ CREATE TYPE substitution_type AS ENUM ( CREATE TABLE substitutions( 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, @@ -142,6 +154,7 @@ CREATE TABLE substitutions( 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 @@ -150,7 +163,8 @@ CREATE TABLE substitution_classes( CREATE TABLE substitution_teachers( id SERIAL PRIMARY KEY, substitution_id INTEGER NOT NULL REFERENCES substitutions(id), - teacher_id INTEGER NOT NULL REFERENCES teachers(id), + position SMALLINT NOT NULL, + teacher_id INTEGER REFERENCES teachers(id), created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP ); @@ -158,6 +172,7 @@ CREATE TABLE substitution_teachers( 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 @@ -166,34 +181,8 @@ CREATE TABLE substitution_subjects( CREATE TABLE substitution_rooms( id SERIAL PRIMARY KEY, substitution_id INTEGER NOT NULL REFERENCES substitutions(id), - room_id INTEGER NOT NULL REFERENCES rooms(id), - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP -); - -CREATE TABLE substitution_planned_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 INDEX ON substitution_planned_queries(active); - -CREATE TABLE substitution_queries( - id SERIAL PRIMARY KEY, - substitution_planned_query_id INTEGER NOT NULL REFERENCES substitution_planned_queries(id), - queried_at TIMESTAMP NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP -); - -CREATE TABLE substitution_query_results( - id SERIAL PRIMARY KEY, - substitution_query_id INTEGER NOT NULL REFERENCES substitution_queries(id), - 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 diff --git a/backend/src/config.rs b/backend/src/config.rs index 27dd549..6011533 100644 --- a/backend/src/config.rs +++ b/backend/src/config.rs @@ -1,5 +1,7 @@ +use anyhow::Result; use envconfig::Envconfig; use lazy_static::lazy_static; +use url::Url; #[derive(Envconfig, Debug)] pub struct Config { @@ -15,6 +17,9 @@ pub struct Config { #[envconfig(from = "BACKEND_UNTIS_RPC_URL")] pub untis_rpc_url: String, + #[envconfig(from = "BACKEND_UNTIS_CLIENT_NAME")] + pub untis_client_name: String, + #[envconfig(from = "BACKEND_UNTIS_SCHOOL")] pub untis_school: String, @@ -28,3 +33,18 @@ pub struct Config { lazy_static! { pub static ref CONFIG: Config = Config::init_from_env().unwrap(); } + +pub fn untis_from_env() -> Result { + Ok(untis::Client { + api_url: Url::parse(&CONFIG.untis_api_url)?, + rpc_url: untis::Client::gen_rpc_url( + Url::parse(&CONFIG.untis_rpc_url)?, + &CONFIG.untis_school, + ), + client_name: CONFIG.untis_client_name.to_owned(), + username: CONFIG.untis_username.to_owned(), + password: CONFIG.untis_password.to_owned(), + session: None, + authorization: None, + }) +} diff --git a/backend/src/db/models.rs b/backend/src/db/models.rs index d98e29e..e9bf8b7 100644 --- a/backend/src/db/models.rs +++ b/backend/src/db/models.rs @@ -3,7 +3,6 @@ use diesel::prelude::*; use std::io::Write; use crate::db::schema; -use crate::untis; #[derive(Identifiable, Queryable, Debug)] #[diesel(table_name = schema::schoolyears)] @@ -286,8 +285,29 @@ impl From for SubstitutionType { } } -#[derive(Identifiable, Queryable, Debug)] +#[derive(Identifiable, Queryable, Associations, 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 created_at: NaiveDateTime, + pub updated_at: Option, +} + +#[derive(Insertable, Debug)] +#[diesel(table_name = schema::substitution_queries)] +pub struct NewSubstitutionQuery { + pub schoolyear_id: i32, + pub date: NaiveDate, + pub active: bool, +} + +#[derive(Identifiable, Queryable, Associations, Debug)] #[diesel(table_name = schema::substitutions)] +#[diesel(belongs_to(SubstitutionQuery))] pub struct Substitution { pub id: i32, pub substitution_query_id: i32, @@ -304,12 +324,12 @@ pub struct Substitution { #[derive(Insertable, Debug)] #[diesel(table_name = schema::substitutions)] pub struct NewSubstitution<'a> { + pub substitution_query_id: i32, pub subst_type: SubstitutionType, pub lesson_id: i32, pub start_time: NaiveTime, pub end_time: NaiveTime, pub text: Option<&'a str>, - pub active: bool, } #[derive(Identifiable, Queryable, Associations, Debug)] @@ -319,6 +339,7 @@ pub struct NewSubstitution<'a> { pub struct SubstitutionClass { pub id: i32, pub substitution_id: i32, + pub position: i16, pub class_id: i32, pub created_at: NaiveDateTime, pub updated_at: Option, @@ -328,6 +349,7 @@ pub struct SubstitutionClass { #[diesel(table_name = schema::substitution_classes)] pub struct NewSubstitutionClass { pub substitution_id: i32, + pub position: i16, pub class_id: i32, } @@ -338,7 +360,8 @@ pub struct NewSubstitutionClass { pub struct SubstitutionTeacher { pub id: i32, pub substitution_id: i32, - pub teacher_id: i32, + pub position: i16, + pub teacher_id: Option, pub created_at: NaiveDateTime, pub updated_at: Option, } @@ -347,7 +370,8 @@ pub struct SubstitutionTeacher { #[diesel(table_name = schema::substitution_teachers)] pub struct NewSubstitutionTeacher { pub substitution_id: i32, - pub teacher_id: i32, + pub position: i16, + pub teacher_id: Option, } #[derive(Identifiable, Queryable, Associations, Debug)] @@ -357,6 +381,7 @@ pub struct NewSubstitutionTeacher { pub struct SubstitutionSubject { pub id: i32, pub substitution_id: i32, + pub position: i16, pub subject_id: i32, pub created_at: NaiveDateTime, pub updated_at: Option, @@ -366,6 +391,7 @@ pub struct SubstitutionSubject { #[diesel(table_name = schema::substitution_subjects)] pub struct NewSubstitutionSubject { pub substitution_id: i32, + pub position: i16, pub subject_id: i32, } @@ -376,7 +402,9 @@ pub struct NewSubstitutionSubject { pub struct SubstitutionRoom { pub id: i32, pub substitution_id: i32, - pub room_id: i32, + pub position: i16, + pub index: i16, + pub room_id: Option, pub created_at: NaiveDateTime, pub updated_at: Option, } @@ -385,62 +413,6 @@ pub struct SubstitutionRoom { #[diesel(table_name = schema::substitution_rooms)] pub struct NewSubstitutionRoom { pub substitution_id: i32, - pub room_id: i32, -} - -#[derive(Identifiable, Queryable, Associations, Debug)] -#[diesel(table_name = schema::substitution_planned_queries)] -#[diesel(belongs_to(Schoolyear))] -pub struct SubstitutionPlannedQuery { - pub id: i32, - pub schoolyear_id: i32, - pub date: NaiveDate, - pub active: bool, - pub created_at: NaiveDateTime, - pub updated_at: Option, -} - -#[derive(Insertable, Debug)] -#[diesel(table_name = schema::substitution_planned_queries)] -pub struct NewSubstitutionPlannedQuery { - pub schoolyear_id: i32, - pub date: NaiveDate, - pub active: bool, -} - -#[derive(Identifiable, Queryable, Associations, Debug)] -#[diesel(table_name = schema::substitution_queries)] -#[diesel(belongs_to(SubstitutionPlannedQuery))] -pub struct SubstitutionQuery { - pub id: i32, - pub substitution_planned_query_id: i32, - pub queried_at: NaiveDateTime, - pub created_at: NaiveDateTime, - pub updated_at: Option, -} - -#[derive(Insertable, Debug)] -#[diesel(table_name = schema::substitution_queries)] -pub struct NewSubstitutionQuery { - pub substitution_planned_query_id: i32, - pub queried_at: NaiveDateTime, -} - -#[derive(Identifiable, Queryable, Associations, Debug)] -#[diesel(table_name = schema::substitution_query_results)] -#[diesel(belongs_to(SubstitutionQuery))] -#[diesel(belongs_to(Substitution))] -pub struct SubstitutionQueryResult { - pub id: i32, - pub substitution_query_id: i32, - pub substitution_id: i32, - 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 substitution_id: i32, + pub position: i16, + pub room_id: Option, } diff --git a/backend/src/db/schema.rs b/backend/src/db/schema.rs index c904a19..5c80b8f 100644 --- a/backend/src/db/schema.rs +++ b/backend/src/db/schema.rs @@ -136,65 +136,7 @@ diesel::table! { } diesel::table! { - use diesel::sql_types::*; - - use super::sql_types::SubstitutionType; - - substitutions { - id -> Integer, - subst_type -> SubstitutionType, - lesson_id -> Integer, - start_time -> Time, - end_time -> Time, - text -> Nullable, - active -> Bool, - created_at -> Timestamp, - updated_at -> Nullable, - } -} - -diesel::table! { - substitution_classes { - id -> Integer, - substitution_id -> Integer, - class_id -> Integer, - created_at -> Timestamp, - updated_at -> Nullable, - } -} - -diesel::table! { - substitution_teachers { - id -> Integer, - substitution_id -> Integer, - teacher_id -> Integer, - created_at -> Timestamp, - updated_at -> Nullable, - } -} - -diesel::table! { - substitution_subjects { - id -> Integer, - substitution_id -> Integer, - subject_id -> Integer, - created_at -> Timestamp, - updated_at -> Nullable, - } -} - -diesel::table! { - substitution_rooms { - id -> Integer, - substitution_id -> Integer, - room_id -> Integer, - created_at -> Timestamp, - updated_at -> Nullable, - } -} - -diesel::table! { - substitution_planned_queries { + substitution_queries { id -> Integer, schoolyear_id -> Integer, date -> Date, @@ -205,20 +147,62 @@ diesel::table! { } diesel::table! { - substitution_queries { + use diesel::sql_types::*; + + use super::sql_types::SubstitutionType; + + substitutions { id -> Integer, - substitution_planned_query_id -> Integer, - queried_at -> Timestamp, + substitution_query_id -> Integer, + subst_type -> SubstitutionType, + lesson_id -> Integer, + start_time -> Time, + end_time -> Time, + text -> Nullable, created_at -> Timestamp, updated_at -> Nullable, } } diesel::table! { - substitution_query_results { + substitution_classes { id -> Integer, - substitution_query_id -> Integer, substitution_id -> Integer, + position -> SmallInt, + class_id -> Integer, + created_at -> Timestamp, + updated_at -> Nullable, + } +} + +diesel::table! { + substitution_teachers { + id -> Integer, + substitution_id -> Integer, + position -> SmallInt, + teacher_id -> Nullable, + created_at -> Timestamp, + updated_at -> Nullable, + } +} + +diesel::table! { + substitution_subjects { + id -> Integer, + substitution_id -> Integer, + position -> SmallInt, + subject_id -> Integer, + created_at -> Timestamp, + updated_at -> Nullable, + } +} + +diesel::table! { + substitution_rooms { + id -> Integer, + substitution_id -> Integer, + position -> SmallInt, + room_id -> Nullable, created_at -> Timestamp, updated_at -> Nullable, } diff --git a/backend/src/lib.rs b/backend/src/lib.rs index 5f89f11..070916b 100644 --- a/backend/src/lib.rs +++ b/backend/src/lib.rs @@ -1,5 +1,4 @@ pub mod config; pub mod db; pub mod graphql; -pub mod untis; pub mod worker; diff --git a/backend/src/untis.rs b/backend/src/untis.rs deleted file mode 100644 index 4760420..0000000 --- a/backend/src/untis.rs +++ /dev/null @@ -1,717 +0,0 @@ -use anyhow::{bail, Context, Result}; -use chrono::Datelike; -use cookie::Cookie; -use serde::Deserialize; -use serde_json::json; - -pub const CLIENT_NAME: &str = "BVplan@OHG-Furtwangen"; - -fn deserialize_date<'de, D>(deserializer: D) -> Result -where - D: serde::de::Deserializer<'de>, -{ - struct Visitor; - - impl<'de> serde::de::Visitor<'de> for Visitor { - type Value = chrono::NaiveDate; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("a naive date serialized as an unsigned integer") - } - - fn visit_u64(self, mut v: u64) -> Result - where - E: serde::de::Error, - { - let y = (v / 10000) as i32; - v %= 10000; - let m = (v / 100) as u32; - v %= 100; - - match chrono::NaiveDate::from_ymd_opt(y, m, v as u32) { - Some(x) => Ok(x), - None => Err(E::custom(format!("No such date: {}-{}-{}", y, m, v))), - } - } - } - - deserializer.deserialize_u64(Visitor) -} - -fn serialize_date(date: chrono::NaiveDate) -> u64 { - date.year() as u64 * 10000 + date.month() as u64 * 100 + date.day() as u64 -} - -fn deserialize_time<'de, D>(deserializer: D) -> Result -where - D: serde::de::Deserializer<'de>, -{ - struct Visitor; - - impl<'de> serde::de::Visitor<'de> for Visitor { - type Value = chrono::NaiveTime; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("a naive time serialized as an unsigned integer") - } - - fn visit_u64(self, mut v: u64) -> Result - where - E: serde::de::Error, - { - let h = v / 100; - v %= 100; - - match chrono::NaiveTime::from_hms_opt(h as u32, v as u32, 0) { - Some(x) => Ok(x), - None => Err(E::custom(format!("No such time: {}:{}", h, v))), - } - } - } - - deserializer.deserialize_u64(Visitor) -} - -#[derive(Deserialize, Debug)] -pub struct RpcError { - pub message: String, - pub code: i32, -} - -#[derive(Deserialize, Debug)] -pub struct RpcResponse { - pub jsonrpc: String, - pub id: String, - pub result: Option, - pub error: Option, -} - -#[derive(Deserialize, Debug)] -struct RpcEmptyResult; - -#[derive(Deserialize, Debug)] -pub struct RpcLogin { - #[serde(rename = "sessionId")] - pub session_id: String, - #[serde(rename = "personType")] - pub person_type: i32, - #[serde(rename = "personId")] - pub person_id: i32, - #[serde(rename = "klasseId")] - pub class_id: i32, -} - -#[derive(Deserialize, Debug)] -pub struct RpcClass { - pub id: i32, - pub name: String, - #[serde(rename = "longName")] - pub long_name: String, - pub active: bool, -} - -#[derive(Deserialize, Debug)] -pub struct RpcSubject { - pub id: i32, - pub name: String, - #[serde(rename = "longName")] - pub long_name: String, -} - -#[derive(Deserialize, Debug)] -pub struct RpcRoom { - pub id: i32, - pub name: String, - #[serde(rename = "longName")] - pub long_name: String, -} - -#[derive(Deserialize, Debug)] -pub struct RpcDepartment { - pub id: i32, - pub name: String, - #[serde(rename = "longName")] - pub long_name: String, -} - -#[derive(Deserialize, Debug)] -pub struct RpcHoliday { - pub id: i32, - pub name: String, - #[serde(rename = "longName")] - pub long_name: String, - #[serde(rename = "startDate", deserialize_with = "deserialize_date")] - pub start_date: chrono::NaiveDate, - #[serde(rename = "endDate", deserialize_with = "deserialize_date")] - pub end_date: chrono::NaiveDate, -} - -#[derive(Deserialize, Debug)] -pub struct RpcTimegridDayTimeUnit { - #[serde(rename = "startTime", deserialize_with = "deserialize_time")] - pub start_time: chrono::NaiveTime, - #[serde(rename = "endTime", deserialize_with = "deserialize_time")] - pub end_time: chrono::NaiveTime, -} - -#[derive(Deserialize, Debug)] -pub struct RpcTimegridDay { - pub day: u8, - #[serde(rename = "timeUnits")] - pub time_units: Vec, -} - -#[derive(Deserialize, Debug)] -pub struct RpcSchoolyear { - pub id: i32, - pub name: String, - #[serde(rename = "startDate", deserialize_with = "deserialize_date")] - pub start_date: chrono::NaiveDate, - #[serde(rename = "endDate", deserialize_with = "deserialize_date")] - pub end_date: chrono::NaiveDate, -} - -#[derive(Deserialize, Clone, Copy, Debug)] -pub enum RpcSubstitionType { - #[serde(rename = "cancel")] - Cancel, - #[serde(rename = "subst")] - Substitution, - #[serde(rename = "add")] - Additional, - #[serde(rename = "shift")] - Shifted, - #[serde(rename = "rmchg")] - RoomChange, - #[serde(rename = "rmlk")] - Locked, - #[serde(rename = "bs")] - BreakSupervision, - #[serde(rename = "oh")] - OfficeHour, - #[serde(rename = "sb")] - Standby, - #[serde(rename = "other")] - Other, - #[serde(rename = "free")] - Free, - #[serde(rename = "exam")] - Exam, - #[serde(rename = "ac")] - Activity, - #[serde(rename = "holi")] - Holiday, - #[serde(rename = "stxt")] - Text, -} - -#[derive(Deserialize, Debug)] -pub struct RpcSubstitutionId { - pub id: i32, - pub name: Option, -} - -#[derive(Deserialize, Debug)] -pub struct RpcSubstitutionReschedule { - #[serde(deserialize_with = "deserialize_date")] - pub date: chrono::NaiveDate, - #[serde(rename = "startTime", deserialize_with = "deserialize_time")] - pub start_time: chrono::NaiveTime, - #[serde(rename = "endTime", deserialize_with = "deserialize_time")] - pub end_time: chrono::NaiveTime, -} - -#[derive(Deserialize, Debug)] -pub struct RpcSubstitution { - #[serde(rename = "type")] - pub subst_type: RpcSubstitionType, - #[serde(rename = "lsid")] - pub lesson_id: i32, - #[serde(rename = "startTime", deserialize_with = "deserialize_time")] - pub start_time: chrono::NaiveTime, - #[serde(rename = "endTime", deserialize_with = "deserialize_time")] - pub end_time: chrono::NaiveTime, - #[serde(rename = "txt")] - pub text: Option, - #[serde(rename = "kl")] - pub classes: Vec, - #[serde(rename = "te")] - pub teachers: Vec, - #[serde(rename = "su")] - pub subjects: Vec, - #[serde(rename = "ro")] - pub rooms: Vec, - pub reschedule: Option, -} - -#[derive(Deserialize, Debug)] -pub struct ApiTenant { - pub id: i32, - #[serde(rename = "displayName")] - pub display_name: String, -} - -#[derive(Deserialize, Debug)] -struct ApiDataResponse { - tenant: ApiTenant, -} - -#[derive(Deserialize, Debug)] -pub struct ApiTeacher { - pub id: i32, - pub name: String, - pub forename: String, - #[serde(rename = "longName")] - pub long_name: String, - #[serde(rename = "displayname")] - pub display_name: String, -} - -#[derive(Deserialize, Debug)] -struct ApiTeachersResponseData { - elements: Vec, -} - -#[derive(Deserialize, Debug)] -struct ApiTeachersResponse { - data: ApiTeachersResponseData, -} - -#[derive(Debug)] -pub struct Client { - pub api_url: url::Url, - pub rpc_url: url::Url, - pub username: String, - pub password: String, - pub session: Option, - pub authorization: Option, -} - -impl Client { - pub fn gen_rpc_url(mut endpoint: url::Url, school: &str) -> url::Url { - endpoint.query_pairs_mut().append_pair("school", school); - - endpoint - } - - pub async fn login_rpc(&mut self) -> Result { - let resp: RpcResponse = reqwest::Client::new() - .get(self.rpc_url.as_str()) - .header(reqwest::header::CONTENT_TYPE, "application/json") - .header(reqwest::header::ACCEPT, "application/json-rpc") - .json(&json!({ - "id": "ID", - "method": "authenticate", - "params": { - "user": self.username, - "password": self.password, - "client": CLIENT_NAME, - }, - "jsonrpc": "2.0" - })) - .send() - .await? - .json() - .await?; - if let Some(e) = resp.error { - bail!("RPC error: {:?}", e); - } - - self.session = Some( - Cookie::new( - "JSESSIONID", - &resp - .result - .as_ref() - .context("Result null event though error not")? - .session_id, - ) - .to_string(), - ); - - if let Some(x) = resp.result { - Ok(x) - } else { - bail!("RPC result is null"); - } - } - - pub async fn login_api(&mut self) -> Result { - let jwt = reqwest::Client::new() - .get(self.api_url.join("token/new")?) - .header( - reqwest::header::COOKIE, - self.session.as_ref().context("Not logged in")?, - ) - .send() - .await? - .text() - .await?; - self.authorization = Some(jwt.to_string()); - - Ok(jwt) - } - - pub async fn login(&mut self) -> Result<()> { - self.login_rpc().await?; - self.login_api().await?; - - Ok(()) - } - - pub async fn logout(&mut self) -> Result<()> { - let resp: RpcResponse = reqwest::Client::new() - .get(self.rpc_url.as_str()) - .header(reqwest::header::CONTENT_TYPE, "application/json") - .header(reqwest::header::ACCEPT, "application/json-rpc") - .header( - reqwest::header::COOKIE, - self.session.as_ref().context("Not logged in")?, - ) - .json(&json!({ - "id": "ID", - "method": "logout", - "params": {}, - "jsonrpc": "2.0" - })) - .send() - .await? - .json() - .await?; - self.session = None; - if let Some(e) = resp.error { - bail!("RPC error: {:?}", e); - } - - Ok(()) - } - - pub async fn classes(&self) -> Result> { - let resp: RpcResponse> = reqwest::Client::new() - .get(self.rpc_url.as_str()) - .header(reqwest::header::CONTENT_TYPE, "application/json") - .header(reqwest::header::ACCEPT, "application/json-rpc") - .header( - reqwest::header::COOKIE, - self.session.as_ref().context("Not logged in")?, - ) - .json(&json!({ - "id": "ID", - "method": "getKlassen", - "params": {}, - "jsonrpc": "2.0" - })) - .send() - .await? - .json() - .await?; - if let Some(e) = resp.error { - bail!("RPC error: {:?}", e); - } - - if let Some(x) = resp.result { - Ok(x) - } else { - bail!("RPC result is null"); - } - } - - pub async fn subjects(&self) -> Result> { - let resp: RpcResponse> = reqwest::Client::new() - .get(self.rpc_url.as_str()) - .header(reqwest::header::CONTENT_TYPE, "application/json") - .header(reqwest::header::ACCEPT, "application/json-rpc") - .header( - reqwest::header::COOKIE, - self.session.as_ref().context("Not logged in")?, - ) - .json(&json!({ - "id": "ID", - "method": "getSubjects", - "params": {}, - "jsonrpc": "2.0" - })) - .send() - .await? - .json() - .await?; - if let Some(e) = resp.error { - bail!("RPC error: {:?}", e); - } - - if let Some(x) = resp.result { - Ok(x) - } else { - bail!("RPC result is null"); - } - } - - pub async fn rooms(&self) -> Result> { - let resp: RpcResponse> = reqwest::Client::new() - .get(self.rpc_url.as_str()) - .header(reqwest::header::CONTENT_TYPE, "application/json") - .header(reqwest::header::ACCEPT, "application/json-rpc") - .header( - reqwest::header::COOKIE, - self.session.as_ref().context("Not logged in")?, - ) - .json(&json!({ - "id": "ID", - "method": "getRooms", - "params": {}, - "jsonrpc": "2.0" - })) - .send() - .await? - .json() - .await?; - if let Some(e) = resp.error { - bail!("RPC error: {:?}", e); - } - - if let Some(x) = resp.result { - Ok(x) - } else { - bail!("RPC result is null"); - } - } - - pub async fn departments(&self) -> Result> { - let resp: RpcResponse> = reqwest::Client::new() - .get(self.rpc_url.as_str()) - .header(reqwest::header::CONTENT_TYPE, "application/json") - .header(reqwest::header::ACCEPT, "application/json-rpc") - .header( - reqwest::header::COOKIE, - self.session.as_ref().context("Not logged in")?, - ) - .json(&json!({ - "id": "ID", - "method": "getDepartments", - "params": {}, - "jsonrpc": "2.0" - })) - .send() - .await? - .json() - .await?; - if let Some(e) = resp.error { - bail!("RPC error: {:?}", e); - } - - if let Some(x) = resp.result { - Ok(x) - } else { - bail!("RPC result is null"); - } - } - - pub async fn holidays(&self) -> Result> { - let resp: RpcResponse> = reqwest::Client::new() - .get(self.rpc_url.as_str()) - .header(reqwest::header::CONTENT_TYPE, "application/json") - .header(reqwest::header::ACCEPT, "application/json-rpc") - .header( - reqwest::header::COOKIE, - self.session.as_ref().context("Not logged in")?, - ) - .json(&json!({ - "id": "ID", - "method": "getHolidays", - "params": {}, - "jsonrpc": "2.0" - })) - .send() - .await? - .json() - .await?; - if let Some(e) = resp.error { - bail!("RPC error: {:?}", e); - } - - if let Some(x) = resp.result { - Ok(x) - } else { - bail!("RPC result is null"); - } - } - - pub async fn timegrid(&self) -> Result> { - let resp: RpcResponse> = reqwest::Client::new() - .get(self.rpc_url.as_str()) - .header(reqwest::header::CONTENT_TYPE, "application/json") - .header(reqwest::header::ACCEPT, "application/json-rpc") - .header( - reqwest::header::COOKIE, - self.session.as_ref().context("Not logged in")?, - ) - .json(&json!({ - "id": "ID", - "method": "getTimegridUnits", - "params": {}, - "jsonrpc": "2.0" - })) - .send() - .await? - .json() - .await?; - if let Some(e) = resp.error { - bail!("RPC error: {:?}", e); - } - - if let Some(x) = resp.result { - Ok(x) - } else { - bail!("RPC result is null"); - } - } - - pub async fn current_schoolyear(&self) -> Result { - let resp: RpcResponse = reqwest::Client::new() - .get(self.rpc_url.as_str()) - .header(reqwest::header::CONTENT_TYPE, "application/json") - .header(reqwest::header::ACCEPT, "application/json-rpc") - .header( - reqwest::header::COOKIE, - self.session.as_ref().context("Not logged in")?, - ) - .json(&json!({ - "id": "ID", - "method": "getCurrentSchoolyear", - "params": {}, - "jsonrpc": "2.0" - })) - .send() - .await? - .json() - .await?; - if let Some(e) = resp.error { - bail!("RPC error: {:?}", e); - } - - if let Some(x) = resp.result { - Ok(x) - } else { - bail!("RPC result is null"); - } - } - - pub async fn schoolyears(&self) -> Result> { - let resp: RpcResponse> = reqwest::Client::new() - .get(self.rpc_url.as_str()) - .header(reqwest::header::CONTENT_TYPE, "application/json") - .header(reqwest::header::ACCEPT, "application/json-rpc") - .header( - reqwest::header::COOKIE, - self.session.as_ref().context("Not logged in")?, - ) - .json(&json!({ - "id": "ID", - "method": "getSchoolyears", - "params": {}, - "jsonrpc": "2.0" - })) - .send() - .await? - .json() - .await?; - if let Some(e) = resp.error { - bail!("RPC error: {:?}", e); - } - - if let Some(x) = resp.result { - Ok(x) - } else { - bail!("RPC result is null"); - } - } - - pub async fn substitutions( - &self, - from: &chrono::NaiveDate, - to: &chrono::NaiveDate, - department: Option, - ) -> Result> { - let resp: RpcResponse> = reqwest::Client::new() - .get(self.rpc_url.as_str()) - .header(reqwest::header::CONTENT_TYPE, "application/json") - .header(reqwest::header::ACCEPT, "application/json-rpc") - .header( - reqwest::header::COOKIE, - self.session.as_ref().context("Not logged in")?, - ) - .json(&json!({ - "id": "ID", - "method": "getSubstitutions", - "params": { - "startDate": serialize_date(*from), - "endDate": serialize_date(*to), - "departmentId": department.map_or(0, |d| d) - }, - "jsonrpc": "2.0" - })) - .send() - .await? - .json() - .await?; - if let Some(e) = resp.error { - bail!("RPC error: {:?}", e); - } - - if let Some(x) = resp.result { - Ok(x) - } else { - bail!("RPC result is null"); - } - } - - pub fn construct_bearer(auth: &str) -> String { - format!("Bearer {}", auth) - } - - pub async fn current_tenant(&self) -> Result { - let resp: ApiDataResponse = reqwest::Client::new() - .get(self.api_url.join("rest/view/v1/app/data")?) - .header(reqwest::header::ACCEPT, "application/json") - .header( - reqwest::header::COOKIE, - self.session.as_ref().context("Not logged in")?, - ) - .header( - reqwest::header::AUTHORIZATION, - Client::construct_bearer(&self.authorization.as_ref().context("Not logged in")?), - ) - .send() - .await? - .json() - .await?; - - Ok(resp.tenant) - } - - pub async fn teachers(&self) -> Result> { - let mut url = self.api_url.join("public/timetable/weekly/pageconfig")?; - url.query_pairs_mut().append_pair("type", "2"); - let resp: ApiTeachersResponse = reqwest::Client::new() - .get(url) - .header(reqwest::header::ACCEPT, "application/json") - .header( - reqwest::header::COOKIE, - self.session.as_ref().context("Not logged in")?, - ) - .header( - reqwest::header::AUTHORIZATION, - Client::construct_bearer(&self.authorization.as_ref().context("Not logged in")?), - ) - .send() - .await? - .json() - .await?; - - Ok(resp.data.elements) - } - - pub async fn exams(&self) -> Result<()> { - Ok(()) - } -} diff --git a/backend/src/worker/update_info.rs b/backend/src/worker/update_info.rs index 871c81a..fa2cb12 100644 --- a/backend/src/worker/update_info.rs +++ b/backend/src/worker/update_info.rs @@ -3,11 +3,8 @@ use celery::error::TaskError; use celery::task::TaskResult; use chrono::prelude::*; use diesel::prelude::*; -use now::DateTimeNow; -use crate::config; -use crate::db; -use crate::untis; +use crate::{config, db}; async fn fetch_schoolyears(client: &untis::Client, conn: &mut PgConnection) -> Result { let existing_schoolyears = db::schema::schoolyears::table @@ -272,42 +269,140 @@ async fn fetch_substitutions( conn: &mut PgConnection, schoolyear_id: i32, ) -> Result<()> { - let now = Utc::now(); - let beginning_of_week = now.beginning_of_week().date_naive(); - let end_of_week = now.end_of_week().date_naive(); - // if diesel::select(diesel::dsl::not(diesel::expression::exists::exists( - // db::schema::substitution_queries::table - // .filter(db::schema::substitution_queries::active) - // .filter(db::schema::substitution_queries::end_date.eq(end_of_week)), - // ))) - // .get_result::(conn)? - // {} + let today = Utc::now().date_naive(); + if diesel::select(diesel::dsl::not(diesel::expression::exists::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, + }) + .execute(conn)?; + } - // for query in db::schema::substitution_queries::table - // .filter(db::schema::substitution_queries::active) - // .load::(conn)? - // { - // dbg!(&query); - // } + for query in db::schema::substitution_queries::table + .filter(db::schema::substitution_queries::active) + .load::(conn)? + { + let now = Utc::now().naive_utc(); + 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_id: 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(), + }) + .returning(db::schema::substitutions::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::(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(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)?; + } + } Ok(()) } #[celery::task] pub async fn update_info() -> TaskResult<()> { - let mut client = untis::Client { - api_url: match url::Url::parse(&config::CONFIG.untis_api_url) { - Ok(x) => x, - Err(e) => return Err(TaskError::UnexpectedError(e.to_string())), - }, - rpc_url: match url::Url::parse(&config::CONFIG.untis_rpc_url) { - Ok(x) => untis::Client::gen_rpc_url(x, &config::CONFIG.untis_school), - Err(e) => return Err(TaskError::UnexpectedError(e.to_string())), - }, - username: config::CONFIG.untis_username.to_owned(), - password: config::CONFIG.untis_password.to_owned(), - session: None, - authorization: None, + let mut client = match config::untis_from_env() { + Ok(x) => x, + Err(e) => return Err(TaskError::UnexpectedError(e.to_string())), }; if let Err(e) = client.login().await { diff --git a/docker-compose.yml b/docker-compose.yml index 616230a..d8660dc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,25 @@ version: "3" +x-backend: + &backend + image: git.dergrimm.net/dergrimm/bvplan_backend:latest + build: + context: ./backend + restart: always + command: worker + depends_on: + - postgres + - rabbitmq + environment: + BACKEND_DB_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_USER} + BACKEND_AMQP_URL: amqp://${RABBITMQ_USER}:${RABBITMQ_PASSWORD}@rabbitmq:5672 + 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} + services: nginx: image: docker.io/byjg/nginx-extras @@ -38,41 +58,12 @@ services: - rabbitmq:/var/lib/rabbitmq worker: - image: git.dergrimm.net/dergrimm/bvplan_backend:latest - build: - context: ./backend - restart: always + <<: *backend command: worker - depends_on: - - postgres - - rabbitmq - environment: - BACKEND_DB_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_USER} - BACKEND_AMQP_URL: amqp://${RABBITMQ_USER}:${RABBITMQ_PASSWORD}@rabbitmq:5672 - BACKEND_UNTIS_API_URL: ${BACKEND_UNTIS_API_URL} - BACKEND_UNTIS_RPC_URL: ${BACKEND_UNTIS_RPC_URL} - BACKEND_UNTIS_SCHOOL: ${BACKEND_UNTIS_SCHOOL} - BACKEND_UNTIS_USERNAME: ${BACKEND_UNTIS_USERNAME} - BACKEND_UNTIS_PASSWORD: ${BACKEND_UNTIS_PASSWORD} api: - image: git.dergrimm.net/dergrimm/bvplan_backend:latest - build: - context: ./backend - restart: always + <<: *backend command: api - depends_on: - - postgres - - rabbitmq - - worker - environment: - BACKEND_DB_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_USER} - BACKEND_AMQP_URL: amqp://${RABBITMQ_USER}:${RABBITMQ_PASSWORD}@rabbitmq:5672 - BACKEND_UNTIS_API_URL: ${BACKEND_UNTIS_API_URL} - BACKEND_UNTIS_RPC_URL: ${BACKEND_UNTIS_RPC_URL} - BACKEND_UNTIS_SCHOOL: ${BACKEND_UNTIS_SCHOOL} - BACKEND_UNTIS_USERNAME: ${BACKEND_UNTIS_USERNAME} - BACKEND_UNTIS_PASSWORD: ${BACKEND_UNTIS_PASSWORD} volumes: postgres: