import { z } from 'zod' import { workspaceIdSchema } from '@/lib/api/contracts/primitives' import type { ContractBody, ContractBodyInput, ContractJsonResponse, } from '@/lib/api/contracts/types' import { defineRouteContract } from '@/lib/api/contracts/types' export const oauthAccountSummarySchema = z.object({ id: z.string(), name: z.string(), }) export type OAuthAccountSummary = z.output export const oauthConnectionSchema = z.object({ provider: z.string(), baseProvider: z.string(), featureType: z.string(), isConnected: z.boolean(), accounts: z.array(oauthAccountSummarySchema), lastConnected: z.string(), scopes: z.array(z.string()), }) export type OAuthConnection = z.output export const disconnectOAuthBodySchema = z.object({ provider: z.string({ error: 'Provider is required' }).min(1, 'Provider is required'), providerId: z.string().optional(), accountId: z.string().optional(), }) export const connectedAccountsQuerySchema = z.object({ provider: z.string().min(1).optional(), }) export const connectedAccountSchema = z.object({ id: z.string(), accountId: z.string(), providerId: z.string(), displayName: z.string(), }) export type ConnectedAccount = z.output export const trelloTokenBodySchema = z.object({ token: z.string().min(1), state: z.string().min(1, 'state is required'), }) const emptyTrelloAuthQuerySchema = z.object({}).passthrough() const trelloCallbackQuerySchema = z .object({ state: z.string().min(1).optional(), error: z.string().min(1).optional(), }) .passthrough() export const oauthTokenRequestBodySchema = z .object({ credentialId: z.string().min(1).optional(), credentialAccountUserId: z.string().min(1).optional(), providerId: z.string().min(1).optional(), workflowId: z.string().min(1).nullish(), scopes: z.array(z.string()).optional(), impersonateEmail: z.string().email().optional(), }) .refine( (data) => data.credentialId || (data.credentialAccountUserId && data.providerId), 'Either credentialId or (credentialAccountUserId + providerId) is required' ) export const oauthTokenGetQuerySchema = z.object({ credentialId: z .string({ error: 'Credential ID is required', }) .min(1, 'Credential ID is required'), }) export const oauthTokenPostQuerySchema = z.object({ userId: z.string().min(1).optional(), }) const oauthTokenResponseSchema = z.object({ accessToken: z.string(), idToken: z.string().optional(), instanceUrl: z.string().optional(), cloudId: z.string().optional(), domain: z.string().optional(), }) export const oauthTokenGetContract = defineRouteContract({ method: 'GET', path: '/api/auth/oauth/token', query: oauthTokenGetQuerySchema, response: { mode: 'json', schema: oauthTokenResponseSchema, }, }) export const oauthTokenPostContract = defineRouteContract({ method: 'POST', path: '/api/auth/oauth/token', query: oauthTokenPostQuerySchema, body: oauthTokenRequestBodySchema, response: { mode: 'json', schema: oauthTokenResponseSchema, }, }) export const shopifyAuthorizeQuerySchema = z.object({ shop: z.string().optional(), returnUrl: z.string().optional(), }) export const shopifyCallbackQuerySchema = z.object({ code: z.string().optional(), state: z.string().optional(), shop: z.string().optional(), }) export const shopifyStoreCookieSchema = z.object({ accessToken: z.string().min(1), shopDomain: z.string().min(1), scope: z.string().optional(), returnUrl: z.string().optional(), }) const SHOPIFY_SHOP_DOMAIN_REGEX = /^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]\.myshopify\.com$/ export const shopifyShopDomainSchema = z.string().regex(SHOPIFY_SHOP_DOMAIN_REGEX) export const listOAuthConnectionsContract = defineRouteContract({ method: 'GET', path: '/api/auth/oauth/connections', response: { mode: 'json', schema: z.object({ connections: z.array(oauthConnectionSchema), }), }, }) export const disconnectOAuthContract = defineRouteContract({ method: 'POST', path: '/api/auth/oauth/disconnect', body: disconnectOAuthBodySchema, response: { mode: 'json', schema: z.object({ success: z.literal(true), }), }, }) export const listConnectedAccountsContract = defineRouteContract({ method: 'GET', path: '/api/auth/accounts', query: connectedAccountsQuerySchema, response: { mode: 'json', schema: z.object({ accounts: z.array(connectedAccountSchema), }), }, }) export const storeTrelloTokenContract = defineRouteContract({ method: 'POST', path: '/api/auth/trello/store', body: trelloTokenBodySchema, response: { mode: 'json', schema: z.object({ success: z.boolean(), error: z.string().optional() }), }, }) export const authorizeTrelloContract = defineRouteContract({ method: 'GET', path: '/api/auth/trello/authorize', query: emptyTrelloAuthQuerySchema, response: { mode: 'redirect' }, }) export const trelloCallbackContract = defineRouteContract({ method: 'GET', path: '/api/auth/trello/callback', query: trelloCallbackQuerySchema, response: { mode: 'text' }, }) export const authorizeOAuth2QuerySchema = z.object({ providerId: z.string().min(1, 'providerId is required'), workspaceId: workspaceIdSchema, callbackURL: z.string().min(1).optional(), }) export const authorizeOAuth2Contract = defineRouteContract({ method: 'GET', path: '/api/auth/oauth2/authorize', query: authorizeOAuth2QuerySchema, response: { mode: 'redirect' }, }) export type StoreTrelloTokenBody = ContractBody export type StoreTrelloTokenBodyInput = ContractBodyInput export type StoreTrelloTokenResponse = ContractJsonResponse