172 lines
5.2 KiB
Python
Raw Normal View History

2025-11-09 23:29:07 +01:00
import argparse
import uvicorn
2025-11-10 15:46:40 +01:00
import logging
from contextlib import asynccontextmanager
2025-11-14 01:59:01 +01:00
from fastapi import FastAPI, Request, HTTPException, status
2025-11-09 23:29:07 +01:00
from fastapi.staticfiles import StaticFiles
2025-11-14 01:59:01 +01:00
from fastapi.responses import HTMLResponse, JSONResponse, Response
2025-11-09 23:29:07 +01:00
from tortoise.contrib.fastapi import register_tortoise
from .settings import settings
2025-11-13 23:22:05 +01:00
from .routers import (
auth,
users,
folders,
files,
shares,
search,
admin,
starred,
billing,
admin_billing,
)
2025-11-09 23:29:07 +01:00
from . import webdav
2025-11-10 15:46:40 +01:00
from .schemas import ErrorResponse
2025-11-13 23:22:05 +01:00
from .middleware.usage_tracking import UsageTrackingMiddleware
2025-11-10 01:58:41 +01:00
2025-11-13 23:22:05 +01:00
logging.basicConfig(
level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
2025-11-10 01:58:41 +01:00
logger = logging.getLogger(__name__)
2025-11-09 23:29:07 +01:00
2025-11-13 23:22:05 +01:00
2025-11-10 15:46:40 +01:00
@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
2025-11-11 12:47:26 +01:00
from .mail import email_service
2025-11-13 23:22:05 +01:00
2025-11-10 15:46:40 +01:00
start_scheduler()
logger.info("Billing scheduler started")
2025-11-11 12:47:26 +01:00
await email_service.start()
logger.info("Email service started")
2025-11-10 15:46:40 +01:00
pricing_count = await PricingConfig.all().count()
if pricing_count == 0:
from decimal import Decimal
2025-11-13 23:22:05 +01:00
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",
)
2025-11-10 15:46:40 +01:00
logger.info("Default pricing configuration initialized")
yield
from .billing.scheduler import stop_scheduler
2025-11-13 23:22:05 +01:00
2025-11-10 15:46:40 +01:00
stop_scheduler()
logger.info("Billing scheduler stopped")
2025-11-11 12:47:26 +01:00
await email_service.stop()
logger.info("Email service stopped")
2025-11-10 15:46:40 +01:00
print("Shutting down...")
2025-11-13 23:22:05 +01:00
2025-11-09 23:29:07 +01:00
app = FastAPI(
2025-11-13 12:05:05 +01:00
title="MyWebdav Cloud Storage",
description="A commercial cloud storage web application",
2025-11-09 23:29:07 +01:00
version="0.1.0",
2025-11-13 23:22:05 +01:00
lifespan=lifespan,
2025-11-09 23:29:07 +01:00
)
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)
2025-11-10 15:46:40 +01:00
app.include_router(admin.router)
app.include_router(starred.router)
app.include_router(billing.router)
app.include_router(admin_billing.router)
2025-11-09 23:29:07 +01:00
app.include_router(webdav.router)
2025-11-10 15:46:40 +01:00
app.add_middleware(UsageTrackingMiddleware)
2025-11-09 23:29:07 +01:00
app.mount("/static", StaticFiles(directory="static"), name="static")
register_tortoise(
app,
db_url=settings.DATABASE_URL,
2025-11-13 23:22:05 +01:00
modules={"models": ["mywebdav.models"], "billing": ["mywebdav.billing.models"]},
2025-11-09 23:29:07 +01:00
generate_schemas=True,
add_exception_handlers=True,
)
2025-11-13 23:22:05 +01:00
2025-11-10 01:58:41 +01:00
@app.exception_handler(HTTPException)
async def http_exception_handler(request: Request, exc: HTTPException):
2025-11-13 23:22:05 +01:00
logger.error(
f"HTTPException: {exc.status_code} - {exc.detail} for URL: {request.url}"
)
2025-11-14 01:59:01 +01:00
headers = exc.headers
# For WebDAV authentication challenges, we must return the headers
# from the exception and an empty body. A JSON body will confuse WebDAV clients.
if request.url.path.startswith("/webdav") and exc.status_code == status.HTTP_401_UNAUTHORIZED:
return Response(status_code=exc.status_code, headers=headers)
# For other WebDAV errors, it's better to return a text body than JSON
if request.url.path.startswith("/webdav"):
return Response(content=exc.detail, status_code=exc.status_code, headers=headers)
2025-11-10 01:58:41 +01:00
return JSONResponse(
status_code=exc.status_code,
2025-11-13 23:05:26 +01:00
content=ErrorResponse(code=exc.status_code, message=exc.detail).model_dump(),
2025-11-14 01:59:01 +01:00
headers=headers,
2025-11-10 01:58:41 +01:00
)
2025-11-09 23:29:07 +01:00
2025-11-13 23:22:05 +01:00
@app.get("/", response_class=HTMLResponse) # Change response_class to HTMLResponse
2025-11-09 23:29:07 +01:00
async def read_root():
with open("static/index.html", "r") as f:
return f.read()
2025-11-13 23:22:05 +01:00
2025-11-09 23:29:07 +01:00
def main():
2025-11-13 20:50:03 +01:00
parser = argparse.ArgumentParser(description="Run the MyWebdav application.")
2025-11-13 23:22:05 +01:00
parser.add_argument(
"--host", type=str, default="0.0.0.0", help="Host address to bind to"
)
2025-11-09 23:29:07 +01:00
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)
2025-11-13 23:22:05 +01:00
2025-11-09 23:29:07 +01:00
if __name__ == "__main__":
main()