diff --git a/barkshark_social/cli.py b/barkshark_social/cli.py index 0d54c4d..7b7cc36 100644 --- a/barkshark_social/cli.py +++ b/barkshark_social/cli.py @@ -1,7 +1,8 @@ -import aputils import click import platform +from aputils import Signer +from blib import HttpDate from pathlib import Path from . import TRANS @@ -29,7 +30,7 @@ def cli_setup(ctx: click.Context) -> None: click.confirm(TRANS.fetch("setup", "prompt-database"), abort = True) ctx.obj.config.sqlite_path.unlink() - current = aputils.HttpDate.new_utc() + current = HttpDate.new_utc() with ctx.obj.database.session(True) as s: s.create_tables() @@ -47,10 +48,10 @@ def cli_setup(ctx: click.Context) -> None: }) 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") + instance_signer = 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://{ctx.obj.config.web_host}/relay") + relay_signer = Signer.new(f"https://{ctx.obj.config.web_host}/relay") s.insert("user", { "id": -99, diff --git a/barkshark_social/database/__init__.py b/barkshark_social/database/__init__.py index 290d6fb..af5caa8 100644 --- a/barkshark_social/database/__init__.py +++ b/barkshark_social/database/__init__.py @@ -1,10 +1,12 @@ -import aputils import sqlite3 +from aputils import MessageDate +from blib import HttpDate, Enum + from .connection import Connection from .schema import SCHEMA -sqlite3.register_adapter(aputils.Enum, lambda v: v.value) -sqlite3.register_adapter(aputils.HttpDate, lambda v: v.timestamp()) -sqlite3.register_adapter(aputils.MessageDate, lambda v: v.timestamp()) +sqlite3.register_adapter(Enum, lambda v: v.value) +sqlite3.register_adapter(HttpDate, lambda v: v.timestamp()) +sqlite3.register_adapter(MessageDate, lambda v: v.timestamp()) diff --git a/barkshark_social/database/config.py b/barkshark_social/database/config.py index 066d77a..4d209e5 100644 --- a/barkshark_social/database/config.py +++ b/barkshark_social/database/config.py @@ -1,5 +1,5 @@ -from aputils import JsonBase -from bsql import Row, boolean +from blib import JsonBase, convert_to_boolean +from bsql import Row from collections.abc import Callable, Iterator, Sequence from typing import Any, Generic, Self, TypeVar @@ -9,7 +9,7 @@ from .. import TRANS T = TypeVar("T") CONVERTERS: dict[type, tuple[Callable[[str], Any], Callable[[Any], str]]] = { - bool: (boolean, str), + bool: (convert_to_boolean, str), int: (int, str), dict: (JsonBase.parse, lambda value: JsonBase(value).to_json()), str: (str, str) diff --git a/barkshark_social/database/connection.py b/barkshark_social/database/connection.py index 6a2c0aa..8290d87 100644 --- a/barkshark_social/database/connection.py +++ b/barkshark_social/database/connection.py @@ -1,11 +1,12 @@ from __future__ import annotations -import aputils -import bsql import secrets +from aputils import Message, MessageDate, Nodeinfo, ObjectType, Signer, Webfinger from argon2 import PasswordHasher from basgi import Application as App +from blib import HttpDate +from bsql import Connection as BsqlConnection, Row from collections.abc import Iterator from typing import TYPE_CHECKING, Any, TypeVar @@ -23,7 +24,7 @@ if TYPE_CHECKING: T = TypeVar("T", bound = ObjectMixin) -class Connection(bsql.Connection): +class Connection(BsqlConnection): @property def app(self) -> Application: return App.get("BarksharkSocial") # type: ignore[return-value] @@ -100,8 +101,6 @@ class Connection(bsql.Connection): 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()) @@ -156,7 +155,7 @@ class Connection(bsql.Connection): description: str | None = None, short_description: str | None = None) -> Instance: - date = aputils.HttpDate.new_utc() + date = HttpDate.new_utc() data = Instance.new( domain = domain, web_domain = web_domain, @@ -171,9 +170,9 @@ class Connection(bsql.Connection): def put_instance_data(self, - nodeinfo: aputils.Nodeinfo, - webfinger: aputils.Webfinger, - actor: aputils.Message) -> Instance: + nodeinfo: Nodeinfo, + webfinger: Webfinger, + actor: Message) -> Instance: if self.get_instance(actor.domain) is not None: raise KeyError("Instance already exists") @@ -193,7 +192,7 @@ class Connection(bsql.Connection): except KeyError: description = None - current = aputils.HttpDate.new_utc() + current = HttpDate.new_utc() instance = Instance( id = 0 if has_instance else 1, domain = webfinger.domain, @@ -229,8 +228,8 @@ class Connection(bsql.Connection): raise ValueError(TRANS.fetch("error", "empty-row")) TRANS.print("setup", "create-key", username = username) - signer = aputils.Signer.new(f"https://{instance.web_domain}/user/{username}#main-key") - date = aputils.HttpDate.new_utc() + signer = Signer.new(f"https://{instance.web_domain}/user/{username}#main-key") + date = HttpDate.new_utc() data = User( id = 0 if has_user else 1, username = username, @@ -260,7 +259,7 @@ class Connection(bsql.Connection): return self.insert_row(data) - def put_remote_user(self, actor: aputils.Message) -> User: + def put_remote_user(self, actor: Message) -> User: with self.run("get-at-least-1-user") as cur: has_user = cur.one() is not None @@ -274,7 +273,7 @@ class Connection(bsql.Connection): date = actor.published except AttributeError: - date = aputils.MessageDate.now() + date = MessageDate.now() data = User( id = 0 if has_user else 1, @@ -288,7 +287,7 @@ class Connection(bsql.Connection): password = None, permission = PermissionLevel.REMOTE, locked = actor.manually_approves_followers, - is_bot = actor.type != aputils.ObjectType.PERSON, + is_bot = actor.type != ObjectType.PERSON, private_key = None, public_key = actor.pubkey, bio = actor.get("summary", ""), @@ -348,7 +347,7 @@ class Connection(bsql.Connection): def put_cookie(self, user: User, user_agent: str | None) -> Cookie: - date = aputils.HttpDate.new_utc() + date = HttpDate.new_utc() params = { "code": secrets.token_hex(16), "userid": user.id, @@ -369,7 +368,7 @@ class Connection(bsql.Connection): pass -def get_count(row: bsql.Row | None) -> int: +def get_count(row: Row | None) -> int: if not row: return 0 diff --git a/barkshark_social/database/objects.py b/barkshark_social/database/objects.py index 939b4ba..02f0d45 100644 --- a/barkshark_social/database/objects.py +++ b/barkshark_social/database/objects.py @@ -1,9 +1,10 @@ from __future__ import annotations -import aputils -import bsql import typing +from aputils import MessageDate, Signer +from blib import HttpDate +from bsql import Row from dataclasses import dataclass from datetime import datetime from typing import Any, Self @@ -29,7 +30,7 @@ class ObjectMixin: @classmethod - def from_row(cls: type[Self], row: bsql.Row | dict[str, Any]) -> Self: + def from_row(cls: type[Self], row: Row | dict[str, Any]) -> Self: data = [] for key in cls.__dataclass_fields__: @@ -40,7 +41,7 @@ class ObjectMixin: value = row[key] if isinstance(value, datetime): - value = aputils.MessageDate.parse(value) + value = MessageDate.parse(value) data.append(value) @@ -51,7 +52,7 @@ class ObjectMixin: @classmethod - def from_possible_row(cls: type[Self], row: bsql.Row | dict[str, Any] | None) -> Self | None: + def from_possible_row(cls: type[Self], row: Row | dict[str, Any] | None) -> Self | None: if row is None: return None @@ -108,7 +109,7 @@ class Follow(ObjectMixin): target.get_instance(connection).id, followid, accept, - aputils.HttpDate.new_utc() + HttpDate.new_utc() ) @@ -177,11 +178,11 @@ class User(ObjectMixin): @property - def signer(self) -> aputils.Signer: + def signer(self) -> Signer: if self.private_key: - return aputils.Signer(self.private_key, self.actor + "#main-key") + return Signer(self.private_key, self.actor + "#main-key") - return aputils.Signer(self.public_key, self.actor + "#main-key") + return Signer(self.public_key, self.actor + "#main-key") # auth middleware stuff diff --git a/barkshark_social/enums.py b/barkshark_social/enums.py index 249ab3a..3b05a94 100644 --- a/barkshark_social/enums.py +++ b/barkshark_social/enums.py @@ -1,4 +1,4 @@ -from aputils import IntEnum, StrEnum +from blib import IntEnum, StrEnum class PermissionLevel(IntEnum): diff --git a/barkshark_social/frontend/page/user.haml b/barkshark_social/frontend/page/user.haml index 06e3e8d..db65496 100644 --- a/barkshark_social/frontend/page/user.haml +++ b/barkshark_social/frontend/page/user.haml @@ -9,7 +9,7 @@ #info .avatar %div - -if user.domain != state.config.host + -if user.domain != app.config.host %p.handle %a(href="{{user.page_url}}" target="_new") << @{{user.handle}} diff --git a/barkshark_social/middleware.py b/barkshark_social/middleware.py index 4ddbf37..38319ac 100644 --- a/barkshark_social/middleware.py +++ b/barkshark_social/middleware.py @@ -4,8 +4,9 @@ import httpx import logging import traceback -from aputils import JsonBase, Message, Signature, SignatureFailureError +from aputils import Message, Signature, SignatureFailureError from basgi import Client, HttpError, Request +from blib import JsonBase from typing import TYPE_CHECKING from .database.connection import Connection diff --git a/barkshark_social/misc.py b/barkshark_social/misc.py index fe6a30a..a5b315e 100644 --- a/barkshark_social/misc.py +++ b/barkshark_social/misc.py @@ -1,6 +1,5 @@ from importlib.resources import files as PackageFiles from pathlib import Path -from typing import Any def get_resource(path: str) -> Path: @@ -10,31 +9,3 @@ 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] diff --git a/barkshark_social/processors.py b/barkshark_social/processors.py index 1d9d8fd..827480f 100644 --- a/barkshark_social/processors.py +++ b/barkshark_social/processors.py @@ -76,6 +76,10 @@ async def handle_undo(request: Request, message: Message, user: User) -> None: else: obj = Message.parse(message.object) + if obj.type not in ObjectType.Actor: + print(repr(obj.type)) + return + if obj.object_id != user.actor: logging.warning( "Follow object does not match actor: msg=%s, actor=%s", @@ -104,6 +108,8 @@ async def handle_undo(request: Request, message: Message, user: User) -> None: async def process_message(request: Request, message: Message, user: User) -> None: + print(f"process_message: {user.handle}, message:", message.to_json(4), flush = True) + if request.state.user.actor != message.actor_id: logging.warning( "Message actor and signature keyid do not match: msg=%s, actor=%s", @@ -120,7 +126,7 @@ async def process_message(request: Request, message: Message, user: User) -> Non logging.error(error.response.read()) except KeyError: - logging.debug("Unhandled message type: %s", message.type.name) + logging.debug("Unhandled message type: %s", message.type) except Exception: traceback.print_exc() diff --git a/barkshark_social/routes/activitypub.py b/barkshark_social/routes/activitypub.py index 7416619..5073adb 100644 --- a/barkshark_social/routes/activitypub.py +++ b/barkshark_social/routes/activitypub.py @@ -59,15 +59,19 @@ async def handle_instance_actor_get(request: Request) -> Response: async def handle_instance_actor_post(request: Request) -> Response: ensure_signed(request) - return Response(202, "UvU") + with request.app.state.database.session(False) as s: + if (user := s.get_user(request.app.state.config.host)) is None: + raise HttpError(404, "User not found") + + message = Message.parse(await request.body()) + asyncio.create_task(process_message(request, message, user)) + return Response(200, "uvu") @router.get("BarksharkSocial", "/relay") async def handle_relay_get(request: Request) -> Response: - config = request.app.state.config - with request.app.state.database.session(False) as s: - if (user := s.get_user("relay", config.host)) is None: + if (user := s.get_user("relay")) is None: raise HttpError(404, "User not found") data = Message.new_actor( @@ -87,7 +91,13 @@ async def handle_relay_get(request: Request) -> Response: async def handle_relay_post(request: Request) -> Response: ensure_signed(request) - return Response(202, "UvU") + with request.app.state.database.session(False) as s: + if (user := s.get_user("relay")) is None: + raise HttpError(404, "User not found") + + message = Message.parse(await request.body()) + asyncio.create_task(process_message(request, message, user)) + return Response(200, "uvu") @router.get("BarksharkSocial", "/user/{username}") diff --git a/barkshark_social/routes/frontend.py b/barkshark_social/routes/frontend.py index 8bd2ad7..6e54eec 100644 --- a/barkshark_social/routes/frontend.py +++ b/barkshark_social/routes/frontend.py @@ -1,6 +1,6 @@ -from aputils import HttpDate from argon2.exceptions import VerifyMismatchError from basgi import HttpError, FileResponse, Request, Response, TemplateResponse, router +from blib import HttpDate from datetime import timedelta from ..misc import get_resource diff --git a/barkshark_social/server.py b/barkshark_social/server.py index 0dcb215..f7d60a4 100644 --- a/barkshark_social/server.py +++ b/barkshark_social/server.py @@ -1,10 +1,10 @@ from __future__ import annotations -import aputils import os import subprocess import sys +from aputils import Signer, register_validator from argon2 import PasswordHasher from bsql import Database from pathlib import Path @@ -126,8 +126,8 @@ class Application(App[Request, RequestState, AppState]): return context -@aputils.register_validator(Request) -async def handle_validate_request(signer: aputils.Signer, request: Request) -> bool: +@register_validator(Request) +async def handle_validate_request(signer: Signer, request: Request) -> bool: return signer.validate_signature( request.method, request.path, diff --git a/dev.py b/dev.py index 04fc7b2..069905f 100755 --- a/dev.py +++ b/dev.py @@ -130,13 +130,8 @@ 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, watch: bool = False) -> None: - if watch: - handle_run_watcher("-m", "barkshark_social", *ctx.args) - return - run_python("-m", "barkshark_social", *ctx.args) diff --git a/pyproject.toml b/pyproject.toml index c40696a..443a5c2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,10 +39,12 @@ dependencies = [ "argon2-cffi == 23.1.0", "barkshark-sql == 0.1.3", "click == 8.1.7", - "itsdangerous == 2.1.2", "pymemcache == 4.0.0", "pyyaml == 6.0.1", - "uvicorn[standard] == 0.29.0" + "uvicorn[standard] == 0.29.0", + + "barkshark-asgi @ https://git.barkshark.xyz/barkshark/basgi/archive/main.tar.gz", + "barkshark-lib @ https://git.barkshark.xyz/barkshark/blib/archive/main.tar.gz" ] [project.urls]