137 lines
3.3 KiB
Python
137 lines
3.3 KiB
Python
from __future__ import annotations
|
|
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
|
|
from aputils import Signer, register_validator
|
|
from argon2 import PasswordHasher
|
|
from bsql import Database
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
from . import __version__, middleware as mw
|
|
from .config import Config
|
|
from .database import Connection
|
|
from .misc import get_resource, read_resource_text
|
|
from .state import AppState, RequestState
|
|
from .translations import Translations
|
|
|
|
from basgi import (
|
|
Application as App,
|
|
HttpError,
|
|
Request,
|
|
Response,
|
|
Template,
|
|
TemplateResponse
|
|
)
|
|
|
|
|
|
class Application(App[Request, RequestState, AppState]):
|
|
def __init__(self, cfg_path: Path | str | None = None, language: str = "en") -> None:
|
|
App.__init__(
|
|
self,
|
|
"BarksharkSocial",
|
|
template_search = [get_resource("frontend")],
|
|
template_context = self.handle_context,
|
|
request_state_class = RequestState,
|
|
app_state_class = AppState
|
|
)
|
|
|
|
self.state.setup(cfg_path, language)
|
|
self.error_handlers[HttpError] = self.handle_http_exception
|
|
self.add_static("/static", get_resource("frontend/static"))
|
|
self.client.useragent = f"BarksharkSocial/{__version__} (https://{self.config.web_host})"
|
|
|
|
self.on_request.connect(mw.FrontendAuthMiddleware)
|
|
self.on_request.connect(mw.ActivitypubAuthMiddleware)
|
|
|
|
|
|
@property
|
|
def config(self) -> Config:
|
|
return self.state.config
|
|
|
|
|
|
@property
|
|
def database(self) -> Database[Connection]:
|
|
return self.state.database
|
|
|
|
|
|
@property
|
|
def hasher(self) -> PasswordHasher:
|
|
return self.state.hasher
|
|
|
|
|
|
@property
|
|
def trans(self) -> Translations:
|
|
return self.state.trans
|
|
|
|
|
|
def run(self) -> None:
|
|
cmd = [
|
|
sys.executable, "-m", "uvicorn",
|
|
"barkshark_social._run_app:app",
|
|
"--host", self.config.addr,
|
|
"--port", str(self.config.port),
|
|
"--lifespan", "on",
|
|
"--no-server-header",
|
|
"--no-access-log"
|
|
]
|
|
|
|
if self.state.config.dev:
|
|
cmd.extend(["--reload", "--reload-dir", str(Path(__file__).parent)])
|
|
|
|
else:
|
|
cmd.extend(["--workers", str(self.config.workers)])
|
|
|
|
env = os.environ.copy()
|
|
env["SOCIAL_CONFIG_PATH"] = str(self.state.config.path)
|
|
subprocess.run(cmd, env = env)
|
|
|
|
|
|
def run_granian(self) -> None:
|
|
cmd = [
|
|
sys.executable, "-m", "granian",
|
|
"barkshark_social._run_app:app",
|
|
"--interface", "asgi",
|
|
"--host", self.config.addr,
|
|
"--port", str(self.config.port),
|
|
"--workers", str(self.config.workers),
|
|
"--loop", "uvloop",
|
|
"--respawn-failed-workers",
|
|
"--opt"
|
|
]
|
|
|
|
if self.state.config.dev:
|
|
cmd.append("--reload")
|
|
|
|
env = os.environ.copy()
|
|
env["SOCIAL_CONFIG_PATH"] = str(self.state.config.path)
|
|
subprocess.run(cmd, env = env)
|
|
|
|
|
|
def handle_http_exception(self, request: Request, exc: HttpError) -> Response:
|
|
if "json" in request.headers.get("Accept", ""):
|
|
return Response.new_json(exc.status, {"error": str(exc)})
|
|
|
|
return TemplateResponse("error.haml", {"error": exc}, exc.status)
|
|
|
|
|
|
def handle_context(self, env: Template, context: dict[str, Any]) -> dict[str, Any]:
|
|
context.update({
|
|
"app": self,
|
|
"read_resource_text": read_resource_text
|
|
})
|
|
|
|
return context
|
|
|
|
|
|
@register_validator(Request)
|
|
async def handle_validate_request(signer: Signer, request: Request) -> bool:
|
|
return signer.validate_signature(
|
|
request.method,
|
|
request.path,
|
|
{key: value for key, value in request.headers.items()},
|
|
await request.body()
|
|
)
|