feat: 将数据库模块迁移至 worker 服务,并实现 D1 数据库自动迁移

This commit is contained in:
gujiangjiang committed 2025-10-17 05:33:12 +08:00
1 parent a35a6bd390
commit 62aefb1f9a
33 files changed
+78 -1598

No files matched your search

+8
View File
@@ -76,6 +76,14 @@ jobs:
COOKIES_SECRET: ${{ secrets.COOKIES_SECRET }}
TURNSTILE_KEY: ${{ secrets.TURNSTILE_KEY }}
TURNSTILE_SECRET: ${{ secrets.TURNSTILE_SECRET }}
# 新增:在部署前应用 D1 数据库迁移
# 这一步会明确地告诉 Wrangler 远程执行 D1 数据库的迁移脚本。
- name: Apply D1 Migrations
run: npx wrangler d1 migrations apply ${{ secrets.D1_DATABASE_NAME }} --remote
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CF_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }}
# 部署到 Cloudflare Workers
- name: Deploy
-2
View File
@@ -15,13 +15,11 @@
"@tanstack/react-query": "^5.17.10",
"axios": "^1.6.7",
"clsx": "^2.1.0",
"database": "workspace:*",
"date-fns": "^3.2.0",
"framer-motion": "^11.0.24",
"i18next": "^23.10.1",
"i18next-browser-languagedetector": "^7.2.0",
"i18next-http-backend": "^2.5.0",
"icons": "workspace:*",
"js-cookie": "^3.0.5",
"react": "^18.2.0",
"react-dom": "^18.2.0",
+2 -1
View File
@@ -3,7 +3,8 @@ import { useTranslation } from 'react-i18next';
import { formatDistanceToNow } from 'date-fns';
import { zhCN } from 'date-fns/locale';
import clsx from 'clsx';
import type { Email } from 'database';
// refactor: 将导入从 'database' 包更改为本地的类型定义文件
import type { Email } from '../database_types';
// 图标导入
import MailIcon from './icons/MailIcon.tsx';
+36
View File
@@ -0,0 +1,36 @@
// feat: 创建此文件以存放从后端共享的数据库类型定义
// 从原 packages/database/schema.ts 迁移过来的类型定义
// 定义 Header 类型,用于存储邮件头信息
export type Header = Record<string, string>;
// 定义 Address 类型,用于存储发件人、收件人等地址信息
export type Address = {
address: string;
name: string;
};
// 定义 Email 类型,这是邮件对象的主要结构
export type Email = {
id: string;
messageFrom: string;
messageTo: string;
headers: Header[];
from: Address;
sender: Address | null;
replyTo: Address[] | null;
deliveredTo: string | null;
returnPath: string | null;
to: Address[] | null;
cc: Address[] | null;
bcc: Address[] | null;
subject: string | null;
messageId: string;
inReplyTo: string | null;
references: string | null;
date: string | null;
html: string | null;
text: string | null;
createdAt: Date; // 注意:这里是Date类型,但在API传输时通常是字符串,使用时请注意转换
updatedAt: Date; // 注意:同上
};
+2 -2
View File
@@ -23,8 +23,8 @@ import Cloudflare from "../components/icons/Cloudflare.tsx";
import Clock from "../components/icons/Clock.tsx";
import Info from "../components/icons/Info.tsx";
// 假设 Email 类型定义在 'database' 包中
import type { Email } from 'database';
// refactor: 将导入从 'database' 包更改为本地的类型定义文件
import type { Email } from '../database_types.ts';
export function Home() {
const config = useConfig();
+2 -3
View File
@@ -1,10 +1,9 @@
// 导入 Email 类型,'database' 是您 monorepo 中的包名
import type { Email } from 'database';
// refactor: 将导入从 'database' 包更改为本地的类型定义文件
import type { Email } from '../database_types';
const API_BASE_URL = '/api';
// 定义 Turnstile 人机验证所需的 token
// 恢复此接口定义,以备将来使用
interface ApiPayload {
token: string;
[key: string]: any;
-5
View File
@@ -1,5 +0,0 @@
import type { Config } from "drizzle-kit";
export default {
schema: "./schema.ts",
out: "./drizzle",
} satisfies Config;
-82
View File
@@ -1,82 +0,0 @@
import { createClient as createWebClient } from "@libsql/client/web";
import * as orm from "drizzle-orm";
import { LibSQLDatabase, drizzle } from "drizzle-orm/libsql";
import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
import { createInsertSchema } from "drizzle-zod";
import * as z from "zod";
function getWebTursoDBFromEnv(): LibSQLDatabase {
const client = createWebClient({
url: process.env.TURSO_DB_URL || "",
authToken: process.env.TURSO_DB_AUTH_TOKEN || "",
});
return drizzle(client);
}
function getWebTursoDB(url: string, authToken: string): LibSQLDatabase {
return drizzle(createWebClient({ url, authToken }));
}
type Header = Record<string, string>;
type Address = {
address: string;
name: string;
};
type Email = typeof emails.$inferSelect;
const emails = sqliteTable("emails", {
id: text("id").primaryKey(),
messageFrom: text("message_from").notNull(),
messageTo: text("message_to").notNull(),
headers: text("headers", { mode: "json" }).$type<Header[]>().notNull(),
from: text("from", { mode: "json" }).$type<Address>().notNull(),
sender: text("sender", { mode: "json" }).$type<Address>(),
replyTo: text("reply_to", { mode: "json" }).$type<Address[]>(),
deliveredTo: text("delivered_to"),
returnPath: text("return_path"),
to: text("to", { mode: "json" }).$type<Address[]>(),
cc: text("cc", { mode: "json" }).$type<Address[]>(),
bcc: text("bcc", { mode: "json" }).$type<Address[]>(),
subject: text("subject"),
messageId: text("message_id").notNull(),
inReplyTo: text("in_reply_to"),
references: text("references"),
date: text("date"),
html: text("html"),
text: text("text"),
createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
updatedAt: integer("updated_at", { mode: "timestamp" }).notNull(),
});
const AddressSchema = z.object({
address: z.string(),
name: z.string(),
});
const insertEmailSchema = createInsertSchema(emails, {
headers: z.array(z.record(z.string())),
from: AddressSchema,
sender: AddressSchema.optional(),
replyTo: z.array(AddressSchema).optional(),
to: z.array(AddressSchema).optional(),
cc: z.array(AddressSchema).optional(),
bcc: z.array(AddressSchema).optional(),
});
type InsertEmail = z.infer<typeof insertEmailSchema>;
export {
Address,
AddressSchema,
Email,
Header,
InsertEmail,
emails,
getWebTursoDB,
getWebTursoDBFromEnv,
insertEmailSchema,
orm,
z,
};
-3
View File
@@ -1,3 +0,0 @@
import * as orm from "drizzle-orm";
export default orm;
-21
View File
@@ -1,21 +0,0 @@
{
"name": "database",
"version": "1.0.0",
"description": "",
"scripts": {
"sqlite": "drizzle-kit generate:sqlite"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@libsql/client": "0.4.0-pre.7",
"drizzle-orm": "^0.29.3",
"drizzle-zod": "^0.5.1",
"zod": "^3.22.4"
},
"devDependencies": {
"drizzle-kit": "^0.20.12",
"typescript": "^5.3.3"
}
}
-109
View File
@@ -1,109 +0,0 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig to read more about this file */
/* Projects */
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */
"module": "commonjs" /* Specify what module code is generated. */,
// "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
// "resolveJsonModule": true, /* Enable importing .json files. */
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
// "outDir": "./", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
/* Type Checking */
"strict": true /* Enable all strict type-checking options. */,
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}
-23
View File
@@ -1,23 +0,0 @@
import ArrowUturnLeft from "./svg/ArrowUturnLeft";
import CheckIcon from "./svg/CheckIcon";
import CopyIcon from "./svg/CopyIcon";
import ExclamationCircle from "./svg/ExclamationCircle";
import GithubIcon from "./svg/GithubIcon";
import InboxIcon from "./svg/InboxIcon";
import MailIcon from "./svg/MailIcon";
import RefreshIcon from "./svg/RefreshIcon";
import ReloadIcon from "./svg/ReloadIcon";
import UserCircleIcon from "./svg/UserCircleIcon";
export {
ArrowUturnLeft,
CheckIcon,
CopyIcon,
ExclamationCircle,
GithubIcon,
InboxIcon,
MailIcon,
RefreshIcon,
ReloadIcon,
UserCircleIcon,
};
-17
View File
@@ -1,17 +0,0 @@
{
"name": "icons",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@types/react": "^18.2.47",
"typescript": "^5.3.3"
},
"dependencies": {
"react": "^18.2.0"
}
}
-20
View File
@@ -1,20 +0,0 @@
import { SVGProps } from "react";
export default function ArrowUturnLeft(props: SVGProps<SVGSVGElement>) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
strokeWidth="2"
stroke="currentColor"
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
{...props}
>
<path d="M9 15 3 9m0 0 6-6M3 9h12a6 6 0 0 1 0 12h-3" />
</svg>
);
}
-21
View File
@@ -1,21 +0,0 @@
import { SVGProps } from "react";
export default function CheckIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
strokeWidth="2"
stroke="currentColor"
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
{...props}
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M5 12l5 5l10 -10" />
</svg>
);
}
-22
View File
@@ -1,22 +0,0 @@
import { SVGProps } from "react";
export default function CopyIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
strokeWidth="2"
stroke="currentColor"
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
{...props}
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M7 7m0 2.667a2.667 2.667 0 0 1 2.667 -2.667h8.666a2.667 2.667 0 0 1 2.667 2.667v8.666a2.667 2.667 0 0 1 -2.667 2.667h-8.666a2.667 2.667 0 0 1 -2.667 -2.667z" />
<path d="M4.012 16.737a2.005 2.005 0 0 1 -1.012 -1.737v-10c0 -1.1 .9 -2 2 -2h10c.75 0 1.158 .385 1.5 1" />
</svg>
);
}
-23
View File
@@ -1,23 +0,0 @@
import { SVGProps } from "react";
export default function ExclamationCircle(props: SVGProps<SVGSVGElement>) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
strokeWidth="2"
stroke="currentColor"
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
{...props}
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" />
<path d="M12 9v4" />
<path d="M12 16v.01" />
</svg>
);
}
-21
View File
@@ -1,21 +0,0 @@
import { SVGProps } from "react";
export default function GithubIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
strokeWidth="2"
stroke="currentColor"
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
{...props}
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M9 19c-4.3 1.4 -4.3 -2.5 -6 -3m12 5v-3.5c0 -1 .1 -1.4 -.5 -2c2.8 -.3 5.5 -1.4 5.5 -6a4.6 4.6 0 0 0 -1.3 -3.2a4.2 4.2 0 0 0 -.1 -3.2s-1.1 -.3 -3.5 1.3a12.3 12.3 0 0 0 -6.2 0c-2.4 -1.6 -3.5 -1.3 -3.5 -1.3a4.2 4.2 0 0 0 -.1 3.2a4.6 4.6 0 0 0 -1.3 3.2c0 4.6 2.7 5.7 5.5 6c-.6 .6 -.6 1.2 -.5 2v3.5" />
</svg>
);
}
-20
View File
@@ -1,20 +0,0 @@
import { SVGProps } from "react";
export default function InboxIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
strokeWidth="2"
stroke="currentColor"
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
{...props}
>
<path d="M2.25 13.5h3.86a2.25 2.25 0 0 1 2.012 1.244l.256.512a2.25 2.25 0 0 0 2.013 1.244h3.218a2.25 2.25 0 0 0 2.013-1.244l.256-.512a2.25 2.25 0 0 1 2.013-1.244h3.859m-19.5.338V18a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 18v-4.162c0-.224-.034-.447-.1-.661L19.24 5.338a2.25 2.25 0 0 0-2.15-1.588H6.911a2.25 2.25 0 0 0-2.15 1.588L2.35 13.177a2.25 2.25 0 0 0-.1.661Z" />
</svg>
);
}
-20
View File
@@ -1,20 +0,0 @@
import { SVGProps } from "react";
export default function MailIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
strokeWidth="2"
stroke="currentColor"
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
{...props}
>
<path d="M21.75 6.75v10.5a2.25 2.25 0 0 1-2.25 2.25h-15a2.25 2.25 0 0 1-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0 0 19.5 4.5h-15a2.25 2.25 0 0 0-2.25 2.25m19.5 0v.243a2.25 2.25 0 0 1-1.07 1.916l-7.5 4.615a2.25 2.25 0 0 1-2.36 0L3.32 8.91a2.25 2.25 0 0 1-1.07-1.916V6.75" />
</svg>
);
}
-19
View File
@@ -1,19 +0,0 @@
import { SVGProps } from "react";
export default function RefreshIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
strokeWidth="2"
stroke="currentColor"
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
{...props}>
<path d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0 3.181 3.183a8.25 8.25 0 0 0 13.803-3.7M4.031 9.865a8.25 8.25 0 0 1 13.803-3.7l3.181 3.182m0-4.991v4.99" />
</svg>
);
}
-22
View File
@@ -1,22 +0,0 @@
import { SVGProps } from "react";
export default function ReloadIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
strokeWidth="2"
stroke="currentColor"
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
{...props}
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M19.933 13.041a8 8 0 1 1 -9.925 -8.788c3.899 -1 7.935 1.007 9.425 4.747" />
<path d="M20 4v5h-5" />
</svg>
);
}
-20
View File
@@ -1,20 +0,0 @@
import { SVGProps } from "react";
export default function UserCircleIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
strokeWidth="2"
stroke="currentColor"
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
{...props}
>
<path d="M17.982 18.725A7.488 7.488 0 0 0 12 15.75a7.488 7.488 0 0 0-5.982 2.975m11.963 0a9 9 0 1 0-11.963 0m11.963 0A8.966 8.966 0 0 1 12 21a8.966 8.966 0 0 1-5.982-2.275M15 9.75a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
</svg>
);
}
-111
View File
@@ -1,111 +0,0 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig to read more about this file */
/* Projects */
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */
"module": "commonjs" /* Specify what module code is generated. */,
// "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
// "resolveJsonModule": true, /* Enable importing .json files. */
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
// "outDir": "./", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
/* Type Checking */
"strict": true /* Enable all strict type-checking options. */,
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true,
/* Skip type checking all .d.ts files. */
"jsx": "react"
}
}
-998
View File
File diff suppressed because it is too large. Load diff
+1 -2
View File
@@ -1,4 +1,3 @@
packages:
- "worker"
- "frontend"
- "packages/*"
- "frontend"
+8
View File
@@ -0,0 +1,8 @@
import type { Config } from "drizzle-kit";
export default {
// 指向新的 schema 文件位置
schema: "./src/database/schema.ts",
// 指定迁移文件的输出目录
out: "./drizzle",
} satisfies Config;
@@ -1,4 +1,5 @@
CREATE TABLE `emails` (
-- fix: 添加 IF NOT EXISTS 以确保表创建的幂等性,防止重复执行时出错
CREATE TABLE IF NOT EXISTS `emails` (
`id` text PRIMARY KEY NOT NULL,
`message_from` text NOT NULL,
`message_to` text NOT NULL,
@@ -20,4 +21,4 @@ CREATE TABLE `emails` (
`text` text,
`created_at` integer NOT NULL,
`updated_at` integer NOT NULL
);
);
@@ -1,6 +1,7 @@
import { count, desc, eq, and, inArray, lt } from "drizzle-orm";
// fix: 将数据库类型从 LibSQLDatabase 更改为 DrizzleD1Database,以匹配 Cloudflare D1
import { DrizzleD1Database } from "drizzle-orm/d1";
// refactor: 更新 schema 的导入路径
import { emails, InsertEmail } from "./schema";
export async function insertEmail(db: DrizzleD1Database, email: InsertEmail) {
File renamed without changes.
@@ -2,15 +2,19 @@ import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
import { createInsertSchema } from "drizzle-zod";
import * as z from "zod";
// 定义 Header 类型,用于存储邮件头信息
export type Header = Record<string, string>;
// 定义 Address 类型,用于存储发件人、收件人等地址信息
export type Address = {
address: string;
name: string;
};
// 定义 Email 类型,通过 drizzle-orm 从数据库表结构推断得出
export type Email = typeof emails.$inferSelect;
// 使用 drizzle-orm 定义 emails 表的结构
export const emails = sqliteTable("emails", {
id: text("id").primaryKey(),
messageFrom: text("message_from").notNull(),
@@ -35,11 +39,13 @@ export const emails = sqliteTable("emails", {
updatedAt: integer("updated_at", { mode: "timestamp" }).notNull(),
});
// 使用 Zod 定义 Address 对象的验证 schema
const AddressSchema = z.object({
address: z.string(),
name: z.string(),
});
// 使用 drizzle-zod 基于 emails 表结构创建用于插入操作的 Zod schema
export const insertEmailSchema = createInsertSchema(emails, {
headers: z.array(z.record(z.string())),
from: AddressSchema,
@@ -50,4 +56,5 @@ export const insertEmailSchema = createInsertSchema(emails, {
bcc: z.array(AddressSchema).optional(),
});
export type InsertEmail = z.infer<typeof insertEmailSchema>;
// 定义 InsertEmail 类型,从 Zod schema 推断
export type InsertEmail = z.infer<typeof insertEmailSchema>;
+5 -8
View File
@@ -1,13 +1,10 @@
import { Hono } from 'hono';
import { serveStatic } from 'hono/cloudflare-workers';
import { cors } from 'hono/cors';
// 路径修正:使用相对路径并导入正确的函数
// feat: 导入 getEmailByPassword 函数
import { deleteEmails, findEmailById, getEmailByPassword, getEmailsByMessageTo, insertEmail, deleteExpiredEmails } from '../../packages/database/dao';
// fix: 修正数据库导入路径
import { getD1DB } from '../../packages/database/db';
import { InsertEmail, insertEmailSchema } from '../../packages/database/schema';
// fix: 修复 nanoid 导入路径问题,解决部署报错
// 导入数据库相关的模块
import { deleteEmails, findEmailById, getEmailByPassword, getEmailsByMessageTo, insertEmail, deleteExpiredEmails } from './database/dao';
import { getD1DB } from './database/db';
import { InsertEmail, insertEmailSchema } from './database/schema';
import { nanoid } from 'nanoid/non-secure';
import PostalMime from 'postal-mime';
@@ -233,4 +230,4 @@ export default {
const oneHourAgo = new Date(Date.now() - 1000 * 60 * 60);
await deleteExpiredEmails(db, oneHourAgo);
},
};
};
+2
View File
@@ -12,6 +12,8 @@ keep_vars = true
binding = "DB"
database_name = "${D1_DATABASE_NAME}"
database_id = "${D1_DATABASE_ID}"
# fix: 将迁移目录直接配置在这里,这是 D1 自动迁移的标准做法
migrations_dir = "worker/drizzle"
# 定义环境变量和密钥
[vars]