Files
edgeever/packages/shared/src/backup.ts
T

145 lines
4.0 KiB
TypeScript

import { z } from "zod";
import type { TiptapDoc } from "./content";
import type { MemoDetail, Notebook, Resource } from "./types";
export const EDGEEVER_ZIP_FORMAT = "edgeever-zip";
export const EDGEEVER_ZIP_FORMAT_VERSION = 1;
export type JsonBackupManifest = {
format: typeof EDGEEVER_ZIP_FORMAT;
formatVersion: typeof EDGEEVER_ZIP_FORMAT_VERSION;
schemaVersion: number;
edgeeverVersion: string;
buildId: string;
exportedAt: string;
includesTrash: false;
counts: {
notebooks: number;
memos: number;
revisions: number;
resources: number;
};
};
export type JsonBackupNotebook = Omit<Notebook, "memoCount" | "lastMemoUpdatedAt">;
export type JsonBackupRevision = {
id: string;
memoId: string;
revision: number;
title: string | null;
tags: string[];
contentJson: TiptapDoc;
contentMarkdown: string;
contentText: string;
contentHash: string;
createdBy: string;
createdAt: string;
};
export type JsonBackupResource = Omit<Resource, "url"> & {
archivePath: string;
};
export type JsonBackupMemo = {
memo: MemoDetail;
revisions: JsonBackupRevision[];
resources: JsonBackupResource[];
};
const DateTimeSchema = z.string().datetime();
export const JsonBackupManifestSchema = z.object({
format: z.literal(EDGEEVER_ZIP_FORMAT),
formatVersion: z.literal(EDGEEVER_ZIP_FORMAT_VERSION),
schemaVersion: z.number().int().positive(),
edgeeverVersion: z.string().trim().min(1),
buildId: z.string().trim().min(1),
exportedAt: DateTimeSchema,
includesTrash: z.literal(false),
counts: z.object({
notebooks: z.number().int().min(0),
memos: z.number().int().min(0),
revisions: z.number().int().min(0),
resources: z.number().int().min(0),
}),
});
export const JsonBackupNotebookSchema = z.object({
id: z.string().trim().min(1),
parentId: z.string().trim().min(1).nullable(),
name: z.string().trim().min(1).max(80),
slug: z.string().nullable(),
icon: z.string().nullable(),
color: z.string().nullable(),
sortOrder: z.number().int(),
createdAt: DateTimeSchema,
updatedAt: DateTimeSchema,
});
const JsonBackupRevisionSchema = z.object({
id: z.string().trim().min(1),
memoId: z.string().trim().min(1),
revision: z.number().int().min(0),
title: z.string().nullable(),
tags: z.array(z.string()),
contentJson: z.unknown(),
contentMarkdown: z.string(),
contentText: z.string(),
contentHash: z.string(),
createdBy: z.string(),
createdAt: DateTimeSchema,
});
const MemoDetailSchema = z.object({
id: z.string().trim().min(1),
notebookId: z.string().trim().min(1),
title: z.string().nullable(),
excerpt: z.string(),
tags: z.array(z.string()),
isPinned: z.boolean(),
isArchived: z.boolean(),
isDeleted: z.boolean(),
revision: z.number().int().min(0),
createdAt: DateTimeSchema,
updatedAt: DateTimeSchema,
deletedAt: DateTimeSchema.nullable(),
contentJson: z.unknown(),
contentMarkdown: z.string(),
contentText: z.string(),
contentHash: z.string(),
sourceMemoIds: z.array(z.string()),
mergeSourceCount: z.number().int().min(0),
mergedIntoMemoId: z.string().nullable(),
});
export const JsonBackupMemoSchema = z.object({
memo: MemoDetailSchema,
revisions: z.array(JsonBackupRevisionSchema),
resources: z.array(z.object({
id: z.string().trim().min(1),
memoId: z.string().trim().min(1),
originalMemoId: z.string().nullable(),
kind: z.enum(["image", "attachment"]),
mimeType: z.string().nullable(),
filename: z.string().nullable(),
byteSize: z.number().int().min(0),
sha256: z.string().nullable(),
width: z.number().int().positive().nullable(),
height: z.number().int().positive().nullable(),
createdAt: DateTimeSchema,
updatedAt: DateTimeSchema,
archivePath: z.string().trim().min(1),
})),
});
export const RestoreJsonNotebooksSchema = z.object({
notebooks: z.array(JsonBackupNotebookSchema).min(1).max(100),
});
export const RestoreJsonMemosSchema = z.object({
memos: z.array(JsonBackupMemoSchema).min(1).max(10),
});
export const JsonBackupResourceMetadataSchema = JsonBackupMemoSchema.shape.resources.element;