upgrades dependencies, remove unused libs, add licensing information and a correct README
This commit is contained in:
parent
45f51b6774
commit
5db7f250e3
16
Cargo.toml
16
Cargo.toml
@ -10,18 +10,10 @@ authors = ["Denys Konovalov <denys.konovalov@protonmail.com>"]
|
||||
[dependencies]
|
||||
rocket = { version = "0.5.0-rc.1", features = ["json"] }
|
||||
serde = "1.0"
|
||||
diesel = { version = "1.4", features = ["postgres", "serde_json"] }
|
||||
reqwest = { version="0.11", features = ["json"] }
|
||||
quickxml_to_serde = "0.4"
|
||||
quickxml_to_serde = "0.5"
|
||||
serde_json = "1.0"
|
||||
jsonwebtoken = "7.2"
|
||||
time = "0.2"
|
||||
serde_derive = "1.0"
|
||||
jsonwebtoken = "8.1"
|
||||
time = "0.3"
|
||||
chrono = "0.4"
|
||||
|
||||
|
||||
[dependencies.serde_derive]
|
||||
version = "1.0"
|
||||
|
||||
[dependencies.rocket_sync_db_pools]
|
||||
version = "0.1.0-rc.1"
|
||||
features = ["diesel_postgres_pool"]
|
||||
|
@ -3,13 +3,13 @@ WORKDIR /app
|
||||
COPY . .
|
||||
RUN cargo install --path .
|
||||
|
||||
FROM debian:buster-slim as runner
|
||||
FROM debian:bullseye-slim as runner
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y ca-certificates tzdata libssl-dev libpq5 \
|
||||
&& apt-get install -y ca-certificates tzdata libssl-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY --from=builder /usr/local/cargo/bin/api /usr/local/bin/api
|
||||
ENV ROCKET_ADDRESS=0.0.0.0
|
||||
EXPOSE 3000
|
||||
EXPOSE 8000
|
||||
CMD ["api"]
|
||||
|
68
README.md
68
README.md
@ -1,2 +1,70 @@
|
||||
# meincantor-api
|
||||
The API backend for the GCG.MeinCantor school platform built with Rocket.rs in Rust.
|
||||
|
||||
It includes a plugin for receiving data from Indiware Mobil and a Keycloak authentication extension.
|
||||
|
||||
See the repository of the [main application](https://git.cantorgymnasium.de/cantortechnik/meincantor-app) for additional information.
|
||||
|
||||
## Building
|
||||
|
||||
Dependencies:
|
||||
- `openssl-devel`
|
||||
|
||||
```bash
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
## Deployment
|
||||
|
||||
### docker-compose
|
||||
|
||||
```
|
||||
version: "3.1"
|
||||
services:
|
||||
api:
|
||||
image: lxdb/meincantor-api
|
||||
restart: always
|
||||
ports:
|
||||
- 8000:8000
|
||||
environment:
|
||||
IW_TIMETABLE_URL: https://stundenplan24.de/EXAMPLE_SCHOOL/mobil/mobdaten
|
||||
IW_TIMETABLE_USER: EXAMPLE_USER
|
||||
IW_TIMETABLE_PASSWORD: EXAMPLE_PASSWORD
|
||||
JWT_SECRET: EXAMPLE_SECRET
|
||||
JWT_ISSUER: Georg-Cantor-Gymnasium Halle(Saale)
|
||||
KC_OPENID_TOKEN_ENDPOINT: https://example.keycloak.com/auth/realms/EXAMPLE_REALM/protocol/openid-connect/token
|
||||
KC_OPENID_USERINFO_ENDPOINT: https://example.keycloak.com/auth/realms/EXAMPLE_REALM/protocol/openid-connect/userinfo
|
||||
KC_CLIENT_ID: EXAMPLE_CLIENT
|
||||
volumes:
|
||||
- ./static:/app/static
|
||||
```
|
||||
|
||||
1. Create folder `mkdir meincantor-api`
|
||||
2. Create `docker-compose.yml` file with the content above
|
||||
3. Start with `docker-compose up -d`
|
||||
|
||||
|
||||
### local
|
||||
|
||||
1. Edit `src/config.rs`
|
||||
2. Rebuild the binary
|
||||
3. Execute the binary with `./target/release/api`
|
||||
|
||||
## Licensing
|
||||
|
||||
```
|
||||
Copyright (C) 2021-2022 Denys Konovalov
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
```
|
||||
|
11
Rocket.toml
11
Rocket.toml
@ -1,11 +0,0 @@
|
||||
[debug]
|
||||
port = 3000
|
||||
|
||||
[debug.databases]
|
||||
timetable = { url = "postgres://meincantor:meincantor_password@localhost/meincantor_db" }
|
||||
|
||||
[release]
|
||||
port = 3000
|
||||
|
||||
[release.databases]
|
||||
timetable = { url = "postgres://meincantor:meincantor_password@localhost/meincantor_db" }
|
@ -1,5 +0,0 @@
|
||||
# For documentation on how to configure this file,
|
||||
# see diesel.rs/guides/configuring-diesel-cli
|
||||
|
||||
[print_schema]
|
||||
file = "src/schema.rs"
|
@ -1,16 +1,10 @@
|
||||
version: "3.1"
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:13-alpine
|
||||
restart: always
|
||||
environment:
|
||||
POSTGRES_PASSWORD: meincantor_password
|
||||
POSTGRES_USER: meincantor
|
||||
POSTGRES_DB: meincantor_db
|
||||
expose: '5432'
|
||||
api:
|
||||
image: lxdb/meincantor-api
|
||||
restart: always
|
||||
ports:
|
||||
- 8000:8000
|
||||
environment:
|
||||
IW_TIMETABLE_URL: https://stundenplan24.de/EXAMPLE_SCHOOL/mobil/mobdaten
|
||||
IW_TIMETABLE_USER: EXAMPLE_USER
|
||||
@ -20,6 +14,5 @@ services:
|
||||
KC_OPENID_TOKEN_ENDPOINT: https://example.keycloak.com/auth/realms/EXAMPLE_REALM/protocol/openid-connect/token
|
||||
KC_OPENID_USERINFO_ENDPOINT: https://example.keycloak.com/auth/realms/EXAMPLE_REALM/protocol/openid-connect/userinfo
|
||||
KC_CLIENT_ID: EXAMPLE_CLIENT
|
||||
ROCKET_DATABASES: '{timetable={url="postgres://meincantor:meincantor_password@postgres/meincantor_db"}}'
|
||||
volumes:
|
||||
- ./static:/app/static
|
||||
|
@ -1,6 +0,0 @@
|
||||
-- This file was automatically created by Diesel to setup helper functions
|
||||
-- and other internal bookkeeping. This file is safe to edit, any future
|
||||
-- changes will be added to existing projects as new migrations.
|
||||
|
||||
DROP FUNCTION IF EXISTS diesel_manage_updated_at(_tbl regclass);
|
||||
DROP FUNCTION IF EXISTS diesel_set_updated_at();
|
@ -1,36 +0,0 @@
|
||||
-- This file was automatically created by Diesel to setup helper functions
|
||||
-- and other internal bookkeeping. This file is safe to edit, any future
|
||||
-- changes will be added to existing projects as new migrations.
|
||||
|
||||
|
||||
|
||||
|
||||
-- Sets up a trigger for the given table to automatically set a column called
|
||||
-- `updated_at` whenever the row is modified (unless `updated_at` was included
|
||||
-- in the modified columns)
|
||||
--
|
||||
-- # Example
|
||||
--
|
||||
-- ```sql
|
||||
-- CREATE TABLE users (id SERIAL PRIMARY KEY, updated_at TIMESTAMP NOT NULL DEFAULT NOW());
|
||||
--
|
||||
-- SELECT diesel_manage_updated_at('users');
|
||||
-- ```
|
||||
CREATE OR REPLACE FUNCTION diesel_manage_updated_at(_tbl regclass) RETURNS VOID AS $$
|
||||
BEGIN
|
||||
EXECUTE format('CREATE TRIGGER set_updated_at BEFORE UPDATE ON %s
|
||||
FOR EACH ROW EXECUTE PROCEDURE diesel_set_updated_at()', _tbl);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE OR REPLACE FUNCTION diesel_set_updated_at() RETURNS trigger AS $$
|
||||
BEGIN
|
||||
IF (
|
||||
NEW IS DISTINCT FROM OLD AND
|
||||
NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at
|
||||
) THEN
|
||||
NEW.updated_at := current_timestamp;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
@ -1,2 +0,0 @@
|
||||
-- This file should undo anything in `up.sql`
|
||||
drop table timetable;
|
@ -1,8 +0,0 @@
|
||||
-- Your SQL goes here
|
||||
create table timetable (
|
||||
id serial primary key,
|
||||
date varchar(255) not null,
|
||||
updated varchar(255) not null,
|
||||
class varchar(255) not null,
|
||||
timetable_data jsonb
|
||||
);
|
@ -1,3 +1,19 @@
|
||||
// GCG.MeinCantor.API - The server-part of GCG.MeinCantor - The school application for the Georg-Cantor-Gymnasium
|
||||
// Copyright (C) 2021-2022 Denys Konovalov
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
// by the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// Timetable source
|
||||
pub static IW_TIMETABLE_URL: &str = "https://stundenplan24.de/EXAMPLE_SCHOOL/mobil/mobdaten";
|
||||
pub static IW_TIMETABLE_USER: &str = "EXAMPLE_USER";
|
||||
|
@ -1,14 +1,26 @@
|
||||
// GCG.MeinCantor.API - The server-part of GCG.MeinCantor - The school application for the Georg-Cantor-Gymnasium
|
||||
// Copyright (C) 2021-2022 Denys Konovalov
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
// by the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::config;
|
||||
use crate::schema::timetable;
|
||||
use crate::DbConn;
|
||||
use diesel::{Insertable, Queryable};
|
||||
use quickxml_to_serde::{xml_string_to_json, Config};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use serde_json::{json, Map};
|
||||
use std::env;
|
||||
|
||||
#[derive(Queryable, Serialize, Insertable, Deserialize, Clone)]
|
||||
#[table_name = "timetable"]
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct Timetable {
|
||||
pub date: String,
|
||||
pub updated: String,
|
||||
@ -70,7 +82,7 @@ async fn get_timetable_xml_data(url: &str) -> Vec<serde_json::value::Value> {
|
||||
classes.to_owned()
|
||||
}
|
||||
|
||||
pub async fn get_timetable(_conn: DbConn, url: String) -> Vec<Timetable> {
|
||||
pub async fn get_timetable(url: String) -> Vec<Timetable> {
|
||||
let xml = get_timetable_xml(&url).await;
|
||||
let classes = get_timetable_xml_data(&url).await;
|
||||
let mut timetable: Vec<Timetable> = Vec::new();
|
||||
@ -322,7 +334,7 @@ pub async fn get_timetable(_conn: DbConn, url: String) -> Vec<Timetable> {
|
||||
timetable_refactored
|
||||
}
|
||||
|
||||
pub async fn get_class_timetable(_conn: DbConn, class: String, url: String) -> TimetableData {
|
||||
pub async fn get_class_timetable(class: String, url: String) -> TimetableData {
|
||||
let xml = get_timetable_xml(&url).await;
|
||||
let classes = get_timetable_xml_data(&url).await;
|
||||
let courses: Vec<rocket::serde::json::Value> = Vec::new();
|
||||
|
@ -1,3 +1,19 @@
|
||||
// GCG.MeinCantor.API - The server-part of GCG.MeinCantor - The school application for the Georg-Cantor-Gymnasium
|
||||
// Copyright (C) 2021-2022 Denys Konovalov
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
// by the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
extern crate reqwest;
|
||||
|
||||
use crate::config;
|
||||
@ -11,7 +27,7 @@ use std::env;
|
||||
use std::error::Error;
|
||||
use std::fmt::Display;
|
||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
use time::OffsetDateTime;
|
||||
use time::{macros::format_description, OffsetDateTime};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct KeycloakAdminToken {
|
||||
@ -228,7 +244,9 @@ pub async fn login(
|
||||
TokenStatus::Success => {
|
||||
let userinfo = get_keycloak_userinfo(token.token.clone()).await.unwrap();
|
||||
let system_time = OffsetDateTime::now_utc();
|
||||
let datetime = system_time.format("%d/%m/%Y %T");
|
||||
let datetime = system_time.format(format_description!(
|
||||
"[day]/[month]/[year] [hour]:[minute]:[second]"
|
||||
));
|
||||
let my_claims = Claims {
|
||||
iss: env::var("JWT_ISSUER").unwrap_or(config::JWT_ISSUER.to_string()),
|
||||
user: userinfo.preferred_username,
|
||||
@ -236,7 +254,7 @@ pub async fn login(
|
||||
groups: userinfo.groups,
|
||||
blacklist: userinfo.blacklist.unwrap_or_default(),
|
||||
whitelist: userinfo.whitelist.unwrap_or_default(),
|
||||
jid: (credentials.devid + "@" + &datetime),
|
||||
jid: (credentials.devid + "@" + &datetime.unwrap()),
|
||||
exp: SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.expect("Time went backwards")
|
||||
|
53
src/main.rs
53
src/main.rs
@ -1,13 +1,25 @@
|
||||
#[macro_use]
|
||||
extern crate rocket;
|
||||
// GCG.MeinCantor.API - The server-part of GCG.MeinCantor - The school application for the Georg-Cantor-Gymnasium
|
||||
// Copyright (C) 2021-2022 Denys Konovalov
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
// by the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#[macro_use]
|
||||
extern crate diesel;
|
||||
extern crate rocket;
|
||||
|
||||
mod config;
|
||||
mod indiware_connector;
|
||||
mod keycloak_connector;
|
||||
mod schema;
|
||||
|
||||
extern crate reqwest;
|
||||
extern crate serde;
|
||||
@ -23,13 +35,9 @@ use rocket::{
|
||||
response::status,
|
||||
serde::json::Json,
|
||||
};
|
||||
use rocket_sync_db_pools::{database, diesel::PgConnection};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::env;
|
||||
|
||||
#[database("timetable")]
|
||||
pub struct DbConn(PgConnection);
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Credentials {
|
||||
user: String,
|
||||
@ -85,10 +93,8 @@ impl<'r> FromRequest<'r> for ApiKey<'r> {
|
||||
async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> {
|
||||
/// Returns true if `key` is a valid API key string.
|
||||
fn is_valid(key: &str) -> bool {
|
||||
let validation = Validation {
|
||||
validate_exp: false,
|
||||
..Default::default()
|
||||
};
|
||||
let mut validation = Validation::default();
|
||||
validation.validate_exp = false;
|
||||
let token = decode::<Claims>(
|
||||
key,
|
||||
&DecodingKey::from_secret(
|
||||
@ -102,10 +108,8 @@ impl<'r> FromRequest<'r> for ApiKey<'r> {
|
||||
}
|
||||
|
||||
fn has_permissions(key: &str, uri: &str) -> bool {
|
||||
let validation = Validation {
|
||||
validate_exp: false,
|
||||
..Default::default()
|
||||
};
|
||||
let mut validation = Validation::default();
|
||||
validation.validate_exp = false;
|
||||
let standard_permissions = vec![
|
||||
String::from("/api/timetable"),
|
||||
String::from("/api/classes"),
|
||||
@ -172,44 +176,38 @@ async fn login(
|
||||
}
|
||||
|
||||
#[get("/latest")]
|
||||
async fn get_latest_timetable(
|
||||
conn: DbConn,
|
||||
_key: ApiKey<'_>,
|
||||
) -> Json<Vec<timetable_connector::Timetable>> {
|
||||
let timetable = timetable_connector::get_timetable(conn, String::from("Klassen.xml")).await;
|
||||
async fn get_latest_timetable(_key: ApiKey<'_>) -> Json<Vec<timetable_connector::Timetable>> {
|
||||
let timetable = timetable_connector::get_timetable(String::from("Klassen.xml")).await;
|
||||
Json::from(timetable)
|
||||
}
|
||||
|
||||
#[get("/<date>")]
|
||||
async fn get_timetable_file(
|
||||
conn: DbConn,
|
||||
_key: ApiKey<'_>,
|
||||
date: String,
|
||||
) -> Json<Vec<timetable_connector::Timetable>> {
|
||||
let timetable = timetable_connector::get_timetable(conn, format!("PlanKl{}.xml", date)).await;
|
||||
let timetable = timetable_connector::get_timetable(format!("PlanKl{}.xml", date)).await;
|
||||
Json::from(timetable)
|
||||
}
|
||||
|
||||
#[get("/latest/<class>")]
|
||||
async fn get_latest_class_timetable(
|
||||
conn: DbConn,
|
||||
class: String,
|
||||
_key: ApiKey<'_>,
|
||||
) -> Json<timetable_connector::TimetableData> {
|
||||
let timetable =
|
||||
timetable_connector::get_class_timetable(conn, class, String::from("Klassen.xml")).await;
|
||||
timetable_connector::get_class_timetable(class, String::from("Klassen.xml")).await;
|
||||
Json::from(timetable)
|
||||
}
|
||||
|
||||
#[get("/<date>/<class>")]
|
||||
async fn get_class_timetable_file(
|
||||
conn: DbConn,
|
||||
class: String,
|
||||
_key: ApiKey<'_>,
|
||||
date: String,
|
||||
) -> Json<timetable_connector::TimetableData> {
|
||||
let timetable =
|
||||
timetable_connector::get_class_timetable(conn, class, format!("PlanKl{}.xml", date)).await;
|
||||
timetable_connector::get_class_timetable(class, format!("PlanKl{}.xml", date)).await;
|
||||
Json::from(timetable)
|
||||
}
|
||||
|
||||
@ -231,7 +229,6 @@ async fn get_class_lessons(
|
||||
#[launch]
|
||||
fn rocket() -> _ {
|
||||
rocket::build()
|
||||
.attach(DbConn::fairing())
|
||||
.mount("/", FileServer::from(relative!("static")))
|
||||
.mount("/login", routes![login])
|
||||
.mount(
|
||||
|
@ -1,9 +0,0 @@
|
||||
table! {
|
||||
timetable (id) {
|
||||
id -> Int4,
|
||||
date -> Varchar,
|
||||
updated -> Varchar,
|
||||
class -> Varchar,
|
||||
timetable_data -> Nullable<Jsonb>,
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user