Feat: block users - #53
Draft
khanzadimahdi wants to merge 1 commit into
Draft
Conversation
Adds the admin control for banning an account and makes the whole app
react when the API refuses a banned session.
- A "banned" switch on the user edit form. It only appears for an existing
user, since banning is an action on an account rather than part of
creating one, and it sends the intent only — the API keeps the date. When
the user is already banned the switch says since when
- The API answers 403 with {"code": "user_banned", "message": "..."} for a
banned session. Both DAL drivers recognise it and force a sign-out
instead of showing the usual permission warning: the server driver via a
ServerBannedInterceptor, the client driver via a redirect to a new
/api/auth/banned route that clears the session and lands on the login
page with the explanation
- The login form says the account is suspended rather than letting the
refusal read as a wrong password
- catch blocks that swallowed failures now call unstable_rethrow first.
Next's redirect() travels as a throw, so without this the forced
sign-out would be caught and discarded by the very code it passes through
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The admin control for banning an account, and the plumbing that makes the whole app react when the API refuses a banned session.
Pairs with backend PR Tarhche/backend#74 — this branch reads the
banned/banned_atfields and the403 {"code": "user_banned"}responses that PR introduces. Merge the backend first; until then the switch has nothing to write to.The admin side
A banned switch on the user edit form. It appears only for an existing user — banning is an action on an account, not part of creating one. It sends the intent only; the API owns the timestamp. When the user is already banned, the switch's description says since when instead of the generic hint.
Reacting to a ban
The API answers
403with{"code": "user_banned", "message": "…"}. Thecodeis what matters: an ordinary permission denial is also a403, and the dashboard already depends on that behaving as it does today (a warning, not a sign-out). So a ban has to be distinguished by code, not status.Both DAL drivers now recognise it and force a sign-out:
ServerBannedInterceptorthat redirects./api/auth/bannedroute that clears the session and lands on the login page with the explanation carried in query params.The login form itself also handles the refusal, so a banned account gets "your account has been suspended" rather than something that reads like a wrong password. The message the API sends is already translated, so it's shown as-is;
errors.bannedis only the fallback for when the API sends none.The
unstable_rethrowsweepThis is the part most worth a look. Next's
redirect()travels as a throw. A number ofcatch { return false }blocks around DAL calls would therefore catch the forced sign-out and discard it — the ban would silently do nothing. Those blocks now callunstable_rethrow(error)first, which re-throws Next's control-flow errors and leaves ordinary failures to be handled as before.The central one is in
extractValidationErrors, which nearly everycatcharound an API call funnels through. The rest are individual delete actions and the settings page.Notes for review
isBannedErroraccepts several spellings of the code (user_banned,user_is_banned,account_banned,banned) so the frontend isn't pinned to one exact string. It reads both error shapes the app produces —DALDriverErrorfrom the server drivers and the rawAxiosErrorfrom the client one.dal/private/users.tswidens its payload type fromRecord<string, string>toRecord<string, string | boolean>, sincebannedis a real JSON boolean.tsc --noEmitandnext buildon this branch alone;/api/auth/bannedappears in the route manifest. The repo has jest configured but no test files.Relationship to the other PRs
Split out of one working branch alongside contact-us and notes. All three are cut from
mainand mergeable in any order; the overlap is the two i18n dictionaries. One thing to know: the notes PR also touchesremove-bookmark.tsanddelete-comment.ts, and carries theirunstable_rethrowline along with its own changes, so that sweep is complete either way.🤖 Generated with Claude Code