Files
publisher-dashboard/apps/api-server/src/procedures/me/sessions.ts
igm 2baf10b0cd Replace String() calls with .toString()/.toLocaleString() per ast-grep rule
- Add formatError() helper in CLI to safely handle unknown error types
- Add uniqueTestId() helper for generating unique test identifiers
- Replace String(id) with id.toString() for database ID conversions
- Replace String(n) with n.toLocaleString() for user-facing number formatting
- Fix TypeScript errors in test files (undefined checks, unused variables)
- Update lint commands to include ast-grep scanning

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 15:02:46 +08:00

91 lines
2.5 KiB
TypeScript

/**
* 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.sessions.list
.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.sessions.revoke
.use(authMiddleware)
.handler(async ({ input, context }) => {
const { sessionId } = input;
// Prevent revoking current session (use logout instead)
if (sessionId.toString() === 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", "=", sessionId.toString())
.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" });
}
return { success: true };
});
/**
* Revoke all sessions handler
* - Requires authentication
* - Revokes all sessions except current
*/
export const revokeAllSessions = os.me.sessions.revokeAll
.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();
return { success: true };
});