Merge origin/master into reviq-auth-login-command

Resolved conflicts:
- apps/api-server/src/router.ts: Use meRoutes from master
- packages/api-contract/src/contract.ts: Keep master's nested sessions/devices/invites structure, add apiTokens

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
igm
2026-01-10 19:03:37 +08:00
67 changed files with 5091 additions and 713 deletions

View File

@@ -48,6 +48,7 @@ import {
setupProfileInputSchema,
trustDeviceInputSchema,
updateProfileInputSchema,
userInviteOutputSchema,
userProfileSchema,
} from "./schemas/user.js";
@@ -150,19 +151,39 @@ export const contract = oc.router({
.output(successResponseSchema),
}),
// Sessions & devices
listSessions: oc.output(z.array(sessionOutputSchema)),
revokeSession: oc
.input(z.object({ sessionId: z.number() }))
.output(successResponseSchema),
revokeAllSessions: oc.output(successResponseSchema),
getDeviceInfo: oc.output(deviceOutputSchema),
trustDevice: oc.input(trustDeviceInputSchema).output(successResponseSchema),
listTrustedDevices: oc.output(z.array(deviceOutputSchema)),
untrustDevice: oc
.input(z.object({ deviceId: z.number() }))
.output(successResponseSchema),
revokeAllTrustedDevices: oc.output(successResponseSchema),
// Org invites for the current user
invites: oc.router({
list: oc.output(z.array(userInviteOutputSchema)),
get: oc
.input(z.object({ inviteId: z.number() }))
.output(userInviteOutputSchema),
accept: oc
.input(z.object({ inviteId: z.number() }))
.output(successResponseSchema),
decline: oc
.input(z.object({ inviteId: z.number() }))
.output(successResponseSchema),
}),
// Sessions
sessions: oc.router({
list: oc.output(z.array(sessionOutputSchema)),
revoke: oc
.input(z.object({ sessionId: z.number() }))
.output(successResponseSchema),
revokeAll: oc.output(successResponseSchema),
}),
// Devices
devices: oc.router({
getInfo: oc.output(deviceOutputSchema),
trust: oc.input(trustDeviceInputSchema).output(successResponseSchema),
listTrusted: oc.output(z.array(deviceOutputSchema)),
untrust: oc
.input(z.object({ deviceId: z.number() }))
.output(successResponseSchema),
revokeAll: oc.output(successResponseSchema),
}),
// API tokens for CLI/programmatic access
apiTokens: oc.router({

View File

@@ -1,5 +1,6 @@
import * as z from "zod";
import { nonEmptyString, optionalString, phoneSchema } from "./common.js";
import { orgRoleSchema } from "./org.js";
/**
* User profile schema
@@ -132,3 +133,21 @@ export const authStatusOutputSchema = z.object({
sessionAuthStatusSchema,
]),
});
/**
* User invite output schema
* Returned by me.invites.list - includes org info for the user's pending invites
*/
export const userInviteOutputSchema = z.object({
id: z.number(),
org: z.object({
id: z.number(),
slug: z.string(),
displayName: z.string(),
logoUrl: z.string().nullable(),
}),
role: orgRoleSchema,
invitedBy: z.string(),
createdAt: z.date(),
expiresAt: z.date(),
});