import argparse import uvicorn import logging from contextlib import asynccontextmanager from fastapi import FastAPI, Request, status, HTTPException from fastapi.staticfiles import StaticFiles from fastapi.responses import HTMLResponse, JSONResponse from tortoise.contrib.fastapi import register_tortoise from .settings import settings from .routers import auth, users, folders, files, shares, search, admin, starred, billing, admin_billing from . import webdav from .schemas import ErrorResponse logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') logger = logging.getLogger(__name__) @asynccontextmanager async def lifespan(app: FastAPI): logger.info("Starting up...") logger.info("Database connected.") from .billing.scheduler import start_scheduler from .billing.models import PricingConfig from .mail import email_service start_scheduler() logger.info("Billing scheduler started") await email_service.start() logger.info("Email service started") pricing_count = await PricingConfig.all().count() if pricing_count == 0: from decimal import Decimal await PricingConfig.create(config_key='storage_per_gb_month', config_value=Decimal('0.0045'), description='Storage cost per GB per month', unit='per_gb_month') await PricingConfig.create(config_key='bandwidth_egress_per_gb', config_value=Decimal('0.009'), description='Bandwidth egress cost per GB', unit='per_gb') await PricingConfig.create(config_key='bandwidth_ingress_per_gb', config_value=Decimal('0.0'), description='Bandwidth ingress cost per GB (free)', unit='per_gb') await PricingConfig.create(config_key='free_tier_storage_gb', config_value=Decimal('15'), description='Free tier storage in GB', unit='gb') await PricingConfig.create(config_key='free_tier_bandwidth_gb', config_value=Decimal('15'), description='Free tier bandwidth in GB per month', unit='gb') await PricingConfig.create(config_key='tax_rate_default', config_value=Decimal('0.0'), description='Default tax rate (0 = no tax)', unit='percentage') logger.info("Default pricing configuration initialized") yield from .billing.scheduler import stop_scheduler stop_scheduler() logger.info("Billing scheduler stopped") await email_service.stop() logger.info("Email service stopped") print("Shutting down...") app = FastAPI( title="MyWebdav Cloud Storage", description="A commercial cloud storage web application", version="0.1.0", lifespan=lifespan ) app.include_router(auth.router) app.include_router(users.router) app.include_router(folders.router) app.include_router(files.router) app.include_router(shares.router) app.include_router(search.router) app.include_router(admin.router) app.include_router(starred.router) app.include_router(billing.router) app.include_router(admin_billing.router) app.include_router(webdav.router) from .middleware.usage_tracking import UsageTrackingMiddleware app.add_middleware(UsageTrackingMiddleware) app.mount("/static", StaticFiles(directory="static"), name="static") register_tortoise( app, db_url=settings.DATABASE_URL, modules={ "models": ["mywebdav.models"], "billing": ["mywebdav.billing.models"] }, generate_schemas=True, add_exception_handlers=True, ) @app.exception_handler(HTTPException) async def http_exception_handler(request: Request, exc: HTTPException): logger.error(f"HTTPException: {exc.status_code} - {exc.detail} for URL: {request.url}") return JSONResponse( status_code=exc.status_code, content=ErrorResponse(code=exc.status_code, message=exc.detail).dict(), ) @app.get("/", response_class=HTMLResponse) # Change response_class to HTMLResponse async def read_root(): with open("static/index.html", "r") as f: return f.read() def main(): parser = argparse.ArgumentParser(description="Run the MyWebdav application.") parser.add_argument("--host", type=str, default="0.0.0.0", help="Host address to bind to") parser.add_argument("--port", type=int, default=8000, help="Port to listen on") args = parser.parse_args() uvicorn.run(app, host=args.host, port=args.port) if __name__ == "__main__": main()