Share one user form, add user photos and Revoke, confirm every form

- Settings > Users uses the people UserForm (useFormInit) instead of its
  own copy; it saves in one request through users/save-user, which now
  takes an optional profile photo (setUserProfileImage)
- Revoke action on user rows adds the revoked type, as the edit form would
- Every admin form asks for confirmation before it saves

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-19 11:03:57 +01:00
co-authored by Claude Opus 5.5
parent c44048dbab
commit 4dd34ed251
35 changed files with 352 additions and 535 deletions
+7 -2
View File
@@ -3,6 +3,7 @@ import ensureCoordinatorBot from "@/src/functions/backend/db/bots/ensure-coordin
import ensureMainComputer from "@/src/functions/backend/db/computers/ensure-main-computer";
import grabUsers from "@/src/functions/backend/db/users/grab-users";
import saveUser from "@/src/functions/backend/db/users/save-user";
import setUserProfileImage from "@/src/functions/backend/db/users/set-user-profile-image";
import logEvent, { grabRequestIP, logError } from "@/src/functions/backend/logger";
import { SaveUserRequestSchema } from "@/src/schemas/user-schema";
import type { BUN_SQLITE_COBBLE_USERS_JOIN } from "@/src/types/sql-joins";
@@ -20,8 +21,8 @@ import type {
/**
* Creates a user (`canCreateUser`) or updates one (`canUpdateUser`):
* name, email, username, password and user types, each type checked
* against the assignment matrix. A new user gets their coordinator and main computer
* name, email, username, password, user types (each checked against the
* assignment matrix) and, when one is sent, their profile photo. A new user gets their coordinator and main computer
* straight away, as at signup. Returns the user.
*/
export const handler: BunextAPIRouteHandler<APIResponseObject<BUN_SQLITE_COBBLE_USERS_JOIN>> = async ({ req, body }) => {
@@ -59,6 +60,10 @@ export const handler: BunextAPIRouteHandler<APIResponseObject<BUN_SQLITE_COBBLE_
}
const { user_id, created } = await saveUser({ ...data, actor_user_types: user_types || [] });
if (data.image) {
await setUserProfileImage({ user_id, data_url: data.image });
}
if (created) {
/**
* The account exists now; `/admin/bots` creates both on their