Commit Graph

63 Commits

Author SHA1 Message Date
primal
5b3330ba07 v47: Fix d:feeds auto-expand for hidden container 2026-01-30 16:31:32 -05:00
primal
97051f3967 v46: Click domain name to toggle feeds div 2026-01-30 16:29:05 -05:00
primal
cf34db1e6c v45: Auto-expand feed details in d:feeds mode 2026-01-30 16:23:03 -05:00
primal
f59e7dcbc3 v44: Left-justify TLD footer 2026-01-30 16:19:08 -05:00
primal
018f47449f v43: Add TLD footer with collapse button 2026-01-30 16:17:59 -05:00
primal
cbf16bfbc8 v42: Revert to persistent session cookie (24h) 2026-01-30 16:13:24 -05:00
primal
aef0826004 v41: Session cookie for browser-close logout 2026-01-30 16:12:33 -05:00
primal
e0602b0123 v40: Persist OAuth sessions to database 2026-01-30 16:09:46 -05:00
primal
31b7b61bb0 v39: Fix session cookie Secure flag for HTTP 2026-01-30 16:05:59 -05:00
primal
c374260e11 v38: d:feeds only shows feeds with items 2026-01-30 16:04:38 -05:00
primal
388e846f18 v37: Add right margin to language column 2026-01-30 16:01:47 -05:00
primal
2504927022 v36: Widen language column to 32px 2026-01-30 16:00:27 -05:00
primal
a5fe2962c3 v35: Add git commit/push to deploy script 2026-01-30 15:58:49 -05:00
primal
8192bce301 Add AT Protocol OAuth 2.0 authentication for dashboard
- Implement full OAuth 2.0 with PKCE using haileyok/atproto-oauth-golang
- Backend For Frontend (BFF) pattern: tokens stored server-side only
- AES-256-GCM encrypted session cookies
- Auto token refresh when near expiry
- Restrict access to allowed handles (1440.news, wehrv.bsky.social)
- Add genkey utility for generating OAuth configuration
- Generic error messages to prevent handle enumeration
- Server-side logging of failed login attempts for security monitoring

New files:
- oauth.go: OAuth client wrapper and DID/handle resolution
- oauth_session.go: Session management with encrypted cookies
- oauth_middleware.go: RequireAuth middleware for route protection
- oauth_handlers.go: Login, callback, logout, metadata endpoints
- cmd/genkey/main.go: Generate OAuth secrets and JWK keypair
- oauth.env.example: Configuration template

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 15:16:51 -05:00
primal
1a2f6c15a9 Remove screenshot 2026-01-30 14:11:26 -05:00
primal
655dbfdbac v26: Fix Safari button spacing with explicit min-width
Safari was collapsing the invisible spacer button. Added min-width
and box-sizing to force consistent width across browsers.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 14:10:56 -05:00
primal
7bd9ee7c78 v21: Button spacing and deploy script
- 1px spacing between status buttons
- Rounded corners on all buttons
- Invisible spacer for consistent row alignment
- Added scripts/deploy.sh for auto-incrementing version on deploy

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 13:39:12 -05:00
primal
36de78bc49 v19: Add consistent row alignment with fail button spacer
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 13:30:54 -05:00
primal
eb83ca3e5d Add partial indexes for domain check and crawl loops
- idx_domains_to_check: status WHERE last_checked_at IS NULL
- idx_domains_to_crawl: status WHERE last_checked_at IS NOT NULL AND last_crawled_at IS NULL

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 13:25:04 -05:00
primal
522233c4a2 Tune concurrency settings: 100 workers, 100 batch size, 100 buffer
- Import batch size: 100 (was 100k)
- Domain check/crawl fetch size: 100 (was 1000)
- Feed check fetch size: 100 (was 1000)
- Worker count: 100 fixed (was NumCPU)
- Channel buffers: 100 (was 256)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 23:33:57 -05:00
primal
516848e529 Revise domain status flow: skip uses takedown, add drop for permanent deletion
- Import default changed from 'hold' to 'pass' (auto-crawl)
- Skip now uses PDS takedown (hides posts but preserves data)
- Added 'drop' status for permanent deletion (requires skip first)
- Added TakedownAccount/RestoreAccount PDS functions
- Un-skip restores PDS accounts and reactivates feeds
- Dashboard shows 'drop' button only for skipped domains

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 23:18:17 -05:00
primal
43916c8042 Exclude skip status domains from default API listing
When no status filter is provided, the domains API now excludes
domains with 'skip' status (including bare TLDs) by default.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 22:34:24 -05:00
primal
edce82f1af Skip bare TLDs during domain import
Domains without a dot (e.g., "com", "net", "org") are bare TLDs
from Common Crawl data and should not be processed as domains.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 22:28:09 -05:00
primal
1066f42189 Refactor large Go files into focused modules
Split dashboard.go (3,528 lines) into:
- routes.go: HTTP route registration
- api_domains.go: Domain API handlers
- api_feeds.go: Feed API handlers
- api_publish.go: Publishing API handlers
- api_search.go: Search API handlers
- templates.go: HTML templates
- dashboard.go: Stats functions only (235 lines)

Split publisher.go (1,502 lines) into:
- pds_auth.go: Authentication and account management
- pds_records.go: Record operations (upload, update, delete)
- handle.go: Handle derivation from feed URLs
- image.go: Image processing and favicon fetching
- publisher.go: Core types and PublishItem (439 lines)

Split feed.go (1,137 lines) into:
- item.go: Item struct and DB operations
- feed_check.go: Feed checking and processing
- feed.go: Feed struct and DB operations (565 lines)

Also includes domain import batch size increase (1k -> 100k).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 22:25:02 -05:00
primal
3999e96f26 Dashboard UI overhaul: inline feed details, TLD filtering, status improvements
- Feed details now expand inline instead of navigating to new page
- Add TLD section headers with domains sorted by TLD then name
- Add TLD filter button to show/hide domain sections by TLD
- Feed status behavior: pass creates account, hold crawls only, skip stops, drop cleans up
- Auto-follow new accounts from directory account (1440.news)
- Fix handle derivation (removed duplicate .1440.news suffix)
- Increase domain import batch size to 100k
- Various bug fixes for account creation and profile updates

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 20:51:05 -05:00
primal
5908a8c03e Full cleanup when revoking feed publish status
When setting publish_status to deny, now performs complete cleanup:
- Deletes all posts from the feed account on PDS
- Deletes the PDS account itself
- Clears published_at timestamps on all items
- Clears the publish_account field on the feed

Added Publisher methods:
- DeleteAllPosts: lists and deletes all posts from an account
- DeleteRecord: deletes a single AT Protocol record
- DeleteAccount: deletes account via PDS admin API

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 13:37:02 -05:00
primal
a5af4e14c3 Simplify: auto-deny all domains starting with digits
Replaced complex checks (digit-dash, all-digit hostname) with simple rule:
deny any domain starting with a digit, except 1440.news.

Database: denied 2,870,090 domains and 24,885 feeds.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 13:32:42 -05:00
primal
2386d551fc Auto-deny all-digit domains, whitelist 1440.news
- Deny domains where hostname is all digits (e.g., 0000114.com)
- Never auto-deny 1440.news or subdomains
- Auto-pass feeds from 1440.news sources
- Updated 554,085 domains and 3,213 feeds in database

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 13:27:48 -05:00
primal
897ae66e81 Fix NULL handling for nullable integer columns in getFeed
TTLMinutes, UpdateFreq, ErrorCount, ItemCount, and NoUpdate columns
can be NULL in the database. Use pointer types and handle them properly
to avoid scan errors.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 13:20:18 -05:00
primal
ad78c1a4c0 Add JSON Feed support
- Detect JSON Feed format (jsonfeed.org) via version field
- Parse JSON Feed metadata and items
- Support application/feed+json MIME type for feed discovery
- Include "json" as valid feed type (not auto-denied)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 13:16:50 -05:00
primal
798f79bfe9 Auto-deny feeds that are not RSS or Atom type
Feeds with type other than 'rss' or 'atom' (e.g., 'unknown') are now
automatically denied on discovery. Also updated 164 existing feeds.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 13:13:22 -05:00
primal
f7535a277f Add sort toggle for domain list (A-Z vs feed count)
Added ability to toggle between alphabetical and feed count sorting when
viewing domains under a TLD. Includes UI toggle in breadcrumb area and
backend support for the sort parameter.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 13:09:31 -05:00
primal
a8c73bb540 Add TLD stats display when viewing a TLD
- Add /api/tldStats endpoint for domain/feed counts per TLD
- Show stats bar at top when browsing a TLD

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 13:04:06 -05:00
primal
8e4993d3c5 Optimize stats and TLD queries for performance
- Reduce stats loop interval from 1 minute to 10 seconds
- Simplify TLD query by removing expensive feeds JOIN (30s -> 1.8s)
- Sort TLDs alphabetically

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 13:00:13 -05:00
primal
f780c493c2 Enable all TLDs for import and auto-deny spam domains
- Remove .com-only filter from vertices import
- Add shouldAutoDenyDomain() to detect spam patterns (^[0-9]-)
- Apply auto-deny to all domain insert paths (save, saveTx, bulk import)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 12:49:52 -05:00
primal
254b751799 Add rich text links, language filter, and domain deny feature
- Use labeled links (Article · Audio) instead of raw URLs in posts
- Add language filter dropdown to dashboard with toggle selection
- Auto-deny feeds with no language on discovery
- Add deny/undeny buttons for domains to block crawling
- Denied domains set feeds to dead status, preventing future checks

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 12:36:58 -05:00
primal
283a221efd Use url.1440.news for shorter URLs (28 chars vs 32)
- Changed short URL format from app.1440.news/r/{code} to url.1440.news/{code}
- Added Traefik routing for url.1440.news domain
- Root handler checks Host header to route url.1440.news requests
- Legacy /r/ path still supported for backwards compatibility

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 22:52:45 -05:00
primal
1ab45033cd Use app.1440.news domain for short URLs
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 22:49:04 -05:00
primal
94d64373ed Add URL shortener for link tracking and shorter posts
- New short_urls and clicks tables for URL mapping and analytics
- /r/{code} redirect endpoint with click tracking
- Short URLs use 6-char base64 hash codes (26 chars total)
- Publish loop now shortens article links and enclosure URLs
- Enables podcast audio URLs to fit in posts (139 → 26 chars)
- Tracks: timestamp, referrer, user agent, anonymized IP

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 22:43:42 -05:00
primal
f4f80e91cc Add enclosure support for podcast/media items
- Load enclosure fields in GetAllUnpublishedItems query
- Only include enclosure URL if it fits within post length limit
- Shorter video/audio enclosures will be included when they fit

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 22:36:28 -05:00
primal
1609220a27 Limit handle subdomain to 18 chars (PDS restriction)
The PDS restricts the first segment of local handles to 18 characters,
not the AT Protocol spec of 63. Added abbreviation map for long
category names:
- science-and-environment -> sci-env
- entertainment-and-arts -> ent-arts
- technology -> tech (when needed)
- etc.

Fixes "Handle too long" errors for BBC category feeds.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 22:22:59 -05:00
primal
1f092c87e9 Add automatic image resize for blobs exceeding size limit
Bluesky has a ~976KB blob limit. Images larger than 900KB are now
automatically resized using CatmullRom scaling and re-encoded as
JPEG with 85% quality. Iteratively scales down (90%, 72%, 58%...)
until under limit, with minimum dimensions of 100x100.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 22:10:10 -05:00
primal
959abf06c0 Enable .com domain import from vertices.txt.gz
Filter imported domains to only .com TLD for now.
Re-enabled the import loop that was disabled for testing.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 21:59:14 -05:00
primal
c54005b5ba Upgrade BBC images from 240px to 800px for better quality
BBC CDN supports larger image sizes by changing the URL path.
Upgrade /standard/240/ and /standard/480/ to /standard/800/.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 21:49:11 -05:00
primal
5975df6771 Use PubDate for TID/rkey generation for consistent ordering
Both createdAt and rkey now use the original publication date,
so posts sort consistently by their original publication time.
Falls back to DiscoveredAt if PubDate is not available.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 21:32:07 -05:00
primal
bce6c93242 Use original publication date for post createdAt
Posts now use the item pub_date for the createdAt field instead
of the current time, so posts show their original publication time.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 21:29:44 -05:00
primal
a1f02cd0bc Fix image embeds and rkey collisions
- Add image_urls to GetAllUnpublishedItems query
- Add aspectRatio to image embeds (required by Bluesky)
- Add image decoding to get dimensions (width/height)
- Fix rkey collision by using XOR of multiple hash bytes

The rkey collision was caused by using only 2 hash bytes (10 bits)
which had ~0.1% collision rate per pair of items with same timestamp.
Now XORs 8 hash bytes for better entropy distribution.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 21:24:35 -05:00
primal
4e4e8c939a Add favicon as profile picture for feed accounts
Fetches the site's favicon and uses it as the avatar when creating
or updating feed account profiles. Tries common favicon locations
(/favicon.ico, /favicon.png, /apple-touch-icon.png) then falls back
to Google's favicon service.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 21:05:50 -05:00
primal
9a43b69b4b Add profile refresh on startup to backfill feed URLs
Updates existing account profiles with the feed URL on startup.
This ensures all accounts have the source feed URL in their
profile description.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 21:03:10 -05:00
primal
9ecf0f700d Add feed URL to profile description
When creating new accounts, include the full RSS/Atom feed URL
in the profile description so users can find the original source.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 20:56:09 -05:00