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 logging.config import fileConfig
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from sqlalchemy import engine_from_config
|
from sqlalchemy import engine_from_config, pool, text, create_engine
|
||||||
from sqlalchemy import pool
|
from sqlalchemy.engine.url import make_url
|
||||||
|
from sqlalchemy.exc import OperationalError
|
||||||
|
|
||||||
from alembic import context
|
from alembic import context
|
||||||
|
|
||||||
@@ -35,6 +36,51 @@ target_metadata = Base.metadata
|
|||||||
config.set_main_option("sqlalchemy.url", settings.database_url)
|
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:
|
def run_migrations_offline() -> None:
|
||||||
"""Run migrations in 'offline' mode.
|
"""Run migrations in 'offline' mode.
|
||||||
|
|
||||||
@@ -66,6 +112,9 @@ def run_migrations_online() -> None:
|
|||||||
and associate a connection with the context.
|
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(
|
connectable = engine_from_config(
|
||||||
config.get_section(config.config_ini_section, {}),
|
config.get_section(config.config_ini_section, {}),
|
||||||
prefix="sqlalchemy.",
|
prefix="sqlalchemy.",
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useRouter, usePathname } from 'next/navigation';
|
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 { useMe } from '@/lib/api/hooks/useAuth';
|
||||||
import { AuthLoadingSkeleton } from '@/components/layout';
|
import { AuthLoadingSkeleton } from '@/components/layout';
|
||||||
import config from '@/config/app.config';
|
import config from '@/config/app.config';
|
||||||
@@ -50,7 +50,7 @@ interface AuthGuardProps {
|
|||||||
export function AuthGuard({ children, requireAdmin = false, fallback }: AuthGuardProps) {
|
export function AuthGuard({ children, requireAdmin = false, fallback }: AuthGuardProps) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const pathname = usePathname();
|
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
|
// Delayed loading state - only show skeleton after 150ms to avoid flicker on fast loads
|
||||||
const [showLoading, setShowLoading] = useState(false);
|
const [showLoading, setShowLoading] = useState(false);
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { usePathname } from 'next/navigation';
|
import { usePathname } from 'next/navigation';
|
||||||
import { useAuthStore } from '@/lib/stores/authStore';
|
import { useAuth } from '@/lib/stores';
|
||||||
import { useLogout } from '@/lib/api/hooks/useAuth';
|
import { useLogout } from '@/lib/api/hooks/useAuth';
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
@@ -67,7 +67,7 @@ function NavLink({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function Header() {
|
export function Header() {
|
||||||
const { user } = useAuthStore();
|
const { user } = useAuth();
|
||||||
const { mutate: logout, isPending: isLoggingOut } = useLogout();
|
const { mutate: logout, isPending: isLoggingOut } = useLogout();
|
||||||
|
|
||||||
const handleLogout = () => {
|
const handleLogout = () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user