# app/main.py import sys import os import json from pathlib import Path from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from app import ssl_heidi from app.routers import ( address, contact, proposal, dewar, shipment, puck, spreadsheet, logistics, auth, sample, ) from app.database import Base, engine, SessionLocal, load_sample_data # Utility function to fetch metadata from pyproject.toml def get_project_metadata(): from pathlib import Path import tomllib # Dynamically resolve the project root folder correctly. # Assume that `pyproject.toml` is located in the heidi-v2 root folder root_dir = Path(__file__).resolve().parent.parent.parent pyproject_path = root_dir / "heidi-v2" / "pyproject.toml" print(f"Looking for pyproject.toml at: {pyproject_path}") # Debugging output if not pyproject_path.exists(): raise FileNotFoundError(f"{pyproject_path} not found") with open(pyproject_path, "rb") as f: pyproject = tomllib.load(f) name = pyproject["project"]["name"] version = pyproject["project"]["version"] return name, version # Get project metadata from pyproject.toml project_name, project_version = get_project_metadata() app = FastAPI( title=project_name, # Syncs with project `name` description="Backend for next-gen sample management system", version=project_version, # Syncs with project `version` ) # Determine environment and configuration file path environment = os.getenv("ENVIRONMENT", "dev") config_file = Path(__file__).resolve().parent.parent / f"config_{environment}.json" # Load configuration with open(config_file) as f: config = json.load(f) cert_path = config["ssl_cert_path"] key_path = config["ssl_key_path"] # Generate SSL Key and Certificate if not exist (only for development) if environment == "dev": Path("ssl").mkdir(parents=True, exist_ok=True) if not Path(cert_path).exists() or not Path(key_path).exists(): ssl_heidi.generate_self_signed_cert(cert_path, key_path) # Apply CORS middleware app.add_middleware( CORSMiddleware, allow_origins=["*"], # Enable CORS for all origins allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) @app.on_event("startup") def on_startup(): # Drop and recreate database schema Base.metadata.drop_all(bind=engine) Base.metadata.create_all(bind=engine) db = SessionLocal() try: load_sample_data(db) finally: db.close() # Include routers with correct configuration app.include_router(auth.router, prefix="/auth", tags=["auth"]) app.include_router(contact.router, prefix="/contacts", tags=["contacts"]) app.include_router(address.router, prefix="/addresses", tags=["addresses"]) app.include_router(proposal.router, prefix="/proposals", tags=["proposals"]) app.include_router(dewar.router, prefix="/dewars", tags=["dewars"]) app.include_router(shipment.router, prefix="/shipments", tags=["shipments"]) app.include_router(puck.router, prefix="/pucks", tags=["pucks"]) app.include_router(spreadsheet.router, tags=["spreadsheet"]) app.include_router(logistics.router, prefix="/logistics", tags=["logistics"]) app.include_router(sample.router, prefix="/samples", tags=["samples"]) if __name__ == "__main__": import uvicorn # Check if the user has passed "generate-openapi" as the first CLI argument if len(sys.argv) > 1 and sys.argv[1] == "generate-openapi": # Generate and save the OpenAPI schema openapi_schema = app.openapi() with open("openapi.json", "w") as openapi_file: json.dump(openapi_schema, openapi_file, indent=2) print("OpenAPI schema has been generated and saved to 'openapi.json'.") else: # Default behavior: Run the FastAPI server import os from multiprocessing import Process from time import sleep # Get environment from an environment variable environment = os.getenv("ENVIRONMENT", "dev") is_ci = os.getenv("CI", "false").lower() == "true" # Check if running in CI port = int(os.getenv("PORT", 8000)) # Default to 8000 if PORT is not set # Paths for SSL certificates if is_ci: cert_path = "ssl/cert.pem" key_path = "ssl/key.pem" host = "127.0.0.1" print("Running in CI mode with self-signed certificates...") # Ensure SSL certificate and key are generated if is_ci or environment == "dev": Path("ssl").mkdir(exist_ok=True) if not Path(cert_path).exists() or not Path(key_path).exists(): print( f"Generating self-signed SSL certificate" f"at {cert_path} and {key_path}" ) ssl_heidi.generate_self_signed_cert(cert_path, key_path) elif environment == "test": cert_path = "ssl/mx-aare-test.psi.ch.pem" key_path = "ssl/mx-aare-test.psi.ch.key" host = "0.0.0.0" print("Using test SSL certificates...") else: cert_path = "ssl/cert.pem" key_path = "ssl/key.pem" host = "127.0.0.1" print("Using development SSL certificates...") def run_server(): uvicorn.run( app, host=host, port=port, log_level="debug", ssl_keyfile=key_path, ssl_certfile=cert_path, ) if is_ci: # In CI, start server in a subprocess and exit after a short delay server_process = Process(target=run_server) server_process.start() sleep(5) # Wait for 5 seconds to ensure the server starts without errors server_process.terminate() # Terminate the server process server_process.join() # Clean up the process print("CI: Server started successfully and exited.") else: # Normal behavior for running the FastAPI server run_server()