Refactor auth hooks and add database existence check during migrations
- Consolidated `useAuthStore` into the unified `useAuth` hook for cleaner imports and consistency across frontend components. - Enhanced database management in Alembic migrations by introducing `ensure_database_exists` to automatically create the database if missing.
This commit is contained in:
@@ -2,8 +2,9 @@ import sys
|
||||
from logging.config import fileConfig
|
||||
from pathlib import Path
|
||||
|
||||
from sqlalchemy import engine_from_config
|
||||
from sqlalchemy import pool
|
||||
from sqlalchemy import engine_from_config, pool, text, create_engine
|
||||
from sqlalchemy.engine.url import make_url
|
||||
from sqlalchemy.exc import OperationalError
|
||||
|
||||
from alembic import context
|
||||
|
||||
@@ -35,6 +36,51 @@ target_metadata = Base.metadata
|
||||
config.set_main_option("sqlalchemy.url", settings.database_url)
|
||||
|
||||
|
||||
def ensure_database_exists(db_url: str) -> None:
|
||||
"""
|
||||
Ensure the target PostgreSQL database exists.
|
||||
If connection to the target DB fails because it doesn't exist, connect to the
|
||||
default 'postgres' database and create it. Safe to call multiple times.
|
||||
"""
|
||||
try:
|
||||
# First, try connecting to the target database
|
||||
test_engine = create_engine(db_url, poolclass=pool.NullPool)
|
||||
with test_engine.connect() as conn:
|
||||
conn.execute(text("SELECT 1"))
|
||||
test_engine.dispose()
|
||||
return
|
||||
except OperationalError:
|
||||
# Likely the database does not exist; proceed to create it
|
||||
pass
|
||||
|
||||
url = make_url(db_url)
|
||||
# Only handle PostgreSQL here
|
||||
if url.get_backend_name() != "postgresql":
|
||||
return
|
||||
|
||||
target_db = url.database
|
||||
if not target_db:
|
||||
return
|
||||
|
||||
# Build admin URL pointing to the default 'postgres' database
|
||||
admin_url = url.set(database="postgres")
|
||||
|
||||
# CREATE DATABASE cannot run inside a transaction
|
||||
admin_engine = create_engine(str(admin_url), isolation_level="AUTOCOMMIT", poolclass=pool.NullPool)
|
||||
try:
|
||||
with admin_engine.connect() as conn:
|
||||
exists = conn.execute(
|
||||
text("SELECT 1 FROM pg_database WHERE datname = :dbname"),
|
||||
{"dbname": target_db},
|
||||
).scalar()
|
||||
if not exists:
|
||||
# Quote the database name safely
|
||||
dbname_quoted = '"' + target_db.replace('"', '""') + '"'
|
||||
conn.execute(text(f"CREATE DATABASE {dbname_quoted}"))
|
||||
finally:
|
||||
admin_engine.dispose()
|
||||
|
||||
|
||||
def run_migrations_offline() -> None:
|
||||
"""Run migrations in 'offline' mode.
|
||||
|
||||
@@ -66,6 +112,9 @@ def run_migrations_online() -> None:
|
||||
and associate a connection with the context.
|
||||
|
||||
"""
|
||||
# Ensure the target database exists (handles first-run cases)
|
||||
ensure_database_exists(settings.database_url)
|
||||
|
||||
connectable = engine_from_config(
|
||||
config.get_section(config.config_ini_section, {}),
|
||||
prefix="sqlalchemy.",
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useRouter, usePathname } from 'next/navigation';
|
||||
import { useAuthStore } from '@/lib/stores/authStore';
|
||||
import { useAuth } from '@/lib/stores';
|
||||
import { useMe } from '@/lib/api/hooks/useAuth';
|
||||
import { AuthLoadingSkeleton } from '@/components/layout';
|
||||
import config from '@/config/app.config';
|
||||
@@ -50,7 +50,7 @@ interface AuthGuardProps {
|
||||
export function AuthGuard({ children, requireAdmin = false, fallback }: AuthGuardProps) {
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const { isAuthenticated, isLoading: authLoading, user } = useAuthStore();
|
||||
const { isAuthenticated, isLoading: authLoading, user } = useAuth();
|
||||
|
||||
// Delayed loading state - only show skeleton after 150ms to avoid flicker on fast loads
|
||||
const [showLoading, setShowLoading] = useState(false);
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
import Link from 'next/link';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import { useAuthStore } from '@/lib/stores/authStore';
|
||||
import { useAuth } from '@/lib/stores';
|
||||
import { useLogout } from '@/lib/api/hooks/useAuth';
|
||||
import {
|
||||
DropdownMenu,
|
||||
@@ -67,7 +67,7 @@ function NavLink({
|
||||
}
|
||||
|
||||
export function Header() {
|
||||
const { user } = useAuthStore();
|
||||
const { user } = useAuth();
|
||||
const { mutate: logout, isPending: isLoggingOut } = useLogout();
|
||||
|
||||
const handleLogout = () => {
|
||||
|
||||
Reference in New Issue
Block a user