Phase 8a-2: /upload command + uploads table + link minting#15
Conversation
Phase 8a-2 of the file relay. Mints upload links; the web endpoints (8a-3)
make them functional.
- Database: new uploads table (id, owner, filename, content_type, size, status,
created_at, uploaded_at, expires_at) + expiry index.
- UploadService: /upload handler generates a 128-bit base64url id, inserts a
pending row, replies ephemerally with PUBLIC_BASE_URL/u/{id}.
- Bot: register /upload guild command + route it.
- Config: WISBOT_PUBLIC_BASE_URL, WISBOT_UPLOAD_MAX_BYTES (500MB),
WISBOT_UPLOAD_RETENTION_DAYS (30).
- .env.example + CLAUDE.md updated.
Next (8a-3): MinIO client + streamed upload/download endpoints in WebService.
There was a problem hiding this comment.
Code review is billed via overage credits. To resume reviews, an organization admin can raise the monthly limit at claude.ai/admin-settings/claude-code.
Once credits are available, reopen this pull request to trigger a review.
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughSummary by CodeRabbit
WalkthroughAdds file-relay support: new env config, uploads DB table, UploadService that mints secure upload links and persists pending uploads, and wiring for a /upload slash command. ChangesFile upload feature with /upload slash command
Sequence Diagram(s)sequenceDiagram
participant User as DiscordUser
participant Bot as Bot
participant Upload as UploadService
participant DB as SQLiteDB
participant Terminal as Terminal
User->>Bot: /upload slash command
Bot->>Upload: Invoke HandleUploadCommand(command)
Upload->>Upload: GenerateId() (CSPRNG token)
Upload->>DB: INSERT uploads (owner, status='pending', created_at, expires_at)
Upload->>Terminal: Log "minted an upload link"
Upload->>Bot: Ephemeral response with Config.PublicBaseUrl/<token> and max size info
Bot->>User: Ephemeral reply (upload link)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 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 `@Config.cs`:
- Line 91: The current Config.cs line assigns any non-empty
WISBOT_PUBLIC_BASE_URL to PublicBaseUrl, which can produce broken relative
links; update the Get("WISBOT_PUBLIC_BASE_URL") handling to validate the value
with Uri.TryCreate and require Uri.IsAbsoluteUri (optionally normalize by
trimming trailing slashes) and if validation fails throw a clear
ConfigurationException (or otherwise stop startup) instead of silently accepting
it; reference the Get method, the "WISBOT_PUBLIC_BASE_URL" key, and the
PublicBaseUrl property when making this change.
In `@Services/UploadService.cs`:
- Line 28: The Log call in UploadService (await Log($"{command.User.Username}
minted upload link {id}");) is writing the bearer upload token/id to logs;
remove the token from logs and either log a non-sensitive indicator or redact
the id (e.g., log only that a link was created or log a truncated/hashed id), so
update the await Log(...) invocation in the method that handles minting (the
code using command.User.Username and id) to omit or redact the raw id and
instead log safe metadata (user, action, timestamp) or a one-way hash if needed
for correlation.
- Around line 47-54: The INSERT currently omits expires_at so pending uploads
never expire; update cmd.CommandText (the INSERT in UploadService.cs) to include
the expires_at column and add a parameter (e.g. "$expires"), and set that
parameter when calling cmd.Parameters.AddWithValue. Compute the value by taking
DateTime.UtcNow and adding the configured retention days (from
WISBOT_UPLOAD_RETENTION_DAYS or the service's retention constant) via
DateTime.UtcNow.AddDays(retentionDays) and pass it formatted consistently (e.g.
ISO/O) or as a DateTime depending on DB binding so minted rows persist the
advertised expiry.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 58ae62c7-94e1-4b16-903c-063a4649d4bc
📒 Files selected for processing (6)
.env.exampleBot.csCLAUDE.mdConfig.csDatabase.csServices/UploadService.cs
📜 Review details
🧰 Additional context used
📓 Path-based instructions (4)
**/*.cs
📄 CodeRabbit inference engine (CLAUDE.md)
Use the
fieldkeyword for properties with logic to avoid manual backing fields in C# 14Use null-conditional assignment (
?.=) for defensive assignmentsUse the
extensionkeyword for grouping extension members in C# 14Use primary constructors for classes and structs unless complex initialization logic is required
Always use
[]for empty or populated collections/spans instead ofnew List<T>()syntaxUse file-scoped namespaces with
namespace MyProject.Models;syntax (no curly braces)Rely on implicit usings and do not include standard system imports unless unique
Use
varonly when the type is obvious from the right side; use explicit types for method returns or literalsUse expression-bodied members (
=>) for simple one-line methods and propertiesPrefer
ReadOnlySpan<char>for string parsing and slicing operationsUse the
requiredkeyword for properties that must be initialized via object initializersUse raw string literals (
""") for multi-line strings or JSON to avoid escaping quotesUse PascalCase for classes, methods, properties, and public fields
Use camelCase for local variables and method arguments
Avoid underscore prefixes for private fields (use
fieldkeyword orthis.prefix if necessary)Use
Task.Run()fire-and-forget with immediateRespondAsync()followed byFollowupAsync()for long operations to avoid 3-second Discord interaction timeoutUse
ConcurrentDictionaryandConcurrentQueuefor thread safety; uselockfor background service lists where neededAll configuration must resolve via
Config.Load()in order: process environment variable → local.envfile → default; never hardcode site-specific valuesUse ISO 8601 strings (
"O"format) forDateTimestorage in the SQLite databaseUse Discord.Net 3.19.0-beta.1 (beta version required for voice audio stream features like
IAudioClient.GetStreams())
Files:
Database.csServices/UploadService.csConfig.csBot.cs
Database.cs
📄 CodeRabbit inference engine (CLAUDE.md)
Use SQLite via
Microsoft.Data.Sqlite; store DiscordulongIDs aslong(SQLite INTEGER) and cast at boundaries
Files:
Database.cs
Services/**/*.cs
📄 CodeRabbit inference engine (CLAUDE.md)
Manual constructor dependency injection: inject
Terminalinto all services andDiscordSocketClientinto services that need Discord APIs after startupEach service lives in its own file under
Services/withBot.csowning instances, wiring events, and handling slash command routing
Files:
Services/UploadService.cs
Bot.cs
📄 CodeRabbit inference engine (CLAUDE.md)
Slash commands must be registered idempotently on
OnReady()(check existing before creating); use/removeallcommandsterminal command to force-clearEnable
GuildMembersprivileged intent forUserJoinedevents (must be enabled in Discord Developer Portal)
Files:
Bot.cs
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: uhstray-io/WisBot
Timestamp: 2026-06-02T14:29:22.592Z
Learning: Run `dotnet build` to validate changes before completing tasks
Learnt from: CR
Repo: uhstray-io/WisBot
Timestamp: 2026-06-02T14:29:22.592Z
Learning: Include appropriate error handling and ensure code is well-structured following best practices
Learnt from: CR
Repo: uhstray-io/WisBot
Timestamp: 2026-06-02T14:29:22.592Z
Learning: Read `.claude/memory/MEMORY.md` at the start of every session and use `/repo-memory` to save or retrieve project memories
Learnt from: CR
Repo: uhstray-io/WisBot
Timestamp: 2026-06-02T14:29:22.592Z
Learning: Use manual constructor dependency injection without a DI container; follow the pattern established in this project
Learnt from: CR
Repo: uhstray-io/WisBot
Timestamp: 2026-06-02T14:29:22.592Z
Learning: Document changes in CLAUDE.md and/or README.md to reflect architectural changes and provide clear documentation for future reference
🔇 Additional comments (6)
CLAUDE.md (1)
42-42: LGTM!Config.cs (1)
31-35: LGTM!.env.example (1)
25-32: LGTM!Database.cs (1)
80-92: LGTM!Services/UploadService.cs (1)
33-37: LGTM!Bot.cs (1)
15-15: LGTM!Also applies to: 107-107, 220-228
…bit) - Config: validate WISBOT_PUBLIC_BASE_URL is an absolute http(s) URL (fail fast) and normalize trailing slash, so a typo can't mint broken links. - UploadService: don't log the link id (it's the bearer credential). - UploadService: set expires_at = created_at + retention on the pending insert, so minted links age out and retention is enforced (refreshed on upload in 8a-3).
Phase 8a-2 —
/uploadcommand + link mintingSecond sub-phase of the file relay (plan §9). Mints the unguessable links; the web upload/download endpoints come in 8a-3.
Changes
Database.cs— newuploadstable (id,owner_user_id,owner_username,filename,content_type,size_bytes,status,created_at,uploaded_at,expires_at) + expiry index.Services/UploadService.cs—/uploadhandler: generates a 128-bit base64url id (CSPRNG), inserts apendingrow tagged with the requesting user, replies ephemerally withWISBOT_PUBLIC_BASE_URL/u/{id}.Bot.cs— register the/uploadguild command + route it.Config.cs—WISBOT_PUBLIC_BASE_URL,WISBOT_UPLOAD_MAX_BYTES(default 500 MB),WISBOT_UPLOAD_RETENTION_DAYS(default 30)..env.example+CLAUDE.mdupdated.Notes
GET/POST /u/{id}endpoints + MinIO). That's fine — the feature only goes live at Phase 8c (agent-cloud MinIO + Caddy), after all sub-phases land./upload.Verification
dotnet build— succeeds, 0 warnings, 0 errors.docker-buildCI validates the image.Next: 8a-3 — MinIO client + streamed upload/download (≤500 MB, attachment downloads), then 8b retention loop.