mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-06 22:21:29 +02:00
Fetch a signed feed.json from the public bambuddy-notifications repo on GitHub at startup and every 6 hours. Nothing about the install is sent; targeting (version, beta channel, install type) is decided locally. - Ed25519 against a key built into the app; an older serial is refused so a withdrawn message can't come back. The feed replaces the stored list, and a failed or rejected fetch keeps the last good one. - Sidebar entry above System with an unread count, a slide-over list, and a banner for unread important/critical messages. Read state per user. - Admins by default; Settings > General > Updates can show them to all users or switch them off, which also stops the fetch. - Plain text only; links to github.com and bambuddy.cool only.
52 lines
2.2 KiB
Python
52 lines
2.2 KiB
Python
"""Announcements from the Bambuddy maintainers, and who has read them.
|
|
|
|
Fetched from a signed feed on GitHub (services/announcements.py). Only the ones
|
|
that target this install are stored, and the feed is authoritative: a message
|
|
withdrawn upstream is deleted here on the next fetch, together with its read
|
|
markers.
|
|
|
|
``AnnouncementRead.user_id`` is nullable: with authentication off there are no
|
|
users, and the install's read state is a single NULL-keyed row per message.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
|
|
from sqlalchemy import DateTime, ForeignKey, Integer, String, Text, UniqueConstraint, func
|
|
from sqlalchemy.orm import Mapped, mapped_column
|
|
|
|
from backend.app.core.database import Base
|
|
|
|
|
|
class Announcement(Base):
|
|
__tablename__ = "announcements"
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
# The feed's id for the message. Stable across edits upstream, so a corrected
|
|
# typo does not come back as unread.
|
|
public_id: Mapped[str] = mapped_column(String(64), unique=True, index=True)
|
|
level: Mapped[str] = mapped_column(String(16)) # info | important | critical
|
|
# JSON {"en": {"title", "body", "link_label"?}, ...}. Text for SQLite/Postgres
|
|
# uniformity; the service serialises with json.dumps.
|
|
texts: Mapped[str] = mapped_column(Text)
|
|
link_url: Mapped[str | None] = mapped_column(String(500), nullable=True)
|
|
# Naive UTC, like every other timestamp in Bambuddy.
|
|
published_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
|
|
expires_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
|
|
created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now())
|
|
|
|
|
|
class AnnouncementRead(Base):
|
|
__tablename__ = "announcement_reads"
|
|
__table_args__ = (UniqueConstraint("announcement_id", "user_id", name="uq_announcement_reads_user"),)
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
announcement_id: Mapped[int] = mapped_column(
|
|
Integer, ForeignKey("announcements.id", ondelete="CASCADE"), index=True
|
|
)
|
|
user_id: Mapped[int | None] = mapped_column(
|
|
Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=True, index=True
|
|
)
|
|
read_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now())
|