add migration and config obj

This commit is contained in:
Joe Ardent 2024-03-07 17:40:08 -08:00
parent d589a88c30
commit ce4c250ba0
5 changed files with 82 additions and 6 deletions

2
.gitignore vendored
View file

@ -1,2 +1,4 @@
/target /target
.env .env
*.db
*.db-*

View file

@ -17,12 +17,12 @@ passwords = { version = "3", default-features = false }
rand = { version = "0.8", default-features = false, features = ["getrandom"] } rand = { version = "0.8", default-features = false, features = ["getrandom"] }
serde = { version = "1", default-features = false, features = ["derive"] } serde = { version = "1", default-features = false, features = ["derive"] }
serde_json = { version = "1", default-features = false } serde_json = { version = "1", default-features = false }
sqlx = { version = "0.7", default-features = false, features = ["runtime-tokio", "sqlite", "tls-none", "migrate"] } sqlx = { version = "0.7", default-features = false, features = ["runtime-tokio", "sqlite", "tls-none", "migrate", "macros"] }
thiserror = { version = "1" } thiserror = { version = "1" }
time = { version = "0.3", default-features = false } time = { version = "0.3", default-features = false }
tokio = { version = "1", default-features = false, features = ["rt-multi-thread"] } tokio = { version = "1", default-features = false, features = ["rt-multi-thread"] }
tower-http = { version = "0.5", default-features = false, features = ["fs"] } tower-http = { version = "0.5", default-features = false, features = ["fs"] }
tower-sessions = { version = "0.11", default-features = false, features = ["axum-core"] } tower-sessions = { version = "0.11", default-features = false, features = ["axum-core"] }
tower-sessions-sqlx-store = { version = "0.11.0", default-features = false, features = ["sqlite"] } tower-sessions-sqlx-store = { version = "0.11", default-features = false, features = ["sqlite"] }
unicode-segmentation = { version = "1", default-features = false } unicode-segmentation = { version = "1", default-features = false }
ureq = { version = "2", default-features = false, features = ["json", "tls"] } ureq = { version = "2", default-features = false, features = ["json", "tls"] }

5
build.rs Normal file
View file

@ -0,0 +1,5 @@
// generated by `sqlx migrate build-script`
fn main() {
// trigger recompilation when a new migration is added
println!("cargo:rerun-if-changed=migrations");
}

View file

@ -0,0 +1,40 @@
create table if not exists customers (
id integer primary key,
username text not null unique,
receipt text not null unique,
billing_email text,
invitation id,
created_at int not null default (unixepoch()),
updated_at int not null default (unixepoch()),
foreign key (invitation) references invitations (id)
);
create index if not exists customers_username_dex on customers (lower(username));
create index if not exists customers_email_dex on customers (lower(billing_email));
create index if not exists customers_invitation_dex on customers (invitation); -- does this need to be created? it's already a foreign key
create trigger if not exists update_last_updated_customers
after update on customers
when OLD.updated_at = NEW.updated_at or OLD.updated_at is null
BEGIN
update customers set updated_at = (select unixepoch()) where id=NEW.id;
END;
create table if not exists invitations (
id integer primary key,
owner integer not null,
remaining integer not null default 1,
expires_at integer,
created_at integer not null default (unixepoch()),
updated_at integer not null default (unixepoch()),
foreign key (owner) references customers (id)
);
create index if not exists invitations_owner_dex on invitations (owner);
create trigger if not exists update_updated_at_invitations
after update on invitations
when OLD.updated_at = NEW.updated_at or OLD.updated_at is null
BEGIN
update invitations set updated_at = (select unixepoch()) where id=NEW.id;
END;

View file

@ -8,6 +8,7 @@ use axum::{
routing::{get, MethodRouter}, routing::{get, MethodRouter},
Router, Router,
}; };
use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions};
use tokio::net::TcpListener; use tokio::net::TcpListener;
use tower_http::services::ServeDir; use tower_http::services::ServeDir;
use tower_sessions::{Expiry, SessionManagerLayer}; use tower_sessions::{Expiry, SessionManagerLayer};
@ -23,6 +24,14 @@ mod handlers;
mod templates; mod templates;
mod user; mod user;
struct Config {
pub admin_token: String,
pub forgejo_url: String,
pub stripe_token: String,
pub annual_link: String,
pub monthly_link: String,
}
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
use handlers::handlers::{get_signup, payment_success, post_signup}; use handlers::handlers::{get_signup, payment_success, post_signup};
@ -33,9 +42,10 @@ async fn main() {
let assets_dir = std::env::current_dir().unwrap().join("assets"); let assets_dir = std::env::current_dir().unwrap().join("assets");
let assets_svc = ServeDir::new(assets_dir.as_path()); let assets_svc = ServeDir::new(assets_dir.as_path());
// just for signups let pool = db().await;
let pool = SqlitePool::connect("sqlite:memory:").await.unwrap(); sqlx::migrate!().run(&pool).await.unwrap();
let session_store = SqliteStore::new(pool);
let session_store = SqliteStore::new(pool.clone());
session_store.migrate().await.unwrap(); session_store.migrate().await.unwrap();
let session_layer = SessionManagerLayer::new(session_store) let session_layer = SessionManagerLayer::new(session_store)
.with_secure(false) .with_secure(false)
@ -49,6 +59,7 @@ async fn main() {
.stripped_clone("/payment_success/", get(payment_success)) .stripped_clone("/payment_success/", get(payment_success))
.route("/payment_success/:receipt", get(payment_success)) .route("/payment_success/:receipt", get(payment_success))
.layer(session_layer) .layer(session_layer)
.with_state(pool)
.into_make_service(); .into_make_service();
let listener = mklistener().await; let listener = mklistener().await;
axum::serve(listener, app).await.unwrap(); axum::serve(listener, app).await.unwrap();
@ -57,7 +68,8 @@ async fn main() {
//-************************************************************************ //-************************************************************************
// li'l helpers // li'l helpers
//-************************************************************************ //-************************************************************************
fn init() { fn init() -> Config {
use std::env::var;
dotenvy::dotenv().expect("Could not read .env file."); dotenvy::dotenv().expect("Could not read .env file.");
env_logger::builder() env_logger::builder()
.format(|buf, record| { .format(|buf, record| {
@ -65,6 +77,23 @@ fn init() {
writeln!(buf, "{}: {}", ts, record.args()) writeln!(buf, "{}: {}", ts, record.args())
}) })
.init(); .init();
Config {
admin_token: var("ADMIN_TOKEN").unwrap(),
forgejo_url: var("FORGEJO_URL").unwrap(),
stripe_token: var("STRIPE_TOKEN").unwrap(),
annual_link: var("ANNUAL_LINK").unwrap(),
monthly_link: var("MONTHLY_LINK").unwrap(),
}
}
async fn db() -> SqlitePool {
let dbfile = std::env::var("DATABASE_URL").unwrap();
let opts = SqliteConnectOptions::new()
.foreign_keys(true)
.filename(dbfile)
.create_if_missing(true)
.optimize_on_close(true, None);
SqlitePoolOptions::new().connect_with(opts).await.unwrap()
} }
async fn mklistener() -> TcpListener { async fn mklistener() -> TcpListener {