Working on rust backend
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -34,5 +34,6 @@ yarn-error.log*
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
|
||||
/target/
|
||||
/dist/
|
||||
target/
|
||||
dist/
|
||||
.vscode/
|
||||
|
||||
4
backend/.env
Normal file
4
backend/.env
Normal file
@@ -0,0 +1,4 @@
|
||||
RUST_LOG=backend=info,actix=info,diesel_migrations=info
|
||||
DATABASE_URL=postgres://postgres:password@localhost/notes_api
|
||||
HOST=127.0.0.1
|
||||
PORT=5000
|
||||
2742
backend/Cargo.lock
generated
Normal file
2742
backend/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,15 +1,24 @@
|
||||
[package]
|
||||
name = "aviation-weather"
|
||||
name = "aviation-weather-backend"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
actix-web = "3.0"
|
||||
actix-rt = "1.0"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
dotenv = "0.15.0"
|
||||
diesel = { version = "1.4", features = ["postgres", "r2d2", "uuid", "chrono"] }
|
||||
diesel_migrations = "1.4"
|
||||
env_logger = "0.10.0"
|
||||
log = "0.4.20"
|
||||
lazy_static = "1.4"
|
||||
listenfd = "0.3"
|
||||
quick-xml = { version = "0.30.0", features = ["serialize"] }
|
||||
r2d2 = "0.8"
|
||||
reqwest = "0.11.19"
|
||||
serde = {version = "1.0.185", features = ["derive"]}
|
||||
serde_json = "1.0"
|
||||
tokio = { version = "1.32.0", features = ["macros", "rt"] }
|
||||
uuid = { version = "0.6", features = ["serde", "v4"] }
|
||||
5
backend/diesel.toml
Normal file
5
backend/diesel.toml
Normal file
@@ -0,0 +1,5 @@
|
||||
# For documentation on how to configure this file,
|
||||
# see diesel.rs/guides/configuring-diesel-cli
|
||||
|
||||
[print_schema]
|
||||
file = "src/schema.rs"
|
||||
5
backend/src/airports/mod.rs
Normal file
5
backend/src/airports/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
mod model;
|
||||
mod routes;
|
||||
|
||||
pub use model::*;
|
||||
pub use routes::init_routes;
|
||||
31
backend/src/airports/model.rs
Normal file
31
backend/src/airports/model.rs
Normal file
@@ -0,0 +1,31 @@
|
||||
use crate::db;
|
||||
use crate::error_handler::CustomError;
|
||||
use crate::schema::airports;
|
||||
use diesel::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, AsChangeset, Insertable)]
|
||||
#[table_name = "airports"]
|
||||
pub struct Airport {
|
||||
pub name: String,
|
||||
pub icao: String,
|
||||
pub latitude: f32,
|
||||
pub longitude: f32,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Queryable)]
|
||||
pub struct Airports {
|
||||
pub id: i32,
|
||||
pub name: String,
|
||||
pub icao: String,
|
||||
pub latitude: f32,
|
||||
pub longitude: f32,
|
||||
}
|
||||
|
||||
impl Airports {
|
||||
pub fn find_all() -> Result<Vec<Self>, CustomError> {
|
||||
let conn = db::connection()?;
|
||||
let airports = airports::table.load::<Airports>(&conn)?;
|
||||
Ok(airports)
|
||||
}
|
||||
}
|
||||
14
backend/src/airports/routes.rs
Normal file
14
backend/src/airports/routes.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
use crate::airports::{Airport, Airports};
|
||||
use crate::error_handler::CustomError;
|
||||
use actix_web::{delete, get, post, put, web, HttpResponse};
|
||||
use serde_json::json;
|
||||
|
||||
#[get("/airports")]
|
||||
async fn find_all() -> Result<HttpResponse, CustomError> {
|
||||
let airports = web::block(|| Airports::find_all()).await.unwrap();
|
||||
Ok(HttpResponse::Ok().json(airports))
|
||||
}
|
||||
|
||||
pub fn init_routes(config: &mut web::ServiceConfig) {
|
||||
config.service(find_all);
|
||||
}
|
||||
30
backend/src/db.rs
Normal file
30
backend/src/db.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
use crate::error_handler::CustomError;
|
||||
use diesel::pg::PgConnection;
|
||||
use diesel::r2d2::ConnectionManager;
|
||||
use lazy_static::lazy_static;
|
||||
use r2d2;
|
||||
use std::env;
|
||||
|
||||
type Pool = r2d2::Pool<ConnectionManager<PgConnection>>;
|
||||
pub type DbConnection = r2d2::PooledConnection<ConnectionManager<PgConnection>>;
|
||||
|
||||
embed_migrations!();
|
||||
|
||||
lazy_static! {
|
||||
static ref POOL: Pool = {
|
||||
let db_url = env::var("DATABASE_URL").expect("Database url not set");
|
||||
let manager = ConnectionManager::<PgConnection>::new(db_url);
|
||||
Pool::new(manager).expect("Failed to create db pool")
|
||||
};
|
||||
}
|
||||
|
||||
pub fn init() {
|
||||
lazy_static::initialize(&POOL);
|
||||
let conn = connection().expect("Failed to get db connection");
|
||||
embedded_migrations::run(&conn).unwrap();
|
||||
}
|
||||
|
||||
pub fn connection() -> Result<DbConnection, CustomError> {
|
||||
POOL.get()
|
||||
.map_err(|e| CustomError::new(500, format!("Failed getting db connection: {}", e)))
|
||||
}
|
||||
55
backend/src/error_handler.rs
Normal file
55
backend/src/error_handler.rs
Normal file
@@ -0,0 +1,55 @@
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::{HttpResponse, ResponseError};
|
||||
use diesel::result::Error as DieselError;
|
||||
use serde::Deserialize;
|
||||
use serde_json::json;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct CustomError {
|
||||
pub error_status_code: u16,
|
||||
pub error_message: String,
|
||||
}
|
||||
|
||||
impl CustomError {
|
||||
pub fn new(error_status_code: u16, error_message: String) -> CustomError {
|
||||
CustomError {
|
||||
error_status_code,
|
||||
error_message,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for CustomError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str(self.error_message.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DieselError> for CustomError {
|
||||
fn from(error: DieselError) -> CustomError {
|
||||
match error {
|
||||
DieselError::DatabaseError(_, err) => CustomError::new(409, err.message().to_string()),
|
||||
DieselError::NotFound => {
|
||||
CustomError::new(404, "The employee record not found".to_string())
|
||||
}
|
||||
err => CustomError::new(500, format!("Unknown Diesel error: {}", err)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ResponseError for CustomError {
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
let status_code = match StatusCode::from_u16(self.error_status_code) {
|
||||
Ok(status_code) => status_code,
|
||||
Err(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
};
|
||||
|
||||
let error_message = match status_code.as_u16() < 500 {
|
||||
true => self.error_message.clone(),
|
||||
false => "Internal server error".to_string(),
|
||||
};
|
||||
|
||||
HttpResponse::build(status_code).json(json!({ "message": error_message }))
|
||||
}
|
||||
}
|
||||
33
backend/src/main.rs
Normal file
33
backend/src/main.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
#[macro_use]
|
||||
extern crate diesel;
|
||||
#[macro_use]
|
||||
extern crate diesel_migrations;
|
||||
|
||||
use actix_web::{App, HttpServer};
|
||||
use dotenv::dotenv;
|
||||
use listenfd::ListenFd;
|
||||
use std::env;
|
||||
|
||||
mod airports;
|
||||
mod db;
|
||||
mod error_handler;
|
||||
mod schema;
|
||||
|
||||
#[actix_rt::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
dotenv().ok();
|
||||
db::init();
|
||||
|
||||
let mut listenfd = ListenFd::from_env();
|
||||
let mut server = HttpServer::new(|| App::new().configure(airports::init_routes));
|
||||
|
||||
server = match listenfd.take_tcp_listener(0)? {
|
||||
Some(listener) => server.listen(listener)?,
|
||||
None => {
|
||||
let host = env::var("HOST").expect("Please set host in .env");
|
||||
let port = env::var("PORT").expect("Please set port in .env");
|
||||
server.bind(format!("{}:{}", host, port))?
|
||||
}
|
||||
};
|
||||
server.run().await
|
||||
}
|
||||
9
backend/src/schema.rs
Normal file
9
backend/src/schema.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
table! {
|
||||
airports (id) {
|
||||
id -> Int4,
|
||||
name -> Varchar,
|
||||
icao -> Varchar,
|
||||
latitude: Int4,
|
||||
longitude: Int4
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user