v9 P1: Public-data watch + one-click capture (polling MVP) #22
Labels
No labels
bug
duplicate
enhancement
future
help wanted
invalid
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
rbrooks/TeaLeaves#22
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Baseline Watch feature: no account linking, public data only, works immediately. P1 alone is a complete, useful feature.
Platform reality check (sets expectations, not a blocker):
app.bsky.feed.getAuthorFeed,app.bsky.feed.searchPosts).Schema:
watchestable:id uuid PK,project_id uuid NOT NULL REFERENCES projects ON DELETE CASCADE,platform text NOT NULL CHECK (platform IN ('bluesky','mastodon')),target_type text NOT NULL CHECK (target_type IN ('account','tag')),target_value text NOT NULL(handle/hashtag without@/#),instance_url text(Mastodon only; validated viaisSafeUrl()),enabled boolean NOT NULL DEFAULT true,cursor text(pagination/since-token; reused as the P2 stream resume-point),last_seen_at timestamptz,last_error text,created_at timestamptz NOT NULL DEFAULT now().UNIQUE (project_id, platform, target_type, target_value, instance_url).discovered_poststable:id uuid PK,watch_id uuid NOT NULL REFERENCES watches ON DELETE CASCADE,source_id text NOT NULL(bluesky:<at-uri>/mastodon:<instance_url>:<status_id>- same dedup shape as importsource_id),author_handle text,author_display_name text,content text NOT NULL(sanitized on write),post_url text NOT NULL,media_urls jsonb,posted_at timestamptz NOT NULL,status text NOT NULL DEFAULT 'new' CHECK (status IN ('new','added','dismissed')),created_entry_id uuid REFERENCES entries ON DELETE SET NULL,created_at timestamptz NOT NULL DEFAULT now().UNIQUE (source_id)- doubles as the ingestion dedup guard across all three phases.Polling worker (
services/watchWorker.ts):federationSyncWorker; interval in Settings (watch.pollIntervalMinutes, default 10)lib/watchAdapters/bluesky.ts,mastodon.ts), mirroring howimportWorkerseparates parserstarget_valuevia/api/v1/accounts/lookup, then poll/api/v1/accounts/:id/statuses?since_id=; tag watches poll/api/v1/timelines/tag/:hashtag?since_id=public.api.bsky.app) for both account feeds and searchOne-click "Add as entry":
addSocialPostAsEntry(post, projectId, userId)helper (used by both the queue and direct search) calls the existingcheckDedup()BEFORE insertPOST /api/discovered-posts/:id/add(setsstatus='added',created_entry_id) andPOST /api/discovered-posts/:id/dismissDirect search (3rd tab on the Watch page, alongside Watches + Queue):
GET /api/watch/search?platform=&type=account|tag&q=&instanceUrl=- Bluesky viaapp.bsky.actor.searchActors/app.bsky.feed.searchPosts; Mastodon via that instance's/api/v2/search+/api/v1/timelines/tag/:hashtagpreview. Results rendered live, not persisted.addSocialPostAsEntry()directly (nodiscovered_postsrow), same dedup-hit handlingresolve=truecross-instance lookup typically needs a session, so account search only reliably finds accounts the queried instance already knows. One-line UI note; improves once P3 ships.Nav + security:
instance_url(user-supplied, watches + direct search) validated viaisSafeUrl()with a private-IP-range / SSRF check before the first outbound request to a new instance. All P1 requests are unauthenticated GETs to public endpoints - no credentials in play yet.Originated in
ROADMAP.md(now retired; this tracker is the single source of truth).