chore: initial commit
Signed-off-by: Alan Brault <alan.brault@visus.io>
This commit is contained in:
0
src/app/v1/pages/[page_id]/route.ts
Normal file
0
src/app/v1/pages/[page_id]/route.ts
Normal file
10
src/lib/schemas/index.ts
Normal file
10
src/lib/schemas/index.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export * from './notion/block.schema';
|
||||
export * from './notion/colors';
|
||||
export * from './notion/emoji.schema';
|
||||
export * from './notion/file.schema';
|
||||
export * from './notion/fileUpload.schema';
|
||||
export * from './notion/page.schema';
|
||||
export * from './notion/pageProperties.schema';
|
||||
export * from './notion/parent.schema';
|
||||
export * from './notion/richText.schema';
|
||||
export * from './notion/user.schema';
|
||||
129
src/lib/schemas/notion/block.schema.ts
Normal file
129
src/lib/schemas/notion/block.schema.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
import { z } from 'zod';
|
||||
import { emojiSchema, fileSchema, NOTION_COLORS, parentSchema, richTextSchema, userSchema } from '@/lib/schemas';
|
||||
|
||||
const headingsObject = z.object({
|
||||
rich_text: z.array(richTextSchema),
|
||||
color: z.enum(NOTION_COLORS),
|
||||
is_toggleable: z.boolean(),
|
||||
});
|
||||
|
||||
/**
|
||||
* Schema representing a Notion block object.
|
||||
*/
|
||||
export const blockSchema = z.object({
|
||||
object: z.literal('block'),
|
||||
id: z.uuid(),
|
||||
parent: parentSchema,
|
||||
type: z.enum([
|
||||
'audio',
|
||||
'bookmark',
|
||||
'breadcrumb',
|
||||
'bulleted_list_item',
|
||||
'callout',
|
||||
'child_database',
|
||||
'child_page',
|
||||
'column',
|
||||
'column_list',
|
||||
'divider',
|
||||
'embed',
|
||||
'equation',
|
||||
'file',
|
||||
'heading_1',
|
||||
'heading_2',
|
||||
'heading_3',
|
||||
'image',
|
||||
'link_preview',
|
||||
'numbered_list_item',
|
||||
'paragraph',
|
||||
'pdf',
|
||||
'quote',
|
||||
'synced_block',
|
||||
'table',
|
||||
'table_of_contents',
|
||||
'table_row',
|
||||
'template',
|
||||
'to_do',
|
||||
'toggle',
|
||||
'unsupported',
|
||||
'video',
|
||||
]),
|
||||
created_time: z.iso.datetime(),
|
||||
created_by: userSchema,
|
||||
last_edited_time: z.iso.datetime(),
|
||||
last_edited_by: userSchema,
|
||||
archived: z.boolean(),
|
||||
in_trash: z.boolean(),
|
||||
has_children: z.boolean(),
|
||||
|
||||
// Optional properties for each type
|
||||
audio: fileSchema.optional(),
|
||||
bookmark: z
|
||||
.object({
|
||||
caption: z.array(richTextSchema),
|
||||
url: z.url(),
|
||||
})
|
||||
.optional(),
|
||||
breadcrumb: z.object({}).optional(),
|
||||
bulleted_list_item: z
|
||||
.object({
|
||||
rich_text: z.array(richTextSchema),
|
||||
color: z.enum(NOTION_COLORS),
|
||||
})
|
||||
.optional(),
|
||||
callout: z
|
||||
.object({
|
||||
rich_text: z.array(richTextSchema),
|
||||
icon: z.union([emojiSchema, fileSchema]),
|
||||
color: z.enum(NOTION_COLORS),
|
||||
})
|
||||
.optional(),
|
||||
child_database: z
|
||||
.object({
|
||||
title: z.string(),
|
||||
})
|
||||
.optional(),
|
||||
child_page: z
|
||||
.object({
|
||||
title: z.string(),
|
||||
})
|
||||
.optional(),
|
||||
code: z
|
||||
.object({
|
||||
caption: z.array(richTextSchema),
|
||||
rich_text: z.array(richTextSchema),
|
||||
language: z.enum([]), // TODO: Fill in with actual languages
|
||||
})
|
||||
.optional(),
|
||||
column_list: z.object({}).optional(),
|
||||
column: z
|
||||
.object({
|
||||
width_ratio: z.number().min(0).max(1).optional(),
|
||||
})
|
||||
.optional(),
|
||||
divider: z.object({}).optional(),
|
||||
embed: z
|
||||
.object({
|
||||
url: z.url(),
|
||||
})
|
||||
.optional(),
|
||||
equation: z.object({ expression: z.string() }).optional(),
|
||||
file: z
|
||||
.object({
|
||||
caption: z.array(richTextSchema),
|
||||
type: z.enum(['file', 'file_upload', 'external']),
|
||||
file: fileSchema,
|
||||
external: fileSchema,
|
||||
file_upload: fileSchema,
|
||||
name: z.string(),
|
||||
})
|
||||
.optional(),
|
||||
heading_1: headingsObject.optional(),
|
||||
heading_2: headingsObject.optional(),
|
||||
heading_3: headingsObject.optional(),
|
||||
image: fileSchema.optional(),
|
||||
link_preview: z.object({ url: z.url() }).optional(),
|
||||
|
||||
// TODO: Continue with mention and others
|
||||
});
|
||||
|
||||
export type NotionBlock = z.infer<typeof blockSchema>;
|
||||
22
src/lib/schemas/notion/colors.ts
Normal file
22
src/lib/schemas/notion/colors.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
/** Notion color options */
|
||||
export const NOTION_COLORS = [
|
||||
'blue',
|
||||
'blue_background',
|
||||
'brown',
|
||||
'brown_background',
|
||||
'default',
|
||||
'gray',
|
||||
'gray_background',
|
||||
'green',
|
||||
'green_background',
|
||||
'orange',
|
||||
'orange_background',
|
||||
'pink',
|
||||
'pink_background',
|
||||
'purple',
|
||||
'purple_background',
|
||||
'red',
|
||||
'red_background',
|
||||
'yellow',
|
||||
'yellow_background',
|
||||
];
|
||||
8
src/lib/schemas/notion/emoji.schema.ts
Normal file
8
src/lib/schemas/notion/emoji.schema.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const emojiSchema = z.object({
|
||||
type: z.literal('emoji'),
|
||||
emoji: z.string(),
|
||||
});
|
||||
|
||||
export type NotionEmoji = z.infer<typeof emojiSchema>;
|
||||
33
src/lib/schemas/notion/file.schema.ts
Normal file
33
src/lib/schemas/notion/file.schema.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
/**
|
||||
* Schema representing a Notion file object.
|
||||
*
|
||||
* This schema can represent three types of files:
|
||||
* 1. 'file': A file hosted by Notion, with a URL and an expiry time.
|
||||
* 2. 'file_upload': A file uploaded to Notion, identified by a unique ID.
|
||||
* 3. 'external': A file hosted externally, represented by a URL.
|
||||
*
|
||||
* Each type has its own structure, and only one type will be present in a valid object.
|
||||
*/
|
||||
export const fileSchema = z.object({
|
||||
type: z.enum(['file', 'file_upload', 'external']),
|
||||
file: z
|
||||
.object({
|
||||
url: z.url(),
|
||||
expiry_time: z.iso.datetime(),
|
||||
})
|
||||
.optional(),
|
||||
file_upload: z
|
||||
.object({
|
||||
id: z.uuid(),
|
||||
})
|
||||
.optional(),
|
||||
external: z
|
||||
.object({
|
||||
url: z.url(),
|
||||
})
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export type NotionFile = z.infer<typeof fileSchema>;
|
||||
22
src/lib/schemas/notion/fileUpload.schema.ts
Normal file
22
src/lib/schemas/notion/fileUpload.schema.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
/**
|
||||
* Schema representing a Notion file upload object.
|
||||
*
|
||||
* Includes details about the file upload status, URLs, and metadata.
|
||||
*/
|
||||
export const fileUploadSchema = z.object({
|
||||
object: z.literal('file_upload'),
|
||||
id: z.uuid(),
|
||||
created_time: z.iso.datetime(),
|
||||
expiry_time: z.iso.datetime().nullable(),
|
||||
status: z.enum(['pending', 'uploaded', 'expired', 'failed']),
|
||||
filename: z.string(),
|
||||
content_type: z.string().nullable(),
|
||||
content_length: z.number().nullable(),
|
||||
upload_url: z.string(),
|
||||
complete_url: z.string(),
|
||||
file_import_result: z.string(),
|
||||
});
|
||||
|
||||
export type NotionFileUpload = z.infer<typeof fileUploadSchema>;
|
||||
26
src/lib/schemas/notion/page.schema.ts
Normal file
26
src/lib/schemas/notion/page.schema.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { z } from 'zod';
|
||||
import { fileSchema, pagePropertiesSchema, parentSchema, userSchema } from '@/lib/schemas';
|
||||
|
||||
/**
|
||||
* Schema representing a Notion page object.
|
||||
*
|
||||
* Includes metadata about the page, its properties, and its parent.
|
||||
*/
|
||||
export const pageSchema = z.object({
|
||||
object: z.literal('page'),
|
||||
id: z.uuid(),
|
||||
created_time: z.iso.datetime(),
|
||||
created_by: userSchema,
|
||||
last_edited_time: z.iso.datetime(),
|
||||
last_edited_by: userSchema,
|
||||
archived: z.boolean(),
|
||||
in_trash: z.boolean(),
|
||||
icon: z.nullable(fileSchema),
|
||||
cover: z.nullable(fileSchema),
|
||||
properties: z.record(z.string(), pagePropertiesSchema),
|
||||
parent: parentSchema,
|
||||
url: z.url(),
|
||||
public_url: z.url().nullable(),
|
||||
});
|
||||
|
||||
export type NotionPage = z.infer<typeof pageSchema>;
|
||||
92
src/lib/schemas/notion/pageProperties.schema.ts
Normal file
92
src/lib/schemas/notion/pageProperties.schema.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import { z } from 'zod';
|
||||
import { NOTION_COLORS, richTextSchema, userSchema } from '@/lib/schemas';
|
||||
|
||||
/**
|
||||
* Schema representing the properties of a Notion page.
|
||||
*
|
||||
* Includes various property types such as text, number, select, multi-select, date, people, files, and more.
|
||||
*/
|
||||
export const pagePropertiesSchema = z.object({
|
||||
id: z.string(),
|
||||
type: z.enum([
|
||||
'checkbox',
|
||||
'created_by',
|
||||
'created_time',
|
||||
'date',
|
||||
'email',
|
||||
'files',
|
||||
'formula',
|
||||
'last_edited_by',
|
||||
'last_edited_time',
|
||||
'multi_select',
|
||||
'number',
|
||||
'people',
|
||||
'phone_number',
|
||||
'relation',
|
||||
'rich_text',
|
||||
'rollup',
|
||||
'select',
|
||||
'status',
|
||||
'title',
|
||||
'url',
|
||||
'unique_id',
|
||||
'verification',
|
||||
]),
|
||||
|
||||
// Optional properties for each type
|
||||
checkbox: z.boolean().optional(),
|
||||
created_by: userSchema.optional(),
|
||||
created_time: z.iso.datetime().optional(),
|
||||
date: z
|
||||
.object({
|
||||
start: z.iso.datetime(),
|
||||
end: z.iso.datetime().optional(),
|
||||
})
|
||||
.optional(),
|
||||
email: z.email().optional(),
|
||||
files: z.array(z.any()).optional(),
|
||||
formula: z.any().optional(),
|
||||
last_edited_by: userSchema.optional(),
|
||||
last_edited_time: z.iso.datetime().optional(),
|
||||
multi_select: z
|
||||
.array(
|
||||
z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
color: z.enum(NOTION_COLORS),
|
||||
})
|
||||
)
|
||||
.optional(),
|
||||
number: z.number().optional(),
|
||||
people: z.array(userSchema).optional(),
|
||||
phone_number: z.string().optional(),
|
||||
relation: z.array(z.object({ id: z.string() })).optional(),
|
||||
rich_text: richTextSchema.optional(),
|
||||
rollup: z.any().optional(),
|
||||
select: z
|
||||
.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
color: z.enum(NOTION_COLORS),
|
||||
})
|
||||
.optional(),
|
||||
status: z
|
||||
.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
color: z.enum(NOTION_COLORS),
|
||||
})
|
||||
.optional(),
|
||||
title: richTextSchema.optional(),
|
||||
url: z.url().optional(),
|
||||
unique_id: z.string().optional(),
|
||||
verification: z
|
||||
.object({
|
||||
state: z.string(),
|
||||
verified_by: userSchema.nullable(),
|
||||
date: z.iso.datetime(),
|
||||
})
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export type NotionPageProperties = z.infer<typeof pagePropertiesSchema>;
|
||||
17
src/lib/schemas/notion/parent.schema.ts
Normal file
17
src/lib/schemas/notion/parent.schema.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
/**
|
||||
* Schema representing the parent of a Notion page.
|
||||
*
|
||||
* Includes various types of parents such as database, data source, page, workspace, or block.
|
||||
*/
|
||||
export const parentSchema = z.object({
|
||||
type: z.enum(['database_id', 'data_source_id', 'page_id', 'workspace', 'block_id']),
|
||||
database_id: z.uuid().optional(),
|
||||
data_source_id: z.uuid().optional(),
|
||||
page_id: z.uuid().optional(),
|
||||
workspace: z.boolean().optional(),
|
||||
block_id: z.uuid().optional(),
|
||||
});
|
||||
|
||||
export type NotionParent = z.infer<typeof parentSchema>;
|
||||
68
src/lib/schemas/notion/richText.schema.ts
Normal file
68
src/lib/schemas/notion/richText.schema.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { z } from 'zod';
|
||||
import { NOTION_COLORS, userSchema } from '@/lib/schemas';
|
||||
|
||||
/**
|
||||
* Schema representing a Notion rich text object.
|
||||
*
|
||||
* Includes text content, annotations, mentions, and equations.
|
||||
*/
|
||||
export const richTextSchema = z.array(
|
||||
z.object({
|
||||
type: z.literal('text'),
|
||||
text: z.object({
|
||||
content: z.string(),
|
||||
link: z.url().nullable(),
|
||||
}),
|
||||
mention: z
|
||||
.object({
|
||||
type: z.enum(['database', 'date', 'link_preview', 'page', 'template_mention', 'user']),
|
||||
database: z
|
||||
.object({
|
||||
id: z.uuid(),
|
||||
})
|
||||
.optional(),
|
||||
date: z
|
||||
.object({
|
||||
start: z.iso.datetime(),
|
||||
end: z.iso.datetime().optional(),
|
||||
})
|
||||
.optional(),
|
||||
link_preview: z
|
||||
.object({
|
||||
url: z.url(),
|
||||
})
|
||||
.optional(),
|
||||
page: z
|
||||
.object({
|
||||
id: z.uuid(),
|
||||
})
|
||||
.optional(),
|
||||
template_mention: z
|
||||
.object({
|
||||
type: z.enum(['template_mention_date', 'template_mention_user']),
|
||||
template_mention_date: z.enum(['today', 'now']).optional(),
|
||||
template_mention_user: z.literal('me').optional(),
|
||||
})
|
||||
.optional(),
|
||||
user: z.object(userSchema).optional(),
|
||||
})
|
||||
.optional(),
|
||||
equation: z
|
||||
.object({
|
||||
expression: z.string(),
|
||||
})
|
||||
.optional(),
|
||||
annotations: z.object({
|
||||
bold: z.boolean(),
|
||||
italic: z.boolean(),
|
||||
strikethrough: z.boolean(),
|
||||
underline: z.boolean(),
|
||||
code: z.boolean(),
|
||||
color: z.enum(NOTION_COLORS),
|
||||
}),
|
||||
plain_text: z.string(),
|
||||
href: z.url().optional(),
|
||||
})
|
||||
);
|
||||
|
||||
export type NotionRichText = z.infer<typeof richTextSchema>;
|
||||
16
src/lib/schemas/notion/user.schema.ts
Normal file
16
src/lib/schemas/notion/user.schema.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
/**
|
||||
* Schema representing a Notion user object.
|
||||
*
|
||||
* Includes both person and bot user types.
|
||||
*/
|
||||
export const userSchema = z.object({
|
||||
object: z.literal('user'),
|
||||
id: z.uuid(),
|
||||
type: z.enum(['person', 'bot']).optional(),
|
||||
name: z.string().optional(),
|
||||
avatar_url: z.url().optional(),
|
||||
});
|
||||
|
||||
export type NotionUser = z.infer<typeof userSchema>;
|
||||
Reference in New Issue
Block a user