Commit Graph
91 Commits
Author SHA1 Message Date
4cuck 07f3ed76b2 tack: redeem 2026-07-14 20:13:41 -04:00
4cuck b75d379642 embeds: match status metadata shape 2026-06-11 22:32:47 -04:00
4cuck f8d391aed3 embeds: fix quoted media 2026-06-11 18:58:41 -04:00
4cuck a7887b29b6 security: harden media and preference boundaries
Default signing keys made media proxy URLs forgeable, and unauthenticated
media fetches let arbitrary upstream hosts consume server-side network
access. This commit makes those boundaries explicit, limits buffered upstream
bodies, and keeps externally sourced text escaped at raw rendering sinks.
2026-06-11 18:58:26 -04:00
4cuck d1947fc9dc flake: expose packages for every linux and darwin system 2026-06-05 22:04:45 -04:00
4cuck 40a6f5a8d0 flake: replace deprecated pkgs.system with stdenv.hostPlatform.system 2026-05-30 22:28:13 -04:00
4cuck 740584d048 cache: bound in-memory cache with size cap and expiry-ordered eviction 2026-05-30 22:28:02 -04:00
4cuck 1eb036d196 ui: redesign from scratch 2026-04-17 13:26:48 -04:00
4cuck 7321ee27bf translate: add Kagi Translate as user-configurable alternative
Users can set their Kagi session token in preferences to use Kagi
Translate instead of Twitter's Strato API. The /translate endpoint
reads the kagiToken cookie and routes accordingly, falling back to
Strato when no token is set.
2026-03-25 17:21:47 -04:00
4cuck 8c59923835 all: cargo fmt 2026-03-20 03:04:00 -04:00
4cuck 29cb6e9010 translate: add click-to-translate for non-English tweets via htmx
Shows a 'Translate post' button on tweets where legacy.lang is not
English. Clicking it fires an htmx GET to /translate/{id} which calls
Twitter's Strato translation API (Google Translate backend) and swaps
the button with the translated text and source language header. Adds
lang and is_translatable fields to the API schema, a Translation
struct, and the translate_tweet client method with cookie-session auth.
2026-03-20 03:04:00 -04:00
4cuck de3c43b64d htmx: add htmx support 2026-03-20 01:33:40 -04:00
4cuck c9c44b921b css: fix alt text overlapping in multi-image galleries
The .alt-text paragraph had no styling, causing it to flow inline and
overlap adjacent images. Position it as an absolute overlay at the
bottom-left of each image attachment.
2026-03-19 18:59:01 -04:00
4cuck 262e69937a embed: add Mastodon API format for Discord footer timestamp and quote formatting 2026-03-17 20:35:52 -04:00
4cuck 1b58f11322 embed: improve Discord embed formatting
Match FxTwitter style: abbreviated stats (38.8K), reordered to
replies/retweets/likes/views, blockquote formatting for quote tweets,
and article:published_time for footer timestamp.
2026-03-17 19:01:25 -04:00
4cuck e38d0e8bda embed: add view count, theme-color, and author avatar to Discord embeds 2026-03-17 14:42:36 -04:00
4cuck 5b74d101d3 flake: update inputs 2026-03-17 04:55:03 -04:00
4cuck 5617d98947 transcode: add GIF transcoding for Discord inline playback 2026-03-17 04:55:03 -04:00
4cuck 12f224a8af embed: include card images in OpenGraph metadata 2026-03-11 04:23:02 -04:00
4cuck a16fa4cb15 config: add publicPort option for URL generation 2026-03-11 04:23:02 -04:00
4cuck 54e3c0c1d2 parser: map UserUnavailable with Suspended reason to UserSuspended error 2026-03-11 04:23:02 -04:00
4cuck f4a184e5fa client: skip tweet fetching for protected and suspended accounts 2026-03-11 04:23:02 -04:00
4cuck 431312ce9a parser: read protected flag from privacy field with legacy fallback 2026-03-11 04:23:02 -04:00
4cuck ce9ccd7e0a schema: rename url_entities to entities to match API field name 2026-03-10 23:31:57 -04:00
4cuck 764a1ba6d9 flake: update inputs 2026-03-09 01:06:15 -04:00
4cuck a57030ae2a clippy: resolve all remaining warnings 2026-03-09 01:03:50 -04:00
4cuck d393ac97d8 formatters: fix format_with_commas for negatives, return Cow from URL replacement
format_with_commas produced garbled output for negative numbers because
it reversed the string including the minus sign. Rewrite to handle the
sign separately and iterate forward, also pre-allocating the result.

Change replace_youtube_urls and replace_reddit_urls to return Cow<str>
so the common case (no replacement configured) avoids allocation. Update
replace_urls_abs to call .into_owned() on the final Cow.
2026-03-08 22:10:14 -04:00
4cuck b6007d8a58 client: replace unsafe unwrap() calls with error propagation
Five header .parse().unwrap() calls on user-controlled session data could
panic on malformed values. Replace with .map_err(?) to surface a proper
Error::Internal instead. Also make the search cursor end-detection safe
by using .get(..64) instead of indexing, which would panic on short
cursors.
2026-03-08 22:10:07 -04:00
4cuck 1c3de8bdfa rss: fix replies feed, deduplicate helpers, use references for threads
Three related RSS improvements:

1. UserRssKind::Replies was grouped with Tweets, calling get_user_tweets
   instead of get_user_tweets_and_replies. Add a dedicated
   user_tweets_and_replies_v2_vars builder for the OAuth path.

2. Remove duplicate strip_html and get_link wrappers from rss.rs — import
   strip_html from layout and tweet_link from renderutils directly.

3. Change render_thread_rss to accept &[&Tweet] instead of &Tweets to
   avoid cloning conversation tweets into a new Vec. The thread_rss route
   handler now builds Vec<&Tweet> references and inlines the Min-Id
   response header since rss_response expects &[Tweet].
2026-03-08 22:09:58 -04:00
4cuck be0f00e64d views: replace get_link wrappers with direct tweet_link import
timeline.rs and rss.rs each had a private get_link fn that just called
renderutils::tweet_link. Remove the wrappers and import tweet_link
directly. (rss.rs changes follow in a later commit.)
2026-03-08 22:09:38 -04:00
4cuck d35d1004f3 entity_expander: fix short_url truncation comparing bytes vs chars
short_url compared truncated.len() (byte length) against stripped.len()
(byte length), but truncated was built with .chars().take(max_len) which
counts code points. For multi-byte characters the byte lengths could
mismatch and produce wrong truncation. Compare char counts instead.
2026-03-08 22:09:30 -04:00
4cuck d9fdbf2555 list: extract maybe_filter_retweets helper to deduplicate
list_by_slug and list_by_id had identical blocks filtering retweets from
timeline groups. Extracted into a shared maybe_filter_retweets function.
Also replaces !is_some_and with is_none_or per clippy preference.
2026-03-08 22:09:23 -04:00
4cuck 278ca2da99 media: check upstream HTTP status before returning proxied image
The proxy handler returned whatever bytes the upstream sent, even on 4xx/5xx.
Now checks response.status().is_success() and returns an error early.
2026-03-08 22:09:15 -04:00
4cuck 501964b7ea debug: gate health and sessions endpoints behind enable_debug
Return 404 instead of session pool internals when enable_debug is false.
Removes the typed return values (Json<HealthResponse>) in favor of
Response to support the early 404 return.
2026-03-08 22:09:09 -04:00
4cuck b53f12cd3e config: fix url_prefix omitting port for non-default HTTPS ports
The condition always omitted the port when https was true, even for
non-standard ports like 8443. Now checks against the default port for
the active scheme (443 for HTTPS, 80 for HTTP) so non-default ports
are correctly included in the URL prefix.
2026-03-08 22:09:03 -04:00
4cuck f5aafae59a auth: fix session pool permanent death with 15-minute auto-recovery
Sessions marked as globally rate-limited were never unmarked, causing
them to be skipped forever. Add a GLOBAL_LIMIT_DURATION_SECS (15 min)
timeout to SessionLimits::is_limited so expired limits are ignored, and
clear the limited flag in update_session_limit when a session responds
successfully after its limit window has elapsed.
2026-03-08 22:08:56 -04:00
4cuck a6041b7c77 rss: add thread RSS feed at /{username}/status/{id}/rss
Fetches the conversation via TweetDetail and collects before + main +
self-thread tweets into a flat RSS feed. Adds a cache key, RSS icon link
on status pages, and render_thread_rss() view helper.
2026-03-08 20:49:41 -04:00
4cuck cf233a879e css: use system font stack instead of bare sans-serif
Adds system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto,
Helvetica, and Arial before the sans-serif fallback. Applies to both
the base stylesheet and the Twemoji override.
2026-03-08 20:49:40 -04:00
4cuck 714983fa88 a11y: fix logo alt text and wrap content in main landmark
The logo alt was a generic 'Logo' string — now uses the instance title.
Page content is wrapped in a <main> element for screen reader navigation.
2026-03-08 20:49:34 -04:00
4cuck f7867b5cfa api: log session id and username on request failures
Structured tracing fields make it clear which session hit the error,
addressing the debugging difficulty reported in issue 1339.
2026-03-08 20:49:27 -04:00
4cuck 8fc51149e0 api: retry request with different session on token rejection
When a session's token is rejected (InvalidToken/BadToken), mark it as
limited and retry the request once. The session pool then picks the next
available session, providing automatic failover without caller changes.
2026-03-08 04:17:34 -04:00
4cuck 77b2faa3d6 a11y: add id/for attributes to form inputs and labels
Preferences checkboxes, text inputs, selects, and search form controls
now have proper id/for associations for screen readers. Adds .sr-only
class for visually-hidden labels on the search input and date fields.
2026-03-08 03:19:04 -04:00
4cuck 66c4ddd292 lists: add ?no_retweets=true query param to exclude retweets
Filters retweet groups client-side after fetching the timeline. Works on
both slug-based and ID-based list routes.
2026-03-08 03:19:04 -04:00
4cuck d663e5ac8e tweets: add .author-{user} and .retweet-{user} CSS classes to timeline items
Enables per-user filtering and styling via uBlock/adblock rules. For
example, `.author-elonmusk { display: none }` hides all tweets from
that user, including when they appear as retweets in your timeline.
2026-03-08 03:18:52 -04:00
4cuck 5a2d2bc10a http: add Content-Security-Policy header
Restricts script/style/media sources to self-origin, blocks eval() and
inline scripts, and prevents framing. Uses if_not_present so individual
routes can override if needed.
2026-03-08 00:15:00 -05:00
4cuck 004c86533f debug: include session kind in debug endpoint output
SessionDetail was missing the kind field, making it impossible to tell
whether a session uses OAuth or Cookie auth from the /.sessions endpoint.
2026-03-08 00:14:55 -05:00
4cuck 77c083e013 scroll: return 204 instead of 404 for empty scroll responses
404 means the resource doesn't exist; 204 (No Content) correctly signals
that pagination reached the end. The JS fetch handler now checks for any
non-2xx status instead of only 404, so it stops scrolling on 204 too.
2026-03-07 23:58:48 -05:00
4cuck a7f9a24f29 api: detect duplicate search pages and clear content at end of results
The Twitter API returns the last page verbatim when no more results exist.
Compare the first 64 chars of the input cursor with the returned bottom
cursor to detect this and clear the timeline, stopping infinite scroll
from looping. Mirrors upstream nitter fixes 2898efab and 4bf3df94.
2026-03-07 23:58:42 -05:00
4cuck e19f55dcc7 search: wrap from-user and filter clauses in parentheses
Without parens, multi-user OR groups leak into adjacent filter expressions,
producing incorrect Twitter search queries. Mirrors upstream nitter fix
b0773dd9. Also wraps the joined filter list in parens and short-circuits
to the raw param when no filters are present.
2026-03-07 23:58:35 -05:00
4cuck 73030b2bf3 config: omit port from generated URLs when https is enabled
The url_prefix included the listen port even behind a reverse proxy, producing unreachable og:image URLs like https://host:7963/pic/... that broke Discord/Slack embeds. When https = true the public port is always 443, so the port is now unconditionally omitted.
2026-03-07 22:10:55 -05:00