h
This commit is contained in:
parent
abe8ed2e6e
commit
848350684f
|
@ -10,40 +10,35 @@ from .server import Application
|
|||
|
||||
|
||||
SRC = Path(__file__).resolve().parent
|
||||
APP = Application(Path("~/.config/barkshark/social/server.yaml").expanduser())
|
||||
|
||||
|
||||
@click.group("cli")
|
||||
@click.option("--config", "-c", type = Path)
|
||||
def cli(config: Path) -> None:
|
||||
@click.pass_context
|
||||
def cli(ctx: click.Context, config: Path) -> None:
|
||||
ctx.obj = Application(config)
|
||||
|
||||
if platform.system() == "Windows":
|
||||
TRANS.print("general", "windows")
|
||||
|
||||
if config:
|
||||
APP.state.config.path = config.expanduser().resolve()
|
||||
|
||||
APP.state.config.load()
|
||||
|
||||
|
||||
@cli.command("setup")
|
||||
def cli_setup() -> None:
|
||||
config = APP.state.config
|
||||
config.save()
|
||||
|
||||
if config.sqlite_path.exists():
|
||||
@click.pass_context
|
||||
def cli_setup(ctx: click.Context) -> None:
|
||||
if ctx.obj.config.sqlite_path.exists():
|
||||
click.confirm(TRANS.fetch("setup", "prompt-database"), abort = True)
|
||||
config.sqlite_path.unlink()
|
||||
ctx.obj.config.sqlite_path.unlink()
|
||||
|
||||
current = aputils.HttpDate.new_utc()
|
||||
|
||||
with APP.state.database.session(True) as s:
|
||||
with ctx.obj.database.session(True) as s:
|
||||
s.create_tables()
|
||||
|
||||
s.insert("instance", {
|
||||
"id": -99,
|
||||
"domain": config.host,
|
||||
"web_domain": config.web_host,
|
||||
"shared_inbox": f"https://{config.web_host}/inbox",
|
||||
"domain": ctx.obj.config.host,
|
||||
"web_domain": ctx.obj.config.web_host,
|
||||
"shared_inbox": f"https://{ctx.obj.config.web_host}/inbox",
|
||||
"name": "Barkshark Social",
|
||||
"description": "Lightweight ActivityPub server",
|
||||
"software": "barksharksocial",
|
||||
|
@ -51,20 +46,20 @@ def cli_setup() -> None:
|
|||
"updated": current
|
||||
})
|
||||
|
||||
click.echo(TRANS.fetch("setup", "create-key", username = config.host))
|
||||
instance_signer = aputils.Signer.new(f"https://{config.web_host}/actor")
|
||||
click.echo(TRANS.fetch("setup", "create-key", username = ctx.obj.config.host))
|
||||
instance_signer = aputils.Signer.new(f"https://{ctx.obj.config.web_host}/actor")
|
||||
|
||||
click.echo(TRANS.fetch("setup", "create-key", username = "relay"))
|
||||
relay_signer = aputils.Signer.new(f"https://{config.web_host}/relay")
|
||||
relay_signer = aputils.Signer.new(f"https://{ctx.obj.config.web_host}/relay")
|
||||
|
||||
s.insert("user", {
|
||||
"id": -99,
|
||||
"username": config.host,
|
||||
"domain": config.host,
|
||||
"display_name": config.host,
|
||||
"actor": f"https://{config.web_host}/actor",
|
||||
"inbox": f"https://{config.web_host}/inbox",
|
||||
"page_url": f"https://{config.web_host}/about",
|
||||
"username": ctx.obj.config.host,
|
||||
"domain": ctx.obj.config.host,
|
||||
"display_name": ctx.obj.config.host,
|
||||
"actor": f"https://{ctx.obj.config.web_host}/actor",
|
||||
"inbox": f"https://{ctx.obj.config.web_host}/inbox",
|
||||
"page_url": f"https://{ctx.obj.config.web_host}/about",
|
||||
"permission": 0,
|
||||
"locked": False,
|
||||
"private_key": instance_signer.export(),
|
||||
|
@ -76,11 +71,11 @@ def cli_setup() -> None:
|
|||
s.insert("user", {
|
||||
"id": -98,
|
||||
"username": "relay",
|
||||
"domain": config.host,
|
||||
"domain": ctx.obj.config.host,
|
||||
"display_name": "Relay",
|
||||
"actor": f"https://{config.web_host}/relay",
|
||||
"inbox": f"https://{config.web_host}/relay",
|
||||
"page_url": f"https://{config.web_host}/about#relay",
|
||||
"actor": f"https://{ctx.obj.config.web_host}/relay",
|
||||
"inbox": f"https://{ctx.obj.config.web_host}/relay",
|
||||
"page_url": f"https://{ctx.obj.config.web_host}/about#relay",
|
||||
"permission": 0,
|
||||
"locked": False,
|
||||
"private_key": relay_signer.export(),
|
||||
|
@ -95,10 +90,11 @@ def cli_setup() -> None:
|
|||
@cli.command("run")
|
||||
@click.option("--language", "-l", default = "en")
|
||||
@click.option("--dev", "-d", is_flag = True)
|
||||
def cli_run(language: str, dev: bool = False) -> None:
|
||||
APP.state.dev = dev
|
||||
APP.state.trans.default = language
|
||||
APP.run()
|
||||
@click.pass_context
|
||||
def cli_run(ctx: click.Context, language: str, dev: bool = False) -> None:
|
||||
ctx.obj.dev = dev
|
||||
ctx.obj.trans.default = language
|
||||
ctx.obj.run()
|
||||
|
||||
|
||||
@cli.group("user")
|
||||
|
@ -111,16 +107,16 @@ def cli_user() -> None:
|
|||
@click.argument("email")
|
||||
@click.option("--permissions", "-p", type = PermissionLevel.parse, default = PermissionLevel.USER)
|
||||
@click.option("--activate", "-a", is_flag = True)
|
||||
@click.pass_context
|
||||
def cli_user_create(
|
||||
ctx: click.Context,
|
||||
username: str,
|
||||
email: str,
|
||||
permissions: PermissionLevel,
|
||||
activate: bool) -> None:
|
||||
|
||||
config = APP.state.config
|
||||
|
||||
with APP.state.database.session(True) as s:
|
||||
if (user := s.get_user(username, config.host)) is not None:
|
||||
with ctx.obj.database.session(True) as s:
|
||||
if (user := s.get_user(username, ctx.obj.config.host)) is not None:
|
||||
TRANS.print("error", "user-exists", handle = user.handle)
|
||||
return
|
||||
|
||||
|
@ -148,8 +144,9 @@ def cli_user_create(
|
|||
|
||||
@cli_user.command("reset-password")
|
||||
@click.argument("username")
|
||||
def cli_user_reset_pass(username: str) -> None:
|
||||
with APP.state.database.session(True) as s:
|
||||
@click.pass_context
|
||||
def cli_user_reset_pass(ctx: click.Context, username: str) -> None:
|
||||
with ctx.obj.database.session(True) as s:
|
||||
if (user := s.get_user(username, None)) is None:
|
||||
click.echo("User does not exist")
|
||||
return
|
||||
|
|
|
@ -83,9 +83,12 @@ class Config(dict[str, dict[str, Any]]):
|
|||
dev: Property[bool] = Property("Advanced", "dev", False)
|
||||
|
||||
|
||||
def __init__(self, path: Path | str) -> None:
|
||||
def __init__(self, path: Path | str | None = None) -> None:
|
||||
dict.__init__(self)
|
||||
|
||||
if path is None:
|
||||
path = Path("~/.config/barkshark/social/server.yaml")
|
||||
|
||||
if isinstance(path, str):
|
||||
path = Path(path)
|
||||
|
||||
|
|
|
@ -96,10 +96,12 @@ class Connection(bsql.Connection):
|
|||
return Instance.from_possible_row(cur.one())
|
||||
|
||||
|
||||
def get_user(self, username: str, domain: str | int | None) -> User | None:
|
||||
def get_user(self, username: str, domain: str | None = None) -> User | None:
|
||||
if not domain:
|
||||
domain = self.config.host
|
||||
|
||||
print(username, domain)
|
||||
|
||||
with self.select("user", username = username, domain = domain) as cur:
|
||||
return User.from_possible_row(cur.one())
|
||||
|
||||
|
@ -362,6 +364,11 @@ class Connection(bsql.Connection):
|
|||
return Cookie.from_row(row)
|
||||
|
||||
|
||||
def del_cookie(self, token: str) -> None:
|
||||
with self.delete("cookie", code = token):
|
||||
pass
|
||||
|
||||
|
||||
def get_count(row: bsql.Row | None) -> int:
|
||||
if not row:
|
||||
return 0
|
||||
|
|
|
@ -103,7 +103,6 @@ SCHEMA = Tables(
|
|||
Column("id", "serial"),
|
||||
Column("code", "text", nullable = False, unique = True),
|
||||
Column("userid", "integer", nullable = False, foreign_key = ("user", "id")),
|
||||
Column("authorization_code", "text", unique = True),
|
||||
Column("user_agent", "text"),
|
||||
Column("last_access", "timestamp"),
|
||||
Column("created", "timestamp")
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
-extends "base.haml"
|
||||
-block content
|
||||
#error.section
|
||||
<h1>HTTP Error {{error.status_code}}</h1>
|
||||
{{error.detail}}
|
||||
<h1>HTTP Error {{error.status.value}}</h1>
|
||||
{{error.message}}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from importlib.resources import files as PackageFiles
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
||||
def get_resource(path: str) -> Path:
|
||||
|
@ -9,3 +10,31 @@ def get_resource(path: str) -> Path:
|
|||
def read_resource_text(path: str) -> str:
|
||||
with get_resource(path).open("r", encoding = "utf-8") as fd:
|
||||
return fd.read()
|
||||
|
||||
|
||||
class DictProperty:
|
||||
"Provide attribute access to a dict key."
|
||||
|
||||
|
||||
def __init__(self, key: str):
|
||||
self.key = key
|
||||
|
||||
|
||||
def __get__(self, obj: dict[str, Any] | None, objtype: type[Any] | None = None) -> Any:
|
||||
if obj is None:
|
||||
return self
|
||||
|
||||
try:
|
||||
return obj[self.key]
|
||||
|
||||
except KeyError:
|
||||
objname = object.__class__.__name__
|
||||
raise AttributeError(f"'{objname}' has no attribute '{self.key}'") from None
|
||||
|
||||
|
||||
def __set__(self, obj: dict[str, Any], value: Any) -> None:
|
||||
obj[self.key] = value
|
||||
|
||||
|
||||
def __delete__(self, obj: dict[str, Any]) -> None:
|
||||
del obj[self.key]
|
||||
|
|
|
@ -47,7 +47,7 @@ async def handle_login_post(request: Request) -> Response:
|
|||
raise HttpError(400, "Invalid field type")
|
||||
|
||||
with state.database.session(False) as s:
|
||||
if (user := s.get_user(username, None)) is None:
|
||||
if (user := s.get_user(username.lower())) is None:
|
||||
raise HttpError(400, "User does not exist")
|
||||
|
||||
try:
|
||||
|
@ -74,6 +74,25 @@ async def handle_login_post(request: Request) -> Response:
|
|||
return response
|
||||
|
||||
|
||||
@router.get("BarksharkSocial", "/logout")
|
||||
async def handle_logout_get(request: Request) -> Response:
|
||||
cname = request.app.state.config.cookie_name
|
||||
response = Response.new_redirect("/")
|
||||
|
||||
try:
|
||||
cookie = request.cookies[cname]
|
||||
|
||||
with request.app.state.database.session(True) as s:
|
||||
s.del_cookie(cookie.value)
|
||||
|
||||
response.delete_cookie(cookie)
|
||||
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
return response
|
||||
|
||||
|
||||
@router.get("BarksharkSocial", "/register")
|
||||
async def handle_register_get(request: Request) -> Response:
|
||||
return TemplateResponse("page/login.haml")
|
||||
|
|
|
@ -63,7 +63,7 @@ async def handle_wk_nodeinfo(request: Request) -> Response:
|
|||
return Response.new_json(200, data)
|
||||
|
||||
|
||||
@router.get("BarksharkSocial", "/nodeinfo/{version:float}.json")
|
||||
@router.get("BarksharkSocial", "/nodeinfo/{version:float}.json", "/nodeinfo/{version:float}")
|
||||
async def handle_nodeinfo(request: Request) -> Response:
|
||||
ni_version = request.params["version"]
|
||||
config = request.app.state.config
|
||||
|
|
|
@ -28,7 +28,7 @@ from basgi import (
|
|||
|
||||
|
||||
class Application(App[Request, RequestState, AppState]):
|
||||
def __init__(self, cfg_path: Path | str, language: str = "en") -> None:
|
||||
def __init__(self, cfg_path: Path | str | None = None, language: str = "en") -> None:
|
||||
App.__init__(
|
||||
self,
|
||||
"BarksharkSocial",
|
||||
|
@ -114,7 +114,7 @@ class Application(App[Request, RequestState, AppState]):
|
|||
if "json" in request.headers.get("Accept", ""):
|
||||
return Response.new_json(exc.status, {"error": str(exc)})
|
||||
|
||||
return TemplateResponse("error.haml", {"error": str(exc)}, exc.status)
|
||||
return TemplateResponse("error.haml", {"error": exc}, exc.status)
|
||||
|
||||
|
||||
def handle_context(self, env: Template, context: dict[str, Any]) -> dict[str, Any]:
|
||||
|
|
|
@ -13,9 +13,11 @@ from .translations import Translations
|
|||
|
||||
|
||||
class AppState(StateProxy):
|
||||
def setup(self, cfg_path: Path | str, language: str = "en") -> None:
|
||||
def setup(self, cfg_path: Path | str | None = None, language: str = "en") -> None:
|
||||
self["trans"] = Translations(get_resource("frontend/translations"), language)
|
||||
self["config"] = Config(cfg_path)
|
||||
self["config"].load()
|
||||
|
||||
self["hasher"] = PasswordHasher(
|
||||
time_cost = 10,
|
||||
memory_cost = 1024 * 100,
|
||||
|
|
7
dev.py
7
dev.py
|
@ -130,8 +130,13 @@ def cli_update_files() -> None:
|
|||
|
||||
|
||||
@cli.command("exec", context_settings = {"allow_extra_args": True})
|
||||
@option("--watch", "-w", is_flag = True)
|
||||
@pass_context
|
||||
def cli_run(ctx: Context) -> None:
|
||||
def cli_run(ctx: Context, watch: bool = False) -> None:
|
||||
if watch:
|
||||
handle_run_watcher("-m", "barkshark_social", *ctx.args)
|
||||
return
|
||||
|
||||
run_python("-m", "barkshark_social", *ctx.args)
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue