From 7a6e8db35b451a2ed89bef3c3c59b6c25b777ac4 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 24 Aug 2026 03:00:46 +0000 Subject: [PATCH 1/2] chore: add Cloud Agent dev environment config Set up a working Cloud Agent development environment for the Cloud Mail (Vue3 + Hono/Cloudflare Workers) monorepo. - .cursor/environment.json: install both packages, build the frontend into mail-worker/dist, seed the local D1 database, and run the worker (wrangler dev, :8787) and frontend (vite, :3001) as terminals. - mail-worker/db/local-init.sql: idempotent local D1 schema + minimal seed. The repo migrations only cover the share tables; the base tables (perm/role_perm/reg_key/star/attachments/verify_record) and the setting/ default-role seed rows required for register+login were missing, so a fresh local database could not complete registration. Schema is derived from the Drizzle entities (SSOT) and is safe to re-run. Co-authored-by: 7iook <7iook@users.noreply.github.com> --- .cursor/environment.json | 15 ++ mail-worker/db/local-init.sql | 313 ++++++++++++++++++++++++++++++++++ 2 files changed, 328 insertions(+) create mode 100644 .cursor/environment.json create mode 100644 mail-worker/db/local-init.sql diff --git a/.cursor/environment.json b/.cursor/environment.json new file mode 100644 index 0000000000..f692e607d3 --- /dev/null +++ b/.cursor/environment.json @@ -0,0 +1,15 @@ +{ + "name": "Cloud Mail", + "install": "cd mail-worker && pnpm install && cd ../mail-vue && pnpm install && pnpm run build && cd ../mail-worker && npx wrangler d1 execute email --local --config wrangler-dev.toml --file db/local-init.sql", + "terminals": [ + { + "name": "mail-worker (Hono API + SPA, :8787)", + "command": "cd mail-worker && npx wrangler dev --config wrangler-dev.toml --port 8787 --ip 0.0.0.0" + }, + { + "name": "mail-vue (Vite dev server, :3001)", + "command": "cd mail-vue && pnpm run dev" + } + ], + "ports": [8787, 3001] +} diff --git a/mail-worker/db/local-init.sql b/mail-worker/db/local-init.sql new file mode 100644 index 0000000000..4e7cf12947 --- /dev/null +++ b/mail-worker/db/local-init.sql @@ -0,0 +1,313 @@ +-- ===================================================== +-- Cloud-Mail 本地开发数据库初始化脚本 (local dev only) +-- 用途: 为 wrangler dev 的本地 D1 (miniflare) 建立完整 schema + 最小种子数据 +-- 依据: mail-worker/src/entity/*.js (Drizzle ORM 实体定义为 schema 单一事实来源) +-- 说明: 仓库内 migrations/ 仅覆盖 share 相关表, 缺少 perm/role_perm/reg_key/ +-- star/attachments/verify_record 等基础表与 setting/role 种子数据, +-- 导致全新本地库无法完成注册/登录。本脚本补齐全部表并可重复执行 (幂等)。 +-- ===================================================== + +-- ---------- 用户表 ---------- +CREATE TABLE IF NOT EXISTS user ( + user_id INTEGER PRIMARY KEY AUTOINCREMENT, + email TEXT NOT NULL, + type INTEGER DEFAULT 1 NOT NULL, + password TEXT NOT NULL, + salt TEXT NOT NULL, + status INTEGER DEFAULT 0 NOT NULL, + create_time TEXT DEFAULT CURRENT_TIMESTAMP, + active_time TEXT, + create_ip TEXT, + active_ip TEXT, + os TEXT, + browser TEXT, + device TEXT, + sort TEXT DEFAULT 0, + send_count TEXT DEFAULT 0, + reg_key_id INTEGER DEFAULT 0 NOT NULL, + is_del INTEGER DEFAULT 0 NOT NULL +); + +-- ---------- 账户表 ---------- +CREATE TABLE IF NOT EXISTS account ( + account_id INTEGER PRIMARY KEY AUTOINCREMENT, + email TEXT NOT NULL, + name TEXT DEFAULT '' NOT NULL, + status INTEGER DEFAULT 0 NOT NULL, + latest_email_time TEXT, + create_time TEXT DEFAULT CURRENT_TIMESTAMP, + user_id INTEGER NOT NULL, + is_del INTEGER DEFAULT 0 NOT NULL +); + +-- ---------- 邮件表 ---------- +CREATE TABLE IF NOT EXISTS email ( + email_id INTEGER PRIMARY KEY AUTOINCREMENT, + send_email TEXT, + name TEXT, + account_id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + subject TEXT, + text TEXT, + content TEXT, + cc TEXT DEFAULT '[]', + bcc TEXT DEFAULT '[]', + recipient TEXT, + to_email TEXT DEFAULT '' NOT NULL, + to_name TEXT DEFAULT '' NOT NULL, + in_reply_to TEXT DEFAULT '', + relation TEXT DEFAULT '', + message_id TEXT DEFAULT '', + type INTEGER DEFAULT 0 NOT NULL, + status INTEGER DEFAULT 0 NOT NULL, + resend_email_id TEXT, + message TEXT, + create_time TEXT DEFAULT CURRENT_TIMESTAMP NOT NULL, + is_del INTEGER DEFAULT 0 NOT NULL, + is_read INTEGER DEFAULT 0 NOT NULL, + read_count INTEGER DEFAULT 0 NOT NULL +); + +-- ---------- 附件表 ---------- +CREATE TABLE IF NOT EXISTS attachments ( + att_id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL, + email_id INTEGER NOT NULL, + account_id INTEGER NOT NULL, + key TEXT NOT NULL, + filename TEXT, + mime_type TEXT, + size INTEGER, + status TEXT DEFAULT 0 NOT NULL, + type INTEGER DEFAULT 0 NOT NULL, + disposition TEXT, + related TEXT, + content_id TEXT, + encoding TEXT, + create_time TEXT DEFAULT CURRENT_TIMESTAMP NOT NULL +); + +-- ---------- 星标表 ---------- +CREATE TABLE IF NOT EXISTS star ( + star_id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL, + email_id INTEGER NOT NULL, + create_time TEXT DEFAULT CURRENT_TIMESTAMP NOT NULL +); + +-- ---------- 角色表 ---------- +CREATE TABLE IF NOT EXISTS role ( + role_id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + key TEXT NOT NULL, + description TEXT, + ban_email TEXT DEFAULT '' NOT NULL, + ban_email_type INTEGER DEFAULT 0 NOT NULL, + avail_domain TEXT DEFAULT '', + sort INTEGER, + is_default INTEGER DEFAULT 0, + create_time TEXT DEFAULT CURRENT_TIMESTAMP NOT NULL, + user_id INTEGER, + send_count INTEGER, + send_type TEXT DEFAULT 'count', + account_count INTEGER +); + +-- ---------- 权限表 ---------- +CREATE TABLE IF NOT EXISTS perm ( + perm_id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + perm_key TEXT, + pid INTEGER DEFAULT 0 NOT NULL, + type INTEGER DEFAULT 2 NOT NULL, + sort INTEGER +); + +-- ---------- 角色-权限关联表 ---------- +CREATE TABLE IF NOT EXISTS role_perm ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + role_id INTEGER, + perm_id INTEGER +); + +-- ---------- 注册卡密表 ---------- +CREATE TABLE IF NOT EXISTS reg_key ( + rege_key_id INTEGER PRIMARY KEY AUTOINCREMENT, + code TEXT DEFAULT '' NOT NULL, + count INTEGER DEFAULT 0 NOT NULL, + role_id INTEGER DEFAULT 0 NOT NULL, + user_id INTEGER DEFAULT 0 NOT NULL, + expire_time TEXT, + create_time TEXT DEFAULT CURRENT_TIMESTAMP NOT NULL +); + +-- ---------- 人机验证记录表 ---------- +CREATE TABLE IF NOT EXISTS verify_record ( + vr_id INTEGER PRIMARY KEY AUTOINCREMENT, + ip TEXT DEFAULT '' NOT NULL, + count INTEGER DEFAULT 1 NOT NULL, + type INTEGER DEFAULT 0 NOT NULL, + update_time TEXT DEFAULT CURRENT_TIMESTAMP NOT NULL +); + +-- ---------- 系统设置表 (单行配置) ---------- +CREATE TABLE IF NOT EXISTS setting ( + register INTEGER DEFAULT 0 NOT NULL, + receive INTEGER DEFAULT 0 NOT NULL, + title TEXT DEFAULT '' NOT NULL, + many_email INTEGER DEFAULT 1 NOT NULL, + add_email INTEGER DEFAULT 0 NOT NULL, + auto_refresh_time INTEGER DEFAULT 0 NOT NULL, + add_email_verify INTEGER DEFAULT 1 NOT NULL, + register_verify INTEGER DEFAULT 1 NOT NULL, + reg_verify_count INTEGER DEFAULT 1 NOT NULL, + add_verify_count INTEGER DEFAULT 1 NOT NULL, + send INTEGER DEFAULT 1 NOT NULL, + r2_domain TEXT, + secret_key TEXT, + site_key TEXT, + reg_key INTEGER DEFAULT 1 NOT NULL, + background TEXT, + tg_bot_token TEXT DEFAULT '' NOT NULL, + tg_chat_id TEXT DEFAULT '' NOT NULL, + tg_bot_status INTEGER DEFAULT 1 NOT NULL, + forward_email TEXT DEFAULT '' NOT NULL, + forward_status INTEGER DEFAULT 1 NOT NULL, + rule_email TEXT DEFAULT '' NOT NULL, + rule_type INTEGER DEFAULT 0 NOT NULL, + login_opacity REAL DEFAULT 0.88, + resend_tokens TEXT DEFAULT '{}' NOT NULL, + notice_title TEXT DEFAULT '' NOT NULL, + notice_content TEXT DEFAULT '' NOT NULL, + notice_type TEXT DEFAULT '' NOT NULL, + notice_duration INTEGER DEFAULT 0 NOT NULL, + notice_position TEXT DEFAULT '' NOT NULL, + notice_offset INTEGER DEFAULT 0 NOT NULL, + notice_width INTEGER DEFAULT 400 NOT NULL, + notice INTEGER DEFAULT 0 NOT NULL, + global_announcement_content TEXT, + global_announcement_version INTEGER, + global_announcement_enabled INTEGER DEFAULT 0 NOT NULL, + global_announcement_title TEXT DEFAULT '' NOT NULL, + global_announcement_display_mode TEXT DEFAULT 'always' NOT NULL, + global_announcement_images TEXT DEFAULT '[]' NOT NULL, + global_announcement_override_share_announcement INTEGER DEFAULT 0 NOT NULL, + global_announcement_auto_apply_new_share INTEGER DEFAULT 1 NOT NULL, + no_recipient INTEGER DEFAULT 1 NOT NULL, + login_domain INTEGER DEFAULT 0 NOT NULL, + bucket TEXT DEFAULT '' NOT NULL, + region TEXT DEFAULT '' NOT NULL, + endpoint TEXT DEFAULT '' NOT NULL, + s3_access_key TEXT DEFAULT '' NOT NULL, + s3_secret_key TEXT DEFAULT '' NOT NULL, + share_whitelist TEXT DEFAULT '' NOT NULL +); + +-- ---------- 分享表 ---------- +CREATE TABLE IF NOT EXISTS share ( + share_id INTEGER PRIMARY KEY AUTOINCREMENT, + share_token TEXT NOT NULL UNIQUE, + target_email TEXT NOT NULL, + share_name TEXT NOT NULL, + keyword_filter TEXT DEFAULT '', + expire_time TEXT NOT NULL, + create_time TEXT DEFAULT CURRENT_TIMESTAMP NOT NULL, + user_id INTEGER NOT NULL, + is_active INTEGER DEFAULT 1 NOT NULL, + rate_limit_per_second INTEGER DEFAULT 5 NOT NULL, + auto_recovery_seconds INTEGER DEFAULT 60 NOT NULL, + status TEXT DEFAULT 'active', + otp_count_daily INTEGER DEFAULT 0, + otp_limit_daily INTEGER DEFAULT 100, + last_reset_date TEXT, + remark TEXT DEFAULT '', + share_type INTEGER DEFAULT 1 NOT NULL, + authorized_emails TEXT DEFAULT '[]' NOT NULL, + verification_code_limit INTEGER DEFAULT 100 NOT NULL, + verification_code_limit_enabled INTEGER DEFAULT 1 NOT NULL, + otp_limit_enabled INTEGER DEFAULT 1 NOT NULL, + share_domain TEXT, + latest_email_count INTEGER, + auto_refresh_enabled INTEGER DEFAULT 0 NOT NULL, + auto_refresh_interval INTEGER DEFAULT 30 NOT NULL, + filter_mode INTEGER DEFAULT 1 NOT NULL, + template_id TEXT, + show_full_email INTEGER DEFAULT 1 NOT NULL, + cooldown_enabled INTEGER DEFAULT 1 NOT NULL, + cooldown_seconds INTEGER DEFAULT 10 NOT NULL, + enable_captcha INTEGER DEFAULT 0 NOT NULL, + announcement_content TEXT, + announcement_version INTEGER, + use_global_announcement INTEGER DEFAULT 1 NOT NULL +); + +-- ---------- 分享访问日志表 ---------- +CREATE TABLE IF NOT EXISTS share_access_log ( + log_id INTEGER PRIMARY KEY AUTOINCREMENT, + share_id INTEGER NOT NULL, + share_token TEXT NOT NULL, + access_ip TEXT NOT NULL, + user_agent TEXT DEFAULT '', + access_email TEXT NOT NULL, + extracted_codes TEXT DEFAULT '[]', + access_result TEXT NOT NULL, + error_message TEXT DEFAULT '', + access_time TEXT DEFAULT CURRENT_TIMESTAMP NOT NULL, + response_time INTEGER DEFAULT 0, + email_count INTEGER DEFAULT 0, + email_ids TEXT DEFAULT '[]' +); + +-- ---------- 邮件模板表 ---------- +CREATE TABLE IF NOT EXISTS email_templates ( + id TEXT PRIMARY KEY, + name TEXT NOT NULL, + sender_pattern TEXT, + subject_pattern TEXT, + body_pattern TEXT, + extraction_regex TEXT NOT NULL, + code_format TEXT, + created_at TEXT DEFAULT CURRENT_TIMESTAMP NOT NULL, + updated_at TEXT DEFAULT CURRENT_TIMESTAMP NOT NULL, + user_id INTEGER NOT NULL, + is_active INTEGER DEFAULT 1 NOT NULL, + description TEXT, + example_email TEXT, + example_code TEXT +); + +-- ---------- 公告已读表 ---------- +CREATE TABLE IF NOT EXISTS announcement_read ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL, + announcement_version INTEGER NOT NULL, + read_at TEXT DEFAULT CURRENT_TIMESTAMP NOT NULL, + UNIQUE(user_id, announcement_version) +); + +-- ---------- 索引 ---------- +CREATE INDEX IF NOT EXISTS idx_user_email ON user(email); +CREATE INDEX IF NOT EXISTS idx_account_email ON account(email); +CREATE INDEX IF NOT EXISTS idx_account_user_id ON account(user_id); +CREATE INDEX IF NOT EXISTS idx_email_user_id ON email(user_id); +CREATE INDEX IF NOT EXISTS idx_email_account_id ON email(account_id); +CREATE INDEX IF NOT EXISTS idx_star_user_id ON star(user_id); +CREATE INDEX IF NOT EXISTS idx_att_email_id ON attachments(email_id); +CREATE INDEX IF NOT EXISTS idx_share_user_id ON share(user_id); +CREATE INDEX IF NOT EXISTS idx_share_token ON share(share_token); +CREATE INDEX IF NOT EXISTS idx_share_access_log_share_id ON share_access_log(share_id); +CREATE INDEX IF NOT EXISTS idx_share_access_log_token ON share_access_log(share_token); + +-- ===================================================== +-- 最小种子数据 (仅当表为空时插入, 保证幂等) +-- ===================================================== + +-- 默认角色: 普通用户 (is_default=1, avail_domain 为空 => 允许全部域名) +INSERT INTO role (name, key, description, ban_email, ban_email_type, avail_domain, sort, is_default, send_count, send_type, account_count) +SELECT '普通用户', 'user', '默认注册角色', '', 0, '', 1, 1, 100, 'day', 5 +WHERE NOT EXISTS (SELECT 1 FROM role); + +-- 系统设置单行: register=0(开放注册), register_verify=1(关闭人机验证), reg_key=1(关闭卡密) +INSERT INTO setting (register) +SELECT 0 +WHERE NOT EXISTS (SELECT 1 FROM setting); From b2cbdf84a6510e0f722d204798c8db1ca80d93fc Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 24 Aug 2026 03:04:43 +0000 Subject: [PATCH 2/2] chore: bootstrap pnpm via corepack in install for portability Ensures the install command works on the default base image (which may not ship pnpm) by activating pnpm@10.33.3 through corepack before installing. Co-authored-by: 7iook <7iook@users.noreply.github.com> --- .cursor/environment.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cursor/environment.json b/.cursor/environment.json index f692e607d3..fac9bf99fe 100644 --- a/.cursor/environment.json +++ b/.cursor/environment.json @@ -1,6 +1,6 @@ { "name": "Cloud Mail", - "install": "cd mail-worker && pnpm install && cd ../mail-vue && pnpm install && pnpm run build && cd ../mail-worker && npx wrangler d1 execute email --local --config wrangler-dev.toml --file db/local-init.sql", + "install": "corepack enable && corepack prepare pnpm@10.33.3 --activate && cd mail-worker && pnpm install && cd ../mail-vue && pnpm install && pnpm run build && cd ../mail-worker && npx wrangler d1 execute email --local --config wrangler-dev.toml --file db/local-init.sql", "terminals": [ { "name": "mail-worker (Hono API + SPA, :8787)",