Skip to content

feat(uploads): migrate uploaders to draft/authtoken#219

Open
ValwareIRC wants to merge 3 commits into
mainfrom
feat/uploaders-draft-authtoken
Open

feat(uploads): migrate uploaders to draft/authtoken#219
ValwareIRC wants to merge 3 commits into
mainfrom
feat/uploaders-draft-authtoken

Conversation

@ValwareIRC
Copy link
Copy Markdown
Contributor

@ValwareIRC ValwareIRC commented May 14, 2026

Summary

Migrates the chat-area file uploader from EXTJWT to draft/authtoken so both uploaders (avatar + chat-area) use the same protocol. Companion changes on the obbyircd and hosted-backend repos (separate PRs) close the loop.

Client (this PR)

  • ChatArea.tsx: replace the requestExtJwt + 1s sleep with a serialised requestToken \"filehost\" loop that mints one Bearer per file. Tokens are single-use (server burns them on validate), so the old reuse-one-bearer pattern doesn't fit. Mints run sequentially because waitForAuthToken resolves on the first matching TOKEN_GENERATE event; parallel calls would race. The uploads themselves still run in parallel.
  • AvatarUpload.tsx: already on draft/authtoken — no change.

Server companion changes

obbyircd (src/modules/authtoken.c) — already deployed to obby.t3ks.com:

  • authtoken { service \"filehost\" { url 'https://obby.t3ks.com'; require-account yes; allow-validate-mask '*@127.0.0.1'; allow-validate-mask '*@::1'; }; } config block
  • New authtoken.validate JSON-RPC method so HTTP backends can validate without spinning up an IRC client

hosted-backend (authtoken.go + main.go route wiring):

  • New AuthTokenMiddleware calls authtoken.validate over the existing RPC connection (gen.pollinations.ai-style — wss://127.0.0.1:8600)
  • Burns the token on validate (single-use)
  • Synthesises a legacy JWTClaims-shaped struct into the request context so the existing upload handlers' claims.Sub/Account/Cmodes reads keep working with no other changes
  • /upload, /upload/avatar/user, /upload/avatar/channel/{ch} switched to the new middleware

Test plan

  • tsc + build clean
  • Manual: upload single file from chat input → mint one token, upload, hosted-backend's authtoken.validate RPC succeeds, file saved
  • Manual: upload N files via drag-and-drop → mint N tokens sequentially, parallel uploads, all succeed
  • Manual: upload user avatar from settings → uses existing draft/authtoken path
  • Manual: upload channel avatar as a chan-op → scope=channel:#xyz, hosted-backend sees operator_of contains #xyz, synthesises Cmodes=[\"o\"], handler passes

Summary by CodeRabbit

  • New Features

    • Added drag-and-drop file upload functionality with visual drop zone overlay in chat area.
  • Improvements

    • Enhanced mobile keyboard experience by refocusing the input field after sending messages.
    • Streamlined file upload flow with improved token handling for file transfers.
  • Localization

    • Added "Drop files to upload" translations across 16 languages (Czech, German, English, Spanish, Finnish, French, Italian, Japanese, Korean, Dutch, Polish, Portuguese, Romanian, Russian, Swedish, Turkish).
    • Updated localization message catalogs.

Review Change Stack

The single-file avatar uploader already used draft/authtoken's TOKEN
GENERATE flow. The multi-file chat-input uploader was the lone
holdout still on EXTJWT. Migrate it so both follow the same path:

- Mint one Bearer per file via ircClient.requestToken("filehost")
  with channel:#xyz scope when uploading in a channel context.
- Sequentialise the mints: waitForAuthToken resolves on the first
  matching TOKEN_GENERATE event, so parallel mints would race for the
  same reply and lose tokens.  N round-trips up-front, then the
  uploads themselves still run in parallel.
- The hosted-backend side (separate repo) consumes the bearer via a
  new AuthTokenMiddleware that calls obbyircd's authtoken.validate
  RPC. Tokens are single-use; the IRCd burns them on validate. Server-
  side config + RPC handler are companion changes.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 14, 2026

📝 Walkthrough

Walkthrough

The PR adds drag-and-drop file uploads to ChatArea with a dedicated drop overlay state, refactors auth from shared JWT reuse to minting one-shot Bearer tokens per file, unifies file selection routing through a centralized handler, removes legacy single-file upload code, and adds localized UI text across 16 languages.

Changes

Drag-and-drop uploads with per-file Bearer token authentication

Layer / File(s) Summary
Drag-and-drop state and overlay rendering
src/components/layout/ChatArea.tsx
Adds isDraggingFile state and dragDepthRef to track nested drag-enter/leave events; wires drag-and-drop handlers to the input container and renders a visual overlay while files hover.
Unified file selection and routing
src/components/layout/ChatArea.tsx
Introduces handleSelectedFiles to route single images to the preview modal and other file sets to the multi-file uploader; simplifies the file picker input.onchange handler and integrates drag event handlers into the unified flow.
Per-file Bearer token authentication
src/components/layout/ChatArea.tsx
Refactors multi-file upload auth to mint a dedicated one-shot filehost Bearer token per file; captures jobIdx during parallel uploads so each job selects tokens[jobIdx] and passes bearerToken to uploadFile instead of a shared token.
Remove legacy single-file upload path
src/components/layout/ChatArea.tsx
Removes the dedicated handleImageUpload helper, consolidating all upload paths into the unified selection/multi-file pipeline.
Update backend path references
src/components/ui/EmojiPackAdminModal.tsx, src/lib/emojiAdminApi.ts
Updates error messages to reference obby.world/FILEHOST instead of draft/FILEHOST.
Internationalization for upload prompt
src/locales/{cs,de,en,es,fi,fr,it,ja,ko,nl,pl,pt,ro,ru,sv,tr}/*
Adds "Drop files to upload" translation entries across 16 languages in both .po source strings and .mjs compiled locale objects.

Sequence Diagram

sequenceDiagram
  participant User
  participant ChatArea
  participant DragOverlay as Drop Overlay
  participant FileHandler as handleSelectedFiles
  participant Auth as Auth Token Service
  participant Uploader as Multi-file Uploader
  
  User->>ChatArea: drag files over input
  ChatArea->>DragOverlay: show overlay (isDraggingFile)
  
  User->>ChatArea: drop files
  ChatArea->>FileHandler: call handleSelectedFiles(files)
  
  alt Single image
    FileHandler->>ChatArea: open preview modal
  else Multiple files or non-image
    FileHandler->>Auth: mint per-file Bearer tokens
    Auth-->>FileHandler: return tokens array
    FileHandler->>Uploader: start parallel uploads with tokens[idx]
    Uploader->>Uploader: upload each file with corresponding bearer token
  end
  
  ChatArea->>DragOverlay: hide overlay (isDraggingFile = false)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

  • ObsidianIRC/ObsidianIRC#184: Switches ChatArea upload auth from reused JWT to fresh per-upload Bearer tokens for filehost Authorization header.
  • ObsidianIRC/ObsidianIRC#82: Earlier refactor of ChatArea's image/multi-file upload pipeline to use per-file filehost Bearer tokens and unified selection handler.
  • ObsidianIRC/ObsidianIRC#76: Prior ChatArea upload implementation that introduced handleImageUpload and image preview/modal flow now being consolidated.

Suggested reviewers

  • matheusfillipe

Poem

🐰 Whisker-twitches as files rain down,
A drop zone emerges, the finest in town!
Per-token bearers march, one-shot and true,
Uploads unified—old paths bid adieu! 🎉

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title mentions draft/authtoken migration but the primary feature is drag-and-drop file uploads in ChatArea. The actual changes focus on drag-and-drop support (89 lines added to ChatArea), localization updates for drop UI across 18 languages, and refactoring the upload pipeline. The authtoken migration is secondary implementation detail. Revise title to reflect the primary user-facing change: 'feat: add drag-and-drop file uploads to chat' or similar, which more accurately represents the main changeset and user-visible feature.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/uploaders-draft-authtoken

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

Pages Preview
Preview URL: https://feat-uploaders-draft-authtok.obsidianirc.pages.dev

Automated deployment preview for the PR in the Cloudflare Pages.

matheusfillipe
matheusfillipe previously approved these changes May 17, 2026
Dropping a file onto the message textarea hit the browser default and
pasted the local file path as text. Add drag handlers on the input
container that intercept file drags (ignoring text drags so normal DnD
still works), show a drop overlay, and route files through the same
picker path. Factor the picker's single-image-preview-vs-batch logic
into a shared handleSelectedFiles.
The token-authenticated uploader (mint a draft/authtoken Bearer, POST to
<host>/upload) is an obby-specific mechanism, not the tokenless IRCv3
draft/FILEHOST. Discover it via the vendor ISUPPORT token
obby.world/FILEHOST instead of squatting on draft/FILEHOST, which is
reserved for the standard tokenless spec (admin-configured external
filehosts). Mechanism is unchanged.

Also drop the dead single-file handleImageUpload path (superseded by
handleFilesUpload + uploadFile).
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/ui/EmojiPackAdminModal.tsx`:
- Around line 229-232: In EmojiPackAdminModal.tsx wrap the user-visible
paragraph text currently rendered as a raw JSX string inside a <Trans>…</Trans>
so it can be extracted for translation (i.e. replace the plain <p> children with
<Trans> children), and ensure the file imports Trans from '`@lingui/macro`' at the
top if it's not already imported; reference the <p
className="text-discord-yellow text-sm mb-4"> element and the
EmojiPackAdminModal component to locate the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c72665bf-f7ba-4486-b13c-641f83cc1550

📥 Commits

Reviewing files that changed from the base of the PR and between e153b25 and 1a51b67.

📒 Files selected for processing (43)
  • src/components/layout/ChatArea.tsx
  • src/components/ui/EmojiPackAdminModal.tsx
  • src/lib/emojiAdminApi.ts
  • src/locales/cs/messages.mjs
  • src/locales/cs/messages.po
  • src/locales/de/messages.mjs
  • src/locales/de/messages.po
  • src/locales/en/messages.mjs
  • src/locales/en/messages.po
  • src/locales/es/messages.mjs
  • src/locales/es/messages.po
  • src/locales/fi/messages.mjs
  • src/locales/fi/messages.po
  • src/locales/fr/messages.mjs
  • src/locales/fr/messages.po
  • src/locales/it/messages.mjs
  • src/locales/it/messages.po
  • src/locales/ja/messages.mjs
  • src/locales/ja/messages.po
  • src/locales/ko/messages.mjs
  • src/locales/ko/messages.po
  • src/locales/nl/messages.mjs
  • src/locales/nl/messages.po
  • src/locales/pl/messages.mjs
  • src/locales/pl/messages.po
  • src/locales/pt/messages.mjs
  • src/locales/pt/messages.po
  • src/locales/ro/messages.mjs
  • src/locales/ro/messages.po
  • src/locales/ru/messages.mjs
  • src/locales/ru/messages.po
  • src/locales/sv/messages.mjs
  • src/locales/sv/messages.po
  • src/locales/tr/messages.mjs
  • src/locales/tr/messages.po
  • src/locales/uk/messages.mjs
  • src/locales/uk/messages.po
  • src/locales/zh-TW/messages.mjs
  • src/locales/zh-TW/messages.po
  • src/locales/zh/messages.mjs
  • src/locales/zh/messages.po
  • src/protocol/isupport.ts
  • src/types/index.ts

Comment on lines 229 to 232
<p className="text-discord-yellow text-sm mb-4">
This server doesn't advertise a backend URL (draft/authtoken or
draft/FILEHOST). The admin endpoints can't be reached.
obby.world/FILEHOST). The admin endpoints can't be reached.
</p>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Localize the updated warning text in JSX.

Line 230–231 adds user-visible copy as a raw JSX string; wrap it in <Trans> so it gets extracted/translated.

Proposed fix
+import { Trans } from "`@lingui/macro`";
...
         {!baseUrl && (
           <p className="text-discord-yellow text-sm mb-4">
-            This server doesn't advertise a backend URL (draft/authtoken or
-            obby.world/FILEHOST). The admin endpoints can't be reached.
+            <Trans>
+              This server doesn't advertise a backend URL (draft/authtoken or
+              obby.world/FILEHOST). The admin endpoints can't be reached.
+            </Trans>
           </p>
         )}

As per coding guidelines, "Wrap all user-visible text in JSX children with <Trans>…</Trans> macro from @lingui/macro."

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<p className="text-discord-yellow text-sm mb-4">
This server doesn't advertise a backend URL (draft/authtoken or
draft/FILEHOST). The admin endpoints can't be reached.
obby.world/FILEHOST). The admin endpoints can't be reached.
</p>
<p className="text-discord-yellow text-sm mb-4">
<Trans>
This server doesn't advertise a backend URL (draft/authtoken or
obby.world/FILEHOST). The admin endpoints can't be reached.
</Trans>
</p>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/ui/EmojiPackAdminModal.tsx` around lines 229 - 232, In
EmojiPackAdminModal.tsx wrap the user-visible paragraph text currently rendered
as a raw JSX string inside a <Trans>…</Trans> so it can be extracted for
translation (i.e. replace the plain <p> children with <Trans> children), and
ensure the file imports Trans from '`@lingui/macro`' at the top if it's not
already imported; reference the <p className="text-discord-yellow text-sm mb-4">
element and the EmojiPackAdminModal component to locate the change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants