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,86 @@
/**
* Session management procedures - list, revoke, revokeAll sessions
*/
import { ORPCError } from "@orpc/server";
import { authMiddleware, os } from "../base.js";
/**
* List sessions handler
* - Requires authentication
* - Returns all sessions for the current user
* - Includes isCurrent flag to identify active session
*/
export const listSessions = os.me.listSessions
.use(authMiddleware)
.handler(async ({ context }) => {
const sessions = await context.db
.selectFrom("sessions")
.selectAll()
.where("user_id", "=", context.user.id)
.orderBy("created_at", "desc")
.execute();
return sessions.map((s) => ({
id: Number(s.id),
ip: s.ip_address ?? "",
city: s.city,
region: s.region,
country: s.country,
userAgent: s.user_agent ?? "",
trustedMode: s.trusted_mode,
createdAt: s.created_at,
isCurrent: s.id === context.session.id,
revokedAt: s.revoked_at,
}));
});
/**
* Revoke session handler
* - Requires authentication
* - Cannot revoke current session (use logout instead)
* @throws NOT_FOUND if session doesn't exist
* @throws BAD_REQUEST if trying to revoke current session
*/
export const revokeSession = os.me.revokeSession
.use(authMiddleware)
.handler(async ({ input, context }) => {
const { sessionId } = input;
// Prevent revoking current session (use logout instead)
if (String(sessionId) === context.session.id) {
throw new ORPCError("BAD_REQUEST", {
message: "Cannot revoke current session. Use logout instead.",
});
}
const result = await context.db
.updateTable("sessions")
.set({ revoked_at: new Date() })
.where("id", "=", String(sessionId))
.where("user_id", "=", context.user.id)
.where("revoked_at", "is", null)
.executeTakeFirst();
if (!result.numUpdatedRows || result.numUpdatedRows === 0n) {
throw new ORPCError("NOT_FOUND", { message: "Session not found" });
}
});
/**
* Revoke all sessions handler
* - Requires authentication
* - Revokes all sessions except current
*/
export const revokeAllSessions = os.me.revokeAllSessions
.use(authMiddleware)
.handler(async ({ context }) => {
// Revoke all sessions except current
await context.db
.updateTable("sessions")
.set({ revoked_at: new Date() })
.where("user_id", "=", context.user.id)
.where("id", "!=", context.session.id)
.where("revoked_at", "is", null)
.execute();
});