feat(uploads): migrate uploaders to draft/authtoken#219
Conversation
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.
📝 WalkthroughWalkthroughThe 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. ChangesDrag-and-drop uploads with per-file Bearer token authentication
Sequence DiagramsequenceDiagram
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)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
|
Automated deployment preview for the PR in the Cloudflare Pages. |
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).
There was a problem hiding this comment.
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
📒 Files selected for processing (43)
src/components/layout/ChatArea.tsxsrc/components/ui/EmojiPackAdminModal.tsxsrc/lib/emojiAdminApi.tssrc/locales/cs/messages.mjssrc/locales/cs/messages.posrc/locales/de/messages.mjssrc/locales/de/messages.posrc/locales/en/messages.mjssrc/locales/en/messages.posrc/locales/es/messages.mjssrc/locales/es/messages.posrc/locales/fi/messages.mjssrc/locales/fi/messages.posrc/locales/fr/messages.mjssrc/locales/fr/messages.posrc/locales/it/messages.mjssrc/locales/it/messages.posrc/locales/ja/messages.mjssrc/locales/ja/messages.posrc/locales/ko/messages.mjssrc/locales/ko/messages.posrc/locales/nl/messages.mjssrc/locales/nl/messages.posrc/locales/pl/messages.mjssrc/locales/pl/messages.posrc/locales/pt/messages.mjssrc/locales/pt/messages.posrc/locales/ro/messages.mjssrc/locales/ro/messages.posrc/locales/ru/messages.mjssrc/locales/ru/messages.posrc/locales/sv/messages.mjssrc/locales/sv/messages.posrc/locales/tr/messages.mjssrc/locales/tr/messages.posrc/locales/uk/messages.mjssrc/locales/uk/messages.posrc/locales/zh-TW/messages.mjssrc/locales/zh-TW/messages.posrc/locales/zh/messages.mjssrc/locales/zh/messages.posrc/protocol/isupport.tssrc/types/index.ts
| <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> |
There was a problem hiding this comment.
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.
| <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.
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 therequestExtJwt+ 1s sleep with a serialisedrequestToken \"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 becausewaitForAuthTokenresolves on the first matchingTOKEN_GENERATEevent; 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 blockauthtoken.validateJSON-RPC method so HTTP backends can validate without spinning up an IRC clienthosted-backend (
authtoken.go+main.goroute wiring):AuthTokenMiddlewarecallsauthtoken.validateover the existing RPC connection (gen.pollinations.ai-style — wss://127.0.0.1:8600)JWTClaims-shaped struct into the request context so the existing upload handlers'claims.Sub/Account/Cmodesreads keep working with no other changes/upload,/upload/avatar/user,/upload/avatar/channel/{ch}switched to the new middlewareTest plan
authtoken.validateRPC succeeds, file savedchannel:#xyz, hosted-backend seesoperator_ofcontains#xyz, synthesisesCmodes=[\"o\"], handler passesSummary by CodeRabbit
New Features
Improvements
Localization