Files
label_editor/server/src/main.rs
T

428 lines
12 KiB
Rust

use std::env;
use axum::{
routing::{get, post},
http::StatusCode,
Json, Router,
};
use axum::extract::{Path, State};
use axum::routing::delete;
use dotenv::dotenv;
use env_logger::fmt::style::Reset;
use http::{header, Method};
use common::models::article::{delete_article, insert_article, load_article, load_articles, update_article, Article};
use common::models::origin::Origin;
use sqlx::{ postgres::PgPoolOptions, PgPool };
use tower_http::cors::{AllowOrigin, Any, CorsLayer};
use tracing::info;
use http::header::{AUTHORIZATION, ACCEPT, ACCESS_CONTROL_ALLOW_ORIGIN, CONTENT_TYPE, ACCESS_CONTROL_ALLOW_CREDENTIALS};
use common::models::article;
use common::models::label::{delete_label, insert_label, load_labels, update_label, Label};
use common::models::settings::{load_settings, update_settings, Settings};
use common::models::unit::Unit;
use common::models::variant::{delete_variant, insert_variant, update_variant, Variant};
#[tokio::main]
async fn main() {
dotenv().ok();
let server_address = env::var("SERVER_ADDRESS").expect("SERVER_ADDRESS is not set in .env file");
let server_port = env::var("SERVER_PORT").expect("SERVER_PORT is not set in .env file");
let database = env::var("DATABASE_URL").expect("DATABASE is not set in .env file");
let addr = format!("{}:{}", server_address, server_port);
// initialize tracing
tracing_subscriber::fmt().init();
let pool = PgPoolOptions::new()
.max_connections(50)
.connect(&database)
.await
.expect(&format!("Can't connect to database: {}", &database));
sqlx::migrate!().run(&pool).await.expect("Migrations failed");
let cors = CorsLayer::new()
.allow_methods([Method::GET, Method::POST, Method::DELETE])
.allow_headers([ACCEPT, ACCESS_CONTROL_ALLOW_ORIGIN, CONTENT_TYPE])
.allow_origin(AllowOrigin::any());
// build our application with a route
let app = Router::new()
.route("/articles", get(get_articles))
.route("/article", post(create_article))
.route("/article/{id}", get(get_article))
.route("/article/{id}", post(update_article_route))
.route("/article/{id}", delete(delete_article_route))
.route("/article/{id}/variant", post(add_variant))
.route("/variant/{id}", post(update_variant_route))
.route("/variant/{id}", delete(delete_variant_route))
.route("/label", get(get_labels_route))
.route("/label", post(add_label_route))
.route("/label/{id}", post(update_label_route))
.route("/label/{id}", delete(delete_label_route))
.route("/settings", get(get_settings_route))
.route("/settings", post(update_settings_route))
.with_state(pool)
.layer(cors);
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
info!("Server started on: {}", &addr);
axum::serve(listener, app).await.unwrap();
}
#[tracing::instrument(ret)]
async fn get_articles(
State(pool): State<PgPool>,
) -> (StatusCode, Json<Vec<Article>>) {
info!("Get all articles");
let result_articles: Option<Vec<Article>> = load_articles(pool).await;
match result_articles {
Some(articles) => {
(StatusCode::OK, Json(articles))
}
None => {
(StatusCode::INTERNAL_SERVER_ERROR, Json(vec![]))
}
}
}
#[tracing::instrument(ret)]
async fn get_article(
Path(id): Path<i64>,
State(pool): State<PgPool>,
) -> (StatusCode, Json<Article>) {
info!("Get article with id: {}", id);
let result_article: Option<Article> = load_article(id, pool).await;
match result_article {
Some(article) => {
(StatusCode::OK, Json(article))
}
None => {
let article = Article {
id,
name: "".to_string(),
bio: false,
origin: Origin::EU,
description: None,
ingredients: None,
default_unit: Unit::G,
variants: vec![],
mhd: None,
bio_origin: None,
allergens_text: None,
bio_info: None,
topm_id: None,
additional_name: None,
};
(StatusCode::INTERNAL_SERVER_ERROR, Json(article))
}
}
}
#[tracing::instrument(ret)]
async fn create_article(
State(pool): State<PgPool>,
Json(payload): Json<Article>,
) -> (StatusCode, Json<Article>) {
info!("Create article");
match insert_article(&payload, pool).await {
Some(article) => {
(StatusCode::CREATED, Json(article))
}
None => {
let article = Article {
id: 0,
name: "".to_string(),
bio: false,
origin: Origin::EU,
description: None,
ingredients: None,
default_unit: Unit::G,
variants: vec![],
mhd: None,
bio_origin: None,
allergens_text: None,
bio_info: None,
topm_id: None,
additional_name: None,
};
(StatusCode::INTERNAL_SERVER_ERROR, Json(article))
}
}
}
async fn update_article_route(
State(pool): State<PgPool>,
Path(article_id): Path<i64>,
Json(payload): Json<Article>,
) -> (StatusCode, Json<Article>) {
info!("Update article");
let update_article = update_article(article_id, &payload, pool).await;
match update_article {
Some(article) => {
(StatusCode::OK, Json(article))
}
None => {
let article = Article {
id: 0,
name: "".to_string(),
bio: false,
origin: Origin::EU,
description: None,
ingredients: None,
default_unit: Unit::G,
variants: vec![],
mhd: None,
bio_origin: None,
allergens_text: None,
bio_info: None,
topm_id: None,
additional_name: None,
};
(StatusCode::INTERNAL_SERVER_ERROR, Json(article))
}
}
}
async fn delete_article_route(
State(pool): State<PgPool>,
Path(article_id): Path<i64>,
) -> StatusCode {
info!("Delete article");
let delete_article_result = delete_article(article_id, pool).await;
match delete_article_result {
Some(_) => {
StatusCode::OK
}
None => {
StatusCode::INTERNAL_SERVER_ERROR
}
}
}
async fn add_variant(
State(pool): State<PgPool>,
Path(article_id): Path<i64>,
Json(payload): Json<Variant>,
) -> (StatusCode, Json<Variant>) {
info!("Add variant");
let created_variant = insert_variant(article_id, &payload, pool).await;
match created_variant {
Some(variant) => {
(StatusCode::OK, Json(variant))
}
None => {
let variant = Variant {
id: 0,
weight: "".to_string(),
ean: None,
name: None,
description: None,
ingredients: None,
unit: Unit::G,
label: None,
variant_description: None,
topm_row: None,
};
(StatusCode::INTERNAL_SERVER_ERROR, Json(variant))
}
}
}
async fn update_variant_route(
State(pool): State<PgPool>,
Path(variant_id): Path<i64>,
Json(payload): Json<Variant>,
) -> (StatusCode, Json<Variant>) {
info!("Update variant");
let update_variant = update_variant(variant_id, &payload, pool).await;
match update_variant {
Some(variant) => {
(StatusCode::OK, Json(variant))
}
None => {
let variant = Variant {
id: 0,
weight: "".to_string(),
ean: None,
name: None,
description: None,
ingredients: None,
unit: Unit::G,
label: None,
variant_description: None,
topm_row: None,
};
(StatusCode::INTERNAL_SERVER_ERROR, Json(variant))
}
}
}
async fn delete_variant_route(
State(pool): State<PgPool>,
Path(variant_id): Path<i64>,
) -> StatusCode {
info!("Delete variant");
let delete_variant_result = delete_variant(variant_id, pool).await;
match delete_variant_result {
Some(_) => {
StatusCode::OK
}
None => {
StatusCode::INTERNAL_SERVER_ERROR
}
}
}
#[tracing::instrument(ret)]
async fn get_labels_route(
State(pool): State<PgPool>,
) -> (StatusCode, Json<Vec<Label>>) {
info!("Get all labels");
match load_labels(pool).await {
Some(labels) => {
(StatusCode::OK, Json(labels))
}
None => {
(StatusCode::INTERNAL_SERVER_ERROR, Json(vec![]))
}
}
}
#[tracing::instrument(ret)]
async fn add_label_route(
State(pool): State<PgPool>,
Json(payload): Json<Label>,
) -> (StatusCode, Json<Label>) {
info!("Add label");
match insert_label(&payload, pool).await {
Some(variant) => {
(StatusCode::OK, Json(variant))
}
None => {
let label = Label {
id: 0,
name: "".to_string(),
};
(StatusCode::INTERNAL_SERVER_ERROR, Json(label))
}
}
}
#[tracing::instrument(ret)]
async fn update_label_route(
State(pool): State<PgPool>,
Path(label_id): Path<i64>,
Json(payload): Json<Label>,
) -> (StatusCode, Json<Label>) {
info!("Update label");
match update_label(label_id, &payload, pool).await {
Some(variant) => {
(StatusCode::OK, Json(variant))
}
None => {
let label = Label {
id: 0,
name: "".to_string(),
};
(StatusCode::INTERNAL_SERVER_ERROR, Json(label))
}
}
}
async fn delete_label_route(
State(pool): State<PgPool>,
Path(label_id): Path<i64>,
) -> StatusCode {
info!("Delete label");
let delete_variant_result = delete_label(label_id, pool).await;
match delete_variant_result {
Some(_) => {
StatusCode::OK
}
None => {
StatusCode::INTERNAL_SERVER_ERROR
}
}
}
#[tracing::instrument(ret)]
async fn get_settings_route(
State(pool): State<PgPool>,
) -> (StatusCode, Json<Settings>) {
info!("Get settings");
match load_settings(&pool).await {
Some(settings) => {
(StatusCode::OK, Json(settings))
}
None => {
(StatusCode::INTERNAL_SERVER_ERROR, Json(
Settings {
id: 0,
mhd: "".to_string(),
bio: "".to_string(),
allergens_text: "".to_string(),
bio_info: "".to_string(),
}
))
}
}
}
#[tracing::instrument(ret)]
async fn update_settings_route(
State(pool): State<PgPool>,
Json(payload): Json<Settings>,
) -> (StatusCode, Json<Settings>) {
info!("Update settings");
match update_settings(&payload, pool).await {
Some(settings) => {
(StatusCode::OK, Json(settings))
}
None => {
(StatusCode::INTERNAL_SERVER_ERROR, Json(
Settings {
id: 0,
mhd: "".to_string(),
bio: "".to_string(),
allergens_text: "".to_string(),
bio_info: "".to_string(),
}
))
}
}
}