vienalatina/apps/board
Claude 5ab9e3cab5
Phase C: private messages, with blocking and photos
An inbox between two members — conversations, per-person unread marks,
photos, blocking. Not live chat: that needs a connection held open per
signed-in member, which the sync workers cannot do.

Membership of the conversation is the whole access rule and is checked on
every hit, answering 404 rather than 403 so a member cannot tell a
conversation that is not theirs from one that does not exist. A picture
in a private message is checked the same way: on the board being signed
in is enough, here it is nowhere near.

Blocking is symmetric. One row stops both directions, and you can only
lift your own. A block that silenced only the blocked person would leave
the blocker writing freely, which is a megaphone rather than a safety
feature. Enforced in the handlers, with a test that posts from a page
held open from before the block.

Erasing a member deletes their private messages, both sides, and their
pictures off disk. A thread outlives its author because other people
replied; a two-party exchange has no remainder, and keeping half of
erased correspondence is what erasure exists to prevent. The guard added
in c9c549e did its job: it failed the moment the new tables landed and
named all four columns.

The part that needed care: schema.sql is all CREATE TABLE IF NOT EXISTS,
so it can add a table and nothing else. Every change so far happened to
be a new table. Letting an attachment belong to a message is not — and
SQLite cannot do it in place, because the table carries a CHECK
constraint and there is no DROP CONSTRAINT. Verified before building on
it: ALTER TABLE ADD COLUMN succeeds and the next insert is refused.

So migrations.py, numbered steps recorded in PRAGMA user_version, run
after the schema so a fresh database finds its work already done. Step 1
rebuilds attachments the documented way. Tested against a database built
in the old shape with rows in it, because a migration tested only on a
fresh database is tested against the one case it was never needed for —
including that the rebuilt CHECK is as strict as the one it replaced.

229 tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NizVpJ2dwzCbjCrTLCjeHn
2026-09-28 16:10:52 +00:00
..
static Let members post pictures, and back them up 2026-09-28 09:02:18 +00:00
templates Phase C: private messages, with blocking and photos 2026-09-28 16:10:52 +00:00
tests Phase C: private messages, with blocking and photos 2026-09-28 16:10:52 +00:00
__init__.py Add a members area: roles and an internal board 2026-09-22 10:57:18 +00:00
app.py Phase C: private messages, with blocking and photos 2026-09-28 16:10:52 +00:00
auth.py Stop showing members the name of the software behind the login 2026-09-25 19:30:06 +00:00
board.py Let members post pictures, and back them up 2026-09-28 09:02:18 +00:00
content.py Let members post pictures, and back them up 2026-09-28 09:02:18 +00:00
db.py Phase C: private messages, with blocking and photos 2026-09-28 16:10:52 +00:00
gitea.py Stop showing members the name of the software behind the login 2026-09-25 19:30:06 +00:00
invites.py Let members get a password of their own 2026-09-25 17:49:39 +00:00
mail.py Let members get a password of their own 2026-09-25 17:49:39 +00:00
members.py Phase C: private messages, with blocking and photos 2026-09-28 16:10:52 +00:00
messages.py Phase C: private messages, with blocking and photos 2026-09-28 16:10:52 +00:00
migrations.py Phase C: private messages, with blocking and photos 2026-09-28 16:10:52 +00:00
render.py Add a members area: roles and an internal board 2026-09-22 10:57:18 +00:00
requirements.txt Write the site from the members area instead of Decap 2026-09-25 15:07:31 +00:00
schema.sql Phase C: private messages, with blocking and photos 2026-09-28 16:10:52 +00:00
security.py Add a members area: roles and an internal board 2026-09-22 10:57:18 +00:00
tokens.py Write the site from the members area instead of Decap 2026-09-25 15:07:31 +00:00
uploads.py Phase C: private messages, with blocking and photos 2026-09-28 16:10:52 +00:00
wsgi.py Add a members area: roles and an internal board 2026-09-22 10:57:18 +00:00