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*
|
||||
/temp
|
||||
/.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]
|
||||
resolver = "2"
|
||||
members = [ "crates/db","crates/server"]
|
||||
[package]
|
||||
name = "elnafo"
|
||||
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.postgresql
|
||||
pkgs.diesel-cli
|
||||
pkgs.cargo-watch
|
||||
pkgs.mold
|
||||
];
|
||||
|
||||
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::*;
|
||||
|
||||
#[derive(Queryable, Selectable)]
|
||||
#[derive(serde::Serialize, Queryable, Selectable)]
|
||||
#[diesel(table_name = schema::users)]
|
||||
#[diesel(check_for_backend(diesel::pg::Pg))]
|
||||
pub struct User {
|
||||
@ -13,7 +13,7 @@ pub struct User {
|
||||
pub is_admin: bool,
|
||||
}
|
||||
|
||||
#[derive(Insertable)]
|
||||
#[derive(serde::Deserialize, Insertable)]
|
||||
#[diesel(table_name = schema::users)]
|
||||
pub struct NewUser<'a> {
|
||||
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