Cleanup and organize code into modules
This commit is contained in:
parent
827a519ca5
commit
10d48a91c3
15 changed files with 509 additions and 171 deletions
236
Cargo.lock
generated
236
Cargo.lock
generated
|
@ -60,6 +60,7 @@ dependencies = [
|
|||
"bitflags 1.3.2",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"headers",
|
||||
"http",
|
||||
"http-body",
|
||||
"hyper",
|
||||
|
@ -113,6 +114,12 @@ dependencies = [
|
|||
"rustc-demangle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
|
@ -125,6 +132,15 @@ version = "2.4.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.5.0"
|
||||
|
@ -146,6 +162,44 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deadpool"
|
||||
version = "0.10.0"
|
||||
|
@ -241,12 +295,34 @@ dependencies = [
|
|||
"syn 2.0.39",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "filetime"
|
||||
version = "0.2.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall 0.3.5",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
|
@ -262,6 +338,15 @@ dependencies = [
|
|||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fsevent-sys"
|
||||
version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.29"
|
||||
|
@ -301,6 +386,16 @@ dependencies = [
|
|||
"pin-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.28.0"
|
||||
|
@ -313,6 +408,30 @@ version = "0.14.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156"
|
||||
|
||||
[[package]]
|
||||
name = "headers"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bytes",
|
||||
"headers-core",
|
||||
"http",
|
||||
"httpdate",
|
||||
"mime",
|
||||
"sha1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "headers-core"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429"
|
||||
dependencies = [
|
||||
"http",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.3"
|
||||
|
@ -392,12 +511,52 @@ dependencies = [
|
|||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inotify"
|
||||
version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"inotify-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inotify-sys"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
|
||||
|
||||
[[package]]
|
||||
name = "kqueue"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c"
|
||||
dependencies = [
|
||||
"kqueue-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kqueue-sys"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
|
@ -447,6 +606,7 @@ dependencies = [
|
|||
"diesel",
|
||||
"diesel_migrations",
|
||||
"maud",
|
||||
"notify",
|
||||
"serde",
|
||||
"tokio",
|
||||
"tower-http",
|
||||
|
@ -551,10 +711,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "notify"
|
||||
version = "6.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d"
|
||||
dependencies = [
|
||||
"bitflags 2.4.1",
|
||||
"crossbeam-channel",
|
||||
"filetime",
|
||||
"fsevent-sys",
|
||||
"inotify",
|
||||
"kqueue",
|
||||
"libc",
|
||||
"log",
|
||||
"mio",
|
||||
"walkdir",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.46.0"
|
||||
|
@ -614,7 +794,7 @@ checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
|
|||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"redox_syscall 0.4.1",
|
||||
"smallvec",
|
||||
"windows-targets",
|
||||
]
|
||||
|
@ -711,6 +891,15 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.4.1"
|
||||
|
@ -782,6 +971,15 @@ version = "1.0.15"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
|
@ -850,6 +1048,17 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1"
|
||||
version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sharded-slab"
|
||||
version = "0.1.7"
|
||||
|
@ -1173,6 +1382,12 @@ version = "0.2.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.7.0"
|
||||
|
@ -1206,6 +1421,16 @@ version = "0.9.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "want"
|
||||
version = "0.3.1"
|
||||
|
@ -1237,6 +1462,15 @@ version = "0.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
|
|
|
@ -5,12 +5,13 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
anyhow = "1.0.75"
|
||||
axum = "0.6.20"
|
||||
axum = { version = "0.6.20", features = ["headers"] }
|
||||
deadpool = "0.10.0"
|
||||
deadpool-diesel = { version = "0.5.0", features = ["rt_tokio_1", "sqlite", "tracing", "serde"] }
|
||||
diesel = { version = "2.1.3", features = ["sqlite"] }
|
||||
diesel_migrations = { version = "2.1.0", features = ["sqlite"] }
|
||||
maud = "0.25.0"
|
||||
notify = "6.1.1"
|
||||
serde = { version = "1.0.192", features = ["derive"] }
|
||||
tokio = { version = "1.34.0", features = ["full", "macros", "rt-multi-thread"] }
|
||||
tower-http = { version = "0.4.4", features = ["fs"] }
|
||||
|
|
|
@ -43,6 +43,9 @@
|
|||
devShell = with pkgs;
|
||||
mkShell {
|
||||
buildInputs = [
|
||||
# dedupe from package inputs?
|
||||
sqlite
|
||||
|
||||
toolchain
|
||||
|
||||
rustfmt
|
||||
|
@ -50,8 +53,6 @@
|
|||
rust-analyzer
|
||||
nodePackages_latest.vscode-langservers-extracted
|
||||
|
||||
# dedupe from package inputs?
|
||||
sqlite
|
||||
diesel-cli
|
||||
hurl
|
||||
];
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
create table users (
|
||||
id text primary key,
|
||||
name text,
|
||||
hashed_password text
|
||||
hashed_password binary
|
||||
)
|
||||
|
|
7
src/app.rs
Normal file
7
src/app.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
use crate::{instrumentation, router, server::listen};
|
||||
|
||||
pub async fn run() -> Result<(), anyhow::Error> {
|
||||
instrumentation::setup_trace_logger();
|
||||
listen(router::new().await?).await;
|
||||
Ok(())
|
||||
}
|
41
src/database.rs
Normal file
41
src/database.rs
Normal file
|
@ -0,0 +1,41 @@
|
|||
use deadpool_diesel::sqlite::{Manager, Pool, Runtime};
|
||||
use diesel::sqlite::Sqlite;
|
||||
use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
|
||||
use tracing::info;
|
||||
|
||||
pub struct Database {
|
||||
pub pool: Pool,
|
||||
}
|
||||
|
||||
const MIGRATIONS: EmbeddedMigrations = embed_migrations!();
|
||||
|
||||
impl Database {
|
||||
// TODO: database seeding?
|
||||
|
||||
fn run_migrations(
|
||||
connection: &mut impl MigrationHarness<Sqlite>,
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
|
||||
// This will run the necessary migrations.
|
||||
//
|
||||
// See the documentation for `MigrationHarness` for
|
||||
// all available methods.
|
||||
connection.run_pending_migrations(MIGRATIONS)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO: make an actual error type
|
||||
pub async fn new<T: AsRef<str>>(database_url: T) -> Result<Self, anyhow::Error> {
|
||||
let manager = Manager::new(database_url.as_ref(), Runtime::Tokio1);
|
||||
let pool = Pool::builder(manager).max_size(8).build().unwrap();
|
||||
|
||||
let conn = pool.get().await?;
|
||||
let _ = conn
|
||||
.interact(|c| Self::run_migrations(c))
|
||||
.await
|
||||
.expect("Failed to run migrations");
|
||||
info!("Migrations completed!");
|
||||
|
||||
return Ok(Database { pool });
|
||||
}
|
||||
}
|
8
src/feather_icons.rs
Normal file
8
src/feather_icons.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
// TODO: perhaps just download all feather icons and include lazily or something?
|
||||
// feather icons
|
||||
pub const FEATHER_ICON_USER_PLUS: &str = r#"<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-user-plus"><path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="8.5" cy="7" r="4"></circle><line x1="20" y1="8" x2="20" y2="14"></line><line x1="23" y1="11" x2="17" y2="11"></line></svg>"#;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub const FEATHER_ICON_LAYOUT: &str = r#"<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-layout"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><line x1="3" y1="9" x2="21" y2="9"></line><line x1="9" y1="21" x2="9" y2="9"></line></svg>"#;
|
||||
|
||||
pub const FEATHER_ICON_LOGIN: &str = r#"<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-log-in"><path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4"></path><polyline points="10 17 15 12 10 7"></polyline><line x1="15" y1="12" x2="3" y2="12"></line></svg>"#;
|
13
src/instrumentation.rs
Normal file
13
src/instrumentation.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
use tracing::{instrument, trace};
|
||||
use tracing_subscriber::{filter::LevelFilter, EnvFilter};
|
||||
|
||||
#[instrument]
|
||||
pub fn setup_trace_logger() {
|
||||
let filter = EnvFilter::builder()
|
||||
.with_default_directive(LevelFilter::INFO.into())
|
||||
.parse_lossy("info,lyrs=trace");
|
||||
|
||||
tracing_subscriber::fmt().with_env_filter(filter).init();
|
||||
|
||||
trace!("Starting...");
|
||||
}
|
178
src/main.rs
178
src/main.rs
|
@ -1,172 +1,20 @@
|
|||
//! lyrs entrypoint
|
||||
|
||||
use axum::{http::StatusCode, response::Html, routing::get, Router};
|
||||
use deadpool_diesel::sqlite::{Manager, Pool, Runtime};
|
||||
use diesel::sqlite::Sqlite;
|
||||
use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
|
||||
use maud::{html, PreEscaped};
|
||||
use std::{env, net::SocketAddr};
|
||||
use tower_http::services::ServeDir;
|
||||
use tower_livereload::LiveReloadLayer;
|
||||
use tracing::{info, instrument, span, trace, Level};
|
||||
use tracing_subscriber::{filter::LevelFilter, EnvFilter};
|
||||
// TODO: Break this module up
|
||||
// TODO: Implement authn
|
||||
|
||||
#[instrument]
|
||||
fn setup_trace_logger() {
|
||||
let filter = EnvFilter::builder()
|
||||
.with_default_directive(LevelFilter::INFO.into())
|
||||
.parse_lossy("info,lyrs=trace");
|
||||
|
||||
tracing_subscriber::fmt().with_env_filter(filter).init();
|
||||
|
||||
trace!("Starting...");
|
||||
}
|
||||
|
||||
// struct State {
|
||||
// pub pool: Pool,
|
||||
// }
|
||||
|
||||
fn run_migrations(
|
||||
connection: &mut impl MigrationHarness<Sqlite>,
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
|
||||
// This will run the necessary migrations.
|
||||
//
|
||||
// See the documentation for `MigrationHarness` for
|
||||
// all available methods.
|
||||
connection.run_pending_migrations(MIGRATIONS)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument]
|
||||
async fn router() -> Result<Router, anyhow::Error> {
|
||||
let app_router = Router::new()
|
||||
.route("/hello-world", get(greet_world))
|
||||
.route("/hello-world-text", get(greet_world_text));
|
||||
|
||||
let assets_dir = ServeDir::new("./assets");
|
||||
|
||||
let pool;
|
||||
|
||||
let dbspan = span!(Level::WARN, "database_setup");
|
||||
{
|
||||
let _s = dbspan.enter();
|
||||
|
||||
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
|
||||
let manager = Manager::new(database_url, Runtime::Tokio1);
|
||||
pool = Pool::builder(manager).max_size(8).build().unwrap();
|
||||
info!("Database pool creation complete!");
|
||||
|
||||
let conn = pool.get().await?;
|
||||
let mspan = span!(Level::INFO, "migrations");
|
||||
{
|
||||
let _s = mspan.enter();
|
||||
let _ = conn
|
||||
.interact(|c| run_migrations(c))
|
||||
.await
|
||||
.expect("Failed to run migrations");
|
||||
info!("Migrations completed!");
|
||||
}
|
||||
}
|
||||
|
||||
// let state = Arc::new(State { pool });
|
||||
|
||||
Ok(Router::new()
|
||||
.fallback(|| async { (StatusCode::NOT_FOUND, "404 page not found") })
|
||||
.nest("/app", app_router)
|
||||
.nest_service("/assets", assets_dir)
|
||||
.route("/", get(index))
|
||||
.layer(LiveReloadLayer::new()))
|
||||
// .with_state(state))
|
||||
}
|
||||
|
||||
async fn listen(r: Router) {
|
||||
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
|
||||
info!("Listening on '{}'", addr);
|
||||
axum::Server::bind(&addr)
|
||||
.serve(r.into_make_service())
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!();
|
||||
mod app;
|
||||
mod database;
|
||||
mod feather_icons;
|
||||
mod instrumentation;
|
||||
mod partials;
|
||||
mod router;
|
||||
mod schema;
|
||||
mod server;
|
||||
mod state;
|
||||
mod views;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), anyhow::Error> {
|
||||
setup_trace_logger();
|
||||
listen(router().await?).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// feather icons
|
||||
const FEATHER_ICON_LAYOUT: &str = r#"<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-layout"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><line x1="3" y1="9" x2="21" y2="9"></line><line x1="9" y1="21" x2="9" y2="9"></line></svg>"#;
|
||||
|
||||
const FEATHER_ICON_LOGIN: &str = r#"<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-log-in"><path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4"></path><polyline points="10 17 15 12 10 7"></polyline><line x1="15" y1="12" x2="3" y2="12"></line></svg>"#;
|
||||
|
||||
#[instrument]
|
||||
async fn index() -> Html<String> {
|
||||
Html(
|
||||
html! {
|
||||
head {
|
||||
link rel="stylesheet" href="/assets/styles.css" {}
|
||||
link rel="icon" href="/assets/favicon.svg" {}
|
||||
}
|
||||
header class="flex" {
|
||||
nav class="flex" {
|
||||
h1 {
|
||||
a href="/" { "lyrs" }
|
||||
}
|
||||
// ul class="flex"
|
||||
// {
|
||||
// a href="/login" { "Login" }
|
||||
// }
|
||||
}
|
||||
nav class="flex" {
|
||||
ul class="flex"
|
||||
{
|
||||
a href="/dashboard" {
|
||||
(PreEscaped(FEATHER_ICON_LAYOUT))
|
||||
"Dashboard"
|
||||
}
|
||||
a href="/login" {
|
||||
(PreEscaped(FEATHER_ICON_LOGIN))
|
||||
"Login"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
main class="prose" {
|
||||
h1 { "Manage live lyrics and music displays" }
|
||||
p { "Stop struggling to share the same messy set of PowerPoint files or Google Presentations. Make editing and controlling your live lyrics and music displays easy and simple." }
|
||||
ul {
|
||||
li { "Live collaboration with your team" }
|
||||
li { "Fully compatible with any device" }
|
||||
li { "Simple workflow" }
|
||||
li { "Dark theme and light theme" }
|
||||
li { "Generous free plan" }
|
||||
li { "Lightweight and fast" }
|
||||
}
|
||||
section class="flex gap" {
|
||||
a class="button bg-primary" href="/register/anonymous" { "Try now" }
|
||||
a class="button" href="/login" { "Login" }
|
||||
}
|
||||
}
|
||||
}
|
||||
.into_string(),
|
||||
)
|
||||
}
|
||||
|
||||
#[instrument]
|
||||
async fn greet_world() -> Html<String> {
|
||||
Html(
|
||||
html! {
|
||||
h1 { (greet_world_text().await) }
|
||||
}
|
||||
.into_string(),
|
||||
)
|
||||
}
|
||||
|
||||
#[instrument]
|
||||
async fn greet_world_text() -> &'static str {
|
||||
"Hello, World!"
|
||||
app::run().await
|
||||
}
|
||||
|
|
51
src/partials.rs
Normal file
51
src/partials.rs
Normal file
|
@ -0,0 +1,51 @@
|
|||
use maud::{html, Markup, PreEscaped};
|
||||
|
||||
use crate::feather_icons;
|
||||
|
||||
pub fn header() -> Markup {
|
||||
html! {
|
||||
head {
|
||||
link rel="stylesheet" href="/assets/styles.css" {}
|
||||
link rel="icon" href="/assets/favicon.svg" {}
|
||||
}
|
||||
body hx-ext="preload" {
|
||||
header class="flex" {
|
||||
nav class="flex" {
|
||||
h1 {
|
||||
a href="/" { "lyrs" }
|
||||
}
|
||||
// ul class="flex"
|
||||
// {
|
||||
// a href="/login" { "Login" }
|
||||
// }
|
||||
}
|
||||
nav class="flex" {
|
||||
ul class="flex"
|
||||
{
|
||||
// a href="/dashboard" {
|
||||
// (PreEscaped(FEATHER_ICON_LAYOUT))
|
||||
// "Dashboard"
|
||||
// }
|
||||
a href="/register" preload="" {
|
||||
(PreEscaped(feather_icons::FEATHER_ICON_USER_PLUS))
|
||||
"Register"
|
||||
}
|
||||
a href="/login" preload="" {
|
||||
(PreEscaped(feather_icons::FEATHER_ICON_LOGIN))
|
||||
"Login"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn footer() -> Markup {
|
||||
html! {
|
||||
footer {
|
||||
script src="https://unpkg.com/htmx.org@1.9.8" crossorigin="anonymous" integrity="sha384-rgjA7mptc2ETQqXoYC3/zJvkU7K/aP44Y+z7xQuJiVnB/422P/Ak+F/AqFR7E4Wr" {}
|
||||
script src="https://unpkg.com/htmx.org@1.9.8/dist/ext/preload.js" crossorigin="anonymous" {}
|
||||
}
|
||||
}
|
||||
}
|
36
src/router.rs
Normal file
36
src/router.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
use std::{path::Path, sync::Arc};
|
||||
|
||||
use axum::{http::StatusCode, routing::get, Router};
|
||||
use notify::Watcher;
|
||||
use tower_http::services::ServeDir;
|
||||
use tower_livereload::LiveReloadLayer;
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::{state::State, views};
|
||||
|
||||
#[instrument]
|
||||
pub async fn new() -> Result<Router, anyhow::Error> {
|
||||
let app_router = Router::new()
|
||||
.route("/hello-world", get(views::greet_world))
|
||||
.route("/hello-world-text", get(views::greet_world_text));
|
||||
|
||||
let assets_dir = ServeDir::new("./assets");
|
||||
let state = Arc::new(State::new().await?);
|
||||
|
||||
let live_reload_layer = LiveReloadLayer::new();
|
||||
let reloader = live_reload_layer.reloader();
|
||||
|
||||
let mut watcher = notify::recommended_watcher(move |_| reloader.reload())?;
|
||||
watcher.watch(Path::new("assets"), notify::RecursiveMode::Recursive)?;
|
||||
|
||||
let router = Router::new()
|
||||
.fallback(|| async { (StatusCode::NOT_FOUND, "404 page not found") })
|
||||
.nest("/app", app_router)
|
||||
.nest_service("/assets", assets_dir)
|
||||
.route("/", get(views::index))
|
||||
.route("/register", get(views::register))
|
||||
.with_state(state)
|
||||
.layer(live_reload_layer);
|
||||
|
||||
Ok(router)
|
||||
}
|
|
@ -4,6 +4,6 @@ diesel::table! {
|
|||
users (id) {
|
||||
id -> Nullable<Text>,
|
||||
name -> Nullable<Text>,
|
||||
hashed_password -> Nullable<Text>,
|
||||
hashed_password -> Nullable<Binary>,
|
||||
}
|
||||
}
|
||||
|
|
13
src/server.rs
Normal file
13
src/server.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
use std::net::SocketAddr;
|
||||
|
||||
use axum::Router;
|
||||
use tracing::info;
|
||||
|
||||
pub async fn listen(r: Router) {
|
||||
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
|
||||
info!("Listening on '{}'", addr);
|
||||
axum::Server::bind(&addr)
|
||||
.serve(r.into_make_service())
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
15
src/state.rs
Normal file
15
src/state.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
use std::env;
|
||||
|
||||
use crate::database;
|
||||
|
||||
pub struct State {
|
||||
pub database: database::Database,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub async fn new() -> Result<Self, anyhow::Error> {
|
||||
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
|
||||
let database = database::Database::new(database_url).await?;
|
||||
Ok(State { database })
|
||||
}
|
||||
}
|
70
src/views.rs
Normal file
70
src/views.rs
Normal file
|
@ -0,0 +1,70 @@
|
|||
use axum::response::Html;
|
||||
use maud::html;
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::partials::{footer, header};
|
||||
|
||||
#[instrument]
|
||||
pub async fn index() -> Html<String> {
|
||||
Html(
|
||||
html! {
|
||||
(header())
|
||||
main class="prose" {
|
||||
h1 { "Manage live lyrics and music displays" }
|
||||
p { "Stop struggling to share the same messy set of PowerPoint files or Google Presentations. Make editing and controlling your live lyrics and music displays easy and simple." }
|
||||
ul {
|
||||
li { "Live collaboration with your team" }
|
||||
li { "Fully compatible with any device" }
|
||||
li { "Simple workflow" }
|
||||
li { "Dark theme and light theme" }
|
||||
li { "Generous free plan" }
|
||||
li { "Lightweight and fast" }
|
||||
}
|
||||
section class="flex gap" {
|
||||
button hx-get="/register" hx-target="body>main" hx-push-url="true" class="button bg-primary" { "Try now" }
|
||||
a class="button" href="/login" { "Login" }
|
||||
}
|
||||
}
|
||||
(footer())
|
||||
}
|
||||
.into_string(),
|
||||
)
|
||||
}
|
||||
|
||||
#[instrument]
|
||||
pub async fn register() -> Html<String> {
|
||||
Html(
|
||||
html! {
|
||||
h1 { "Register an account" }
|
||||
p { "Stop struggling to share the same messy set of PowerPoint files or Google Presentations. Make editing and controlling your live lyrics and music displays easy and simple." }
|
||||
ul {
|
||||
li { "Live collaboration with your team" }
|
||||
li { "Fully compatible with any device" }
|
||||
li { "Simple workflow" }
|
||||
li { "Dark theme and light theme" }
|
||||
li { "Generous free plan" }
|
||||
li { "Lightweight and fast" }
|
||||
}
|
||||
section class="flex gap" {
|
||||
a class="button bg-primary" href="/register/anonymous" { "Try now" }
|
||||
a class="button" href="/login" { "Login" }
|
||||
}
|
||||
}
|
||||
.into_string(),
|
||||
)
|
||||
}
|
||||
|
||||
#[instrument]
|
||||
pub async fn greet_world() -> Html<String> {
|
||||
Html(
|
||||
html! {
|
||||
h1 { (greet_world_text().await) }
|
||||
}
|
||||
.into_string(),
|
||||
)
|
||||
}
|
||||
|
||||
#[instrument]
|
||||
pub async fn greet_world_text() -> &'static str {
|
||||
"Hello, World!"
|
||||
}
|
Loading…
Reference in a new issue