feat: Add Manage Photos page and inactivity timeout hook
This commit introduces a new Manage Photos page in the frontend, allowing users to manage their photos effectively. The Layout component has been updated to include navigation to the new page. Additionally, a custom hook for handling user inactivity timeouts has been implemented, enhancing security by logging users out after a specified period of inactivity. The user management functionality has also been improved with new sorting options and validation for frontend permissions. Documentation has been updated to reflect these changes.
This commit is contained in:
+1
-1
@@ -28,7 +28,7 @@ security = HTTPBearer()
|
||||
# Placeholder secrets - replace with env vars in production
|
||||
SECRET_KEY = "dev-secret-key-change-in-production"
|
||||
ALGORITHM = "HS256"
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES = 30
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES = 360
|
||||
REFRESH_TOKEN_EXPIRE_DAYS = 7
|
||||
|
||||
# Single user mode placeholder
|
||||
|
||||
+92
-64
@@ -31,6 +31,84 @@ def get_auth_db_optional() -> Session | None:
|
||||
return None
|
||||
|
||||
|
||||
def create_auth_user_if_missing(
|
||||
email: str,
|
||||
full_name: str,
|
||||
password_hash: str,
|
||||
is_admin: bool,
|
||||
) -> None:
|
||||
"""Create matching auth user if one does not already exist."""
|
||||
if not email:
|
||||
return
|
||||
|
||||
auth_db = get_auth_db_optional()
|
||||
if auth_db is None:
|
||||
return
|
||||
|
||||
try:
|
||||
check_result = auth_db.execute(
|
||||
text(
|
||||
"""
|
||||
SELECT id FROM users
|
||||
WHERE email = :email
|
||||
"""
|
||||
),
|
||||
{"email": email},
|
||||
)
|
||||
|
||||
existing_auth = check_result.first()
|
||||
if existing_auth:
|
||||
return
|
||||
|
||||
dialect = auth_db.bind.dialect.name if auth_db.bind else "postgresql"
|
||||
supports_returning = dialect == "postgresql"
|
||||
has_write_access = is_admin
|
||||
|
||||
if supports_returning:
|
||||
auth_db.execute(
|
||||
text(
|
||||
"""
|
||||
INSERT INTO users (email, name, password_hash, is_admin, has_write_access)
|
||||
VALUES (:email, :name, :password_hash, :is_admin, :has_write_access)
|
||||
"""
|
||||
),
|
||||
{
|
||||
"email": email,
|
||||
"name": full_name,
|
||||
"password_hash": password_hash,
|
||||
"is_admin": is_admin,
|
||||
"has_write_access": has_write_access,
|
||||
},
|
||||
)
|
||||
auth_db.commit()
|
||||
else:
|
||||
auth_db.execute(
|
||||
text(
|
||||
"""
|
||||
INSERT INTO users (email, name, password_hash, is_admin, has_write_access)
|
||||
VALUES (:email, :name, :password_hash, :is_admin, :has_write_access)
|
||||
"""
|
||||
),
|
||||
{
|
||||
"email": email,
|
||||
"name": full_name,
|
||||
"password_hash": password_hash,
|
||||
"is_admin": is_admin,
|
||||
"has_write_access": has_write_access,
|
||||
},
|
||||
)
|
||||
auth_db.commit()
|
||||
except Exception as e: # pragma: no cover - logging helper
|
||||
auth_db.rollback()
|
||||
import traceback
|
||||
|
||||
print(
|
||||
f"Warning: Failed to create auth user: {str(e)}\n{traceback.format_exc()}"
|
||||
)
|
||||
finally:
|
||||
auth_db.close()
|
||||
|
||||
|
||||
def get_current_admin_user(
|
||||
current_user: Annotated[dict, Depends(get_current_user)],
|
||||
db: Session = Depends(get_db),
|
||||
@@ -151,71 +229,13 @@ def create_user(
|
||||
db.commit()
|
||||
db.refresh(user)
|
||||
|
||||
# If frontend permission is requested, create user in auth database
|
||||
if request.give_frontend_permission:
|
||||
auth_db = get_auth_db_optional()
|
||||
if auth_db is None:
|
||||
# Auth database not configured - this is okay, just continue
|
||||
# The backend user was created successfully
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
# Check if user with same email already exists in auth db
|
||||
check_result = auth_db.execute(text("""
|
||||
SELECT id FROM users
|
||||
WHERE email = :email
|
||||
"""), {"email": request.email})
|
||||
|
||||
existing_auth = check_result.first()
|
||||
if existing_auth:
|
||||
# User already exists in auth db, skip creation
|
||||
# This is not an error - user might have been created separately
|
||||
pass
|
||||
else:
|
||||
# Insert new user in auth database
|
||||
# Check database dialect for RETURNING support
|
||||
dialect = auth_db.bind.dialect.name if auth_db.bind else 'postgresql'
|
||||
supports_returning = dialect == 'postgresql'
|
||||
|
||||
# Set has_write_access based on admin status
|
||||
# Admins get write access by default, regular users don't
|
||||
has_write_access = request.is_admin
|
||||
|
||||
# Use the same password hash
|
||||
if supports_returning:
|
||||
auth_db.execute(text("""
|
||||
INSERT INTO users (email, name, password_hash, is_admin, has_write_access)
|
||||
VALUES (:email, :name, :password_hash, :is_admin, :has_write_access)
|
||||
"""), {
|
||||
"email": request.email,
|
||||
"name": request.full_name,
|
||||
"password_hash": password_hash,
|
||||
"is_admin": request.is_admin,
|
||||
"has_write_access": has_write_access,
|
||||
})
|
||||
auth_db.commit()
|
||||
else:
|
||||
# SQLite - insert then select
|
||||
auth_db.execute(text("""
|
||||
INSERT INTO users (email, name, password_hash, is_admin, has_write_access)
|
||||
VALUES (:email, :name, :password_hash, :is_admin, :has_write_access)
|
||||
"""), {
|
||||
"email": request.email,
|
||||
"name": request.full_name,
|
||||
"password_hash": password_hash,
|
||||
"is_admin": request.is_admin,
|
||||
"has_write_access": has_write_access,
|
||||
})
|
||||
auth_db.commit()
|
||||
except Exception as e:
|
||||
# If auth user creation fails, rollback and log but don't fail the whole request
|
||||
# The backend user was already created successfully
|
||||
auth_db.rollback()
|
||||
# In production, you might want to log this to a proper logging system
|
||||
import traceback
|
||||
print(f"Warning: Failed to create auth user: {str(e)}\n{traceback.format_exc()}")
|
||||
finally:
|
||||
auth_db.close()
|
||||
create_auth_user_if_missing(
|
||||
email=request.email,
|
||||
full_name=request.full_name,
|
||||
password_hash=password_hash,
|
||||
is_admin=request.is_admin,
|
||||
)
|
||||
|
||||
return UserResponse.model_validate(user)
|
||||
|
||||
@@ -286,6 +306,14 @@ def update_user(
|
||||
db.add(user)
|
||||
db.commit()
|
||||
db.refresh(user)
|
||||
|
||||
if request.give_frontend_permission:
|
||||
create_auth_user_if_missing(
|
||||
email=user.email,
|
||||
full_name=user.full_name or user.username,
|
||||
password_hash=user.password_hash,
|
||||
is_admin=user.is_admin,
|
||||
)
|
||||
|
||||
return UserResponse.model_validate(user)
|
||||
|
||||
|
||||
@@ -48,6 +48,10 @@ class UserUpdateRequest(BaseModel):
|
||||
full_name: str = Field(..., min_length=1, max_length=200, description="Full name (required)")
|
||||
is_active: Optional[bool] = None
|
||||
is_admin: Optional[bool] = None
|
||||
give_frontend_permission: Optional[bool] = Field(
|
||||
None,
|
||||
description="Create user in auth database for frontend access if True",
|
||||
)
|
||||
|
||||
|
||||
class UsersListResponse(BaseModel):
|
||||
|
||||
Reference in New Issue
Block a user