Files
zooqueenandHanzo Dev dff97c65fb sqlite: name the enum columns by their type instead of restating them
The last of it, all one shape: sqlite has no enum, so those columns read back
as `string`, while hand-written prop and payload types restated the literals.
Each now names the type the shim already exports — DownloadType, ViewType —
so the column and the code that consumes it stay in step.

`linkType` reaches an outgoing webhook, whose contract is a zod enum and
should stay strict, so the assertion happens where the payload is built and
LINK_TYPES lives with the schema that defines it.

DocumentPage.embeddedLinks is the last postgres String[] that became Json;
duplicating a document read it back out and wrote it straight into the copy.

99 errors -> 0. Verified from a clean `prisma generate`.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-09 12:01:34 -07:00

176 lines
4.9 KiB
TypeScript

import { z } from "zod";
import { WEBHOOK_TRIGGERS } from "@/lib/webhook/constants";
export const createWebhookSchema = z.object({
name: z.string().min(1).max(40),
url: z.string().url().max(190),
secret: z.string().startsWith("whsec_"),
triggers: z.array(z.enum(WEBHOOK_TRIGGERS)),
});
export const updateWebhookSchema = createWebhookSchema.partial();
// Base event schema
// The link kinds this payload can carry. sqlite has no enum, so the column
// reads back as a plain string and the value is asserted against this list
// where the payload is built.
export const LINK_TYPES = [
"DOCUMENT_LINK",
"DATAROOM_LINK",
"WORKFLOW_LINK",
] as const;
const baseEventSchema = z.object({
id: z.string().startsWith("evt_"),
event: z.enum(WEBHOOK_TRIGGERS),
createdAt: z.string().datetime(),
});
// View Event schema
const viewEventSchema = z.object({
viewedAt: z.string().datetime(),
viewId: z.string(),
email: z.string().email().nullable(),
emailVerified: z.boolean().default(false),
country: z.string().nullable(),
city: z.string().nullable(),
device: z.string().nullable(),
browser: z.string().nullable(),
os: z.string().nullable(),
ua: z.string().nullable(),
referer: z.string().nullable(),
});
// Link Event schema
const linkEventSchema = z.object({
id: z.string(),
url: z
.string()
.describe(
"This is the full URL of the link e.g. https://dataroom.hanzo.ai/view/1234566",
),
name: z.string().nullable(),
domain: z.string(),
key: z.string(),
expiresAt: z.string().datetime().nullable(),
hasPassword: z.boolean().default(false),
allowList: z.array(z.string()),
denyList: z.array(z.string()),
enabledEmailProtection: z.boolean().default(false),
enabledEmailVerification: z.boolean().default(false),
allowDownload: z.boolean().default(false),
isArchived: z.boolean().default(false),
enabledNotification: z.boolean().default(true),
enabledFeedback: z.boolean().default(false),
enabledQuestion: z.boolean().default(false),
enabledScreenshotProtection: z.boolean().default(false),
enabledAgreement: z.boolean().default(false),
enabledWatermark: z.boolean().default(false),
metaTitle: z.string().nullable(),
metaDescription: z.string().nullable(),
metaImage: z.string().nullable(),
metaFavicon: z.string().nullable(),
documentId: z.string().nullable(),
dataroomId: z.string().nullable(),
groupId: z.string().nullable(),
linkType: z.enum(LINK_TYPES),
teamId: z.string(),
createdAt: z.string().datetime(),
updatedAt: z.string().datetime(),
});
// Document Event schema
const documentEventSchema = z.object({
id: z.string(),
name: z.string(),
contentType: z.string().nullable(),
teamId: z.string(),
createdAt: z.string().datetime(),
});
// Dataroom Event schema
const dataroomEventSchema = z.object({
id: z.string(),
name: z.string(),
teamId: z.string(),
createdAt: z.string().datetime(),
});
// link.created
export const linkCreatedWebhookSchema = z.object({
id: z.string().startsWith("evt_"),
event: z.literal("link.created"),
createdAt: z.string().datetime(),
data: z.object({
link: linkEventSchema,
document: documentEventSchema.optional(),
dataroom: dataroomEventSchema.optional(),
}),
});
// document.created
export const documentCreatedWebhookSchema = z.object({
id: z.string().startsWith("evt_"),
event: z.literal("document.created"),
createdAt: z.string().datetime(),
data: z.object({
document: documentEventSchema,
}),
});
// dataroom.created
export const dataroomCreatedWebhookSchema = z.object({
id: z.string().startsWith("evt_"),
event: z.literal("dataroom.created"),
createdAt: z.string().datetime(),
data: z.object({
dataroom: dataroomEventSchema,
}),
});
// link.viewed
export const linkViewedWebhookSchema = z.object({
id: z.string().startsWith("evt_"),
event: z.literal("link.viewed"),
createdAt: z.string().datetime(),
data: z.object({
view: viewEventSchema,
link: linkEventSchema,
document: documentEventSchema.optional(),
dataroom: dataroomEventSchema.optional(),
}),
});
export const webhookPayloadSchema = z.discriminatedUnion("event", [
linkCreatedWebhookSchema,
documentCreatedWebhookSchema,
dataroomCreatedWebhookSchema,
linkViewedWebhookSchema,
]);
export type WebhookPayload = z.infer<typeof webhookPayloadSchema>;
export type LinkCreatedWebhookPayload = z.infer<
typeof linkCreatedWebhookSchema
>;
export type DocumentCreatedWebhookPayload = z.infer<
typeof documentCreatedWebhookSchema
>;
export type DataroomCreatedWebhookPayload = z.infer<
typeof dataroomCreatedWebhookSchema
>;
// Schema of the response posted to the webhook callback URL
export const webhookCallbackSchema = z.object({
status: z.number(),
url: z.string(),
createdAt: z.number(),
sourceMessageId: z.string(),
body: z.string().optional().default(""), // Response from the original webhook URL
sourceBody: z.string(), // Original request payload from Hanzo Dataroom
});