chore: set up skeleton project
commit
b6a8ee0bbe
|
@ -0,0 +1,2 @@
|
|||
/target
|
||||
data
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,14 @@
|
|||
[package]
|
||||
name = "otter"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
axum = "0.8.1"
|
||||
diesel = { version = "2.2.7", features = ["r2d2", "sqlite"] }
|
||||
diesel_migrations = { version = "2.2.0", features = ["sqlite"] }
|
||||
libsqlite3-sys = { version = "0.31.0", features = ["bundled"] }
|
||||
tokio = { version = "1.43.0", features = ["full"] }
|
||||
tower-http = { version = "0.6.2", features = ["set-header", "trace"] }
|
||||
tracing = "0.1.41"
|
||||
tracing-subscriber = "0.3.19"
|
|
@ -0,0 +1,9 @@
|
|||
# For documentation on how to configure this file,
|
||||
# see https://diesel.rs/guides/configuring-diesel-cli
|
||||
|
||||
[print_schema]
|
||||
file = "src/db/schema.rs"
|
||||
custom_type_derives = ["diesel::query_builder::QueryId", "Clone"]
|
||||
|
||||
[migrations_directory]
|
||||
dir = "migrations"
|
|
@ -0,0 +1,62 @@
|
|||
mod models;
|
||||
mod schema;
|
||||
|
||||
use diesel::{
|
||||
r2d2::{ConnectionManager, Pool},
|
||||
SqliteConnection,
|
||||
};
|
||||
use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
|
||||
|
||||
use std::{error::Error, fmt, path::Path};
|
||||
|
||||
pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations");
|
||||
|
||||
pub type DbPool = Pool<ConnectionManager<SqliteConnection>>;
|
||||
pub type DbResult<T> = Result<T, DbError>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum DbError {
|
||||
Pool(diesel::r2d2::PoolError),
|
||||
Db(diesel::result::Error),
|
||||
}
|
||||
|
||||
impl fmt::Display for DbError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Pool(_) => write!(f, "failed to acquire connection from pool"),
|
||||
Self::Db(_) => write!(f, "error while executing query"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for DbError {
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
match self {
|
||||
Self::Pool(err) => Some(err),
|
||||
Self::Db(err) => Some(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<diesel::r2d2::PoolError> for DbError {
|
||||
fn from(value: diesel::r2d2::PoolError) -> Self {
|
||||
Self::Pool(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<diesel::result::Error> for DbError {
|
||||
fn from(value: diesel::result::Error) -> Self {
|
||||
Self::Db(value)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn initialize_db(path: impl AsRef<Path>, run_migrations: bool) -> Result<DbPool, DbError> {
|
||||
let manager = ConnectionManager::<SqliteConnection>::new(path.as_ref().to_string_lossy());
|
||||
let pool = Pool::new(manager)?;
|
||||
|
||||
if run_migrations {
|
||||
pool.get()?.run_pending_migrations(MIGRATIONS).unwrap();
|
||||
}
|
||||
|
||||
Ok(pool)
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
// @generated automatically by Diesel CLI.
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
mod db;
|
||||
mod server;
|
||||
|
||||
fn main() {
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
tracing::info!("Initializing database and running migrations");
|
||||
|
||||
let pool = db::initialize_db("data/db.sqlite3", true).unwrap();
|
||||
|
||||
let ctx = server::Context { pool };
|
||||
let app = server::app().with_state(ctx);
|
||||
|
||||
let rt = tokio::runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let address = "0.0.0.0:8080";
|
||||
|
||||
tracing::info!("Starting server on {address}");
|
||||
|
||||
rt.block_on(async {
|
||||
let listener = tokio::net::TcpListener::bind(address).await.unwrap();
|
||||
axum::serve(listener, app.into_make_service())
|
||||
.await
|
||||
.unwrap()
|
||||
});
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
use axum::{
|
||||
http::{HeaderName, HeaderValue},
|
||||
Router,
|
||||
};
|
||||
use tower_http::{set_header::SetResponseHeaderLayer, trace::TraceLayer};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Context {
|
||||
pub pool: crate::db::DbPool,
|
||||
}
|
||||
|
||||
pub fn app() -> Router<Context> {
|
||||
Router::new()
|
||||
.layer(TraceLayer::new_for_http())
|
||||
// https://gpoddernet.readthedocs.io/en/latest/api/reference/general.html#cors
|
||||
// All endpoints should send this CORS header value so the endpoints can be used from web
|
||||
// applications
|
||||
.layer(SetResponseHeaderLayer::overriding(
|
||||
HeaderName::from_static("access-control-allow-origin"),
|
||||
HeaderValue::from_static("*"),
|
||||
))
|
||||
}
|
Loading…
Reference in New Issue