🐛 Accept username in request of API delete user

This commit is contained in:
lethemanh
2025-03-22 23:38:47 +07:00
committed by Anton Shepilov
parent 1f975f1ed5
commit bebf337d28
4 changed files with 59 additions and 25 deletions
@@ -1,3 +1,4 @@
import { UserPrimaryKey } from "src/services/user/entities/user";
import gr from "../../../../../services/global-resolver"; import gr from "../../../../../services/global-resolver";
import type { ExecutionContext } from "../../../../platform/framework/api/crud-service"; import type { ExecutionContext } from "../../../../platform/framework/api/crud-service";
@@ -13,27 +14,50 @@ export class AdminDeleteUserController {
} }
/** Begin or forward the deletion process of a user, if `deleteData` is false, only anonymises the user entry */ /** Begin or forward the deletion process of a user, if `deleteData` is false, only anonymises the user entry */
async deleteUser(userId: string, deleteData: boolean): Promise<"failed" | "deleting" | "done"> { async deleteUser(
userId: string,
deleteData: boolean,
username?: string,
): Promise<{ status: "failed" | "deleting" | "done"; userId?: string }> {
try { try {
if ( let pk: UserPrimaryKey = { id: userId };
await gr.services.users.anonymizeAndDelete( if (username) {
{ id: userId }, pk = { username_canonical: username };
{ }
user: { server_request: true },
} as unknown as ExecutionContext, const data = await gr.services.users.anonymizeAndDelete(
deleteData, pk,
) {
) user: { server_request: true },
return "done"; } as unknown as ExecutionContext,
deleteData,
);
if (data.isDeleted)
return {
status: "done",
userId: data.userId,
};
const existingUser = await (await this.getRepos()).user.findOne({ id: userId }); const existingUser = await (await this.getRepos()).user.findOne({ id: userId });
if (existingUser?.deleted) { if (existingUser?.deleted) {
if (existingUser.delete_process_started_epoch > 0) return "deleting"; if (existingUser.delete_process_started_epoch > 0) {
return {
status: "deleting",
userId: existingUser.id,
};
}
} }
} catch (err) { } catch (err) {
adminLogger.error({ err, userId }, "User deletion error"); adminLogger.error("[DELETE USER] ", JSON.stringify({ err, userId }), "User deletion error");
return "failed"; return {
status: "failed",
userId,
};
} }
return "done"; return {
status: "done",
userId,
};
} }
/** Get an array of 2 item arrays with `[ user IDs, delete_process_started_epoch ]` that are incompletely deleted */ /** Get an array of 2 item arrays with `[ user IDs, delete_process_started_epoch ]` that are incompletely deleted */
@@ -14,15 +14,19 @@ function authenticateAdminQuery(request: FastifyRequest, reply: FastifyReply) {
return true; return true;
} }
type TUserDeleteQueryBody = TQueryBody & { userId: string; deleteData: boolean }; type TUserDeleteQueryBody = TQueryBody & { userId: string; deleteData: boolean; username?: string };
function getUserIfValidQuery(request: FastifyRequest, reply: FastifyReply) { function getUserIfValidQuery(request: FastifyRequest, reply: FastifyReply) {
if (!authenticateAdminQuery(request, reply)) return { userId: "", deleteData: false }; if (!authenticateAdminQuery(request, reply)) return { userId: "", deleteData: false };
const body = request.body as TUserDeleteQueryBody; const body = request.body as TUserDeleteQueryBody;
if (!body.userId?.length) { if (!body.userId?.length && !body.username) {
reply.status(400).send(); reply.status(400).send();
return { userId: "", deleteData: false }; return { userId: "", username: "", deleteData: false };
} }
return body; return {
userId: body.userId || "",
username: body.username,
deleteData: body.deleteData,
};
} }
const routes: FastifyPluginCallback = async (fastify: FastifyInstance, _opts, next) => { const routes: FastifyPluginCallback = async (fastify: FastifyInstance, _opts, next) => {
@@ -31,13 +35,13 @@ const routes: FastifyPluginCallback = async (fastify: FastifyInstance, _opts, ne
const controller = new AdminDeleteUserController(); const controller = new AdminDeleteUserController();
if (config?.endpointSecret?.length) { if (config?.endpointSecret?.length) {
fastify.post(urlRoot, async (request, reply) => { fastify.post(urlRoot, async (request, reply) => {
const { userId, deleteData } = getUserIfValidQuery(request, reply); const { userId, deleteData, username } = getUserIfValidQuery(request, reply);
if (!userId) return false; if (!userId && !username) return false;
if (userId == "e2e_simulate_timeout") if (userId == "e2e_simulate_timeout")
return new Promise(() => { return new Promise(() => {
return; return;
}); });
return { status: await controller.deleteUser(userId, deleteData) }; return await controller.deleteUser(userId, deleteData, username);
}); });
fastify.post(`${urlRoot}/pending`, async (request, reply) => { fastify.post(`${urlRoot}/pending`, async (request, reply) => {
@@ -149,9 +149,12 @@ export type UserNotificationPreferences = {
}; };
}; };
export type UserPrimaryKey = Pick<User, "id">; export type UserPrimaryKey = {
id?: uuid;
username_canonical?: string;
};
export function getInstance(user: Partial<User>): User { export function getInstance(user: Partial<User>): User {
user.creation_date = !isNumber(user.creation_date) ? Date.now() : user.creation_date; user.creation_date = !isNumber(user?.creation_date) ? Date.now() : user?.creation_date;
return merge(new User(), user); return merge(new User(), user);
} }
@@ -185,7 +185,10 @@ export class UserServiceImpl {
user: user, user: user,
}); });
return deleteData && (await gr.platformServices.admin.deleteUser(userCopy)); return {
isDeleted: deleteData && (await gr.platformServices.admin.deleteUser(userCopy)),
userId: user.id,
};
} }
} }