- Add successResponseSchema to common.ts for explicit success responses
- Update all auth, me, orgs, and admin procedures to return { success: true }
- Update contract.ts to use successResponseSchema instead of z.void()
- Add ast-grep rule to prevent future z.void() usage in contracts
- Add build:packages script to root package.json
- Fix test file lint errors with eslint-disable comments
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
53 lines
1.6 KiB
TypeScript
53 lines
1.6 KiB
TypeScript
/**
|
|
* 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);
|
|
|
|
return { success: true };
|
|
});
|