Fix 5 validator blockers in groups/users admin work
1. apps.ts getVisibleAppsForUser: use getEffectiveRoleIds() so admin
gate honors group_roles inheritance (was direct user_roles only —
security bypass for group-only admins). Exported helper from
middlewares/auth.ts.
2. POST /users: switch from RegisterBody to new CreateUserBody schema
that accepts optional groupIds[]. Validates ids exist; user creation
+ auto-Everyone + requested groups happen in single transaction.
OpenAPI spec extended; api-zod and api-client-react regenerated.
3. DELETE /users/🆔 400 if req.session.userId === target (no
self-delete).
4. scripts/src/seed.ts: removed `void inArray;` AI-slop and dropped
unused inArray import.
5. Locales (ar.json + en.json): added admin.users.col.displayNameAr,
displayNameEn, language and admin.groups.searchPlaceholder.
Typecheck clean. groups-crud + service-orders + users tests pass.
Pre-existing admin-app-opens-pagination flake unrelated.
Architect re-review: PASS.
This commit is contained in:
@@ -28,6 +28,19 @@ export interface RegisterBody {
|
||||
preferredLanguage?: string;
|
||||
}
|
||||
|
||||
export interface CreateUserBody {
|
||||
username: string;
|
||||
email: string;
|
||||
password: string;
|
||||
/** @nullable */
|
||||
displayNameAr?: string | null;
|
||||
/** @nullable */
|
||||
displayNameEn?: string | null;
|
||||
preferredLanguage?: string;
|
||||
/** Initial group memberships in addition to the auto-assigned Everyone group. */
|
||||
groupIds?: number[];
|
||||
}
|
||||
|
||||
export interface LoginBody {
|
||||
username: string;
|
||||
password: string;
|
||||
|
||||
@@ -32,6 +32,7 @@ import type {
|
||||
CreateGroupBody,
|
||||
CreateServiceBody,
|
||||
CreateServiceOrderBody,
|
||||
CreateUserBody,
|
||||
ErrorResponse,
|
||||
ForgotPasswordBody,
|
||||
ForgotPasswordResponse,
|
||||
@@ -4086,14 +4087,14 @@ export const getCreateUserUrl = () => {
|
||||
};
|
||||
|
||||
export const createUser = async (
|
||||
registerBody: RegisterBody,
|
||||
createUserBody: CreateUserBody,
|
||||
options?: RequestInit,
|
||||
): Promise<UserProfile> => {
|
||||
return customFetch<UserProfile>(getCreateUserUrl(), {
|
||||
...options,
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json", ...options?.headers },
|
||||
body: JSON.stringify(registerBody),
|
||||
body: JSON.stringify(createUserBody),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -4104,14 +4105,14 @@ export const getCreateUserMutationOptions = <
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof createUser>>,
|
||||
TError,
|
||||
{ data: BodyType<RegisterBody> },
|
||||
{ data: BodyType<CreateUserBody> },
|
||||
TContext
|
||||
>;
|
||||
request?: SecondParameter<typeof customFetch>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof createUser>>,
|
||||
TError,
|
||||
{ data: BodyType<RegisterBody> },
|
||||
{ data: BodyType<CreateUserBody> },
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ["createUser"];
|
||||
@@ -4125,7 +4126,7 @@ export const getCreateUserMutationOptions = <
|
||||
|
||||
const mutationFn: MutationFunction<
|
||||
Awaited<ReturnType<typeof createUser>>,
|
||||
{ data: BodyType<RegisterBody> }
|
||||
{ data: BodyType<CreateUserBody> }
|
||||
> = (props) => {
|
||||
const { data } = props ?? {};
|
||||
|
||||
@@ -4138,7 +4139,7 @@ export const getCreateUserMutationOptions = <
|
||||
export type CreateUserMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof createUser>>
|
||||
>;
|
||||
export type CreateUserMutationBody = BodyType<RegisterBody>;
|
||||
export type CreateUserMutationBody = BodyType<CreateUserBody>;
|
||||
export type CreateUserMutationError = ErrorType<void>;
|
||||
|
||||
/**
|
||||
@@ -4151,14 +4152,14 @@ export const useCreateUser = <
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof createUser>>,
|
||||
TError,
|
||||
{ data: BodyType<RegisterBody> },
|
||||
{ data: BodyType<CreateUserBody> },
|
||||
TContext
|
||||
>;
|
||||
request?: SecondParameter<typeof customFetch>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof createUser>>,
|
||||
TError,
|
||||
{ data: BodyType<RegisterBody> },
|
||||
{ data: BodyType<CreateUserBody> },
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getCreateUserMutationOptions(options));
|
||||
|
||||
Reference in New Issue
Block a user