import type { IUser, LicenseModule } from '@rocket.chat/core-typings'; import type { Logger } from '@rocket.chat/logger'; import type { Method, MethodOf, OperationParams, OperationResult, PathPattern, UrlParams } from '@rocket.chat/rest-typings'; import type { ValidateFunction } from 'ajv'; import type { ITwoFactorOptions } from '../../2fa/server/code'; import type { DeprecationLoggerNextPlannedVersion } from '../../lib/server/lib/deprecationWarningLogger'; export type SuccessStatusCodes = Exclude, Range<200>>; export type RedirectStatusCodes = Exclude, Range<300>>; export type AuthorizationStatusCodes = Exclude, Range<400>>; export type ErrorStatusCodes = Exclude, Range<500>>, 509>; export type SuccessResult = { statusCode: TStatusCode; body: T extends object ? { success: true } & T : T; }; export type FailureResult = { statusCode: 400; body: T extends object ? { success: false } & T : { success: false; error?: T; stack?: TStack; errorType?: TErrorType; details?: TErrorDetails; message?: string; } & (undefined extends TErrorType ? object : { errorType: TErrorType }) & (undefined extends TErrorDetails ? object : { details: TErrorDetails extends string ? unknown : TErrorDetails }); }; export type RedirectResult = { statusCode: TStatusCode; body: T; }; export type UnauthorizedResult = { statusCode: 401; body: { success: false; error: T | 'unauthorized'; }; }; export type ForbiddenResult = { statusCode: 403; body: { success: false; // TODO: MAJOR remove 'unauthorized' error: T | 'forbidden' | 'unauthorized'; }; }; export type InternalError = { statusCode: StatusCode; body: { error: T | D; success: false; }; }; export type UnavailableResult = { statusCode: StatusCode; body: { error: T | 'Service Unavailable'; success: false; }; }; export type NotFoundResult = { statusCode: 404; body: { success: false; error: T; }; }; export type TOperation = 'hasAll' | 'hasAny'; export type NonEnterpriseTwoFactorOptions = { authRequired: true; forceTwoFactorAuthenticationForNonEnterprise: true; twoFactorRequired: true; permissionsRequired?: string[] | { [key in Method]: string[] } | { [key in Method]: { operation: TOperation; permissions: string[] } }; twoFactorOptions: ITwoFactorOptions; }; export type Options = SharedOptions<'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'>; export type SharedOptions = ( | { permissionsRequired?: | string[] | ({ [key in TMethod]?: string[] } & { '*'?: string[] }) | ({ [key in TMethod]?: { operation: TOperation; permissions: string[] } } & { '*'?: { operation: TOperation; permissions: string[] }; }); authRequired?: boolean; forceTwoFactorAuthenticationForNonEnterprise?: boolean; rateLimiterOptions?: | { numRequestsAllowed?: number; intervalTimeInMS?: number; } | boolean; queryOperations?: string[]; queryFields?: string[]; } | { permissionsRequired?: | string[] | ({ [key in TMethod]?: string[] } & { '*'?: string[] }) | ({ [key in TMethod]?: { operation: TOperation; permissions: string[] } } & { '*'?: { operation: TOperation; permissions: string[] }; }); authRequired: true; twoFactorRequired: true; twoFactorOptions?: ITwoFactorOptions; rateLimiterOptions?: | { numRequestsAllowed?: number; intervalTimeInMS?: number; } | boolean; queryOperations?: string[]; queryFields?: string[]; } ) & { /** * @deprecated The `validateParams` option is deprecated. Use `query` and/OR `body` instead. */ validateParams?: ValidateFunction | { [key in TMethod]?: ValidateFunction }; authOrAnonRequired?: true; deprecation?: { version: DeprecationLoggerNextPlannedVersion; alternatives?: PathPattern[]; }; }; export type GenericRouteExecutionContext = ActionThis; export type RouteExecutionContext = ActionThis< TMethod, TPathPattern, TOptions >; export type ActionThis = { readonly logger: Logger; route: string; readonly requestIp: string; urlParams: UrlParams; readonly response: Response; // TODO make it unsafe readonly queryParams: TMethod extends 'GET' ? TOptions extends { validateParams: ValidateFunction } ? T : TOptions extends { validateParams: { GET: ValidateFunction } } ? T : Partial> & { offset?: number; count?: number } : Record; // TODO make it unsafe readonly bodyParams: TMethod extends 'GET' ? Record : TOptions extends { validateParams: ValidateFunction } ? T : TOptions extends { validateParams: infer V } ? V extends { [key in TMethod]: ValidateFunction } ? T : Partial> : // TODO remove the extra (optionals) params when all the endpoints that use these are typed correctly Partial>; readonly request: Request; readonly queryOperations: TOptions extends { queryOperations: infer T } ? T : never; readonly queryFields: TOptions extends { queryFields: infer T } ? T : never; parseJsonQuery(): Promise<{ sort: Record; /** * @deprecated To access "fields" parameter, use ALLOW_UNSAFE_QUERY_AND_FIELDS_API_PARAMS environment variable. */ fields: Record; /** * @deprecated To access "query" parameter, use ALLOW_UNSAFE_QUERY_AND_FIELDS_API_PARAMS environment variable. */ query: Record; }>; readonly connection: { token: string; id: string; close: () => void; clientAddress: string; httpHeaders: Record; }; } & (TOptions extends { authRequired: true } ? { user: IUser; userId: string; readonly token: string; } : TOptions extends { authOrAnonRequired: true } ? { user?: IUser; userId?: string; readonly token?: string; } : { user?: IUser | null; userId?: string | undefined; readonly token?: string; }); export type ResultFor = ( | SuccessResult> | FailureResult | UnauthorizedResult | NotFoundResult | { statusCode: number; body: unknown; } ) & { headers?: Record; }; export type Action = | ((this: ActionThis) => Promise>) | ((this: ActionThis) => ResultFor); export type InnerAction = Action extends (this: infer This) => infer Result ? This extends ActionThis ? (this: Mutable) => Result : never : never; type Mutable = { -readonly [key in keyof Immutable]: Immutable[key]; }; type Operation = | Action | ({ action: Action; } & { twoFactorRequired: boolean }); type ActionOperation = { action: Action; } & TEndpointOptions; export type Operations = { [M in MethodOf as Lowercase]: Operation, TPathPattern, TOptions>; }; export type ActionOperations = { [M in MethodOf as Lowercase]: ActionOperation, TPathPattern, TOptions>; }; type Range = Result['length'] extends N ? Result[number] : Range; type HTTPStatusCodes = SuccessStatusCodes | RedirectStatusCodes | AuthorizationStatusCodes | ErrorStatusCodes; export type TypedOptions = { response: { [K in HTTPStatusCodes]?: ValidateFunction; }; query?: ValidateFunction; body?: ValidateFunction; tags?: string[]; typed?: boolean; license?: LicenseModule[]; } & SharedOptions<'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'>; export type TypedThis = { userId: TOptions['authRequired'] extends true ? string : string | undefined; user: TOptions['authRequired'] extends true ? IUser : IUser | null; token: TOptions['authRequired'] extends true ? string : string | undefined; queryParams: TOptions['query'] extends ValidateFunction ? Query : never; urlParams: UrlParams extends Record ? UrlParams : never; parseJsonQuery(): Promise<{ sort: Record; /** * @deprecated To access "fields" parameter, use ALLOW_UNSAFE_QUERY_AND_FIELDS_API_PARAMS environment variable. */ fields: Record; /** * @deprecated To access "query" parameter, use ALLOW_UNSAFE_QUERY_AND_FIELDS_API_PARAMS environment variable. */ query: Record; }>; bodyParams: TOptions['body'] extends ValidateFunction ? Body : never; requestIp?: string; route: string; response: Response; }; type PromiseOrValue = T | Promise; type InferResult = TResult extends ValidateFunction ? T : TResult; type InferNon200Result = InferResult extends { success: false; error?: infer TError; } ? TError : never; type Results = { [K in keyof TResponse]: K extends SuccessStatusCodes ? SuccessResult, K> : K extends RedirectStatusCodes ? RedirectResult, K> : K extends 400 ? FailureResult> : K extends 401 ? UnauthorizedResult> : K extends 403 ? ForbiddenResult> : K extends 404 ? NotFoundResult> : K extends ErrorStatusCodes ? InternalError, K> : never; }[keyof TResponse] & { headers?: Record; }; export type TypedAction< TOptions extends TypedOptions, TPath extends string = '', TThis extends TypedThis = TypedThis, > = (this: TThis, request: Request) => PromiseOrValue>;