forked from TWS/kalkutago
Add client dir, move .git from server to parent dir
This commit is contained in:
parent
b8b0e51046
commit
f794f5c974
41 changed files with 757 additions and 7 deletions
1
server/.dockerignore
Normal file
1
server/.dockerignore
Normal file
|
|
@ -0,0 +1 @@
|
|||
**/target
|
||||
5
server/.vscode/settings.json
vendored
Normal file
5
server/.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"cSpell.words": [
|
||||
"sqlx"
|
||||
]
|
||||
}
|
||||
2893
server/Cargo.lock
generated
Normal file
2893
server/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
44
server/Cargo.toml
Normal file
44
server/Cargo.toml
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
[package]
|
||||
name = "kalkulog-server"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[[bin]]
|
||||
name = "kalkulog-server"
|
||||
path = "src/main.rs"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
sea-orm-migration = "0.11.3"
|
||||
serde_json = "1.0.96"
|
||||
thiserror = "1.0.40"
|
||||
|
||||
[dependencies.derive_builder]
|
||||
version = "0.12.0"
|
||||
features = ["clippy"]
|
||||
|
||||
[dependencies.tokio]
|
||||
version = "1.28.1"
|
||||
features = ["full"]
|
||||
|
||||
[dependencies.sea-orm]
|
||||
version = "^0"
|
||||
features = [
|
||||
"sqlx-postgres",
|
||||
"runtime-tokio-rustls",
|
||||
"macros",
|
||||
"with-time"
|
||||
]
|
||||
|
||||
[dependencies.rocket]
|
||||
git = "https://github.com/SergioBenitez/Rocket"
|
||||
rev = "v0.5.0-rc.3"
|
||||
features = ["json"]
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1.0.163"
|
||||
features = ["derive"]
|
||||
|
||||
[dependencies.either]
|
||||
version = "1.8.1"
|
||||
features = ["serde"]
|
||||
11
server/Dockerfile
Normal file
11
server/Dockerfile
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
FROM rustlang/rust:nightly-bullseye-slim
|
||||
WORKDIR /src
|
||||
ADD Cargo.toml Cargo.lock /src/
|
||||
RUN echo "fn main() {}" > dummy.rs &&\
|
||||
sed -i "s:src/main.rs:dummy.rs:" Cargo.toml
|
||||
RUN cargo build --release
|
||||
ADD src/ src/
|
||||
RUN rm dummy.rs &&\
|
||||
sed -i "s:dummy.rs:src/main.rs:" Cargo.toml
|
||||
RUN cargo build --release
|
||||
CMD ["target/release/kalkulog-server"]
|
||||
31
server/src/api/error.rs
Normal file
31
server/src/api/error.rs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
use crate::error::Error;
|
||||
|
||||
#[derive(Responder)]
|
||||
#[response(status = 500, content_type = "json")]
|
||||
pub(crate) struct ErrorResponder {
|
||||
message: String,
|
||||
}
|
||||
|
||||
pub(crate) type ApiResult<T> = Result<T, ErrorResponder>;
|
||||
|
||||
// The following impl's are for easy conversion of error types.
|
||||
|
||||
impl From<Error> for ErrorResponder {
|
||||
fn from(err: Error) -> ErrorResponder {
|
||||
ErrorResponder {
|
||||
message: err.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for ErrorResponder {
|
||||
fn from(string: String) -> ErrorResponder {
|
||||
ErrorResponder { message: string }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for ErrorResponder {
|
||||
fn from(str: &str) -> ErrorResponder {
|
||||
str.to_owned().into()
|
||||
}
|
||||
}
|
||||
71
server/src/api/groups.rs
Normal file
71
server/src/api/groups.rs
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
use either::{Either, Left, Right};
|
||||
use rocket::{http::Status, serde::json::Json, State};
|
||||
use sea_orm::{prelude::*, DatabaseConnection};
|
||||
|
||||
use crate::{
|
||||
entities::{prelude::*, *},
|
||||
error::Error,
|
||||
};
|
||||
|
||||
use super::error::ApiResult;
|
||||
|
||||
#[get("/")]
|
||||
pub(super) async fn all_groups(
|
||||
db: &State<DatabaseConnection>,
|
||||
) -> ApiResult<Json<Vec<groups::Model>>> {
|
||||
Ok(Json(
|
||||
Groups::find()
|
||||
.all(db as &DatabaseConnection)
|
||||
.await
|
||||
.map_err(Error::from)?,
|
||||
))
|
||||
}
|
||||
|
||||
#[get("/<id>")]
|
||||
pub(super) async fn group(
|
||||
db: &State<DatabaseConnection>,
|
||||
id: i32,
|
||||
) -> Result<Json<groups::Model>, Either<Status, super::ErrorResponder>> {
|
||||
match Groups::find_by_id(id).one(db as &DatabaseConnection).await {
|
||||
Ok(Some(group)) => Ok(Json(group)),
|
||||
Ok(None) => Err(Left(Status::NotFound)),
|
||||
Err(err) => Err(Right(Error::from(err).into())),
|
||||
}
|
||||
}
|
||||
|
||||
#[post("/", format = "application/json", data = "<group>")]
|
||||
pub(super) async fn insert_group(
|
||||
db: &State<DatabaseConnection>,
|
||||
group: Json<serde_json::Value>,
|
||||
) -> ApiResult<Json<groups::Model>> {
|
||||
Ok(Json(
|
||||
groups::ActiveModel::from_json(group.0)
|
||||
.map_err(Error::from)?
|
||||
.insert(db as &DatabaseConnection)
|
||||
.await
|
||||
.map_err(Error::from)?,
|
||||
))
|
||||
}
|
||||
|
||||
#[put("/", format = "application/json", data = "<group>")]
|
||||
pub(super) async fn update_group(
|
||||
db: &State<DatabaseConnection>,
|
||||
group: Json<serde_json::Value>,
|
||||
) -> ApiResult<Json<groups::Model>> {
|
||||
Ok(Json(
|
||||
groups::ActiveModel::from_json(group.0)
|
||||
.map_err(Error::from)?
|
||||
.update(db as &DatabaseConnection)
|
||||
.await
|
||||
.map_err(Error::from)?,
|
||||
))
|
||||
}
|
||||
|
||||
#[delete("/<id>")]
|
||||
pub(super) async fn delete_group(db: &State<DatabaseConnection>, id: i32) -> ApiResult<Status> {
|
||||
Groups::delete_by_id(id)
|
||||
.exec(db as &DatabaseConnection)
|
||||
.await
|
||||
.map_err(Error::from)?;
|
||||
Ok(Status::Ok)
|
||||
}
|
||||
44
server/src/api/mod.rs
Normal file
44
server/src/api/mod.rs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
mod error;
|
||||
mod groups;
|
||||
mod ticks;
|
||||
mod tracks;
|
||||
|
||||
use std::default::default;
|
||||
use std::net::{IpAddr, Ipv4Addr};
|
||||
|
||||
use rocket::Config;
|
||||
use sea_orm::DatabaseConnection;
|
||||
|
||||
use crate::rocket::{Build, Rocket};
|
||||
|
||||
pub(crate) use error::ErrorResponder;
|
||||
|
||||
#[get("/status")]
|
||||
fn status() -> &'static str {
|
||||
"Ok"
|
||||
}
|
||||
|
||||
pub(crate) fn start_server(db: DatabaseConnection) -> Rocket<Build> {
|
||||
use groups::*;
|
||||
use ticks::*;
|
||||
use tracks::*;
|
||||
rocket::build()
|
||||
.configure(Config {
|
||||
address: IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)),
|
||||
..default()
|
||||
})
|
||||
.manage(db)
|
||||
.mount("/api/v1", routes![status])
|
||||
.mount(
|
||||
"/api/v1/tracks",
|
||||
routes![all_tracks, track, insert_track, update_track, delete_track],
|
||||
)
|
||||
.mount(
|
||||
"/api/v1/ticks",
|
||||
routes![all_ticks, tick, insert_tick, update_tick, delete_tick],
|
||||
)
|
||||
.mount(
|
||||
"/api/v1/groups",
|
||||
routes![all_groups, group, insert_group, update_group, delete_group],
|
||||
)
|
||||
}
|
||||
68
server/src/api/ticks.rs
Normal file
68
server/src/api/ticks.rs
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
use either::{Either, Left, Right};
|
||||
use rocket::{http::Status, serde::json::Json, State};
|
||||
use sea_orm::{prelude::*, DatabaseConnection};
|
||||
|
||||
use crate::{
|
||||
entities::{prelude::*, *},
|
||||
error::Error,
|
||||
};
|
||||
|
||||
use super::error::ApiResult;
|
||||
|
||||
#[get("/")]
|
||||
pub(super) async fn all_ticks(
|
||||
db: &State<DatabaseConnection>,
|
||||
) -> ApiResult<Json<Vec<ticks::Model>>> {
|
||||
let db = db as &DatabaseConnection;
|
||||
let ticks = Ticks::find().all(db).await.map_err(Error::from)?;
|
||||
Ok(Json(ticks))
|
||||
}
|
||||
|
||||
#[get("/<id>")]
|
||||
pub(super) async fn tick(
|
||||
db: &State<DatabaseConnection>,
|
||||
id: i32,
|
||||
) -> Result<Json<ticks::Model>, Either<Status, super::ErrorResponder>> {
|
||||
match Ticks::find_by_id(id).one(db as &DatabaseConnection).await {
|
||||
Ok(Some(tick)) => Ok(Json(tick)),
|
||||
Ok(None) => Err(Left(Status::NotFound)),
|
||||
Err(err) => Err(Right(Error::from(err).into())),
|
||||
}
|
||||
}
|
||||
|
||||
#[post("/", format = "application/json", data = "<tick>")]
|
||||
pub(super) async fn insert_tick(
|
||||
db: &State<DatabaseConnection>,
|
||||
tick: Json<serde_json::Value>,
|
||||
) -> ApiResult<Json<ticks::Model>> {
|
||||
Ok(Json(
|
||||
ticks::ActiveModel::from_json(tick.0)
|
||||
.map_err(Error::from)?
|
||||
.insert(db as &DatabaseConnection)
|
||||
.await
|
||||
.map_err(Error::from)?,
|
||||
))
|
||||
}
|
||||
|
||||
#[put("/", format = "application/json", data = "<tick>")]
|
||||
pub(super) async fn update_tick(
|
||||
db: &State<DatabaseConnection>,
|
||||
tick: Json<serde_json::Value>,
|
||||
) -> ApiResult<Json<ticks::Model>> {
|
||||
Ok(Json(
|
||||
ticks::ActiveModel::from_json(tick.0)
|
||||
.map_err(Error::from)?
|
||||
.update(db as &DatabaseConnection)
|
||||
.await
|
||||
.map_err(Error::from)?,
|
||||
))
|
||||
}
|
||||
|
||||
#[delete("/<id>")]
|
||||
pub(super) async fn delete_tick(db: &State<DatabaseConnection>, id: i32) -> ApiResult<Status> {
|
||||
Tracks::delete_by_id(id)
|
||||
.exec(db as &DatabaseConnection)
|
||||
.await
|
||||
.map_err(Error::from)?;
|
||||
Ok(Status::Ok)
|
||||
}
|
||||
69
server/src/api/tracks.rs
Normal file
69
server/src/api/tracks.rs
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
use crate::api::{self, error::ApiResult};
|
||||
use crate::entities::{prelude::*, *};
|
||||
use crate::error::Error;
|
||||
use either::Either::{self, Left, Right};
|
||||
use rocket::http::Status;
|
||||
use rocket::{serde::json::Json, State};
|
||||
use sea_orm::{prelude::*, DatabaseConnection};
|
||||
use std::default::default;
|
||||
|
||||
#[get("/")]
|
||||
pub(super) async fn all_tracks(
|
||||
db: &State<DatabaseConnection>,
|
||||
) -> ApiResult<Json<Vec<tracks::Model>>> {
|
||||
let db = db as &DatabaseConnection;
|
||||
let tracks = Tracks::find().all(db).await.unwrap();
|
||||
Ok(Json(tracks))
|
||||
}
|
||||
|
||||
#[get("/<id>")]
|
||||
pub(super) async fn track(
|
||||
db: &State<DatabaseConnection>,
|
||||
id: i32,
|
||||
) -> Result<Json<tracks::Model>, Either<Status, api::ErrorResponder>> {
|
||||
let db = db as &DatabaseConnection;
|
||||
match Tracks::find_by_id(id).one(db).await {
|
||||
Ok(Some(track)) => Ok(Json(track)),
|
||||
Ok(None) => Err(Left(Status::NotFound)),
|
||||
Err(err) => Err(Right(Error::from(err).into())),
|
||||
}
|
||||
}
|
||||
|
||||
#[post("/", format = "application/json", data = "<track>")]
|
||||
pub(super) async fn insert_track(
|
||||
db: &State<DatabaseConnection>,
|
||||
track: Json<serde_json::Value>,
|
||||
) -> ApiResult<Json<tracks::Model>> {
|
||||
let mut track = track.0;
|
||||
let db = db as &DatabaseConnection;
|
||||
let mut model: tracks::ActiveModel = default();
|
||||
track["id"] = 0.into(); // dummy value. set_from_json doesn't use this value
|
||||
// but for some reason requires it be set
|
||||
model.set_from_json(track).map_err(Error::from)?;
|
||||
Ok(Json(model.insert(db).await.map_err(Error::from)?))
|
||||
}
|
||||
|
||||
#[put("/", format = "application/json", data = "<track>")]
|
||||
pub(super) async fn update_track(
|
||||
db: &State<DatabaseConnection>,
|
||||
track: Json<serde_json::Value>,
|
||||
) -> ApiResult<Json<tracks::Model>> {
|
||||
let db = db as &DatabaseConnection;
|
||||
Ok(Json(
|
||||
tracks::ActiveModel::from_json(track.0)
|
||||
.map_err(Error::from)?
|
||||
.update(db)
|
||||
.await
|
||||
.map_err(Error::from)?,
|
||||
))
|
||||
}
|
||||
|
||||
#[delete("/<id>")]
|
||||
pub(super) async fn delete_track(db: &State<DatabaseConnection>, id: i32) -> ApiResult<Status> {
|
||||
let db = db as &DatabaseConnection;
|
||||
Tracks::delete_by_id(id)
|
||||
.exec(db)
|
||||
.await
|
||||
.map_err(Error::from)?;
|
||||
Ok(Status::Ok)
|
||||
}
|
||||
60
server/src/db/mod.rs
Normal file
60
server/src/db/mod.rs
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
use std::{
|
||||
default::default,
|
||||
env,
|
||||
ffi::{OsStr, OsString},
|
||||
fs::File,
|
||||
io::Read,
|
||||
};
|
||||
|
||||
// from https://doc.rust-lang.org/std/ffi/struct.OsString.html
|
||||
fn concat_os_strings(a: &OsStr, b: &OsStr) -> OsString {
|
||||
let mut ret = OsString::with_capacity(a.len() + b.len()); // This will allocate
|
||||
ret.push(a); // This will not allocate further
|
||||
ret.push(b); // This will not allocate further
|
||||
ret
|
||||
}
|
||||
|
||||
/// Check for an environment variable named for the given key with _FILE
|
||||
/// appended to the end. If that exists, return the contents of the file, or
|
||||
/// panic if it doesn't exist. If the `"${key}_FILE"` environment variable is
|
||||
/// not set, return the value of the environment variable set by the given key,
|
||||
/// or `None` if it's not set.
|
||||
///
|
||||
/// Panics:
|
||||
/// - if the given file variable doesn't exist
|
||||
/// - if there's an error reading the specified file
|
||||
/// - if the environment variable string contains invalid unicode.
|
||||
fn get_env_var_or_file<A: AsRef<OsStr>>(key: A) -> Option<String> {
|
||||
let key = key.as_ref();
|
||||
let file_key = concat_os_strings(key, "_FILE".as_ref());
|
||||
if let Some(path) = env::var_os(file_key) {
|
||||
// open the file and read it
|
||||
let mut file = File::open(&path).unwrap_or_else(|_| panic!("no such file at {path:?}"));
|
||||
let mut val: String = default();
|
||||
file.read_to_string(&mut val)
|
||||
.unwrap_or_else(|_| panic!("reading file at {path:?}"));
|
||||
Some(val)
|
||||
} else {
|
||||
env::var_os(key).map(|val| {
|
||||
val.to_str()
|
||||
.expect(&format!("value for ${key:?} contains invalid unicode"))
|
||||
.to_string()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Connect to the database using environment variables for configuration.
|
||||
/// Panics on any failure.
|
||||
pub(crate) fn connection_url() -> String {
|
||||
let user = get_env_var_or_file("POSTGRES_USER").expect("$POSTGRES_USER");
|
||||
let password = get_env_var_or_file("POSTGRES_PASSWORD").expect("$POSTGRES_PASSWORD");
|
||||
let db = get_env_var_or_file("POSTGRES_DB").expect("$POSTGRES_DB");
|
||||
let host = get_env_var_or_file("POSTGRES_HOST").unwrap_or_else(|| "localhost".into());
|
||||
let port = get_env_var_or_file("POSTGRES_PORT")
|
||||
.map(|port| {
|
||||
port.parse::<u16>()
|
||||
.expect("$POSTGRES_PORT is not a valid port number")
|
||||
})
|
||||
.unwrap_or(5432_u16);
|
||||
format!("postgres://{user}:{password}@{host}:{port}/{db}")
|
||||
}
|
||||
28
server/src/entities/groups.rs
Normal file
28
server/src/entities/groups.rs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
|
||||
#[sea_orm(table_name = "groups")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub order: Option<i32>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(has_many = "super::track2_groups::Entity")]
|
||||
Track2Groups,
|
||||
}
|
||||
|
||||
impl Related<super::track2_groups::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Track2Groups.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
8
server/src/entities/mod.rs
Normal file
8
server/src/entities/mod.rs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3
|
||||
|
||||
pub mod prelude;
|
||||
|
||||
pub mod groups;
|
||||
pub mod ticks;
|
||||
pub mod track2_groups;
|
||||
pub mod tracks;
|
||||
6
server/src/entities/prelude.rs
Normal file
6
server/src/entities/prelude.rs
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3
|
||||
|
||||
pub use super::groups::Entity as Groups;
|
||||
pub use super::ticks::Entity as Ticks;
|
||||
pub use super::track2_groups::Entity as Track2Groups;
|
||||
pub use super::tracks::Entity as Tracks;
|
||||
39
server/src/entities/ticks.rs
Normal file
39
server/src/entities/ticks.rs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
|
||||
#[sea_orm(table_name = "ticks")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
pub track_id: Option<i32>,
|
||||
pub year: Option<i32>,
|
||||
pub month: Option<i32>,
|
||||
pub day: Option<i32>,
|
||||
pub hour: Option<i32>,
|
||||
pub minute: Option<i32>,
|
||||
pub second: Option<i32>,
|
||||
pub has_time_info: Option<i32>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(
|
||||
belongs_to = "super::tracks::Entity",
|
||||
from = "Column::TrackId",
|
||||
to = "super::tracks::Column::Id",
|
||||
on_update = "NoAction",
|
||||
on_delete = "NoAction"
|
||||
)]
|
||||
Tracks,
|
||||
}
|
||||
|
||||
impl Related<super::tracks::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Tracks.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
46
server/src/entities/track2_groups.rs
Normal file
46
server/src/entities/track2_groups.rs
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||
#[sea_orm(table_name = "track2_groups")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
pub track_id: i32,
|
||||
pub group_id: i32,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(
|
||||
belongs_to = "super::groups::Entity",
|
||||
from = "Column::GroupId",
|
||||
to = "super::groups::Column::Id",
|
||||
on_update = "NoAction",
|
||||
on_delete = "NoAction"
|
||||
)]
|
||||
Groups,
|
||||
#[sea_orm(
|
||||
belongs_to = "super::tracks::Entity",
|
||||
from = "Column::TrackId",
|
||||
to = "super::tracks::Column::Id",
|
||||
on_update = "NoAction",
|
||||
on_delete = "NoAction"
|
||||
)]
|
||||
Tracks,
|
||||
}
|
||||
|
||||
impl Related<super::groups::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Groups.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::tracks::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Tracks.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
40
server/src/entities/tracks.rs
Normal file
40
server/src/entities/tracks.rs
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
|
||||
#[sea_orm(table_name = "tracks")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub icon: String,
|
||||
pub enabled: i32,
|
||||
pub multiple_entries_per_day: Option<i32>,
|
||||
pub color: Option<i32>,
|
||||
pub order: Option<i32>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(has_many = "super::ticks::Entity")]
|
||||
Ticks,
|
||||
#[sea_orm(has_many = "super::track2_groups::Entity")]
|
||||
Track2Groups,
|
||||
}
|
||||
|
||||
impl Related<super::ticks::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Ticks.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::track2_groups::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Track2Groups.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
11
server/src/error.rs
Normal file
11
server/src/error.rs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
use derive_builder::UninitializedFieldError;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error(transparent)]
|
||||
Builder(#[from] UninitializedFieldError),
|
||||
#[error(transparent)]
|
||||
SeaOrm(#[from] sea_orm::DbErr),
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
36
server/src/main.rs
Normal file
36
server/src/main.rs
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
#![feature(default_free_fn, proc_macro_hygiene, decl_macro)]
|
||||
#[macro_use]
|
||||
extern crate rocket;
|
||||
mod api;
|
||||
mod db;
|
||||
mod entities;
|
||||
mod error;
|
||||
mod migrator;
|
||||
use crate::migrator::Migrator;
|
||||
use sea_orm::Database;
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
#[launch]
|
||||
async fn rocket_defines_the_main_fn() -> _ {
|
||||
let url = db::connection_url();
|
||||
let db = Database::connect(url).await.expect("db connection");
|
||||
let schema_manager = SchemaManager::new(&db);
|
||||
Migrator::refresh(&db).await.expect("migration");
|
||||
assert!(schema_manager
|
||||
.has_table("tracks")
|
||||
.await
|
||||
.expect("fetch tracks table"));
|
||||
assert!(schema_manager
|
||||
.has_table("ticks")
|
||||
.await
|
||||
.expect("fetch ticks table"));
|
||||
assert!(schema_manager
|
||||
.has_table("groups")
|
||||
.await
|
||||
.expect("fetch groups table"));
|
||||
assert!(schema_manager
|
||||
.has_table("track2_groups")
|
||||
.await
|
||||
.expect("fetch track2groups table"));
|
||||
api::start_server(db)
|
||||
}
|
||||
58
server/src/migrator/m20230606_000001_create_tracks_table.rs
Normal file
58
server/src/migrator/m20230606_000001_create_tracks_table.rs
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
use sea_orm_migration::{async_trait::async_trait, prelude::*};
|
||||
|
||||
pub struct Migration;
|
||||
|
||||
impl MigrationName for Migration {
|
||||
fn name(&self) -> &str {
|
||||
"m20230606_000001_create_tracks_table"
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl MigrationTrait for Migration {
|
||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.create_table(
|
||||
Table::create()
|
||||
.table(Tracks::Table)
|
||||
.col(
|
||||
ColumnDef::new(Tracks::Id)
|
||||
.integer()
|
||||
.not_null()
|
||||
.auto_increment()
|
||||
.primary_key(),
|
||||
)
|
||||
.col(ColumnDef::new(Tracks::Name).string().not_null())
|
||||
.col(ColumnDef::new(Tracks::Description).string().not_null())
|
||||
.col(ColumnDef::new(Tracks::Icon).string().not_null())
|
||||
.col(ColumnDef::new(Tracks::Enabled).integer().not_null())
|
||||
.col(
|
||||
ColumnDef::new(Tracks::MultipleEntriesPerDay)
|
||||
.integer()
|
||||
.default(0),
|
||||
)
|
||||
.col(ColumnDef::new(Tracks::Color).integer().default(0))
|
||||
.col(ColumnDef::new(Tracks::Order).integer().default(-1))
|
||||
.to_owned(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.drop_table(Table::drop().table(Tracks::Table).to_owned())
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Iden)]
|
||||
pub enum Tracks {
|
||||
Table,
|
||||
Id,
|
||||
Name,
|
||||
Description,
|
||||
Icon,
|
||||
Enabled,
|
||||
MultipleEntriesPerDay,
|
||||
Color,
|
||||
Order,
|
||||
}
|
||||
63
server/src/migrator/m20230606_000002_create_ticks_table.rs
Normal file
63
server/src/migrator/m20230606_000002_create_ticks_table.rs
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
use super::m20230606_000001_create_tracks_table::Tracks;
|
||||
use sea_orm_migration::{async_trait::async_trait, prelude::*};
|
||||
|
||||
pub struct Migration;
|
||||
|
||||
impl MigrationName for Migration {
|
||||
fn name(&self) -> &str {
|
||||
"m20230606_000002_create_ticks_table"
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl MigrationTrait for Migration {
|
||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.create_table(
|
||||
Table::create()
|
||||
.table(Ticks::Table)
|
||||
.col(
|
||||
ColumnDef::new(Ticks::Id)
|
||||
.integer()
|
||||
.not_null()
|
||||
.auto_increment()
|
||||
.primary_key(),
|
||||
)
|
||||
.col(ColumnDef::new(Ticks::TrackId).integer())
|
||||
.col(ColumnDef::new(Ticks::Year).integer())
|
||||
.col(ColumnDef::new(Ticks::Month).integer())
|
||||
.col(ColumnDef::new(Ticks::Day).integer())
|
||||
.col(ColumnDef::new(Ticks::Hour).integer())
|
||||
.col(ColumnDef::new(Ticks::Minute).integer())
|
||||
.col(ColumnDef::new(Ticks::Second).integer())
|
||||
.col(ColumnDef::new(Ticks::HasTimeInfo).integer().default(0))
|
||||
.foreign_key(
|
||||
ForeignKey::create()
|
||||
.name("fk-ticks-track_id")
|
||||
.from(Ticks::Table, Ticks::TrackId)
|
||||
.to(Tracks::Table, Tracks::Id),
|
||||
)
|
||||
.to_owned(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.drop_table(Table::drop().table(Ticks::Table).to_owned())
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Iden)]
|
||||
pub enum Ticks {
|
||||
Table,
|
||||
Id,
|
||||
TrackId,
|
||||
Year,
|
||||
Month,
|
||||
Day,
|
||||
Hour,
|
||||
Minute,
|
||||
Second,
|
||||
HasTimeInfo,
|
||||
}
|
||||
46
server/src/migrator/m20230606_000003_create_groups_table.rs
Normal file
46
server/src/migrator/m20230606_000003_create_groups_table.rs
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
use sea_orm_migration::{async_trait::async_trait, prelude::*};
|
||||
|
||||
pub struct Migration;
|
||||
|
||||
impl MigrationName for Migration {
|
||||
fn name(&self) -> &str {
|
||||
"m20230606_000003_create_groups_table"
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl MigrationTrait for Migration {
|
||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.create_table(
|
||||
Table::create()
|
||||
.table(Groups::Table)
|
||||
.col(
|
||||
ColumnDef::new(Groups::Id)
|
||||
.integer()
|
||||
.not_null()
|
||||
.auto_increment()
|
||||
.primary_key(),
|
||||
)
|
||||
.col(ColumnDef::new(Groups::Name).string().not_null())
|
||||
.col(ColumnDef::new(Groups::Description).string().not_null())
|
||||
.col(ColumnDef::new(Groups::Order).integer().default(-1))
|
||||
.to_owned(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.drop_table(Table::drop().table(Groups::Table).to_owned())
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Iden)]
|
||||
pub enum Groups {
|
||||
Table,
|
||||
Id,
|
||||
Name,
|
||||
Description,
|
||||
Order,
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
use sea_orm_migration::{async_trait::async_trait, prelude::*};
|
||||
|
||||
use super::{
|
||||
m20230606_000001_create_tracks_table::Tracks, m20230606_000003_create_groups_table::Groups,
|
||||
};
|
||||
|
||||
pub struct Migration;
|
||||
|
||||
impl MigrationName for Migration {
|
||||
fn name(&self) -> &str {
|
||||
"m20230606_000001_create_track2groups_table"
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl MigrationTrait for Migration {
|
||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.create_table(
|
||||
Table::create()
|
||||
.table(Track2Groups::Table)
|
||||
.col(
|
||||
ColumnDef::new(Track2Groups::Id)
|
||||
.integer()
|
||||
.not_null()
|
||||
.primary_key()
|
||||
.auto_increment(),
|
||||
)
|
||||
.col(ColumnDef::new(Track2Groups::TrackId).integer().not_null())
|
||||
.col(ColumnDef::new(Track2Groups::GroupId).integer().not_null())
|
||||
.foreign_key(
|
||||
ForeignKey::create()
|
||||
.name("fk-track2groups-track_id")
|
||||
.from(Track2Groups::Table, Track2Groups::TrackId)
|
||||
.to(Tracks::Table, Tracks::Id),
|
||||
)
|
||||
.foreign_key(
|
||||
ForeignKey::create()
|
||||
.name("fk-track2groups-group_id")
|
||||
.from(Track2Groups::Table, Track2Groups::GroupId)
|
||||
.to(Groups::Table, Groups::Id),
|
||||
)
|
||||
.to_owned(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.drop_table(Table::drop().table(Track2Groups::Table).to_owned())
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Iden)]
|
||||
pub enum Track2Groups {
|
||||
Table,
|
||||
Id,
|
||||
TrackId,
|
||||
GroupId,
|
||||
}
|
||||
20
server/src/migrator/mod.rs
Normal file
20
server/src/migrator/mod.rs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
mod m20230606_000001_create_tracks_table;
|
||||
mod m20230606_000002_create_ticks_table;
|
||||
mod m20230606_000003_create_groups_table;
|
||||
mod m20230606_000004_create_track2groups_table;
|
||||
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
pub struct Migrator;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl MigratorTrait for Migrator {
|
||||
fn migrations() -> Vec<Box<dyn MigrationTrait>> {
|
||||
vec![
|
||||
Box::new(m20230606_000001_create_tracks_table::Migration),
|
||||
Box::new(m20230606_000002_create_ticks_table::Migration),
|
||||
Box::new(m20230606_000003_create_groups_table::Migration),
|
||||
Box::new(m20230606_000004_create_track2groups_table::Migration),
|
||||
]
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue