Run SQLx migrations at startup

Add an initial services table migration and enable the sqlx
"migrate" feature. Introduce run_migrations in db.rs, run
migrations on startup, and pass the PgPool into the router.
This commit is contained in:
2026-01-16 10:35:45 +01:00
parent 4b36cd83a1
commit 36124043b6
5 changed files with 47 additions and 6 deletions

View File

@@ -34,4 +34,4 @@ tracing-subscriber = { version = "0.3.19", features = ["env-filter"]}
sentry-tracing = "0.42.0" sentry-tracing = "0.42.0"
tower-http = { version = "0.6.2", features = ["trace"] } tower-http = { version = "0.6.2", features = ["trace"] }
sqlx = { version = "0.8.3", features = ["runtime-tokio", "postgres", "macros", "chrono"] } sqlx = { version = "0.8.3", features = ["runtime-tokio", "postgres", "macros", "chrono", "migrate"] }

View File

@@ -0,0 +1,27 @@
-- Initial schema migration for services table
-- This migration is idempotent and safe to run on existing databases
-- Create services table if it doesn't exist
CREATE TABLE IF NOT EXISTS services (
id SERIAL PRIMARY KEY,
token VARCHAR(128) NOT NULL UNIQUE,
"user" BIGINT NOT NULL,
status VARCHAR(12) NOT NULL,
created_time TIMESTAMPTZ NOT NULL,
cache VARCHAR(12) NOT NULL,
username VARCHAR(64) NOT NULL
);
-- Create unique index on token if it doesn't exist
-- Note: The UNIQUE constraint already creates an index, but we ensure it exists
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_indexes
WHERE tablename = 'services'
AND indexname = 'services_token_key'
) THEN
CREATE UNIQUE INDEX services_token_key ON services(token);
END IF;
END
$$;

View File

@@ -1,6 +1,16 @@
use crate::config::CONFIG; use crate::config::CONFIG;
use sqlx::{postgres::PgPoolOptions, PgPool}; use sqlx::{postgres::PgPoolOptions, PgPool};
use tracing::info;
pub async fn run_migrations(pool: &PgPool) {
info!("Running database migrations...");
sqlx::migrate!("./migrations")
.run(pool)
.await
.expect("Failed to run migrations");
info!("Database migrations completed successfully");
}
pub async fn get_pg_pool() -> PgPool { pub async fn get_pg_pool() -> PgPool {
let database_url: String = format!( let database_url: String = format!(

View File

@@ -10,7 +10,13 @@ use tracing_subscriber::{filter, layer::SubscriberExt, util::SubscriberInitExt};
use std::{net::SocketAddr, str::FromStr}; use std::{net::SocketAddr, str::FromStr};
async fn start_app() { async fn start_app() {
let app = views::get_router().await; // Initialize database pool
let pool = db::get_pg_pool().await;
// Run migrations
db::run_migrations(&pool).await;
let app = views::get_router(pool).await;
let addr = SocketAddr::from(([0, 0, 0, 0], 8080)); let addr = SocketAddr::from(([0, 0, 0, 0], 8080));

View File

@@ -13,7 +13,7 @@ use sqlx::PgPool;
use tower_http::trace::{self, TraceLayer}; use tower_http::trace::{self, TraceLayer};
use tracing::Level; use tracing::Level;
use crate::{config::CONFIG, db::get_pg_pool}; use crate::config::CONFIG;
pub type Database = Extension<PgPool>; pub type Database = Extension<PgPool>;
@@ -218,9 +218,7 @@ async fn auth(req: Request<axum::body::Body>, next: Next) -> Result<Response, St
Ok(next.run(req).await) Ok(next.run(req).await)
} }
pub async fn get_router() -> Router { pub async fn get_router(client: PgPool) -> Router {
let client = get_pg_pool().await;
let (prometheus_layer, metric_handle) = PrometheusMetricLayer::pair(); let (prometheus_layer, metric_handle) = PrometheusMetricLayer::pair();
let app_router = Router::new() let app_router = Router::new()