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-10 01:58:41 +01:00
from fastapi import FastAPI , Request , status , HTTPException
2025-11-09 23:29:07 +01:00
from fastapi . staticfiles import StaticFiles
2025-11-10 01:58:41 +01:00
from fastapi . responses import HTMLResponse , JSONResponse
2025-11-09 23:29:07 +01:00
from tortoise . contrib . fastapi import register_tortoise
from . settings import settings
2025-11-10 15:46:40 +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-10 01:58:41 +01:00
logging . basicConfig ( level = logging . INFO , format = ' %(asctime)s - %(name)s - %(levelname)s - %(message)s ' )
logger = logging . getLogger ( __name__ )
2025-11-09 23:29:07 +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
start_scheduler ( )
logger . info ( " Billing scheduler 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 " )
print ( " Shutting down... " )
2025-11-09 23:29:07 +01:00
app = FastAPI (
title = " RBox Cloud Storage " ,
description = " A self-hosted cloud storage web application " ,
version = " 0.1.0 " ,
2025-11-10 15:46:40 +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
from . middleware . usage_tracking import UsageTrackingMiddleware
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-10 15:46:40 +01:00
modules = {
" models " : [ " rbox.models " ] ,
" billing " : [ " rbox.billing.models " ]
} ,
2025-11-09 23:29:07 +01:00
generate_schemas = True ,
add_exception_handlers = True ,
)
2025-11-10 01:58:41 +01:00
@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 ( ) ,
)
2025-11-09 23:29:07 +01:00
@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 RBox 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 ( )