Spaces:
Running
Running
Commit
·
6ccc851
1
Parent(s):
38342f7
Addition of Middlewares
Browse files- FileStream/server/API/__init__.py +12 -3
- FileStream/server/APP/__init__.py +7 -11
- FileStream/server/APP/routes_app.py +3 -0
- FileStream/server/Authentication/__init__.py +0 -0
- FileStream/server/Middlewares/__init__.py +5 -0
- FileStream/server/Middlewares/app_token_middleware.py +12 -0
- FileStream/server/Middlewares/jwt_middleware.py +26 -0
- FileStream/server/Middlewares/logging_middleware.py +8 -0
- FileStream/server/Middlewares/rate_limit_middleware.py +8 -0
- FileStream/server/Middlewares/security_headers_middleware.py +14 -0
- FileStream/server/__init__.py +18 -2
- requirements.txt +2 -0
FileStream/server/API/__init__.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
import aiohttp_cors
|
| 2 |
from aiohttp import web
|
| 3 |
|
| 4 |
-
|
| 5 |
from .listings import (
|
| 6 |
list_all_files_db,
|
| 7 |
list_all_files,
|
|
@@ -13,17 +13,26 @@ from .listings import (
|
|
| 13 |
from .downloads import stream_handler
|
| 14 |
from .uploads import upload_file
|
| 15 |
|
|
|
|
|
|
|
| 16 |
async def handle_v2(request):
|
| 17 |
return web.Response(text="Hello from app api!")
|
| 18 |
|
| 19 |
-
|
| 20 |
-
|
|
|
|
| 21 |
cors = aiohttp_cors.setup(api, defaults={"*": aiohttp_cors.ResourceOptions(
|
| 22 |
allow_credentials=False,
|
| 23 |
expose_headers="*",
|
| 24 |
allow_headers="*",
|
| 25 |
allow_methods="*"
|
| 26 |
)})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
|
| 28 |
cors.add(api.router.add_get('/', handle_v2))
|
| 29 |
|
|
|
|
| 1 |
import aiohttp_cors
|
| 2 |
from aiohttp import web
|
| 3 |
|
| 4 |
+
#---------------------- Local Imports --------------------#
|
| 5 |
from .listings import (
|
| 6 |
list_all_files_db,
|
| 7 |
list_all_files,
|
|
|
|
| 13 |
from .downloads import stream_handler
|
| 14 |
from .uploads import upload_file
|
| 15 |
|
| 16 |
+
|
| 17 |
+
#-----------------------------Functions------------------------#
|
| 18 |
async def handle_v2(request):
|
| 19 |
return web.Response(text="Hello from app api!")
|
| 20 |
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
"""
|
| 24 |
cors = aiohttp_cors.setup(api, defaults={"*": aiohttp_cors.ResourceOptions(
|
| 25 |
allow_credentials=False,
|
| 26 |
expose_headers="*",
|
| 27 |
allow_headers="*",
|
| 28 |
allow_methods="*"
|
| 29 |
)})
|
| 30 |
+
"""
|
| 31 |
+
|
| 32 |
+
#---------------------------Routes ---------------------------#
|
| 33 |
+
# Web server setup with optimized CORS handling
|
| 34 |
+
api = web.Application()
|
| 35 |
+
|
| 36 |
|
| 37 |
cors.add(api.router.add_get('/', handle_v2))
|
| 38 |
|
FileStream/server/APP/__init__.py
CHANGED
|
@@ -2,24 +2,20 @@ import os
|
|
| 2 |
from aiohttp import web
|
| 3 |
from aiohttp.http_exceptions import BadStatusLine
|
| 4 |
|
| 5 |
-
|
| 6 |
from .render_template import render_page, render_upload
|
| 7 |
from .routes_app import stream_handler,upload_handler
|
| 8 |
from FileStream.Exceptions import FIleNotFound, InvalidHash
|
| 9 |
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
async def handle_v2(request):
|
| 14 |
return web.Response(text="Hello from app v2!")
|
| 15 |
|
| 16 |
-
sub_app = web.Application()
|
| 17 |
-
|
| 18 |
|
|
|
|
|
|
|
| 19 |
#static_dir = os.path.join("FileStream", "bot", "server", "template")
|
| 20 |
#sub_app.router.add_static('/static/', path=static_dir, name='static')
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
sub_app.router.add_get('/watch/{path}', stream_handler)
|
| 25 |
-
sub_app.router.add_get('/up', upload_handler)
|
|
|
|
| 2 |
from aiohttp import web
|
| 3 |
from aiohttp.http_exceptions import BadStatusLine
|
| 4 |
|
| 5 |
+
#-----------------------------Local Imports-----------------------------#
|
| 6 |
from .render_template import render_page, render_upload
|
| 7 |
from .routes_app import stream_handler,upload_handler
|
| 8 |
from FileStream.Exceptions import FIleNotFound, InvalidHash
|
| 9 |
|
| 10 |
+
#------------------------------Functions--------------------------------#
|
|
|
|
|
|
|
| 11 |
async def handle_v2(request):
|
| 12 |
return web.Response(text="Hello from app v2!")
|
| 13 |
|
|
|
|
|
|
|
| 14 |
|
| 15 |
+
#----------------------------Server Routes-----------------------------#
|
| 16 |
+
app = web.Application()
|
| 17 |
#static_dir = os.path.join("FileStream", "bot", "server", "template")
|
| 18 |
#sub_app.router.add_static('/static/', path=static_dir, name='static')
|
| 19 |
+
app.router.add_get('/', handle_v2)
|
| 20 |
+
app.router.add_get('/watch/{path}', stream_handler)
|
| 21 |
+
app.router.add_get('/up', upload_handler)
|
|
|
|
|
|
FileStream/server/APP/routes_app.py
CHANGED
|
@@ -2,10 +2,13 @@ import os
|
|
| 2 |
from aiohttp import web
|
| 3 |
from aiohttp.http_exceptions import BadStatusLine
|
| 4 |
|
|
|
|
| 5 |
from ..Functions.downloader import media_streamer
|
| 6 |
from .render_template import render_page, render_upload
|
| 7 |
from FileStream.Exceptions import FIleNotFound, InvalidHash
|
| 8 |
|
|
|
|
|
|
|
| 9 |
#@sub_app.get("/watch/{path}", allow_head=True)
|
| 10 |
async def stream_handler(request: web.Request):
|
| 11 |
try:
|
|
|
|
| 2 |
from aiohttp import web
|
| 3 |
from aiohttp.http_exceptions import BadStatusLine
|
| 4 |
|
| 5 |
+
#--------------------------Local Import -----------------------------#
|
| 6 |
from ..Functions.downloader import media_streamer
|
| 7 |
from .render_template import render_page, render_upload
|
| 8 |
from FileStream.Exceptions import FIleNotFound, InvalidHash
|
| 9 |
|
| 10 |
+
|
| 11 |
+
#------------------------------Functions------------------------------#
|
| 12 |
#@sub_app.get("/watch/{path}", allow_head=True)
|
| 13 |
async def stream_handler(request: web.Request):
|
| 14 |
try:
|
FileStream/server/Authentication/__init__.py
ADDED
|
File without changes
|
FileStream/server/Middlewares/__init__.py
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .app_token_middleware import app_token_middleware
|
| 2 |
+
from .jwt_middleware import jwt_middleware,create_jwt
|
| 3 |
+
from .logging_middleware import logging_middleware
|
| 4 |
+
from .rate_limit_middleware import rate_limit_middleware
|
| 5 |
+
from .security_headers_middleware import security_headers_middleware
|
FileStream/server/Middlewares/app_token_middleware.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from aiohttp import web
|
| 2 |
+
|
| 3 |
+
DEFAULT_APP_TOKEN = "supersecureapptoken"
|
| 4 |
+
|
| 5 |
+
@web.middleware
|
| 6 |
+
async def app_token_middleware(request, handler):
|
| 7 |
+
"""Middleware to enforce token authentication for /app."""
|
| 8 |
+
if request.path.startswith("/app"):
|
| 9 |
+
token = request.headers.get("App-Token")
|
| 10 |
+
if token != DEFAULT_APP_TOKEN:
|
| 11 |
+
raise web.HTTPUnauthorized(reason="Invalid or missing App-Token")
|
| 12 |
+
return await handler(request)
|
FileStream/server/Middlewares/jwt_middleware.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import jwt
|
| 2 |
+
from aiohttp import web
|
| 3 |
+
|
| 4 |
+
SECRET_KEY = "your_secret_key" # Use a strong, randomly generated secret key
|
| 5 |
+
|
| 6 |
+
def create_jwt(user_id: str):
|
| 7 |
+
"""Create a JWT token."""
|
| 8 |
+
payload = {"sub": user_id, "role": "api_user"}
|
| 9 |
+
return jwt.encode(payload, SECRET_KEY, algorithm="HS256")
|
| 10 |
+
|
| 11 |
+
@web.middleware
|
| 12 |
+
async def jwt_middleware(request, handler):
|
| 13 |
+
"""JWT Middleware for /api endpoints."""
|
| 14 |
+
if request.path.startswith("/api"):
|
| 15 |
+
token = request.headers.get("Authorization")
|
| 16 |
+
if not token or not token.startswith("Bearer "):
|
| 17 |
+
raise web.HTTPUnauthorized(reason="Authorization header missing or malformed")
|
| 18 |
+
token = token.split("Bearer ")[1]
|
| 19 |
+
try:
|
| 20 |
+
decoded = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
|
| 21 |
+
request["user"] = decoded # Attach user info to the request
|
| 22 |
+
except jwt.ExpiredSignatureError:
|
| 23 |
+
raise web.HTTPUnauthorized(reason="Token has expired")
|
| 24 |
+
except jwt.InvalidTokenError:
|
| 25 |
+
raise web.HTTPUnauthorized(reason="Invalid token")
|
| 26 |
+
return await handler(request)
|
FileStream/server/Middlewares/logging_middleware.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import logging
|
| 2 |
+
from aiohttp import web
|
| 3 |
+
|
| 4 |
+
@web.middleware
|
| 5 |
+
async def logging_middleware(request, handler):
|
| 6 |
+
"""Log each request."""
|
| 7 |
+
logging.info(f"Incoming request: {request.method} {request.path}")
|
| 8 |
+
return await handler(request)
|
FileStream/server/Middlewares/rate_limit_middleware.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from aiohttp_rate_limiter import RateLimiter
|
| 2 |
+
|
| 3 |
+
rate_limiter = RateLimiter(per_minute=60)
|
| 4 |
+
|
| 5 |
+
@rate_limiter.limit("60/minute")
|
| 6 |
+
async def rate_limit_middleware(request, handler):
|
| 7 |
+
"""Rate limiter middleware."""
|
| 8 |
+
return await handler(request)
|
FileStream/server/Middlewares/security_headers_middleware.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from aiohttp import web
|
| 2 |
+
|
| 3 |
+
@web.middleware
|
| 4 |
+
async def security_headers_middleware(request, handler):
|
| 5 |
+
"""Add security headers."""
|
| 6 |
+
response = await handler(request)
|
| 7 |
+
response.headers.update({
|
| 8 |
+
"Content-Security-Policy": "default-src 'self'",
|
| 9 |
+
"X-Content-Type-Options": "nosniff",
|
| 10 |
+
"X-Frame-Options": "DENY",
|
| 11 |
+
"Referrer-Policy": "no-referrer",
|
| 12 |
+
"Strict-Transport-Security": "max-age=31536000; includeSubDomains"
|
| 13 |
+
})
|
| 14 |
+
return response
|
FileStream/server/__init__.py
CHANGED
|
@@ -4,10 +4,15 @@ from aiohttp import web
|
|
| 4 |
|
| 5 |
#-----------------------Local Imports-------------------------#
|
| 6 |
from .API import api
|
| 7 |
-
from .APP import
|
|
|
|
| 8 |
from .routes_main import routes
|
| 9 |
from FileStream.bot import MULTI_CLIENTS, WORK_LOADS, ACTIVE_CLIENTS
|
| 10 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
|
| 12 |
# Set time to consider a client inactive (e.g., 10 minutes)
|
| 13 |
INACTIVITY_TIMEOUT = 180 # 3 minutes
|
|
@@ -32,9 +37,20 @@ async def root_route_handler(request):
|
|
| 32 |
|
| 33 |
def web_server():
|
| 34 |
web_app = web.Application(client_max_size=500)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
web_app.router.add_get('/', root_route_handler)
|
| 36 |
web_app.add_routes(routes)
|
| 37 |
-
web_app.add_subapp('/app',
|
| 38 |
web_app.add_subapp('/api', api)
|
|
|
|
| 39 |
# Return the app to be used with AppRunner
|
| 40 |
return web_app
|
|
|
|
| 4 |
|
| 5 |
#-----------------------Local Imports-------------------------#
|
| 6 |
from .API import api
|
| 7 |
+
from .APP import app
|
| 8 |
+
from .Authentication import auth
|
| 9 |
from .routes_main import routes
|
| 10 |
from FileStream.bot import MULTI_CLIENTS, WORK_LOADS, ACTIVE_CLIENTS
|
| 11 |
|
| 12 |
+
from .Middlewares.jwt_middleware import jwt_middleware
|
| 13 |
+
from .Middlewares.app_token_middleware import app_token_middleware
|
| 14 |
+
from .Middlewares.logging_middleware import logging_middleware
|
| 15 |
+
from .Middlewares.rate_limit_middleware import rate_limit_middleware
|
| 16 |
|
| 17 |
# Set time to consider a client inactive (e.g., 10 minutes)
|
| 18 |
INACTIVITY_TIMEOUT = 180 # 3 minutes
|
|
|
|
| 37 |
|
| 38 |
def web_server():
|
| 39 |
web_app = web.Application(client_max_size=500)
|
| 40 |
+
|
| 41 |
+
# Add middlewares
|
| 42 |
+
web_app.middleware.extend([
|
| 43 |
+
logging_middleware,
|
| 44 |
+
security_headers_middleware,
|
| 45 |
+
jwt_middleware,
|
| 46 |
+
app_token_middleware,
|
| 47 |
+
rate_limit_middleware
|
| 48 |
+
])
|
| 49 |
+
|
| 50 |
web_app.router.add_get('/', root_route_handler)
|
| 51 |
web_app.add_routes(routes)
|
| 52 |
+
web_app.add_subapp('/app', app)
|
| 53 |
web_app.add_subapp('/api', api)
|
| 54 |
+
web_app.add_subapp('/auth', auth)
|
| 55 |
# Return the app to be used with AppRunner
|
| 56 |
return web_app
|
requirements.txt
CHANGED
|
@@ -2,6 +2,8 @@ tmdbv3api==1.9.0
|
|
| 2 |
aiofiles==23.2.1
|
| 3 |
aiohttp==3.9.3
|
| 4 |
aiohttp_cors
|
|
|
|
|
|
|
| 5 |
aiosignal==1.3.1
|
| 6 |
aiosqlite==0.20.0
|
| 7 |
async-timeout==4.0.3
|
|
|
|
| 2 |
aiofiles==23.2.1
|
| 3 |
aiohttp==3.9.3
|
| 4 |
aiohttp_cors
|
| 5 |
+
aiohttp-rate-limiter==0.3.0
|
| 6 |
+
PyJWT==2.6.0
|
| 7 |
aiosignal==1.3.1
|
| 8 |
aiosqlite==0.20.0
|
| 9 |
async-timeout==4.0.3
|