First commit

This commit is contained in:
James Pattinson
2025-11-10 13:57:46 +00:00
parent cffb5e8b8e
commit 3751ee0076
31 changed files with 2356 additions and 0 deletions

View File

@@ -0,0 +1 @@
# Core package initialization

View File

@@ -0,0 +1,53 @@
from pydantic_settings import BaseSettings
from typing import List
import os
class Settings(BaseSettings):
# Application
APP_NAME: str = "Swansea Airport Stakeholders Alliance"
APP_VERSION: str = "1.0.0"
DEBUG: bool = True
ENVIRONMENT: str = "development"
# API
API_V1_PREFIX: str = "/api/v1"
SECRET_KEY: str
ALGORITHM: str = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES: int = 30
# Database
DATABASE_HOST: str
DATABASE_PORT: int = 3306
DATABASE_USER: str
DATABASE_PASSWORD: str
DATABASE_NAME: str
@property
def DATABASE_URL(self) -> str:
return f"mysql+pymysql://{self.DATABASE_USER}:{self.DATABASE_PASSWORD}@{self.DATABASE_HOST}:{self.DATABASE_PORT}/{self.DATABASE_NAME}"
# Square Payment
SQUARE_ACCESS_TOKEN: str
SQUARE_ENVIRONMENT: str = "sandbox"
SQUARE_LOCATION_ID: str
# Email
SMTP2GO_API_KEY: str
SMTP2GO_API_URL: str = "https://api.smtp2go.com/v3/email/send"
EMAIL_FROM: str
EMAIL_FROM_NAME: str
# CORS
BACKEND_CORS_ORIGINS: List[str] = ["http://localhost:3000", "http://localhost:8080"]
# File Storage
UPLOAD_DIR: str = "/app/uploads"
MAX_UPLOAD_SIZE: int = 10485760 # 10MB
class Config:
env_file = ".env"
case_sensitive = True
settings = Settings()

View File

@@ -0,0 +1,23 @@
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from .config import settings
engine = create_engine(
settings.DATABASE_URL,
pool_pre_ping=True,
pool_recycle=3600,
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
def get_db():
"""Dependency for getting database session"""
db = SessionLocal()
try:
yield db
finally:
db.close()

View File

@@ -0,0 +1,56 @@
from sqlalchemy.orm import Session
from ..models.models import MembershipTier, User, UserRole
from .security import get_password_hash
from datetime import datetime
def init_default_data(db: Session):
"""Initialize database with default data if empty"""
# Check if membership tiers exist
existing_tiers = db.query(MembershipTier).count()
if existing_tiers == 0:
print("Creating default membership tiers...")
default_tiers = [
MembershipTier(
name="Personal",
description="Basic membership for individual members",
annual_fee=5.00,
benefits="Access to member portal, meeting notifications, event participation",
is_active=True
),
MembershipTier(
name="Aircraft Owners",
description="Group membership for aircraft owners",
annual_fee=25.00,
benefits="All Personal benefits plus priority event registration, aircraft owner resources",
is_active=True
),
MembershipTier(
name="Corporate",
description="Corporate membership for businesses",
annual_fee=100.00,
benefits="All benefits plus corporate recognition, promotional opportunities, file access",
is_active=True
)
]
db.add_all(default_tiers)
db.commit()
print(f"✓ Created {len(default_tiers)} default membership tiers")
# Check if admin user exists
admin_exists = db.query(User).filter(User.email == "admin@swanseaairport.org").first()
if not admin_exists:
print("Creating default admin user...")
admin_user = User(
email="admin@swanseaairport.org",
hashed_password=get_password_hash("admin123"),
first_name="System",
last_name="Administrator",
role=UserRole.SUPER_ADMIN,
is_active=True
)
db.add(admin_user)
db.commit()
print("✓ Created default admin user (admin@swanseaairport.org / admin123)")
print(" ⚠️ Remember to change the admin password!")

View File

@@ -0,0 +1,44 @@
from datetime import datetime, timedelta
from typing import Optional, Union, Any
from jose import JWTError, jwt
from passlib.context import CryptContext
from .config import settings
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def create_access_token(
subject: Union[str, Any], expires_delta: Optional[timedelta] = None
) -> str:
"""Create JWT access token"""
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(
minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES
)
to_encode = {"exp": expire, "sub": str(subject)}
encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
return encoded_jwt
def verify_password(plain_password: str, hashed_password: str) -> bool:
"""Verify a password against a hash"""
return pwd_context.verify(plain_password, hashed_password)
def get_password_hash(password: str) -> str:
"""Hash a password"""
return pwd_context.hash(password)
def decode_token(token: str) -> Optional[str]:
"""Decode JWT token and return subject"""
try:
payload = jwt.decode(
token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM]
)
return payload.get("sub")
except JWTError:
return None