from typing import Optional, Union
import uuid
from datetime import datetime
from sqlalchemy.orm import Session
from sqlalchemy.exc import SQLAlchemyError
# Assuming the models are in a file named `models.py` in the `app.db` directory
from app.db import models
class UserService:
def __init__(self):
pass
def save_user(self, db: Session, oidc_id: str, email: str, username: str) -> str:
"""
Saves or updates a user record based on their OIDC ID.
If a user with this OIDC ID exists, it returns their existing ID.
Otherwise, it creates a new user record.
Returns the user's ID.
"""
try:
# Check if a user with this OIDC ID already exists
existing_user = db.query(models.User).filter(models.User.oidc_id == oidc_id).first()
if existing_user:
# Update the user's information if needed
existing_user.email = email
existing_user.username = username
db.commit()
return existing_user.id
else:
# Create a new user record
new_user = models.User(
id=str(uuid.uuid4()), # Generate a unique ID for the user
oidc_id=oidc_id,
email=email,
username=username,
created_at=datetime.utcnow()
)
db.add(new_user)
db.commit()
db.refresh(new_user)
return new_user.id
except SQLAlchemyError as e:
db.rollback()
raise
def get_user_by_id(self, db: Session, user_id: str) -> Optional[models.User]:
"""
Retrieves a user record by their unique ID.
Returns the User object if found, otherwise None.
"""
try:
# Query the database for a user with the given ID
user = db.query(models.User).filter(models.User.id == user_id).first()
return user
except SQLAlchemyError as e:
# Log the error and return None in case of a database issue.
print(f"Database error while fetching user by ID: {e}")
return None
# --- Framework-dependent helper functions ---
# These functions are placeholders and would need to be integrated with your
# specific web framework (e.g., FastAPI, Flask, Django).
def login_required(f):
"""
A decorator to protect API endpoints and web pages.
It ensures a user is logged in and is a registered (non-anonymous) user.
If not, it redirects them to the login page.
This is a generic implementation. You would replace the logic inside
with code specific to your web framework's authentication system.
"""
async def wrapper(*args, **kwargs):
# Placeholder logic: Check for user in the request context
# For example, in FastAPI, you might use Depends(get_current_user)
# In Flask, you might use session or current_user
user_id = kwargs.get("user_id") # Assuming user_id is passed in via a dependency
if not user_id:
# Depending on the framework, this would return an Unauthorized error or a redirect
# For example: raise HTTPException(status_code=401, detail="Unauthorized")
pass
return await f(*args, **kwargs)
return wrapper
def get_current_user_id() -> Optional[str]:
"""
A helper function to get the current user's ID from the session.
This is a placeholder and needs to be implemented with your framework's
session/authentication management system.
"""
# Placeholder logic
# Example for FastAPI:
# from fastapi import Depends, Request
# from app.auth import get_current_user
#
# return get_current_user(request).id
# For now, we return None as a generic placeholder
return None