import { ParameterizedContext, DefaultContext } from "koa"; import { IRouterParamContext } from "koa-router"; import { InferAttributes, Model, Transaction } from "sequelize"; import { z } from "zod"; import { CollectionSort, NavigationNode, Client, CollectionPermission, JSONValue, UnfurlResourceType, ProsemirrorData, UnfurlResponse, } from "@shared/types"; import { BaseSchema } from "@server/routes/api/schema"; import { AccountProvisionerResult } from "./commands/accountProvisioner"; import type { ApiKey, Attachment, AuthenticationProvider, FileOperation, Revision, Team, User, UserMembership, WebhookSubscription, Pin, Star, Document, Collection, Group, Integration, Comment, Subscription, View, Notification, Share, GroupMembership, Import, OAuthClient, } from "./models"; export enum AuthenticationType { API = "api", APP = "app", OAUTH = "oauth", } export type AuthenticationResult = AccountProvisionerResult & { client: Client; }; export type Authentication = { user: User; token: string; type?: AuthenticationType; }; export type Pagination = { limit: number; offset: number; nextPath: string; }; export type AppState = { auth: Authentication | Record; transaction: Transaction; pagination: Pagination; }; export type AppContext = ParameterizedContext; export type BaseReq = z.infer; export type BaseRes = unknown; export interface APIContext extends ParameterizedContext< AppState, DefaultContext & IRouterParamContext, ResT > { /** Typed and validated version of request, consisting of validated body, query, etc. */ input: ReqT; /** The current request's context, which is passed to database mutations. */ context: { transaction?: Transaction; auth: Authentication; ip?: string; }; } type BaseEvent = { teamId: string; actorId: string; ip: string | null; authType?: AuthenticationType | null; changes?: { attributes: Partial>; previous: Partial>; } | null; }; export type ApiKeyEvent = BaseEvent & { name: "api_keys.create" | "api_keys.delete"; modelId: string; data: { name: string; }; }; export type AttachmentEvent = BaseEvent & ( | { name: "attachments.create"; modelId: string; data: { name: string; source?: "import"; }; } | { name: "attachments.update"; modelId: string; } | { name: "attachments.delete"; modelId: string; data: { name: string; }; } ); export type AuthenticationProviderEvent = BaseEvent & { name: "authenticationProviders.update"; modelId: string; data: { enabled: boolean; }; }; export type UserEvent = BaseEvent & ( | { name: | "users.signin" | "users.signout" | "users.update" | "users.suspend" | "users.activate" | "users.delete"; userId: string; } | { name: "users.create" | "users.promote" | "users.demote"; userId: string; data: { name: string; }; } | { name: "users.invite"; userId: string; data: { email: string; name: string; }; } ); export type UserMembershipEvent = BaseEvent & { name: "userMemberships.update"; modelId: string; userId: string; documentId: string; data: { index: string | null; }; }; export type DocumentMovedEvent = BaseEvent & { name: "documents.move"; documentId: string; collectionId: string; data: { collectionIds: string[]; documentIds: string[]; }; }; export type DocumentEvent = BaseEvent & ( | { name: | "documents.create" | "documents.publish" | "documents.delete" | "documents.permanent_delete" | "documents.archive" | "documents.restore"; documentId: string; collectionId: string; data: { title: string; source?: "import"; }; } | { name: "documents.unpublish"; documentId: string; collectionId: string; } | { name: "documents.unarchive"; documentId: string; collectionId: string; data: { title: string; /** Id of collection from which the document is unarchived */ sourceCollectionId: string; }; } | { name: | "documents.update" | "documents.update.delayed" | "documents.update.debounced"; documentId: string; collectionId: string; createdAt: string; data: { title: string; autosave: boolean; done: boolean; }; } | { name: "documents.title_change"; documentId: string; collectionId: string; createdAt: string; data: { title: string; previousTitle: string; }; } | DocumentMovedEvent ); export type EmptyTrashEvent = { name: "documents.empty_trash"; teamId: string; actorId: string; }; export type RevisionEvent = BaseEvent & { name: "revisions.create"; documentId: string; modelId: string; }; export type FileOperationEvent = BaseEvent & { name: | "fileOperations.create" | "fileOperations.update" | "fileOperations.delete"; modelId: string; data: Partial; }; export type CollectionUserEvent = BaseEvent & { name: "collections.add_user" | "collections.remove_user"; userId: string; modelId: string; collectionId: string; data: { isNew?: boolean; }; }; export type CollectionGroupEvent = BaseEvent & { name: "collections.add_group" | "collections.remove_group"; collectionId: string; modelId: string; data: { membershipId: string }; }; export type DocumentUserEvent = BaseEvent & { name: "documents.add_user" | "documents.remove_user"; userId: string; modelId: string; documentId: string; data: { isNew?: boolean; }; }; export type DocumentGroupEvent = BaseEvent & { name: "documents.add_group" | "documents.remove_group"; documentId: string; modelId: string; data: { isNew?: boolean; membershipId: string; }; }; export type CollectionEvent = BaseEvent & { name: | "collections.create" | "collections.update" | "collections.delete" | "collections.archive" | "collections.restore" | "collections.move" | "collections.permission_changed"; collectionId: string; }; export type GroupUserEvent = BaseEvent & { name: "groups.add_user" | "groups.remove_user"; userId: string; modelId: string; }; export type GroupEvent = BaseEvent & ( | GroupUserEvent | { name: "groups.create" | "groups.delete" | "groups.update"; modelId: string; } ); export type IntegrationEvent = BaseEvent & { name: "integrations.create" | "integrations.update" | "integrations.delete"; modelId: string; }; export type TeamEvent = BaseEvent & { name: "teams.create" | "teams.update" | "teams.delete" | "teams.destroy"; }; export type PinEvent = BaseEvent & { name: "pins.create" | "pins.update" | "pins.delete"; modelId: string; documentId: string; collectionId?: string; }; export type CommentUpdateEvent = BaseEvent & { name: "comments.update"; modelId: string; documentId: string; actorId: string; data?: { newMentionIds: string[]; }; }; export type CommentReactionEvent = BaseEvent & { name: "comments.add_reaction" | "comments.remove_reaction"; modelId: string; documentId: string; data: { emoji: string; }; }; export type CommentEvent = | (BaseEvent & { name: "comments.create"; modelId: string; documentId: string; actorId: string; }) | CommentUpdateEvent | (BaseEvent & { name: "comments.delete"; modelId: string; documentId: string; actorId: string; collectionId: string; }) | CommentReactionEvent; export type StarEvent = BaseEvent & { name: "stars.create" | "stars.update" | "stars.delete"; modelId: string; documentId: string; userId: string; }; export type ShareEvent = BaseEvent & { name: "shares.create" | "shares.update" | "shares.revoke"; modelId: string; documentId: string; collectionId?: string; }; export type SubscriptionEvent = BaseEvent & { name: "subscriptions.create" | "subscriptions.delete"; modelId: string; userId: string; documentId: string | null; collectionId: string | null; }; export type ViewEvent = BaseEvent & { name: "views.create"; documentId: string; collectionId: string; modelId: string; data: { title: string; }; }; export type WebhookDeliveryStatus = "pending" | "success" | "failed"; export type WebhookSubscriptionEvent = BaseEvent & { name: | "webhookSubscriptions.create" | "webhookSubscriptions.delete" | "webhookSubscriptions.update"; modelId: string; data: { name: string; url: string; events: string[]; }; }; export type NotificationEvent = BaseEvent & { name: "notifications.create" | "notifications.update"; modelId: string; teamId: string; userId: string; actorId: string; commentId?: string; documentId?: string; collectionId?: string; membershipId?: string; }; export type OAuthClientEvent = BaseEvent & { name: "oauthClients.create" | "oauthClients.update" | "oauthClients.delete"; modelId: string; }; // oxlint-disable-next-line @typescript-eslint/no-explicit-any export type ImportEvent = BaseEvent> & { name: | "imports.create" | "imports.update" | "imports.processed" | "imports.delete"; modelId: string; }; export type Event = | ApiKeyEvent | AttachmentEvent | AuthenticationProviderEvent | DocumentEvent | DocumentUserEvent | DocumentMovedEvent | DocumentGroupEvent | PinEvent | CommentEvent | StarEvent | CollectionEvent | CollectionUserEvent | CollectionGroupEvent | FileOperationEvent | IntegrationEvent | GroupEvent | RevisionEvent | ShareEvent | SubscriptionEvent | TeamEvent | UserEvent | UserMembershipEvent | ViewEvent | WebhookSubscriptionEvent | NotificationEvent | OAuthClientEvent | EmptyTrashEvent | ImportEvent; export type NotificationMetadata = { notificationId?: string; }; export type JSONExportMetadata = { /** The version of the export, allows updated structure in the future. */ exportVersion: number; /** The version of the application that created the export. */ version: string; /** The date the export was created. */ createdAt: string; /** The ID of the user that created the export. */ createdById: string; /** The email of the user that created the export. */ createdByEmail: string | null; }; export type DocumentJSONExport = { id: string; urlId: string; title: string; /** * For backward compatibility, maintain the `emoji` field. * Future exports will use the `icon` field. * */ emoji?: string | null; icon: string | null; color: string | null; data: ProsemirrorData; createdById: string; createdByName: string; createdByEmail: string | null; createdAt: string; updatedAt: string; publishedAt: string | null; fullWidth: boolean; template: boolean; parentDocumentId: string | null; }; export type AttachmentJSONExport = { id: string; documentId: string | null; contentType: string; name: string; size: number; key: string; }; export type CollectionJSONExport = { collection: { id: string; urlId: string; name: string; data?: ProsemirrorData | null; description?: string | null; permission?: CollectionPermission | null; color?: string | null; icon?: string | null; sort: CollectionSort; documentStructure: NavigationNode[] | null; }; documents: { [id: string]: DocumentJSONExport; }; attachments: { [id: string]: AttachmentJSONExport; }; }; export type UnfurlIssueOrPR = | UnfurlResponse[UnfurlResourceType.Issue] | UnfurlResponse[UnfurlResourceType.PR]; export type Unfurl = | UnfurlIssueOrPR | { type: Exclude< UnfurlResourceType, UnfurlResourceType.Issue | UnfurlResourceType.PR >; [x: string]: JSONValue; }; export type UnfurlError = { error: string }; export type UnfurlSignature = ( url: string, actor?: User ) => Promise; export type UninstallSignature = (integration: Integration) => Promise; export type Replace = { [P in keyof T as P extends K ? N : P]: T[P extends K ? K : P]; };