refactor: Introduce utility functions for exception handling and database operations; update auth and playlist services to use new exception methods
All checks were successful
Backend CI / test (push) Successful in 3m58s
All checks were successful
Backend CI / test (push) Successful in 3m58s
This commit is contained in:
140
app/utils/validation.py
Normal file
140
app/utils/validation.py
Normal file
@@ -0,0 +1,140 @@
|
||||
"""Common validation utility functions."""
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Any, Optional
|
||||
|
||||
# Password validation constants
|
||||
MIN_PASSWORD_LENGTH = 8
|
||||
|
||||
|
||||
def validate_email(email: str) -> bool:
|
||||
"""Validate email address format.
|
||||
|
||||
Args:
|
||||
email: Email address to validate
|
||||
|
||||
Returns:
|
||||
True if email format is valid, False otherwise
|
||||
"""
|
||||
pattern = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
|
||||
return bool(re.match(pattern, email))
|
||||
|
||||
|
||||
def validate_password_strength(password: str) -> tuple[bool, str | None]:
|
||||
"""Validate password meets security requirements.
|
||||
|
||||
Args:
|
||||
password: Password to validate
|
||||
|
||||
Returns:
|
||||
Tuple of (is_valid, error_message)
|
||||
"""
|
||||
if len(password) < MIN_PASSWORD_LENGTH:
|
||||
msg = f"Password must be at least {MIN_PASSWORD_LENGTH} characters long"
|
||||
return False, msg
|
||||
|
||||
if not re.search(r"[A-Z]", password):
|
||||
return False, "Password must contain at least one uppercase letter"
|
||||
|
||||
if not re.search(r"[a-z]", password):
|
||||
return False, "Password must contain at least one lowercase letter"
|
||||
|
||||
if not re.search(r"\d", password):
|
||||
return False, "Password must contain at least one number"
|
||||
|
||||
return True, None
|
||||
|
||||
|
||||
def validate_filename(
|
||||
filename: str, allowed_extensions: list[str] | None = None
|
||||
) -> bool:
|
||||
"""Validate filename format and extension.
|
||||
|
||||
Args:
|
||||
filename: Filename to validate
|
||||
allowed_extensions: List of allowed file extensions (with dots)
|
||||
|
||||
Returns:
|
||||
True if filename is valid, False otherwise
|
||||
"""
|
||||
if not filename or filename.startswith(".") or "/" in filename or "\\" in filename:
|
||||
return False
|
||||
|
||||
if allowed_extensions:
|
||||
file_path = Path(filename)
|
||||
return file_path.suffix.lower() in [ext.lower() for ext in allowed_extensions]
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def validate_audio_filename(filename: str) -> bool:
|
||||
"""Validate audio filename has allowed extension.
|
||||
|
||||
Args:
|
||||
filename: Audio filename to validate
|
||||
|
||||
Returns:
|
||||
True if filename has valid audio extension, False otherwise
|
||||
"""
|
||||
audio_extensions = [".mp3", ".wav", ".flac", ".ogg", ".m4a", ".aac", ".wma"]
|
||||
return validate_filename(filename, audio_extensions)
|
||||
|
||||
|
||||
def sanitize_filename(filename: str) -> str:
|
||||
"""Sanitize filename by removing/replacing invalid characters.
|
||||
|
||||
Args:
|
||||
filename: Filename to sanitize
|
||||
|
||||
Returns:
|
||||
Sanitized filename safe for filesystem
|
||||
"""
|
||||
# Remove or replace problematic characters
|
||||
sanitized = re.sub(r'[<>:"/\\|?*]', "_", filename)
|
||||
|
||||
# Remove leading/trailing whitespace and dots
|
||||
sanitized = sanitized.strip(" .")
|
||||
|
||||
# Ensure not empty
|
||||
if not sanitized:
|
||||
sanitized = "untitled"
|
||||
|
||||
return sanitized
|
||||
|
||||
|
||||
def validate_url(url: str) -> bool:
|
||||
"""Validate URL format.
|
||||
|
||||
Args:
|
||||
url: URL to validate
|
||||
|
||||
Returns:
|
||||
True if URL format is valid, False otherwise
|
||||
"""
|
||||
pattern = r"^https?://[^\s/$.?#].[^\s]*$"
|
||||
return bool(re.match(pattern, url))
|
||||
|
||||
|
||||
def validate_positive_integer(value: Any, field_name: str = "value") -> int:
|
||||
"""Validate and convert value to positive integer.
|
||||
|
||||
Args:
|
||||
value: Value to validate and convert
|
||||
field_name: Name of field for error messages
|
||||
|
||||
Returns:
|
||||
Validated positive integer
|
||||
|
||||
Raises:
|
||||
ValueError: If value is not a positive integer
|
||||
"""
|
||||
try:
|
||||
int_value = int(value)
|
||||
if int_value <= 0:
|
||||
msg = f"{field_name} must be a positive integer"
|
||||
raise ValueError(msg)
|
||||
return int_value
|
||||
except (TypeError, ValueError) as e:
|
||||
msg = f"{field_name} must be a positive integer"
|
||||
raise ValueError(msg) from e
|
||||
Reference in New Issue
Block a user