Refactor me.* procedures with code review fixes

- Fix silent failures: add 404 NOT_FOUND for invalid resources in
  passkeysRename, revokeSession, trustDevice, untrustDevice
- Fix race condition in passkeysDelete using transaction
- Extract helper functions: requireDeviceFingerprint, defaultDeviceName
- Improve type safety in updateProfile with Kysely's Updateable<Users>
- Extract me.* procedures to separate files under procedures/me/
- Standardize naming to verb-first: listPasskeys, renamePasskey, deletePasskey

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
RevIQ
2026-01-09 16:24:10 +08:00
parent 1ebcf12cb9
commit 9b898678c7
10 changed files with 555 additions and 125 deletions

View File

@@ -0,0 +1,50 @@
/**
* Delete account procedure - permanently deletes user account
*/
import { ORPCError } from "@orpc/server";
import { COOKIE_NAMES, deleteCookie } from "../../utils/cookies.js";
import { verifyPassword } from "../../utils/password.js";
import { authMiddleware, os } from "../base.js";
/**
* Delete account handler
* - Requires authentication
* - Requires password confirmation (passkey-only users must set password first)
* - Deletes user record (cascades to sessions, devices, passkeys, etc.)
* - Clears session cookie
*/
export const meDelete = os.me.delete
.use(authMiddleware)
.handler(async ({ input, context }) => {
const { password } = input;
// Fetch user with password hash
const user = await context.db
.selectFrom("users")
.select(["password_hash"])
.where("id", "=", context.user.id)
.executeTakeFirstOrThrow();
// Verify password (required for account deletion)
if (!user.password_hash) {
throw new ORPCError("BAD_REQUEST", {
message:
"Cannot delete account without a password. Please set a password first.",
});
}
const valid = await verifyPassword(password, user.password_hash);
if (!valid) {
throw new ORPCError("BAD_REQUEST", { message: "Incorrect password" });
}
// Delete user (cascades to sessions, devices, passkeys, etc.)
await context.db
.deleteFrom("users")
.where("id", "=", context.user.id)
.execute();
// Clear session cookie
deleteCookie(context.resHeaders, COOKIE_NAMES.SESSION_TOKEN);
});