feat: Enhance auth database setup and environment variable loading
This commit improves the setup of the authentication database by adding a new function to create necessary tables for both frontends. It also ensures that environment variables are loaded from a `.env` file before any database operations, enhancing configuration management. Additionally, minor updates are made to related scripts for better clarity and functionality.
This commit is contained in:
parent
68d280e8f5
commit
32be5c7f23
@ -123,3 +123,4 @@ npm run dev:viewer
|
||||
4. Update CI/CD pipelines if applicable
|
||||
5. Archive or remove the old `punimtag-viewer` repository
|
||||
|
||||
|
||||
|
||||
@ -61,12 +61,22 @@ def start_worker() -> None:
|
||||
if "cursor" in python_executable.lower() or not python_executable.startswith("/usr"):
|
||||
python_executable = "/usr/bin/python3"
|
||||
|
||||
# Ensure PYTHONPATH is set correctly
|
||||
# Ensure PYTHONPATH is set correctly and pass DATABASE_URL_AUTH explicitly
|
||||
# Load .env file to get DATABASE_URL_AUTH if not already in environment
|
||||
from dotenv import load_dotenv
|
||||
env_file = project_root / ".env"
|
||||
if env_file.exists():
|
||||
load_dotenv(dotenv_path=env_file)
|
||||
|
||||
worker_env = {
|
||||
**{k: v for k, v in os.environ.items()},
|
||||
"PYTHONPATH": str(project_root),
|
||||
}
|
||||
|
||||
# Explicitly ensure DATABASE_URL_AUTH is passed to worker subprocess
|
||||
if "DATABASE_URL_AUTH" in os.environ:
|
||||
worker_env["DATABASE_URL_AUTH"] = os.environ["DATABASE_URL_AUTH"]
|
||||
|
||||
_worker_process = subprocess.Popen(
|
||||
[
|
||||
python_executable,
|
||||
@ -769,11 +779,13 @@ async def lifespan(app: FastAPI):
|
||||
ensure_face_excluded_column(inspector)
|
||||
ensure_role_permissions_table(inspector)
|
||||
|
||||
# Note: Auth database schema and tables are managed by the frontend
|
||||
# Only check/update if the database exists (don't create it)
|
||||
# Setup auth database tables for both frontends (viewer and admin)
|
||||
if auth_engine is not None:
|
||||
try:
|
||||
ensure_auth_user_is_active_column()
|
||||
# Import and call worker's setup function to create all auth tables
|
||||
from backend.worker import setup_auth_database_tables
|
||||
setup_auth_database_tables()
|
||||
except Exception as auth_exc:
|
||||
# Auth database might not exist yet - that's okay, frontend will handle it
|
||||
print(f"ℹ️ Auth database not available: {auth_exc}")
|
||||
|
||||
@ -4,19 +4,206 @@ from __future__ import annotations
|
||||
|
||||
import signal
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import NoReturn
|
||||
|
||||
import uuid
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables from .env file before importing anything that needs them
|
||||
# Path calculation: backend/worker.py -> backend/ -> punimtag/ -> .env
|
||||
env_path = Path(__file__).parent.parent / ".env"
|
||||
env_path = env_path.resolve() # Make absolute path
|
||||
if env_path.exists():
|
||||
load_dotenv(dotenv_path=env_path, override=True)
|
||||
|
||||
from rq import Worker
|
||||
from redis import Redis
|
||||
|
||||
from backend.services.tasks import import_photos_task, process_faces_task
|
||||
from backend.db.session import auth_engine
|
||||
from sqlalchemy import text
|
||||
|
||||
# Redis connection for RQ
|
||||
redis_conn = Redis(host="localhost", port=6379, db=0, decode_responses=False)
|
||||
|
||||
|
||||
def setup_auth_database_tables() -> None:
|
||||
"""Create all necessary tables in the auth database for both frontends."""
|
||||
if auth_engine is None:
|
||||
print("[Worker] ⚠️ Auth database not configured (DATABASE_URL_AUTH not set), skipping table creation")
|
||||
return
|
||||
|
||||
try:
|
||||
print("[Worker] 🗃️ Setting up auth database tables...")
|
||||
|
||||
with auth_engine.connect() as conn:
|
||||
# Create users table
|
||||
conn.execute(text("""
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id SERIAL PRIMARY KEY,
|
||||
email VARCHAR(255) UNIQUE NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
password_hash VARCHAR(255) NOT NULL,
|
||||
is_admin BOOLEAN DEFAULT FALSE,
|
||||
has_write_access BOOLEAN DEFAULT FALSE,
|
||||
email_verified BOOLEAN DEFAULT FALSE,
|
||||
email_confirmation_token VARCHAR(255) UNIQUE,
|
||||
email_confirmation_token_expiry TIMESTAMP,
|
||||
password_reset_token VARCHAR(255) UNIQUE,
|
||||
password_reset_token_expiry TIMESTAMP,
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
"""))
|
||||
|
||||
# Add missing columns if table already exists
|
||||
for col_def in [
|
||||
"ALTER TABLE users ADD COLUMN IF NOT EXISTS is_admin BOOLEAN DEFAULT FALSE",
|
||||
"ALTER TABLE users ADD COLUMN IF NOT EXISTS has_write_access BOOLEAN DEFAULT FALSE",
|
||||
"ALTER TABLE users ADD COLUMN IF NOT EXISTS email_verified BOOLEAN DEFAULT FALSE",
|
||||
"ALTER TABLE users ADD COLUMN IF NOT EXISTS email_confirmation_token VARCHAR(255)",
|
||||
"ALTER TABLE users ADD COLUMN IF NOT EXISTS email_confirmation_token_expiry TIMESTAMP",
|
||||
"ALTER TABLE users ADD COLUMN IF NOT EXISTS password_reset_token VARCHAR(255)",
|
||||
"ALTER TABLE users ADD COLUMN IF NOT EXISTS password_reset_token_expiry TIMESTAMP",
|
||||
"ALTER TABLE users ADD COLUMN IF NOT EXISTS is_active BOOLEAN DEFAULT TRUE",
|
||||
]:
|
||||
try:
|
||||
conn.execute(text(col_def))
|
||||
except Exception:
|
||||
pass # Column might already exist or error is expected
|
||||
|
||||
# Create unique indexes for nullable columns
|
||||
conn.execute(text("""
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS users_email_confirmation_token_key
|
||||
ON users(email_confirmation_token)
|
||||
WHERE email_confirmation_token IS NOT NULL;
|
||||
"""))
|
||||
conn.execute(text("""
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS users_password_reset_token_key
|
||||
ON users(password_reset_token)
|
||||
WHERE password_reset_token IS NOT NULL;
|
||||
"""))
|
||||
|
||||
# Create pending_identifications table
|
||||
conn.execute(text("""
|
||||
CREATE TABLE IF NOT EXISTS pending_identifications (
|
||||
id SERIAL PRIMARY KEY,
|
||||
face_id INTEGER NOT NULL,
|
||||
user_id INTEGER NOT NULL,
|
||||
first_name VARCHAR(255) NOT NULL,
|
||||
last_name VARCHAR(255) NOT NULL,
|
||||
middle_name VARCHAR(255),
|
||||
maiden_name VARCHAR(255),
|
||||
date_of_birth DATE,
|
||||
status VARCHAR(50) DEFAULT 'pending',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
);
|
||||
"""))
|
||||
|
||||
# Create indexes for pending_identifications
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS idx_pending_identifications_face_id ON pending_identifications(face_id);"))
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS idx_pending_identifications_user_id ON pending_identifications(user_id);"))
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS idx_pending_identifications_status ON pending_identifications(status);"))
|
||||
|
||||
# Create pending_photos table
|
||||
conn.execute(text("""
|
||||
CREATE TABLE IF NOT EXISTS pending_photos (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL,
|
||||
filename VARCHAR(255) NOT NULL,
|
||||
original_filename VARCHAR(255) NOT NULL,
|
||||
file_path VARCHAR(512) NOT NULL,
|
||||
file_size INTEGER NOT NULL,
|
||||
mime_type VARCHAR(100) NOT NULL,
|
||||
status VARCHAR(50) DEFAULT 'pending',
|
||||
submitted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
reviewed_at TIMESTAMP,
|
||||
reviewed_by INTEGER,
|
||||
rejection_reason TEXT,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
);
|
||||
"""))
|
||||
|
||||
# Create indexes for pending_photos
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS idx_pending_photos_user_id ON pending_photos(user_id);"))
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS idx_pending_photos_status ON pending_photos(status);"))
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS idx_pending_photos_submitted_at ON pending_photos(submitted_at);"))
|
||||
|
||||
# Create inappropriate_photo_reports table
|
||||
conn.execute(text("""
|
||||
CREATE TABLE IF NOT EXISTS inappropriate_photo_reports (
|
||||
id SERIAL PRIMARY KEY,
|
||||
photo_id INTEGER NOT NULL,
|
||||
user_id INTEGER NOT NULL,
|
||||
status VARCHAR(50) DEFAULT 'pending',
|
||||
reported_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
reviewed_at TIMESTAMP,
|
||||
reviewed_by INTEGER,
|
||||
review_notes TEXT,
|
||||
report_comment TEXT,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
CONSTRAINT uq_photo_user_report UNIQUE (photo_id, user_id)
|
||||
);
|
||||
"""))
|
||||
|
||||
# Create indexes for inappropriate_photo_reports
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS idx_inappropriate_photo_reports_photo_id ON inappropriate_photo_reports(photo_id);"))
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS idx_inappropriate_photo_reports_user_id ON inappropriate_photo_reports(user_id);"))
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS idx_inappropriate_photo_reports_status ON inappropriate_photo_reports(status);"))
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS idx_inappropriate_photo_reports_reported_at ON inappropriate_photo_reports(reported_at);"))
|
||||
|
||||
# Create pending_linkages table
|
||||
conn.execute(text("""
|
||||
CREATE TABLE IF NOT EXISTS pending_linkages (
|
||||
id SERIAL PRIMARY KEY,
|
||||
photo_id INTEGER NOT NULL,
|
||||
tag_id INTEGER,
|
||||
tag_name VARCHAR(255),
|
||||
user_id INTEGER NOT NULL,
|
||||
status VARCHAR(50) DEFAULT 'pending',
|
||||
notes TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
);
|
||||
"""))
|
||||
|
||||
# Create indexes for pending_linkages
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS idx_pending_linkages_photo_id ON pending_linkages(photo_id);"))
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS idx_pending_linkages_tag_id ON pending_linkages(tag_id);"))
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS idx_pending_linkages_user_id ON pending_linkages(user_id);"))
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS idx_pending_linkages_status ON pending_linkages(status);"))
|
||||
|
||||
# Create photo_favorites table
|
||||
conn.execute(text("""
|
||||
CREATE TABLE IF NOT EXISTS photo_favorites (
|
||||
id SERIAL PRIMARY KEY,
|
||||
photo_id INTEGER NOT NULL,
|
||||
user_id INTEGER NOT NULL,
|
||||
favorited_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
CONSTRAINT uq_photo_user_favorite UNIQUE (photo_id, user_id)
|
||||
);
|
||||
"""))
|
||||
|
||||
# Create indexes for photo_favorites
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS idx_photo_favorites_photo_id ON photo_favorites(photo_id);"))
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS idx_photo_favorites_user_id ON photo_favorites(user_id);"))
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS idx_photo_favorites_favorited_at ON photo_favorites(favorited_at);"))
|
||||
|
||||
conn.commit()
|
||||
|
||||
print("[Worker] ✅ Auth database tables created/verified successfully")
|
||||
except Exception as e:
|
||||
print(f"[Worker] ⚠️ Failed to create auth database tables: {e}")
|
||||
print("[Worker] Tables may already exist or database may not be accessible")
|
||||
# Don't exit - worker can still function without auth tables
|
||||
|
||||
|
||||
def main() -> NoReturn:
|
||||
"""Worker entrypoint - starts RQ worker to process background jobs."""
|
||||
def _handle_sigterm(_signum, _frame):
|
||||
@ -31,6 +218,9 @@ def main() -> NoReturn:
|
||||
print(f"[Worker] Starting worker: {worker_name}")
|
||||
print(f"[Worker] Listening on queue: default")
|
||||
|
||||
# Setup auth database tables for both frontends
|
||||
setup_auth_database_tables()
|
||||
|
||||
# Check if Redis is accessible
|
||||
try:
|
||||
redis_conn.ping()
|
||||
|
||||
@ -13,3 +13,4 @@ else
|
||||
echo "⚠️ Failed to drop auth database (it may not exist)"
|
||||
fi
|
||||
|
||||
|
||||
|
||||
@ -50,3 +50,4 @@ if __name__ == "__main__":
|
||||
success = fix_admin_password()
|
||||
sys.exit(0 if success else 1)
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user