From 36124043b640e77022728f11a552e187c2b31732 Mon Sep 17 00:00:00 2001 From: Bulat Kurbanov Date: Fri, 16 Jan 2026 10:35:45 +0100 Subject: [PATCH] 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. --- Cargo.toml | 2 +- migrations/20260116092854_initial_schema.sql | 27 ++++++++++++++++++++ src/db.rs | 10 ++++++++ src/main.rs | 8 +++++- src/views.rs | 6 ++--- 5 files changed, 47 insertions(+), 6 deletions(-) create mode 100644 migrations/20260116092854_initial_schema.sql diff --git a/Cargo.toml b/Cargo.toml index 08b41c8..1dcb708 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,4 +34,4 @@ tracing-subscriber = { version = "0.3.19", features = ["env-filter"]} sentry-tracing = "0.42.0" 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"] } diff --git a/migrations/20260116092854_initial_schema.sql b/migrations/20260116092854_initial_schema.sql new file mode 100644 index 0000000..9c641f1 --- /dev/null +++ b/migrations/20260116092854_initial_schema.sql @@ -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 +$$; diff --git a/src/db.rs b/src/db.rs index 08cef04..865a671 100644 --- a/src/db.rs +++ b/src/db.rs @@ -1,6 +1,16 @@ use crate::config::CONFIG; 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 { let database_url: String = format!( diff --git a/src/main.rs b/src/main.rs index 7ceecf1..6453228 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,7 +10,13 @@ use tracing_subscriber::{filter, layer::SubscriberExt, util::SubscriberInitExt}; use std::{net::SocketAddr, str::FromStr}; 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)); diff --git a/src/views.rs b/src/views.rs index afea145..684c8a7 100644 --- a/src/views.rs +++ b/src/views.rs @@ -13,7 +13,7 @@ use sqlx::PgPool; use tower_http::trace::{self, TraceLayer}; use tracing::Level; -use crate::{config::CONFIG, db::get_pg_pool}; +use crate::config::CONFIG; pub type Database = Extension; @@ -218,9 +218,7 @@ async fn auth(req: Request, next: Next) -> Result Router { - let client = get_pg_pool().await; - +pub async fn get_router(client: PgPool) -> Router { let (prometheus_layer, metric_handle) = PrometheusMetricLayer::pair(); let app_router = Router::new()