Files
publisher-dashboard/packages/api-contract/src/contract.ts
RevIQ 2e9ea74bc9 Connect frontend to oRPC server
- Add Vite proxy to forward /api/v1/rpc to API server (port 9861)
- Create oRPC client in src/lib/api/client.ts
- Add @orpc/client and @orpc/contract dependencies
- Add @reviq/api-contract workspace dependency
- Extract DEFAULT_PORT constant to api-server/src/constants.ts
- Change API server default port from 3001 to 9861

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

237 lines
7.2 KiB
TypeScript

import { oc } from "@orpc/contract";
import { z } from "zod";
import {
adminAddSiteInputSchema,
adminCreateOrgInputSchema,
adminCreateUserInputSchema,
adminUpdateUserInputSchema,
} from "./schemas/admin.js";
import {
forgotPasswordInputSchema,
loginPasswordInputSchema,
loginRequestInputSchema,
loginRequestOutputSchema,
loginStatusOutputSchema,
resetPasswordInputSchema,
signupInputSchema,
verifyEmailInputSchema,
} from "./schemas/auth.js";
import { emailSchema, slugSchema } from "./schemas/common.js";
import {
createInviteInputSchema,
createOrgInputSchema,
orgInviteOutputSchema,
orgMemberOutputSchema,
orgOutputSchema,
orgSiteOutputSchema,
updateMemberRoleInputSchema,
} from "./schemas/org.js";
import {
deviceOutputSchema,
passkeyOutputSchema,
sessionOutputSchema,
setPasswordInputSchema,
setupProfileInputSchema,
trustDeviceInputSchema,
updateProfileInputSchema,
userProfileSchema,
} from "./schemas/user.js";
/**
* oRPC API Contract for the Publisher Dashboard
* This defines all RPC procedure signatures served at /api/v1/rpc
*/
export const contract = oc.router({
auth: oc.router({
// Signup and verification
signup: oc.input(signupInputSchema).output(z.void()),
verifyEmail: oc.input(verifyEmailInputSchema).output(z.void()),
resendVerificationEmail: oc.output(z.void()),
// Login flow
createLoginRequest: oc
.input(loginRequestInputSchema)
.output(loginRequestOutputSchema),
loginPassword: oc.input(loginPasswordInputSchema).output(z.void()),
loginPasswordConfirm: oc
.input(z.object({ token: z.string() }))
.output(z.void()),
loginIfRequestIsCompleted: oc.output(loginStatusOutputSchema),
// Password reset
forgotPassword: oc.input(forgotPasswordInputSchema).output(z.void()),
resetPassword: oc.input(resetPasswordInputSchema).output(z.void()),
// Logout
logout: oc.output(z.void()),
// WebAuthn procedures
webauthn: oc.router({
createRegistrationOptions: oc
.input(z.object({ email: emailSchema }))
.output(
z.object({
challengeId: z.number(),
options: z.any(), // PublicKeyCredentialCreationOptionsJSON
}),
),
verifyRegistration: oc
.input(
z.object({
challengeId: z.number(),
response: z.any(), // RegistrationResponseJSON
}),
)
.output(z.void()),
createAuthenticationOptions: oc.output(
z.object({
challengeId: z.number(),
options: z.any(), // PublicKeyCredentialRequestOptionsJSON
}),
),
verifyAuthentication: oc
.input(
z.object({
challengeId: z.number(),
response: z.any(), // AuthenticationResponseJSON
}),
)
.output(z.void()),
}),
}),
me: oc.router({
// Profile
get: oc.output(userProfileSchema),
setupProfile: oc.input(setupProfileInputSchema).output(z.void()),
updateProfile: oc.input(updateProfileInputSchema).output(z.void()),
delete: oc.input(z.object({ password: z.string() })).output(z.void()),
// Authentication settings
setPassword: oc.input(setPasswordInputSchema).output(z.void()),
listPasskeys: oc.output(z.array(passkeyOutputSchema)),
createPasskey: oc.input(z.object({ name: z.string() })).output(
z.object({
challengeId: z.number(),
options: z.any(), // PublicKeyCredentialCreationOptionsJSON
}),
),
renamePasskey: oc
.input(z.object({ passkeyId: z.number(), name: z.string() }))
.output(z.void()),
deletePasskey: oc
.input(z.object({ passkeyId: z.number() }))
.output(z.void()),
// Sessions & devices
listSessions: oc.output(z.array(sessionOutputSchema)),
revokeSession: oc
.input(z.object({ sessionId: z.number() }))
.output(z.void()),
revokeAllSessions: oc.output(z.void()),
getDeviceInfo: oc.output(deviceOutputSchema),
trustDevice: oc.input(trustDeviceInputSchema).output(z.void()),
listTrustedDevices: oc.output(z.array(deviceOutputSchema)),
untrustDevice: oc
.input(z.object({ deviceId: z.number() }))
.output(z.void()),
revokeAllTrustedDevices: oc.output(z.void()),
}),
orgs: oc.router({
// Org management
list: oc.output(z.array(orgOutputSchema)),
create: oc
.input(createOrgInputSchema)
.output(z.object({ slug: z.string() })),
get: oc.input(z.object({ slug: slugSchema })).output(orgOutputSchema),
update: oc
.input(
z.object({
slug: slugSchema,
displayName: z.string().optional(),
logoUrl: z.string().optional(),
}),
)
.output(z.void()),
delete: oc.input(z.object({ slug: slugSchema })).output(z.void()),
leave: oc.input(z.object({ slug: slugSchema })).output(z.void()),
// Members
members: oc.router({
list: oc
.input(z.object({ slug: slugSchema }))
.output(z.array(orgMemberOutputSchema)),
updateRole: oc.input(updateMemberRoleInputSchema).output(z.void()),
remove: oc
.input(z.object({ slug: slugSchema, userId: z.number() }))
.output(z.void()),
}),
// Invites
invites: oc.router({
list: oc
.input(z.object({ slug: slugSchema }))
.output(z.array(orgInviteOutputSchema)),
create: oc.input(createInviteInputSchema).output(z.void()),
cancel: oc
.input(z.object({ slug: slugSchema, inviteId: z.number() }))
.output(z.void()),
accept: oc.input(z.object({ token: z.string() })).output(z.void()),
}),
// Sites
sites: oc.router({
list: oc
.input(z.object({ slug: slugSchema }))
.output(z.array(orgSiteOutputSchema)),
}),
}),
admin: oc.router({
// Admin org management
orgs: oc.router({
list: oc.output(z.array(orgOutputSchema)),
get: oc.input(z.object({ slug: slugSchema })).output(orgOutputSchema),
create: oc
.input(adminCreateOrgInputSchema)
.output(z.object({ slug: z.string() })),
update: oc
.input(
z.object({
slug: slugSchema,
displayName: z.string().optional(),
logoUrl: z.string().optional(),
}),
)
.output(z.void()),
delete: oc.input(z.object({ slug: slugSchema })).output(z.void()),
listSites: oc
.input(z.object({ slug: slugSchema }))
.output(z.array(orgSiteOutputSchema)),
addSite: oc.input(adminAddSiteInputSchema).output(z.void()),
removeSite: oc
.input(z.object({ slug: slugSchema, domain: z.string() }))
.output(z.void()),
}),
// Admin user management
users: oc.router({
list: oc.output(z.array(userProfileSchema)),
get: oc.input(z.object({ email: emailSchema })).output(userProfileSchema),
create: oc.input(adminCreateUserInputSchema).output(z.void()),
update: oc.input(adminUpdateUserInputSchema).output(z.void()),
confirmEmail: oc.input(z.object({ email: emailSchema })).output(z.void()),
}),
// Admin auth management
auth: oc.router({
completeLogin: oc
.input(z.object({ email: emailSchema }))
.output(z.void()),
}),
}),
});
export type APIContract = typeof contract;