summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub/src')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/api/admin.py11
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/api/relay.py3
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/api/users.py4
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/api/webapp.py3
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/csam.py10
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/db/models.py17
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/tasks/cleanup.py3
7 files changed, 33 insertions, 18 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/api/admin.py b/packages/meshbay-hub/src/meshbay_hub/api/admin.py
index d8e13e9..b4b2f4f 100644
--- a/packages/meshbay-hub/src/meshbay_hub/api/admin.py
+++ b/packages/meshbay-hub/src/meshbay_hub/api/admin.py
@@ -317,7 +317,8 @@ async def admin_patch_user(
if body.role not in ("user", "moderator", "admin"):
raise HTTPException(status_code=422, detail="role must be user, moderator, or admin")
user.role = body.role
- log.info("User %s role changed to %s by %s", user.username, body.role, current_user.username)
+ log.info("User %s role changed to %s by %s",
+ user.username, body.role, current_user.username)
await create_notification(
db, user.id, "role_change",
f"Your role has been changed to {body.role}",
@@ -325,7 +326,9 @@ async def admin_patch_user(
if body.status is not None:
if body.status not in ("active", "suspended", "revoked"):
- raise HTTPException(status_code=422, detail="status must be active, suspended, or revoked")
+ raise HTTPException(
+ status_code=422,
+ detail="status must be active, suspended, or revoked")
user.status = body.status
log.info("User %s status changed to %s by %s",
user.username, body.status, current_user.username)
@@ -483,7 +486,9 @@ async def admin_patch_group(
if body.status is not None:
if body.status not in ("active", "suspended", "revoked"):
- raise HTTPException(status_code=422, detail="status must be active, suspended, or revoked")
+ raise HTTPException(
+ status_code=422,
+ detail="status must be active, suspended, or revoked")
group.status = body.status
log.info("Group %s status changed to %s by %s",
group.name, body.status, current_user.username)
diff --git a/packages/meshbay-hub/src/meshbay_hub/api/relay.py b/packages/meshbay-hub/src/meshbay_hub/api/relay.py
index 08d935b..7bb3f66 100644
--- a/packages/meshbay-hub/src/meshbay_hub/api/relay.py
+++ b/packages/meshbay-hub/src/meshbay_hub/api/relay.py
@@ -102,7 +102,8 @@ async def relay_register(
approved = _relays.get(body.relay_id)
if not approved or approved.get("pk") != body.pk_relay:
raise HTTPException(status_code=403,
- detail="Relay not approved — ask hub admin to run POST /v1/relays/approve")
+ detail="Relay not approved — ask the hub admin to "
+ "run POST /v1/relays/approve")
if body.timestamp is None or not body.signature:
raise HTTPException(
diff --git a/packages/meshbay-hub/src/meshbay_hub/api/users.py b/packages/meshbay-hub/src/meshbay_hub/api/users.py
index ee8aabc..2666e86 100644
--- a/packages/meshbay-hub/src/meshbay_hub/api/users.py
+++ b/packages/meshbay-hub/src/meshbay_hub/api/users.py
@@ -1312,7 +1312,9 @@ async def register_node_key(
if len(raw) != 32:
raise ValueError
except Exception:
- raise HTTPException(status_code=400, detail="Invalid Ed25519 public key (need 32 bytes base64)")
+ raise HTTPException(
+ status_code=400,
+ detail="Invalid Ed25519 public key (need 32 bytes base64)")
current_user.pk_node_ed25519 = body.pk_node_ed25519
await db.commit()
diff --git a/packages/meshbay-hub/src/meshbay_hub/api/webapp.py b/packages/meshbay-hub/src/meshbay_hub/api/webapp.py
index 3e23961..054d04a 100644
--- a/packages/meshbay-hub/src/meshbay_hub/api/webapp.py
+++ b/packages/meshbay-hub/src/meshbay_hub/api/webapp.py
@@ -168,7 +168,8 @@ _HTML = """\
though it had scrolled away. This asks for the keyboard to resize the
layout viewport instead, so what is pinned stays where it is looked at.
Ignored by browsers that do not know it. -->
- <meta name="viewport" content="width=device-width, initial-scale=1, interactive-widget=resizes-content">
+ <meta name="viewport"
+ content="width=device-width, initial-scale=1, interactive-widget=resizes-content">
<title>MeshBay</title>
<link rel="stylesheet" href="/a/{v}/style.css">
</head>
diff --git a/packages/meshbay-hub/src/meshbay_hub/csam.py b/packages/meshbay-hub/src/meshbay_hub/csam.py
index e540068..b8e8d04 100644
--- a/packages/meshbay-hub/src/meshbay_hub/csam.py
+++ b/packages/meshbay-hub/src/meshbay_hub/csam.py
@@ -22,6 +22,11 @@ must be reported to NCMEC (US law) or relevant authority immediately.
import logging
from pathlib import Path
+from fastapi import APIRouter, Depends, HTTPException
+
+from meshbay_hub.api.deps import require_admin
+from meshbay_hub.db.models import User
+
log = logging.getLogger(__name__)
# Default path for the CSAM hash database (blake3 hex hashes, one per line)
@@ -121,11 +126,6 @@ def check_content_hash(blake3_hex: str) -> bool:
# ── Hub API integration ───────────────────────────────────────────────────────
-from fastapi import APIRouter, Depends, HTTPException
-
-from meshbay_hub.api.deps import require_admin
-from meshbay_hub.db.models import User
-
csam_router = APIRouter(prefix="/v1/admin/csam", tags=["csam"])
diff --git a/packages/meshbay-hub/src/meshbay_hub/db/models.py b/packages/meshbay-hub/src/meshbay_hub/db/models.py
index 38c4723..ac1828f 100644
--- a/packages/meshbay-hub/src/meshbay_hub/db/models.py
+++ b/packages/meshbay-hub/src/meshbay_hub/db/models.py
@@ -53,10 +53,12 @@ class User(Base):
# the node does the wrapping, nothing reads a key from this directory. Keys
# are generated per node and pinned there (meshbay_node/roster.py).
email_hash: Mapped[str | None] = mapped_column(String(64), nullable=True) # HMAC blind index
- pk_node_ed25519: Mapped[str | None] = mapped_column(String(64), nullable=True) # node daemon key
+ pk_node_ed25519: Mapped[str | None] = mapped_column(String(64), nullable=True)
hub_id: Mapped[str] = mapped_column(String(128), nullable=False)
- role: Mapped[str] = mapped_column(String(16), default="user") # user|moderator|admin
- status: Mapped[str] = mapped_column(String(16), default="active") # active|suspended|revoked
+ # user|moderator|admin
+ role: Mapped[str] = mapped_column(String(16), default="user")
+ # active|suspended|revoked
+ status: Mapped[str] = mapped_column(String(16), default="active")
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_now)
nodes: Mapped[list["Node"]] = relationship(back_populates="user")
@@ -105,7 +107,8 @@ class Group(Base):
visibility: Mapped[str] = mapped_column(String(16), default="private") # public|private
join_policy: Mapped[str] = mapped_column(String(16), default="invite") # open|request|invite
description: Mapped[str | None] = mapped_column(String(512))
- status: Mapped[str] = mapped_column(String(16), default="active") # active|suspended|revoked
+ # active|suspended|revoked
+ status: Mapped[str] = mapped_column(String(16), default="active")
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_now)
# First time a node registered on /v1/nodes/ws announcing that it hosts this
# group. Until then the group has no files, no key and nobody to serve it, so
@@ -382,7 +385,8 @@ class IPLog(Base):
__tablename__ = "ip_logs"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
- user_id: Mapped[str | None] = mapped_column(ForeignKey("users.id")) # null for failed logins
+ # null for failed logins
+ user_id: Mapped[str | None] = mapped_column(ForeignKey("users.id"))
# The name this account had, written when it is deleted. The username is
# released on deletion and the row itself is tombstoned, so the join that
# normally supplies the name would answer "deleted-3f9a1c" for exactly the
@@ -390,7 +394,8 @@ class IPLog(Base):
username: Mapped[str | None] = mapped_column(String(64))
event: Mapped[str] = mapped_column(String(32), nullable=False)
ip_address: Mapped[str] = mapped_column(String(45), nullable=False) # IPv4 or IPv6
- detail: Mapped[str | None] = mapped_column(String(256)) # e.g. username on fail
+ # e.g. username on fail
+ detail: Mapped[str | None] = mapped_column(String(256))
timestamp: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_now)
user: Mapped["User | None"] = relationship(back_populates="ip_logs")
diff --git a/packages/meshbay-hub/src/meshbay_hub/tasks/cleanup.py b/packages/meshbay-hub/src/meshbay_hub/tasks/cleanup.py
index 429575f..1ef7796 100644
--- a/packages/meshbay-hub/src/meshbay_hub/tasks/cleanup.py
+++ b/packages/meshbay-hub/src/meshbay_hub/tasks/cleanup.py
@@ -50,7 +50,8 @@ async def cleanup_loop(get_session):
async with get_session() as db:
deleted = await purge_old_ip_logs(db)
if deleted:
- log.info("Purged %d IP log entries older than %d days", deleted, RETENTION_DAYS)
+ log.info("Purged %d IP log entries older than %d days",
+ deleted, RETENTION_DAYS)
expired = await purge_expired_verifications(db)
if expired:
log.info("Purged %d expired email verifications", expired)