Skip to content

Beta media-upload permission gate: admin-approved uploads + grant-request emails - #302

Open
lopugit wants to merge 6 commits into
developfrom
claude/media-upload-permission-gate
Open

Beta media-upload permission gate: admin-approved uploads + grant-request emails#302
lopugit wants to merge 6 commits into
developfrom
claude/media-upload-permission-gate

Conversation

@lopugit

@lopugit lopugit commented Aug 18, 2026

Copy link
Copy Markdown
Owner

Beta media-upload permission gate + admin approval flow

During the beta, new users cannot upload media or files until a Thingtime admin manually grants their account the permission. Every registration emails the admin inbox requesting a grant. This is Phase A of the NSFW/TOS media-moderation programme (analysis pipeline lands in a follow-up PR stacked on this branch).

Hotfix note

This branch is cut directly from origin/main (0bd6ee3), so it merges into main cleanly as a hotfix with no unrelated develop content. PR base is develop per repo policy — retarget or cherry-pick for the main hotfix as you prefer.

What's in it

  • Per-user grant flag mirroring the admin flag exactly: root boolean secureMediaUpload on user things / meta.mediaUpload on legacy docs, dual-store setUserMediaUpload writer, pure canUploadMediaDoc predicate (admins always pass), PublicUser.canUploadMedia (self-view only; never on PublicProfile).
  • Upload gate at the single attachment-mutation choke point (createAttachmentMutationAction): uploads, uploads/parts, uploads/complete require the grant and return 403 { code: "media_upload_not_granted" }; abort/delete stay open so revoked accounts can clean up drafts. One gate covers posts, comments, messenger media, custom emoji, and profile avatars/banners (all purposes flow through startAttachmentUpload).
  • Admin approval email: every registration fire-and-forgets an admin.media_upload_request email to THINGTIME_ADMIN_EMAIL (default admin@thingtime.com) with username/email/user-id and an /admin link; recorded in email_messages. Service accounts and temporary users don't page the admin.
  • Admin API + UI: POST /api/v1/admin/set-media-upload { userId, granted } (mirrors set-admin; registered in the import map + apiDocs, which auto-adds two docs smoke tests). /admin → Users gains a purple media badge and a per-row Grant media / Revoke media button with Lopu toasts.
  • Upload UI: ungranted accounts see a 🔐 approval-pending card instead of the dropzone in every composer (post/comment/messenger/emoji); attachmentUploadError maps the new code to friendly copy for direct-API surfaces (profile media).
  • Migration grant-media-upload-to-existing-users: grandfathers every account that exists at run time (both stores, idempotent). Run it once at deploy if existing users should keep uploading; skip it to require approval for everyone.
  • Docs: README fork-safe env setup (THINGTIME_ADMIN_EMAIL), TESTING.md checklist lines, CHANGELOG entry, PRs/ note.

Verification

  • corepack pnpm --dir remix run test:attachments104/104 pass, including new gate coverage (403 + stable code for ungranted, pass for granted, cleanup routes stay open, service never invoked when blocked).
  • corepack pnpm --dir remix run test:unit — exit 0, all 25 sub-suites green.
  • corepack pnpm --dir remix run lint:files -- <20 changed files> — clean.
  • Live smoke on the worktree dev stack (port 16270): register → POST /api/v1/attachments/uploads returned 403 { code: "media_upload_not_granted" }; the admin request email was recorded in email_messages and (local SES env) actually delivered to admin@thingtime.com; /api/root-data carries canUploadMedia:false for the fresh account.
  • API suite gains admin-set-media-upload-guarded + attachments-upload-requires-media-grant.

Notes / follow-ups

  • Browser-level UI walkthrough (composer card, admin toggle) is being finished as part of the moderation follow-up work; server behavior is fully verified above.
  • Existing users are NOT auto-granted — that's an explicit deploy-time choice via the migration.

🤖 Generated with Claude Code

@vercel

vercel Bot commented Aug 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
thingtime Ready Ready Preview Aug 18, 2026 1:34pm
thingtime (develop) Ready Ready Preview Aug 18, 2026 1:34pm

Request Review

@github-actions

github-actions Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

✅ Develop S3 preview ready

The alias passed the develop bucket CORS preflight and a final live PR/SHA fence.

Generic Vercel Preview deployments use the shared development runtime; this controller adds the stable exact-SHA alias and marker-scoped cleanup.

@github-actions
github-actions Bot temporarily deployed to develop-pr-302 August 18, 2026 05:32 Destroyed
lopugit added a commit that referenced this pull request Aug 18, 2026
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions
github-actions Bot temporarily deployed to develop-pr-302 August 18, 2026 05:33 Destroyed
@github-actions

github-actions Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

🤝 Merged develop into claude/media-upload-permission-gate — conflicts auto-resolved by the resolve-pr-conflicts workflow.

Conflicted files:

  • README.md
  • remix/CHANGELOG.md
  • remix/app/api/utils/attachments/attachmentResponses.ts
  • remix/app/api/utils/auth/users.ts
  • remix/app/components/Admin/AdminDashboard.tsx
  • remix/app/docs/apiDocs.ts
  • remix/app/hooks/useApi.tsx
  • remix/app/routes/api/v1/attachments/uploads/_uploads.tsx

Please review the merge commit before relying on it.

@github-actions

github-actions Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

🤖 Auto-rebase running — the stack rebase started working on this PR at 13:14 UTC.

Expected to finish around 13:29 UTC (most rebases still land in 5-15 minutes; the hosted-runner ceiling is 360). On success this branch is force-pushed onto its new base and a result comment follows — no manual action is needed meanwhile.

Live status

@github-actions github-actions Bot added ai-rebase-paused AI stack rebase failed; manual retry or review required and removed ai-rebase-in-progress AI stack rebase currently owns this PR branch labels Aug 18, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ The AI PR/stack rebase workflow did not complete. The workflow stopped before it attempted its force-with-lease push.

ai-rebase-paused was added for this exact ref/topology snapshot; review the run, then use a manual PR-number run to retry.

@lopugit lopugit removed the ai-rebase-paused AI stack rebase failed; manual retry or review required label Aug 18, 2026
@github-actions
github-actions Bot temporarily deployed to develop-pr-302 August 18, 2026 08:20 Destroyed
@github-actions github-actions Bot added the ai-rebase-in-progress AI stack rebase currently owns this PR branch label Aug 18, 2026
lopugit added a commit that referenced this pull request Aug 18, 2026
…on) into moderation branch

Brings the PR #301/#302 upload-gate consolidation and current develop into
the moderation stack; resolves TESTING/CHANGELOG/package.json overlaps
keeping both the consolidated gate story and the moderation additions.
attachments 108/108, moderation 10/10, migrations 19/19 on the merged tree.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions
github-actions Bot temporarily deployed to develop-pr-302 August 18, 2026 08:37 Destroyed
@github-actions github-actions Bot removed the ai-rebase-in-progress AI stack rebase currently owns this PR branch label Aug 18, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ The AI PR/stack rebase workflow did not complete. The workflow stopped before it attempted its force-with-lease push.

The PR changed refs or resolver ownership, so no pause was added; the current state remains eligible for automatic re-detection.

@github-actions github-actions Bot added the ai-rebase-in-progress AI stack rebase currently owns this PR branch label Aug 18, 2026
lopugit added a commit that referenced this pull request Aug 18, 2026
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions
github-actions Bot temporarily deployed to develop-pr-302 August 18, 2026 12:56 Destroyed
@github-actions github-actions Bot added the ai-rebase-in-progress AI stack rebase currently owns this PR branch label Aug 18, 2026
@github-actions
github-actions Bot temporarily deployed to develop-pr-302 August 18, 2026 12:59 Destroyed
@github-actions github-actions Bot removed the ai-rebase-in-progress AI stack rebase currently owns this PR branch label Aug 18, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ The AI PR/stack rebase workflow did not complete. The workflow stopped before it attempted its force-with-lease push.

The PR changed refs or resolver ownership, so no pause was added; the current state remains eligible for automatic re-detection.

@github-actions github-actions Bot added the ai-rebase-in-progress AI stack rebase currently owns this PR branch label Aug 18, 2026
lopugit and others added 5 commits August 18, 2026 13:21
…flow

New users cannot upload media/files until an admin grants the per-account
meta.mediaUpload permission (root secureMediaUpload on user things,
mirroring the admin flag's dual-store pattern). Upload start/parts/complete
return 403 { code: media_upload_not_granted } for ungranted accounts;
abort/delete stay open so revoked users can clean up drafts. Every
registration emails THINGTIME_ADMIN_EMAIL (default admin@thingtime.com)
requesting a grant; /admin -> Users gains Grant/Revoke media controls via
POST /api/v1/admin/set-media-upload; the attachment composer shows an
approval-pending card to ungranted accounts; and the
grant-media-upload-to-existing-users migration grandfathers pre-gate
accounts. Verified live: register -> 403 gate + admin email recorded, unit
suite + lint green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… one permission

PR #301 (signup-permissions hotfix, now on main) and this branch shipped the
same feature in parallel; after the AI resolver merged develop in, BOTH
gates were enforced and no single admin toggle could unblock a new account.
Consolidate on #301's tri-state meta.publicUploads as the one stored
permission: canUploadMediaDoc (auth/mediaUpload.ts) is now THE predicate,
userPublicUploadsEnabled delegates to it, and PublicUser.canUploadMedia /
publicUploadsEnabled can never drift. Removed as duplicates: the
meta.mediaUpload / secureMediaUpload storage, setUserMediaUpload,
POST /api/v1/admin/set-media-upload (+docs/useApi/UI/guard test), the
registration-time admin email (+template/env — #301's verification-time
admin.new_user alert is the consolidated notification), and the
grant-media-upload-to-existing-users migration (absent = grandfathered).
Kept: the composer approval-pending card, media_upload_not_granted client
copy, requireUploadGrant on parts/complete, and the route gate tests.
attachments 108/108, migrations 19/19, full test:unit exit 0, lint clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@lopugit
lopugit force-pushed the claude/media-upload-permission-gate branch from 780a385 to b695585 Compare August 18, 2026 13:29
@github-actions github-actions Bot removed the ai-rebase-in-progress AI stack rebase currently owns this PR branch label Aug 18, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🧬 Rebased claude/media-upload-permission-gate onto develop with the AI PR/stack rebase workflow.

  • Previous head: 780a3855f1fa7f110c39fa9cd5837d8bd9ad5375
  • Rewritten head: b695585f6fa55de76d88ff9c859467582e1350e5
  • graphify: refreshed and committed (semantic mode: none)
  • Direct stacked children dispatched: 1

The branch was published once with an exact force-with-lease. Please review the rewritten commits before relying on them.

…I rebase

The rebase replayed pre-integration commits, resurrecting the purpose-blind
requireUploadGrant gate (which 403s #310 private-only accounts on message/
profile uploads), the single-flag canUploadMediaDoc + its
userPublicUploadsEnabled delegation, the purpose-blind composer card, and
the dead set-media-upload docs entry/useApi method. Restore the verified
integrated versions (commit 780a385 content): purpose-aware gate only,
any-scope alias predicate, purpose-aware composer card, scoped error copy,
scoped tests. attachments 111/111, full nitro build green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
lopugit added a commit that referenced this pull request Aug 18, 2026
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
lopugit added a commit that referenced this pull request Aug 18, 2026
lopugit added a commit that referenced this pull request Aug 18, 2026
github-actions Bot added a commit that referenced this pull request Aug 18, 2026
github-actions Bot added a commit that referenced this pull request Aug 19, 2026
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.

1 participant