database pool, app config, simplify structure
This commit is contained in:
parent
6b8371936e
commit
3ce3f894bc
2
.cargo/config.toml
Normal file
2
.cargo/config.toml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[target.x86_64-unknown-linux-gnu]
|
||||||
|
#rustflags = ["-C", "link-arg=-fuse-ld=mold"]
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,3 +2,4 @@
|
|||||||
result*
|
result*
|
||||||
/temp
|
/temp
|
||||||
/.direnv
|
/.direnv
|
||||||
|
.env
|
||||||
|
1058
Cargo.lock
generated
1058
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
24
Cargo.toml
24
Cargo.toml
@ -1,3 +1,21 @@
|
|||||||
[workspace]
|
[package]
|
||||||
resolver = "2"
|
name = "elnafo"
|
||||||
members = [ "crates/db","crates/server"]
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
authors = ["L-Nafaryus <l.nafaryus@elnafo.ru"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
axum = "0.7.4"
|
||||||
|
tokio = { version = "1.36.0", default-features = false, features = [
|
||||||
|
"macros",
|
||||||
|
"fs",
|
||||||
|
"rt-multi-thread",
|
||||||
|
] }
|
||||||
|
dotenvy = "0.15.7"
|
||||||
|
tracing = "0.1.40"
|
||||||
|
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||||
|
diesel = { version = "2.1.4", features = ["postgres"] }
|
||||||
|
deadpool-diesel = { version = "0.5.0", features = ["postgres"] }
|
||||||
|
diesel_migrations = "2.1.0"
|
||||||
|
serde = { version = "1.0.197", features = ["derive"] }
|
||||||
|
serde_json = "1.0.114"
|
||||||
|
@ -1 +0,0 @@
|
|||||||
DATABASE_URL=postgres://elnafo:test@localhost/elnafo
|
|
@ -1,8 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "db"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
diesel = { version = "2.1.4", features = ["postgres"] }
|
|
||||||
dotenvy = "0.15.7"
|
|
@ -1,9 +0,0 @@
|
|||||||
# For documentation on how to configure this file,
|
|
||||||
# see https://diesel.rs/guides/configuring-diesel-cli
|
|
||||||
|
|
||||||
[print_schema]
|
|
||||||
file = "src/schema.rs"
|
|
||||||
custom_type_derives = ["diesel::query_builder::QueryId"]
|
|
||||||
|
|
||||||
[migrations_directory]
|
|
||||||
dir = "migrations"
|
|
@ -1,51 +0,0 @@
|
|||||||
pub mod models;
|
|
||||||
pub mod schema;
|
|
||||||
|
|
||||||
use diesel::prelude::*;
|
|
||||||
use dotenvy::dotenv;
|
|
||||||
use std::env;
|
|
||||||
|
|
||||||
use crate::models::{NewUser, User};
|
|
||||||
|
|
||||||
pub fn establish_connection() -> PgConnection {
|
|
||||||
dotenv().ok();
|
|
||||||
|
|
||||||
let db_url = env::var("DATABASE_URL").expect("Missed DATABASE_URL");
|
|
||||||
|
|
||||||
PgConnection::establish(&db_url).unwrap_or_else(|_| panic!("Error connecting to {}", db_url))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_user(
|
|
||||||
connection: &mut PgConnection,
|
|
||||||
login: &str,
|
|
||||||
hashed_password: &str,
|
|
||||||
name: &str,
|
|
||||||
email: &str,
|
|
||||||
is_admin: bool,
|
|
||||||
) -> User {
|
|
||||||
use crate::schema::users;
|
|
||||||
|
|
||||||
let new_user = NewUser {
|
|
||||||
login,
|
|
||||||
hashed_password,
|
|
||||||
name,
|
|
||||||
email,
|
|
||||||
is_admin,
|
|
||||||
};
|
|
||||||
|
|
||||||
diesel::insert_into(users::table)
|
|
||||||
.values(&new_user)
|
|
||||||
.returning(User::as_returning())
|
|
||||||
.get_result(connection)
|
|
||||||
.expect("Error creating new user")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn it_works() {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "elnafo"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
authors = ["L-Nafaryus <l.nafaryus@elnafo.ru"]
|
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "elnafo"
|
|
||||||
path = "src/main.rs"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
diesel = { version = "2.1.4", features = ["postgres"] }
|
|
||||||
db = { path = "../db" }
|
|
@ -1,24 +0,0 @@
|
|||||||
use db::{create_user, establish_connection, models::User};
|
|
||||||
use diesel::prelude::*;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
use db::schema::users::dsl::*;
|
|
||||||
|
|
||||||
let connection = &mut establish_connection();
|
|
||||||
|
|
||||||
create_user(
|
|
||||||
connection,
|
|
||||||
"L-Nafaryus",
|
|
||||||
"asdasd",
|
|
||||||
"L-Nafaryus",
|
|
||||||
"l.nafaryus@elnafo.ru",
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
let results = users
|
|
||||||
.select(User::as_select())
|
|
||||||
.load(connection)
|
|
||||||
.expect("Error loading users");
|
|
||||||
|
|
||||||
println!("Found {} users", results.len());
|
|
||||||
}
|
|
6
diesel.toml
Normal file
6
diesel.toml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
[print_schema]
|
||||||
|
file = "src/db/schema.rs"
|
||||||
|
custom_type_derives = ["diesel::query_builder::QueryId"]
|
||||||
|
|
||||||
|
[migrations_directory]
|
||||||
|
dir = "src/db/migrations"
|
@ -62,6 +62,8 @@
|
|||||||
pkgs.ripgrep
|
pkgs.ripgrep
|
||||||
pkgs.postgresql
|
pkgs.postgresql
|
||||||
pkgs.diesel-cli
|
pkgs.diesel-cli
|
||||||
|
pkgs.cargo-watch
|
||||||
|
pkgs.mold
|
||||||
];
|
];
|
||||||
|
|
||||||
shellHook = ''
|
shellHook = ''
|
||||||
|
14
src/api/v1/mod.rs
Normal file
14
src/api/v1/mod.rs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
pub fn add(left: usize, right: usize) -> usize {
|
||||||
|
left + right
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_works() {
|
||||||
|
let result = add(2, 2);
|
||||||
|
assert_eq!(result, 4);
|
||||||
|
}
|
||||||
|
}
|
71
src/config.rs
Normal file
71
src/config.rs
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
use dotenvy::dotenv;
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Config {
|
||||||
|
pub database: Database,
|
||||||
|
pub server: Server,
|
||||||
|
pub jwt: Jwt,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Database {
|
||||||
|
pub host: String,
|
||||||
|
pub port: i32,
|
||||||
|
pub user: String,
|
||||||
|
pub password: String,
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Server {
|
||||||
|
pub address: String,
|
||||||
|
pub port: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Jwt {
|
||||||
|
pub secret: String,
|
||||||
|
pub expires_in: String,
|
||||||
|
pub maxage: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
dotenv().ok();
|
||||||
|
|
||||||
|
Config {
|
||||||
|
database: Database {
|
||||||
|
host: env::var("DATABASE_HOST").unwrap_or("localhost".to_string()),
|
||||||
|
port: env::var("DATABASE_PORT")
|
||||||
|
.unwrap_or("5432".to_string())
|
||||||
|
.parse()
|
||||||
|
.unwrap(),
|
||||||
|
user: env::var("DATABASE_USER").unwrap_or("elnafo".to_string()),
|
||||||
|
password: env::var("DATABASE_PASSWORD").unwrap_or("test".to_string()),
|
||||||
|
name: env::var("DATABASE_NAME").unwrap_or("elnafo".to_string()),
|
||||||
|
},
|
||||||
|
server: Server {
|
||||||
|
address: env::var("SERVER_ADDRESS").unwrap_or("0.0.0.0".to_string()),
|
||||||
|
port: env::var("SERVER_PORT")
|
||||||
|
.unwrap_or("54600".to_string())
|
||||||
|
.parse()
|
||||||
|
.unwrap(),
|
||||||
|
},
|
||||||
|
jwt: Jwt {
|
||||||
|
secret: env::var("JWT_SECRET").unwrap_or("change_this_secret".to_string()),
|
||||||
|
expires_in: env::var("JWT_EXPIRES_IN").unwrap_or("60m".to_string()),
|
||||||
|
maxage: env::var("JWT_MAXAGE")
|
||||||
|
.unwrap_or("60".to_string())
|
||||||
|
.parse()
|
||||||
|
.unwrap(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
fn default() -> Self {
|
||||||
|
Config::new()
|
||||||
|
}
|
||||||
|
}
|
60
src/db/mod.rs
Normal file
60
src/db/mod.rs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
pub mod models;
|
||||||
|
pub mod schema;
|
||||||
|
|
||||||
|
use deadpool_diesel::postgres::Manager;
|
||||||
|
pub use deadpool_diesel::postgres::Pool;
|
||||||
|
use diesel::prelude::*;
|
||||||
|
use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
|
||||||
|
|
||||||
|
use crate::db::models::{NewUser, User};
|
||||||
|
|
||||||
|
pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("src/db/migrations/");
|
||||||
|
|
||||||
|
pub fn create_pool(database_url: String) -> Pool {
|
||||||
|
let manager = Manager::new(database_url, deadpool_diesel::Runtime::Tokio1);
|
||||||
|
|
||||||
|
Pool::builder(manager).build().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn run_migrations(pool: &Pool) {
|
||||||
|
let conn = pool.get().await.unwrap();
|
||||||
|
conn.interact(|conn| conn.run_pending_migrations(MIGRATIONS).map(|_| ()))
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_user(
|
||||||
|
connection: &mut PgConnection,
|
||||||
|
login: &str,
|
||||||
|
hashed_password: &str,
|
||||||
|
name: &str,
|
||||||
|
email: &str,
|
||||||
|
is_admin: bool,
|
||||||
|
) -> User {
|
||||||
|
use crate::db::schema::users;
|
||||||
|
|
||||||
|
let new_user = NewUser {
|
||||||
|
login,
|
||||||
|
hashed_password,
|
||||||
|
name,
|
||||||
|
email,
|
||||||
|
is_admin,
|
||||||
|
};
|
||||||
|
|
||||||
|
diesel::insert_into(users::table)
|
||||||
|
.values(&new_user)
|
||||||
|
.returning(User::as_returning())
|
||||||
|
.get_result(connection)
|
||||||
|
.expect("Error creating new user")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_works() {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
use crate::schema;
|
use crate::db::schema;
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
|
|
||||||
#[derive(Queryable, Selectable)]
|
#[derive(serde::Serialize, Queryable, Selectable)]
|
||||||
#[diesel(table_name = schema::users)]
|
#[diesel(table_name = schema::users)]
|
||||||
#[diesel(check_for_backend(diesel::pg::Pg))]
|
#[diesel(check_for_backend(diesel::pg::Pg))]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
@ -13,7 +13,7 @@ pub struct User {
|
|||||||
pub is_admin: bool,
|
pub is_admin: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Insertable)]
|
#[derive(serde::Deserialize, Insertable)]
|
||||||
#[diesel(table_name = schema::users)]
|
#[diesel(table_name = schema::users)]
|
||||||
pub struct NewUser<'a> {
|
pub struct NewUser<'a> {
|
||||||
pub login: &'a str,
|
pub login: &'a str,
|
105
src/main.rs
Normal file
105
src/main.rs
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
mod config;
|
||||||
|
mod db;
|
||||||
|
|
||||||
|
use axum::{extract::State, http::StatusCode, response::Json, routing::get, Router};
|
||||||
|
use diesel::RunQueryDsl;
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
use std::{env, net::Ipv4Addr};
|
||||||
|
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
||||||
|
|
||||||
|
use crate::config::Config;
|
||||||
|
use db::{create_user, models::User};
|
||||||
|
|
||||||
|
pub struct AppState {
|
||||||
|
database: db::Pool,
|
||||||
|
config: Config,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
init_tracing();
|
||||||
|
|
||||||
|
let config = Config::new();
|
||||||
|
let database_url = format!(
|
||||||
|
"postgres://{}:{}@{}:{}/{}",
|
||||||
|
config.database.user,
|
||||||
|
config.database.password,
|
||||||
|
config.database.host,
|
||||||
|
config.database.port,
|
||||||
|
config.database.name
|
||||||
|
);
|
||||||
|
|
||||||
|
let pool = db::create_pool(database_url);
|
||||||
|
|
||||||
|
db::run_migrations(&pool).await;
|
||||||
|
|
||||||
|
let state = AppState {
|
||||||
|
database: pool.clone(),
|
||||||
|
config: config.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let address: SocketAddr = format!("{}:{}", config.server.address, config.server.port)
|
||||||
|
.parse()
|
||||||
|
.unwrap(); //SocketAddr::from((Ipv4Addr::UNSPECIFIED, 54600));
|
||||||
|
|
||||||
|
let lister = tokio::net::TcpListener::bind(&address).await.unwrap();
|
||||||
|
|
||||||
|
let app = Router::new()
|
||||||
|
.route("/api/v1/users", get(users))
|
||||||
|
.with_state(pool);
|
||||||
|
|
||||||
|
println!("listening on http://{}", address);
|
||||||
|
|
||||||
|
axum::serve(lister, app.into_make_service())
|
||||||
|
.await
|
||||||
|
.map_err(internal_error)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn users(State(pool): State<db::Pool>) -> Result<Json<Vec<User>>, (StatusCode, String)> {
|
||||||
|
use db::schema::users::dsl::*;
|
||||||
|
|
||||||
|
let conn = pool.get().await.unwrap();
|
||||||
|
|
||||||
|
let result = conn
|
||||||
|
.interact(move |conn| users.load(conn))
|
||||||
|
.await
|
||||||
|
.map_err(internal_error)?
|
||||||
|
.map_err(internal_error)?;
|
||||||
|
|
||||||
|
Ok(Json(result))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_tracing() {
|
||||||
|
tracing_subscriber::registry()
|
||||||
|
.with(
|
||||||
|
tracing_subscriber::EnvFilter::try_from_default_env()
|
||||||
|
.unwrap_or_else(|_| "example_tokio_postgres=debug".into()),
|
||||||
|
)
|
||||||
|
.with(tracing_subscriber::fmt::layer())
|
||||||
|
.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn internal_error<E>(err: E) -> (StatusCode, String)
|
||||||
|
where
|
||||||
|
E: std::error::Error,
|
||||||
|
{
|
||||||
|
(StatusCode::INTERNAL_SERVER_ERROR, err.to_string())
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
create_user(
|
||||||
|
connection,
|
||||||
|
"L-Nafaryus",
|
||||||
|
"asdasd",
|
||||||
|
"L-Nafaryus",
|
||||||
|
"l.nafaryus@elnafo.ru",
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
let results = users
|
||||||
|
.select(User::as_select())
|
||||||
|
.load(connection)
|
||||||
|
.expect("Error loading users");
|
||||||
|
|
||||||
|
println!("Found {} users", results.len());
|
||||||
|
*/
|
Loading…
Reference in New Issue
Block a user