Changeset 0403187 in Klonkt for src/config/database.js


Ignore:
Timestamp:
07/01/2026 12:53:32 PM (2 months ago)
Author:
roboburr <roboburr@…>
Branches:
main
Children:
bb3f67c
Parents:
731e431
Message:

feat(fediverse): host your own polls (federate as AS2 Question)

A post can carry a poll that federates as an AS2 Question so remote (Mastodon)
followers vote from their own app; votes are tallied server-side and the fresh
counts are pushed back as Update(Question). Voting is fediverse-only; the site
shows live, read-only results. Complements the existing inbound poll support.

  • src/config/database.js — posts.poll_json (our poll definition) + poll_votes table (post_id, actor_uri, choice; UNIQUE) backing the tally + per-actor dedupe.
  • src/services/ActivityPubService.js — parseOwnPoll/pollTally/ownPollView helpers; buildNote emits a Question (oneOf/anyOf + replies.totalItems + endTime/closed + votersCount) for a poll post; handleInbox records a ballot (Note with name + inReplyTo our poll) before the reply path, deduped per actor; a debounced Update(Question) pushes fresh counts to followers; votersCount added to AP_CONTEXT.
  • src/services/Scheduler.js — closeExpiredPolls() marks a poll closed once its endTime passes and pushes the final tally; runs on the existing 60s tick.
  • src/routes/posts.js — parsePollForm() turns the editor fields into poll_json on create/save (a poll with votes is frozen), passes poll_json to the federation hooks, and hands the post page a render-ready ownPollView.
  • src/views/pages/post-edit.ejs — poll section (options, multiple-choice, duration); disabled once the poll has votes.
  • src/views/pages/post.ejs — display-only poll with result bars + voter/close meta.
  • src/services/i18n.js — poll.* + pedit.poll_* strings (nl/en/de).
  • test/polls.test.js — Question shape, tally, percentages, closed state, AS2 term.
  • CHANGELOG(.nl/.de).md — "Create your own polls" under Unreleased.

Co-Authored-By: Claude <noreply@…>

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/config/database.js

    r731e431 r0403187  
    8787  ensureColumn('posts', 'content_warning', 'TEXT');        // custom CW label (empty = default "Gevoelige inhoud")
    8888  ensureColumn('posts', 'type',    "TEXT DEFAULT 'post'");  // post | foto | video | audio
     89  ensureColumn('posts', 'poll_json', 'TEXT');              // a poll WE host → federates as AS2 Question: {multiple,options[{name}],endTime,closed}
    8990
    9091  // Statistics (premium module) — bare counters, cookie-free.
     
    331332    );
    332333    CREATE INDEX IF NOT EXISTS idx_ap_delivery_due ON ap_delivery(next_at);
     334    CREATE TABLE IF NOT EXISTS poll_votes (
     335      id INTEGER PRIMARY KEY AUTOINCREMENT,
     336      post_id INTEGER NOT NULL,     -- our local poll post (posts.id)
     337      actor_uri TEXT NOT NULL,      -- the remote voter's AP actor URI
     338      choice TEXT NOT NULL,         -- the chosen option's name (matches poll_json options[].name)
     339      created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
     340      UNIQUE(post_id, actor_uri, choice)
     341    );
     342    CREATE INDEX IF NOT EXISTS idx_poll_votes_post ON poll_votes(post_id);
    333343  `);
    334344  // "Feature" a followed account: its posts show in the local Cirkel.
Note: See TracChangeset for help on using the changeset viewer.