import type { AuthenticationResponseJSON, PublicKeyCredentialCreationOptionsJSON, PublicKeyCredentialRequestOptionsJSON, RegistrationResponseJSON, } from "@simplewebauthn/types"; import { oc } from "@orpc/contract"; import * as 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, successResponseSchema, } from "./schemas/common.js"; import { createInviteInputSchema, createOrgInputSchema, orgInviteOutputSchema, orgMemberOutputSchema, orgOutputSchema, orgSiteOutputSchema, updateMemberRoleInputSchema, } from "./schemas/org.js"; import { authStatusOutputSchema, deviceOutputSchema, passkeyOutputSchema, sessionOutputSchema, setPasswordInputSchema, setupProfileInputSchema, trustDeviceInputSchema, updateProfileInputSchema, userInviteOutputSchema, 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(successResponseSchema), verifyEmail: oc.input(verifyEmailInputSchema).output(successResponseSchema), resendVerificationEmail: oc.output(successResponseSchema), // Login flow createLoginRequest: oc .input(loginRequestInputSchema) .output(loginRequestOutputSchema), loginPassword: oc .input(loginPasswordInputSchema) .output(successResponseSchema), loginPasswordConfirm: oc .input(z.object({ token: z.string() })) .output(successResponseSchema), loginIfRequestIsCompleted: oc.output(loginStatusOutputSchema), // Password reset forgotPassword: oc .input(forgotPasswordInputSchema) .output(successResponseSchema), resetPassword: oc .input(resetPasswordInputSchema) .output(successResponseSchema), // Logout logout: oc.output(successResponseSchema), // WebAuthn procedures webauthn: oc.router({ createRegistrationOptions: oc .input(z.object({ email: emailSchema })) .output( z.object({ challengeId: z.number(), options: z.custom(), }), ), verifyRegistration: oc .input( z.object({ challengeId: z.number(), response: z.custom(), }), ) .output(z.object({ passkeyId: z.number() })), createAuthenticationOptions: oc.output( z.object({ challengeId: z.number(), options: z.custom(), }), ), verifyAuthentication: oc .input( z.object({ challengeId: z.number(), response: z.custom(), }), ) .output(successResponseSchema), }), }), me: oc.router({ // Profile get: oc.output(userProfileSchema), setupProfile: oc .input(setupProfileInputSchema) .output(successResponseSchema), updateProfile: oc .input(updateProfileInputSchema) .output(successResponseSchema), delete: oc .input(z.object({ password: z.string() })) .output(successResponseSchema), // Auth status (for CLI and debugging) authStatus: oc.output(authStatusOutputSchema), // Authentication settings setPassword: oc.input(setPasswordInputSchema).output(successResponseSchema), // Passkeys passkeys: oc.router({ list: oc.output(z.array(passkeyOutputSchema)), rename: oc .input(z.object({ passkeyId: z.number(), name: z.string() })) .output(successResponseSchema), delete: oc .input(z.object({ passkeyId: z.number() })) .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 & 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), }), 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(successResponseSchema), delete: oc .input(z.object({ slug: slugSchema })) .output(successResponseSchema), leave: oc .input(z.object({ slug: slugSchema })) .output(successResponseSchema), // Members members: oc.router({ list: oc .input(z.object({ slug: slugSchema })) .output(z.array(orgMemberOutputSchema)), updateRole: oc .input(updateMemberRoleInputSchema) .output(successResponseSchema), remove: oc .input(z.object({ slug: slugSchema, userId: z.number() })) .output(successResponseSchema), }), // Invites invites: oc.router({ list: oc .input(z.object({ slug: slugSchema })) .output(z.array(orgInviteOutputSchema)), create: oc.input(createInviteInputSchema).output(successResponseSchema), cancel: oc .input(z.object({ slug: slugSchema, inviteId: z.number() })) .output(successResponseSchema), accept: oc .input(z.object({ token: z.string() })) .output(successResponseSchema), }), // 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(successResponseSchema), delete: oc .input(z.object({ slug: slugSchema })) .output(successResponseSchema), listSites: oc .input(z.object({ slug: slugSchema })) .output(z.array(orgSiteOutputSchema)), addSite: oc.input(adminAddSiteInputSchema).output(successResponseSchema), removeSite: oc .input(z.object({ slug: slugSchema, domain: z.string() })) .output(successResponseSchema), }), // 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(successResponseSchema), update: oc .input(adminUpdateUserInputSchema) .output(successResponseSchema), confirmEmail: oc .input(z.object({ email: emailSchema })) .output(successResponseSchema), }), // Admin auth management auth: oc.router({ completeLogin: oc .input(z.object({ email: emailSchema })) .output(successResponseSchema), }), }), }); export type APIContract = typeof contract;