feat: 0.2.27 - 抽离数据库基础设施模块

- 新增 ias-db crate,集中管理 PostgreSQL 连接池和 SQLx 迁移
- 迁移 ias-service/migrations 到 ias-db/migrations
- ias-main 改用 ias_db::connect_db(),ias-service 只接 PgPool
- 新增操作日志 HTTP 查询接口 (GET /v1/logs, GET /v1/logs/{id})
- 新增操作日志 Web 前端页面,支持按级别/模块/关键词筛选
- 消息队列关键节点增加操作日志写入
- ias-mail 新增 mark_remote_message_seen,IMAP 已读同步
- 工作流配置加载改为文件级容错 (load_configs_with_warnings)
- ias-context 工具调用说明更新为 query_api_docs + call_http_api
- 新增高德地图地理编码 HTTP 能力配置

模块版本: ias-db 0.1.0, ias-ai 0.1.14, ias-context 0.1.6, ias-http 0.1.15, ias-mail 0.1.16, ias-service 0.1.25, ias-main 0.2.27
This commit is contained in:
2026-07-08 17:44:21 +08:00
parent 7d5140c8d2
commit 7deae286ca
83 changed files with 1440 additions and 107 deletions
+15
View File
@@ -0,0 +1,15 @@
[package]
name = "ias-db"
version = "0.1.0"
edition = "2024"
[dependencies]
anyhow = "1"
sqlx = { version = "0.9.0", features = [
"runtime-tokio",
"tls-rustls",
"postgres",
"macros",
"migrate",
"chrono",
] }
+26
View File
@@ -0,0 +1,26 @@
use std::fs;
use std::path::Path;
fn main() {
let migrations = Path::new("migrations");
println!("cargo:rerun-if-changed={}", migrations.display());
let mut count = 0u64;
if let Ok(entries) = fs::read_dir(migrations) {
for entry in entries.flatten() {
let path = entry.path();
if path.extension().is_some_and(|extension| extension == "sql") {
println!("cargo:rerun-if-changed={}", path.display());
count = count.wrapping_add(fs::metadata(&path).map(|m| m.len()).unwrap_or(0));
}
}
}
let out_dir = std::env::var("OUT_DIR").unwrap();
let dest = Path::new(&out_dir).join("migration_stamp.rs");
fs::write(
&dest,
format!("// generated by build.rs\npub const MIGRATION_STAMP: u64 = {count};\n"),
)
.unwrap();
}
@@ -0,0 +1,12 @@
-- Add migration script here
CREATE TABLE ias_wechat_accounts (
id BIGSERIAL PRIMARY KEY,
user_name VARCHAR(100) NOT NULL,
token VARCHAR(255),
account_id VARCHAR(255),
base_url VARCHAR(255),
user_id VARCHAR(255),
updates_buf VARCHAR(255),
user_status INTEGER,
created_at TIMESTAMP NOT NULL DEFAULT NOW()
);
@@ -0,0 +1,13 @@
CREATE TABLE ias_web_pages (
id BIGSERIAL PRIMARY KEY,
slug VARCHAR(64) NOT NULL UNIQUE,
page_type VARCHAR(64) NOT NULL,
title VARCHAR(255) NOT NULL,
summary TEXT,
content TEXT NOT NULL,
source_label VARCHAR(255),
metadata TEXT NOT NULL DEFAULT '{}',
created_at TIMESTAMP NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_ias_web_pages_type_slug ON ias_web_pages(page_type, slug);
@@ -0,0 +1,66 @@
CREATE TABLE ias_long_term_memories (
id BIGSERIAL PRIMARY KEY,
scope_key VARCHAR(768) NOT NULL,
channel VARCHAR(64) NOT NULL,
account_id VARCHAR(255) NOT NULL,
from_user_id VARCHAR(255) NOT NULL,
context_label TEXT,
content TEXT NOT NULL,
metadata TEXT NOT NULL DEFAULT '{}',
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMP
);
CREATE INDEX idx_ias_long_term_memories_scope_updated
ON ias_long_term_memories(scope_key, updated_at DESC)
WHERE deleted_at IS NULL;
CREATE TABLE ias_context_sessions (
id BIGSERIAL PRIMARY KEY,
scope_key VARCHAR(768) NOT NULL,
channel VARCHAR(64) NOT NULL,
account_id VARCHAR(255) NOT NULL,
from_user_id VARCHAR(255) NOT NULL,
context_label TEXT,
started_at TIMESTAMP NOT NULL DEFAULT NOW(),
last_message_at TIMESTAMP NOT NULL DEFAULT NOW(),
ended_at TIMESTAMP,
end_reason VARCHAR(64),
summary TEXT
);
CREATE INDEX idx_ias_context_sessions_active
ON ias_context_sessions(scope_key, started_at DESC)
WHERE ended_at IS NULL;
CREATE INDEX idx_ias_context_sessions_ended
ON ias_context_sessions(scope_key, ended_at DESC)
WHERE ended_at IS NOT NULL;
CREATE TABLE ias_context_messages (
id BIGSERIAL PRIMARY KEY,
session_id BIGINT NOT NULL REFERENCES ias_context_sessions(id) ON DELETE CASCADE,
role VARCHAR(32) NOT NULL,
content TEXT NOT NULL,
tool_call_id VARCHAR(255),
tool_calls TEXT,
created_at TIMESTAMP NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_ias_context_messages_session
ON ias_context_messages(session_id, id);
CREATE TABLE ias_context_summaries (
id BIGSERIAL PRIMARY KEY,
scope_key VARCHAR(768) NOT NULL,
summary_type VARCHAR(32) NOT NULL,
source_session_id BIGINT REFERENCES ias_context_sessions(id) ON DELETE SET NULL,
content TEXT NOT NULL,
started_at TIMESTAMP,
ended_at TIMESTAMP,
created_at TIMESTAMP NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_ias_context_summaries_latest
ON ias_context_summaries(scope_key, summary_type, created_at DESC);
@@ -0,0 +1,23 @@
ALTER TABLE ias_long_term_memories
ALTER COLUMN created_at TYPE TIMESTAMPTZ USING created_at AT TIME ZONE 'UTC',
ALTER COLUMN updated_at TYPE TIMESTAMPTZ USING updated_at AT TIME ZONE 'UTC',
ALTER COLUMN deleted_at TYPE TIMESTAMPTZ USING deleted_at AT TIME ZONE 'UTC',
ALTER COLUMN created_at SET DEFAULT NOW(),
ALTER COLUMN updated_at SET DEFAULT NOW();
ALTER TABLE ias_context_sessions
ALTER COLUMN started_at TYPE TIMESTAMPTZ USING started_at AT TIME ZONE 'UTC',
ALTER COLUMN last_message_at TYPE TIMESTAMPTZ USING last_message_at AT TIME ZONE 'UTC',
ALTER COLUMN ended_at TYPE TIMESTAMPTZ USING ended_at AT TIME ZONE 'UTC',
ALTER COLUMN started_at SET DEFAULT NOW(),
ALTER COLUMN last_message_at SET DEFAULT NOW();
ALTER TABLE ias_context_messages
ALTER COLUMN created_at TYPE TIMESTAMPTZ USING created_at AT TIME ZONE 'UTC',
ALTER COLUMN created_at SET DEFAULT NOW();
ALTER TABLE ias_context_summaries
ALTER COLUMN started_at TYPE TIMESTAMPTZ USING started_at AT TIME ZONE 'UTC',
ALTER COLUMN ended_at TYPE TIMESTAMPTZ USING ended_at AT TIME ZONE 'UTC',
ALTER COLUMN created_at TYPE TIMESTAMPTZ USING created_at AT TIME ZONE 'UTC',
ALTER COLUMN created_at SET DEFAULT NOW();
@@ -0,0 +1,29 @@
UPDATE ias_long_term_memories
SET scope_key = channel || ':' || account_id || ':' || from_user_id;
UPDATE ias_context_sessions
SET scope_key = channel || ':' || account_id || ':' || from_user_id;
UPDATE ias_context_summaries AS summary
SET scope_key = session.scope_key
FROM ias_context_sessions AS session
WHERE summary.source_session_id = session.id;
WITH ranked AS (
SELECT
id,
ROW_NUMBER() OVER (
PARTITION BY scope_key
ORDER BY started_at DESC, id DESC
) AS rank
FROM ias_context_sessions
WHERE ended_at IS NULL
)
UPDATE ias_context_sessions AS session
SET
ended_at = last_message_at,
end_reason = 'scope_normalized',
summary = COALESCE(summary, '旧上下文 scope 合并时自动结束。')
FROM ranked
WHERE session.id = ranked.id
AND ranked.rank > 1;
@@ -0,0 +1,62 @@
CREATE TABLE ias_upgrade_logs (
id BIGSERIAL PRIMARY KEY,
version VARCHAR(64) NOT NULL UNIQUE,
released_at TIMESTAMPTZ NOT NULL,
title VARCHAR(255) NOT NULL,
description TEXT NOT NULL,
changes JSONB NOT NULL DEFAULT '[]'::jsonb,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_ias_upgrade_logs_released
ON ias_upgrade_logs(released_at DESC);
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes
) VALUES (
'0.1.1',
'2026-07-06T00:00:00+08:00',
'Context management and startup notices',
'Added standalone context management, memory tooling, session summaries, LLM response compatibility, startup online notices, and initial version/changelog files.',
'[
"Added the standalone Rust context module for long-term memories, session context, session summaries, and recent-week summaries.",
"Added timestamped memory records and context APIs for querying, creating, updating, and deleting memories.",
"Added context-window rollover rules for /new, two-hour inactivity, and token-limit thresholds.",
"Added automatic session summary generation when a new context is opened.",
"Added memory tools so the assistant can query and update user memories during conversation.",
"Fixed LLM response parsing so router responses without service_tier are accepted.",
"Added startup online notices so users receive a friendly message after the service comes back online.",
"Added the project version file and changelog, and documented the version/changelog rule."
]'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes;
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes
) VALUES (
'0.1.2',
'2026-07-06T00:00:00+08:00',
'Persisted upgrade logs and Codex rules',
'Added persisted upgrade records and clarified that version/changelog rules must live in the AGENTS.md file Codex reads for the current context.',
'[
"Added the ias_upgrade_logs database table for persisted upgrade records.",
"Added migration-seeded upgrade records for versions 0.1.1 and 0.1.2.",
"Updated Codex project rules in AGENTS.md so version bumps update VERSION, crate versions, CHANGELOG.md, and database upgrade records together.",
"Clarified that project rules should live in the AGENTS.md file Codex actually reads for the current context."
]'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes;
@@ -0,0 +1,21 @@
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes
) VALUES (
'0.1.3',
'2026-07-06T00:00:00+08:00',
'Detached background service startup',
'Fixed the service supervisor so background start and restart detach the child process from the invoking command session, then recorded the upgrade in the database.',
'[
"Fixed background service startup so ias service start/restart detaches the child service process from the invoking command session.",
"Added a database upgrade-log migration for version 0.1.3.",
"Verified service health using ias service status after restart."
]'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes;
@@ -0,0 +1,48 @@
CREATE TABLE ias_mail_messages (
id BIGSERIAL PRIMARY KEY,
source_key VARCHAR(512) NOT NULL,
mailbox VARCHAR(255) NOT NULL,
uid VARCHAR(255) NOT NULL,
message_id TEXT,
from_label TEXT NOT NULL DEFAULT '',
subject TEXT NOT NULL DEFAULT '',
summary TEXT NOT NULL DEFAULT '',
content_preview TEXT NOT NULL DEFAULT '',
preview_page_id BIGINT REFERENCES ias_web_pages(id) ON DELETE SET NULL,
preview_url TEXT,
received_at TIMESTAMPTZ,
raw_size BIGINT NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (source_key, mailbox, uid)
);
CREATE INDEX idx_ias_mail_messages_created
ON ias_mail_messages(created_at DESC);
CREATE INDEX idx_ias_mail_messages_received
ON ias_mail_messages(received_at DESC);
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes
) VALUES (
'0.1.4',
'2026-07-06T00:00:00+08:00',
'Mail polling and standalone HTTP module',
'Added a standalone mail module for IMAP polling and notifications, moved HTTP serving and web preview pages into a standalone Rust module, and added mail persistence tables.',
'[
"Added the standalone Rust mail module for IMAP mailbox polling, message parsing, database deduplication, preview-page creation, and user notifications.",
"Added the standalone Rust ias-http module for HTTP serving, health checks, and reusable web preview pages.",
"Moved the existing web preview routes out of service and into ias-http.",
"Added /mail-api/messages and /mail-api/messages/{id} for inspecting stored mail records.",
"Added the ias_mail_messages database table and persisted the 0.1.4 upgrade record.",
"Added mail polling configuration through IA_MAIL_* environment variables; polling stays disabled when mailbox settings are absent."
]'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes;
@@ -0,0 +1,56 @@
CREATE TABLE ias_mail_accounts (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
source_key VARCHAR(512) NOT NULL UNIQUE,
imap_host VARCHAR(255) NOT NULL,
imap_port INTEGER NOT NULL DEFAULT 993,
imap_username TEXT NOT NULL,
imap_password TEXT NOT NULL,
mailbox VARCHAR(255) NOT NULL DEFAULT 'INBOX',
notify_account_id VARCHAR(255) NOT NULL,
notify_user_id VARCHAR(255) NOT NULL,
poll_seconds INTEGER NOT NULL DEFAULT 60,
fetch_limit INTEGER NOT NULL DEFAULT 10,
accept_invalid_certs BOOLEAN NOT NULL DEFAULT FALSE,
enabled BOOLEAN NOT NULL DEFAULT TRUE,
last_checked_at TIMESTAMPTZ,
last_error TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_ias_mail_accounts_enabled
ON ias_mail_accounts(enabled, id);
ALTER TABLE ias_mail_messages
ADD COLUMN mail_account_id BIGINT REFERENCES ias_mail_accounts(id) ON DELETE SET NULL,
ADD COLUMN content TEXT NOT NULL DEFAULT '',
ADD COLUMN raw_headers TEXT;
CREATE INDEX idx_ias_mail_messages_account
ON ias_mail_messages(mail_account_id, created_at DESC);
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes
) VALUES (
'0.1.5',
'2026-07-06T00:00:00+08:00',
'Database-backed mail auth and cache',
'Added guided mail account configuration with ias mail auth, persisted mail accounts in the database, and expanded mail message caching.',
'[
"Added database-backed mail account configuration through the guided ias mail auth command.",
"Added the ias_mail_accounts table and linked cached mail messages back to their configured account.",
"Extended the mail cache to persist full parsed mail content and raw headers in ias_mail_messages.",
"Updated mail polling to load enabled mail accounts from the database first, while keeping environment-based configuration as a fallback.",
"Added mail account listing through ias mail list.",
"Persisted the 0.1.5 upgrade record."
]'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes;
@@ -0,0 +1,28 @@
CREATE TABLE ias_version_notifications (
version VARCHAR(64) PRIMARY KEY,
notified_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
recipient_count INTEGER NOT NULL DEFAULT 0
);
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes
) VALUES (
'0.1.6',
'2026-07-06T00:00:00+08:00',
'Version update notifications and changelog pages',
'Added HTTP upgrade-log pages and one-time startup notifications that tell users which version the service updated to.',
'[
"Added HTTP-rendered upgrade log pages at /updates and /updates/{version}.",
"Added one-time startup notifications for new service versions, including the updated version number and an HTTP upgrade-log link.",
"Added persisted version notification state so users are not notified repeatedly after ordinary restarts.",
"Persisted the 0.1.6 upgrade record."
]'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes;
@@ -0,0 +1,73 @@
-- 将已有升级日志翻译为中文
UPDATE ias_upgrade_logs SET
title = '上下文管理和启动通知',
description = '新增独立的上下文管理模块、记忆工具、会话摘要、LLM 响应兼容性、启动上线通知,以及初始版本和更新日志文件。',
changes = '[
"新增独立的 Rust context 模块,支持长期记忆、会话上下文、会话摘要和近一周摘要。",
"新增带时间戳的记忆记录和上下文 API,支持查询、创建、更新和删除记忆。",
"新增上下文窗口切换规则,涵盖 /new 命令、两小时不活跃和 token 上限阈值。",
"新增开启新上下文时自动生成会话摘要。",
"新增记忆工具,使助手在对话中可以查询和更新用户记忆。",
"修复 LLM 响应解析,使不带 service_tier 的路由响应也能正常接受。",
"新增启动上线通知,服务恢复后向用户发送友好提示消息。",
"新增项目版本文件和更新日志,并记录版本/更新日志规则。"
]'::jsonb
WHERE version = '0.1.1';
UPDATE ias_upgrade_logs SET
title = '持久化升级日志和 Codex 规则',
description = '新增持久化升级记录,并明确版本/更新日志规则必须写在 Codex 实际读取的 AGENTS.md 文件中。',
changes = '[
"新增 ias_upgrade_logs 数据库表,用于持久化升级记录。",
"新增迁移种子数据,写入 0.1.1 和 0.1.2 的升级记录。",
"更新 AGENTS.md 中的 Codex 项目规则,使版本升级同步更新 VERSION、crate 版本、CHANGELOG.md 和数据库升级记录。",
"明确项目规则应写在 Codex 实际读取的 AGENTS.md 文件中。"
]'::jsonb
WHERE version = '0.1.2';
UPDATE ias_upgrade_logs SET
title = '分离后台服务启动进程',
description = '修复服务管理器,使后台启动和重启能将子进程与调用命令的会话分离,并在数据库中记录本次升级。',
changes = '[
"修复后台服务启动问题,使 ias service start/restart 能将子服务进程与调用命令的会话分离。",
"新增版本 0.1.3 的数据库升级日志迁移。",
"重启后通过 ias service status 验证服务健康状态。"
]'::jsonb
WHERE version = '0.1.3';
UPDATE ias_upgrade_logs SET
title = '邮件轮询和独立 HTTP 模块',
description = '新增独立的邮件模块用于 IMAP 轮询和通知,将 HTTP 服务和网页预览页面迁移到独立 Rust 模块,并新增邮件持久化表。',
changes = '[
"新增独立的 Rust mail 模块,实现 IMAP 邮箱轮询、邮件解析、数据库去重、预览页面创建和用户通知。",
"新增独立的 Rust ias-http 模块,提供 HTTP 服务、健康检查和可复用的网页预览页面。",
"将现有的网页预览路由从 service 迁移至 ias-http。",
"新增 /mail-api/messages 和 /mail-api/messages/{id} 接口,用于查看存储的邮件记录。",
"新增 ias_mail_messages 数据库表,持久化 0.1.4 升级记录。",
"新增通过 IA_MAIL_* 环境变量配置邮件轮询;未配置邮箱时轮询保持禁用。"
]'::jsonb
WHERE version = '0.1.4';
UPDATE ias_upgrade_logs SET
title = '基于数据库的邮件认证和缓存',
description = '新增通过 ias mail auth 引导命令配置邮件账号,将邮件账号持久化到数据库,并扩展邮件消息缓存。',
changes = '[
"新增基于数据库的邮件账号配置,通过 ias mail auth 引导命令完成。",
"新增 ias_mail_accounts 表,并将缓存的邮件消息关联到对应配置账号。",
"扩展邮件缓存,在 ias_mail_messages 中持久化完整解析后的邮件内容和原始头信息。",
"更新邮件轮询逻辑,优先从数据库加载启用的邮件账号,同时保留环境变量配置作为回退方案。",
"新增 ias mail list 命令查看邮件账号列表。",
"持久化 0.1.5 升级记录。"
]'::jsonb
WHERE version = '0.1.5';
UPDATE ias_upgrade_logs SET
title = '版本更新通知和更新日志页面',
description = '新增 HTTP 更新日志页面和一次性启动通知,告知用户服务更新到了哪个版本。',
changes = '[
"新增 HTTP 渲染的更新日志页面,路由为 /updates 和 /updates/{version}。",
"新增服务版本一次性启动通知,包含更新的版本号和 HTTP 更新日志链接。",
"新增持久化的版本通知状态,避免普通重启后重复通知用户。",
"持久化 0.1.6 升级记录。"
]'::jsonb
WHERE version = '0.1.6';
@@ -0,0 +1,38 @@
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes,
modules
) VALUES (
'0.1.7',
'2026-07-06T00:00:00+08:00',
'模块统一 ias 前缀、独立版本与内网地址支持',
'所有 Rust crate 统一加 ias- 前缀,各模块开始独立维护版本号,新增内网地址环境变量,版本通知同时发送公网和内网链接,并将已有升级日志翻译为中文。',
'[
"所有 Rust crate 统一加 ias- 前缀:ai → ias-ai、common → ias-common、context → ias-context、mail → ias-mail、service → ias-service、wechat → ias-wechat。",
"新增 ias-main 模块承载 CLI 入口和二进制打包。",
"各模块开始独立维护版本号,ias-main 0.1.7 代表整体版本。",
"新增 IA_WEB_INTERNAL_URL 环境变量,用于配置内网可访问的基础 URL。",
"新增 internal_url_for_path() 函数,版本更新通知同时发送公网和内网地址的 Markdown 链接。",
"将已有升级日志的 title、description、changes 翻译为中文。",
"ias_upgrade_logs 新增 modules JSONB 字段记录各模块独立版本。",
"持久化 0.1.7 升级记录。"
]'::jsonb,
'{
"ias-common": "0.1.0",
"ias-ai": "0.1.0",
"ias-wechat": "0.1.0",
"ias-context": "0.1.0",
"ias-http": "0.1.0",
"ias-mail": "0.1.0",
"ias-service": "0.1.0",
"ias-main": "0.1.7"
}'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes,
modules = EXCLUDED.modules;
@@ -0,0 +1,27 @@
-- 新增 modules 字段,记录各模块独立版本
ALTER TABLE ias_upgrade_logs
ADD COLUMN modules JSONB NOT NULL DEFAULT '{}'::jsonb;
-- 更新已有记录的模块版本信息
UPDATE ias_upgrade_logs SET modules = '{
"ias-common": "0.1.0",
"ias-ai": "0.1.0",
"ias-wechat": "0.1.0",
"ias-context": "0.1.0",
"ias-http": "0.1.0",
"ias-mail": "0.1.0",
"ias-service": "0.1.0",
"ias-main": "0.1.0"
}'::jsonb WHERE version IN ('0.1.1', '0.1.2', '0.1.3', '0.1.4', '0.1.5', '0.1.6');
-- 更新 0.1.7 的记录
UPDATE ias_upgrade_logs SET modules = '{
"ias-common": "0.1.0",
"ias-ai": "0.1.0",
"ias-wechat": "0.1.0",
"ias-context": "0.1.0",
"ias-http": "0.1.0",
"ias-mail": "0.1.0",
"ias-service": "0.1.0",
"ias-main": "0.1.7"
}'::jsonb WHERE version = '0.1.7';
@@ -0,0 +1,35 @@
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes,
modules
) VALUES (
'0.1.8',
'2026-07-06T00:00:00+08:00',
'邮件账户修改与连通性检查 CLI',
'新增邮件账户修改和 IMAP 连通性检查命令,便于在不直接操作数据库的情况下维护邮箱配置并确认服务器可访问。',
'[
"新增 ias mail edit <id> 命令,可交互式修改已保存的 IMAP 邮件账户配置,密码留空时保留原值。",
"新增 ias mail check <id> 命令,可使用已保存配置检查 IMAP 服务器连通性和邮箱目录可访问性。",
"邮件账户检查成功会更新 last_checked_at 并清空 last_error,失败会记录错误信息。",
"复用新增账户时的 IMAP 连接测试逻辑,TLS 证书错误仍支持交互式选择接受无效证书后重试。",
"无数据库结构变化。"
]'::jsonb,
'{
"ias-common": "0.1.0",
"ias-ai": "0.1.0",
"ias-wechat": "0.1.0",
"ias-context": "0.1.0",
"ias-http": "0.1.0",
"ias-mail": "0.1.1",
"ias-service": "0.1.0",
"ias-main": "0.1.8"
}'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes,
modules = EXCLUDED.modules;
@@ -0,0 +1,35 @@
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes,
modules
) VALUES (
'0.1.9',
'2026-07-06T00:00:00+08:00',
'邮件最近 20 封未读检查',
'调整邮件轮询策略,每轮读取邮箱最近 20 封邮件并基于 FLAGS 判断未读状态,仅对未读且未入库的邮件生成预览页并发送提醒。',
'[
"调整邮件轮询逻辑:每轮固定读取邮箱最近 20 封邮件,而不是仅搜索 UNSEEN 集合。",
"读取最近邮件时同步获取 FLAGS,只对未包含 \\Seen 标记的邮件入库、生成预览页并发送微信提醒。",
"邮件正文抓取改用 BODY.PEEK[],避免检查最近邮件时把未读邮件隐式标记为已读。",
"新增最近 20 封 sequence range 的单元测试,并通过 cargo test -p ias-mail 与 cargo check 验证。",
"无数据库结构变化。"
]'::jsonb,
'{
"ias-common": "0.1.0",
"ias-ai": "0.1.0",
"ias-wechat": "0.1.0",
"ias-context": "0.1.0",
"ias-http": "0.1.0",
"ias-mail": "0.1.2",
"ias-service": "0.1.0",
"ias-main": "0.1.9"
}'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes,
modules = EXCLUDED.modules;
@@ -0,0 +1,35 @@
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes,
modules
) VALUES (
'0.1.10',
'2026-07-06T00:00:00+08:00',
'手动邮件拉取与版本更新链接修复',
'新增手动拉取最新邮件的 CLI 命令,并修复服务使用模块版本生成更新日志链接导致 /updates/0.1.0 不存在的问题。',
'[
"新增 ias mail pull [id] 命令,可手动拉取最近 20 封中的未读邮件;不传 id 时拉取所有启用邮件账户,传 id 时拉取指定账户。",
"手动拉取会复用后台轮询的解析、去重、入库和邮件预览页生成逻辑,并输出本次未读、新增和已存在数量。",
"修复服务启动和版本更新通知使用 ias-service crate 版本的问题,改为读取根目录 VERSION 作为整体项目版本。",
"修复版本更新通知可能链接到不存在的 /updates/0.1.0 页面的问题;本版本通知会指向 /updates/0.1.10。",
"无数据库结构变化。"
]'::jsonb,
'{
"ias-common": "0.1.0",
"ias-ai": "0.1.0",
"ias-wechat": "0.1.0",
"ias-context": "0.1.0",
"ias-http": "0.1.0",
"ias-mail": "0.1.3",
"ias-service": "0.1.1",
"ias-main": "0.1.10"
}'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes,
modules = EXCLUDED.modules;
@@ -0,0 +1,34 @@
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes,
modules
) VALUES (
'0.1.11',
'2026-07-06T00:00:00+08:00',
'手动邮件拉取同步最近 20 封',
'修正手动邮件拉取命令的语义,使其抓取最近 20 封邮件并入库,同时保留后台轮询只提醒未读邮件的行为。',
'[
"修正 ias mail pull [id] 的手动拉取语义:现在会抓取最近 20 封邮件并入库,不再只处理未读邮件。",
"ias mail pull 输出新增扫描数量,便于确认 IMAP 确实返回了最近邮件;同时继续显示其中未读、新增和已存在数量。",
"后台邮件轮询保持只处理未读邮件并发送微信提醒,手动拉取不发送微信提醒。",
"无数据库结构变化。"
]'::jsonb,
'{
"ias-common": "0.1.0",
"ias-ai": "0.1.0",
"ias-wechat": "0.1.0",
"ias-context": "0.1.0",
"ias-http": "0.1.0",
"ias-mail": "0.1.4",
"ias-service": "0.1.1",
"ias-main": "0.1.11"
}'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes,
modules = EXCLUDED.modules;
@@ -0,0 +1,35 @@
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes,
modules
) VALUES (
'0.1.12',
'2026-07-06T00:00:00+08:00',
'邮件 HTTP 查询服务增强',
'增强邮件 HTTP 查询接口,支持关键词、账户、邮箱目录、来源和分页查询,并新增查询语义别名路由。',
'[
"增强邮件 HTTP 查询服务:GET /mail-api/messages 支持关键词、账户、邮箱目录、来源和分页查询。",
"新增 GET /mail-api/search 作为邮件查询别名,便于客户端直接按查询语义调用。",
"邮件列表响应新增 page、per_page 和 total 字段,保留 limit 参数的兼容行为。",
"查询关键词 q 会匹配发件人、主题、摘要、正文和 Message-ID。",
"无数据库结构变化。"
]'::jsonb,
'{
"ias-common": "0.1.0",
"ias-ai": "0.1.0",
"ias-wechat": "0.1.0",
"ias-context": "0.1.0",
"ias-http": "0.1.0",
"ias-mail": "0.1.5",
"ias-service": "0.1.1",
"ias-main": "0.1.12"
}'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes,
modules = EXCLUDED.modules;
@@ -0,0 +1,35 @@
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes,
modules
) VALUES (
'0.1.13',
'2026-07-06T00:00:00+08:00',
'邮件查询 API 与页面',
'新增 v1 邮件查询 API,并提供邮件列表和邮件预览页面,同时保留旧 mail-api 路由兼容已有调用。',
'[
"新增 GET /v1/mail/messages、GET /v1/mail/messages/{id} 和 GET /v1/mail/search,作为邮件查询 API 的新地址。",
"保留 GET /mail-api/messages、GET /mail-api/messages/{id} 和 GET /mail-api/search 兼容旧调用。",
"新增 GET /mail 邮件列表页面,支持关键词、账户、邮箱目录、来源和分页查询。",
"新增 GET /mail/messages/{id} 邮件预览页面,可查看邮件正文、元信息和原始头信息。",
"无数据库结构变化。"
]'::jsonb,
'{
"ias-common": "0.1.0",
"ias-ai": "0.1.0",
"ias-wechat": "0.1.0",
"ias-context": "0.1.0",
"ias-http": "0.1.0",
"ias-mail": "0.1.6",
"ias-service": "0.1.1",
"ias-main": "0.1.13"
}'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes,
modules = EXCLUDED.modules;
@@ -0,0 +1,35 @@
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes,
modules
) VALUES (
'0.1.14',
'2026-07-06T00:00:00+08:00',
'统一 v1 API 路由',
'删除旧邮件查询兼容入口,并将 JSON API 统一迁移到 /v1 前缀;页面路由保持非 /v1 地址。',
'[
"删除旧邮件查询兼容路由 GET /mail-api/messages、GET /mail-api/messages/{id} 和 GET /mail-api/search。",
"所有 JSON API 统一使用 /v1/...:邮件、上下文记忆、动态页面创建和健康检查接口均迁移到新地址。",
"页面路由继续保持非 /v1 地址:/mail、/mail/messages/{id}、/mail/{slug}、/web/{page_type}/{slug} 和 /updates。",
"AI 内部工具调用同步改用 /v1/web/pages 与 /v1/context/memories...。",
"无数据库结构变化。"
]'::jsonb,
'{
"ias-common": "0.1.0",
"ias-ai": "0.1.1",
"ias-wechat": "0.1.0",
"ias-context": "0.1.1",
"ias-http": "0.1.1",
"ias-mail": "0.1.7",
"ias-service": "0.1.1",
"ias-main": "0.1.14"
}'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes,
modules = EXCLUDED.modules;
@@ -0,0 +1,36 @@
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes,
modules
) VALUES (
'0.1.15',
'2026-07-06T00:00:00+08:00',
'邮件预览页面优化',
'邮件预览页面全新设计,支持上下篇导航、HTML 邮件渲染和元信息一键复制。',
'[
"邮件预览页面全新设计:现代化卡片布局,顶部展示发件人、时间和来源等关键信息。",
"新增上一封/下一封快速导航,无需返回列表即可连续浏览邮件。",
"详细信息折叠面板,UID 和 Message-ID 支持一键复制。",
"HTML 邮件自动检测并在沙箱 iframe 中渲染,纯文本邮件保持原有展示方式。",
"新增 find_adjacent_ids 仓库方法,按 ID 查询相邻邮件记录。",
"无数据库结构变化。"
]'::jsonb,
'{
"ias-common": "0.1.0",
"ias-ai": "0.1.1",
"ias-wechat": "0.1.0",
"ias-context": "0.1.1",
"ias-http": "0.1.1",
"ias-mail": "0.1.8",
"ias-service": "0.1.1",
"ias-main": "0.1.15"
}'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes,
modules = EXCLUDED.modules;
@@ -0,0 +1,40 @@
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes,
modules
) VALUES (
'0.2.1',
'2026-07-06T00:00:00+08:00',
'全面采用 Tailwind CSS,回滚服务端渲染',
'移除独立 React 前端,恢复服务端 HTML 渲染,全面采用 Tailwind CSS CDN 统一页面样式。',
'[
"全面采用 Tailwind CSS:通过 CDN 引入,统一所有页面样式,替代原有内联 CSS。",
"更新日志页面:卡片布局,蓝色版本标签点缀,响应式设计。",
"邮件列表页面:现代化搜索筛选栏,表格交互 Hover 高亮,分页器优化。",
"邮件详情页面:发件人/时间/来源信息卡片,详细信息折叠面板,键盘左右键快速导航。",
"Web 页面/Mail 提醒页面:元数据网格展示,文本内容自动换行。",
"提取公共 shell 模块 (ias-http/src/shell.rs):统一 HTML 布局、顶部导航栏和 Tailwind CDN 引入。",
"新增首页 / 重定向到更新日志。",
"移除独立 React 前端项目 ias-web 及相关 build.rs。",
"保留所有 JSON API 端点 (/v1/*),供内部调用。",
"无数据库结构变化。"
]'::jsonb,
'{
"ias-common": "0.1.0",
"ias-ai": "0.1.1",
"ias-wechat": "0.1.0",
"ias-context": "0.1.1",
"ias-http": "0.1.2",
"ias-mail": "0.1.9",
"ias-service": "0.1.2",
"ias-main": "0.2.1"
}'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes,
modules = EXCLUDED.modules;
@@ -0,0 +1,37 @@
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes,
modules
) VALUES (
'0.2.0',
'2026-07-06T00:00:00+08:00',
'前后端分离与 React 前端',
'将服务端 HTML 渲染调整为 RESTful JSON API 与 React 前端,并新增前端静态文件构建与托管能力。',
'[
"重大架构调整:前后端分离,将 HTML 服务端渲染改为 RESTful JSON API + React 前端。",
"新增 ias-web 前端项目,基于 Vite + React + TypeScript 构建。",
"后端 API 路由调整:更新日志、Web 页面、邮件页面均提供 /v1 JSON 接口。",
"后端添加 CORS 支持,并集成前端静态文件托管。",
"新增 build.rs,前端源文件变更时自动触发 npm build。",
"支持 SKIP_FRONTEND_BUILD 和 IA_STATIC_DIR 环境变量。",
"前端开发代理配置自动转发 /v1 API 请求到后端。"
]'::jsonb,
'{
"ias-common": "0.1.0",
"ias-ai": "0.1.1",
"ias-wechat": "0.1.0",
"ias-context": "0.1.1",
"ias-http": "0.1.2",
"ias-mail": "0.1.9",
"ias-service": "0.1.2",
"ias-main": "0.2.0"
}'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes,
modules = EXCLUDED.modules;
@@ -0,0 +1,36 @@
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes,
modules
) VALUES (
'0.2.2',
'2026-07-06T00:00:00+08:00',
'修复 HTML 渲染安全与错误页响应',
'修复邮件详情和 Web 动态页面中的 HTML 渲染边界问题,并补齐缺失的 0.2.0 升级日志记录。',
'[
"修复邮件详情页预览链接的 HTML 属性转义,避免异常 URL 破坏页面结构或注入属性。",
"修复长中文或 Emoji 邮件内容、Message-ID 截断时可能因 UTF-8 非字符边界切片导致的页面错误。",
"收紧 HTML 邮件 iframe 沙箱:移除同源权限,并通过 CSP 默认禁止外部资源、表单提交和脚本加载。",
"修复 Web 动态页面 404/500 错误页的响应类型,确保浏览器按 HTML 页面渲染。",
"补齐 0.2.0 数据库升级日志记录。",
"新增针对邮件 HTML 安全渲染、Unicode 截断和 Web 错误页响应类型的单元测试。"
]'::jsonb,
'{
"ias-common": "0.1.0",
"ias-ai": "0.1.1",
"ias-wechat": "0.1.0",
"ias-context": "0.1.1",
"ias-http": "0.1.3",
"ias-mail": "0.1.10",
"ias-service": "0.1.3",
"ias-main": "0.2.2"
}'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes,
modules = EXCLUDED.modules;
@@ -0,0 +1,34 @@
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes,
modules
) VALUES (
'0.2.3',
'2026-07-07T00:00:00+08:00',
'修复邮件自动通知漏发',
'修复后台邮件轮询因 IMAP 已读标记跳过新邮件通知的问题,并让轮询抓取范围遵循账户配置。',
'[
"修复邮件自动通知漏发:后台轮询现在会对所有新入库邮件发送通知,不再因为 IMAP \\Seen 已读标记跳过通知。",
"邮件轮询现在尊重账户配置的 fetch_limit,不再固定只检查最近 20 封邮件。",
"邮件轮询启动日志新增 fetch_limit,便于确认实际加载的抓取范围。",
"新增单元测试覆盖已读新邮件仍会被自动轮询处理,以及不同抓取上限下的 IMAP 序列范围计算。"
]'::jsonb,
'{
"ias-common": "0.1.0",
"ias-ai": "0.1.1",
"ias-wechat": "0.1.0",
"ias-context": "0.1.1",
"ias-http": "0.1.3",
"ias-mail": "0.1.11",
"ias-service": "0.1.4",
"ias-main": "0.2.3"
}'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes,
modules = EXCLUDED.modules;
@@ -0,0 +1,33 @@
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes,
modules
) VALUES (
'0.2.4',
'2026-07-07T00:00:00+08:00',
'新增邮件查询 YAML HTTP 工具',
'新增 YAML 配置的邮件查询 HTTP 工具,允许通过现有邮件 API 查询或搜索邮件列表。',
'[
"新增 YAML 配置的邮件查询 HTTP 工具 query_mail_messages,通过 /v1/mail/messages 查询或搜索邮件列表。",
"工具支持关键词、邮箱目录、来源、页码和每页数量参数,返回分页信息和当前页邮件 JSON 列表。",
"无数据库结构变化。"
]'::jsonb,
'{
"ias-common": "0.1.0",
"ias-ai": "0.1.2",
"ias-wechat": "0.1.0",
"ias-context": "0.1.1",
"ias-http": "0.1.3",
"ias-mail": "0.1.11",
"ias-service": "0.1.5",
"ias-main": "0.2.4"
}'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes,
modules = EXCLUDED.modules;
@@ -0,0 +1,33 @@
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes,
modules
) VALUES (
'0.2.5',
'2026-07-07T00:00:00+08:00',
'修复迁移文件变化后的后台服务重编译',
'修复新增 SQL 迁移文件后后台服务可能继续使用旧二进制迁移列表的问题,确保迁移目录变化会触发重新编译。',
'[
"修复新增 SQL 迁移文件后后台服务可能继续使用旧二进制迁移列表的问题。",
"ias-service 新增 build.rs,显式监听 migrations 目录和 SQL 文件变化,确保 sqlx::migrate! 重新嵌入最新迁移。",
"无数据库结构变化。"
]'::jsonb,
'{
"ias-common": "0.1.0",
"ias-ai": "0.1.2",
"ias-wechat": "0.1.0",
"ias-context": "0.1.1",
"ias-http": "0.1.3",
"ias-mail": "0.1.11",
"ias-service": "0.1.6",
"ias-main": "0.2.5"
}'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes,
modules = EXCLUDED.modules;
@@ -0,0 +1,36 @@
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes,
modules
) VALUES (
'0.2.6',
'2026-07-07T00:00:00+08:00',
'新增工具和用户配置列表',
'新增服务端渲染的工具列表、用户配置列表,以及对应的只读 JSON API。',
'[
"新增工具列表 HTTP 页面 /tools,展示内置工具和 tools/*.yaml 工具定义。",
"新增工具列表 API GET /v1/tools,返回内置工具、YAML 工具、参数、环境变量名和工具目录加载告警。",
"新增用户配置列表 HTTP 页面 /configs,展示微信渠道账号和邮件账户配置。",
"新增用户配置列表 API GET /v1/user-configs,返回脱敏后的微信与邮件配置,不暴露微信 token、更新游标和邮件密码明文。",
"顶部导航新增工具和配置入口。",
"无数据库结构变化。"
]'::jsonb,
'{
"ias-common": "0.1.0",
"ias-ai": "0.1.2",
"ias-wechat": "0.1.0",
"ias-context": "0.1.1",
"ias-http": "0.1.4",
"ias-mail": "0.1.11",
"ias-service": "0.1.7",
"ias-main": "0.2.6"
}'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes,
modules = EXCLUDED.modules;
@@ -0,0 +1,34 @@
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes,
modules
) VALUES (
'0.2.7',
'2026-07-07T00:00:00+08:00',
'修复 HTML 邮件详情渲染',
'修复 multipart 邮件详情页只展示纯文本 fallback 的问题,优先保存和渲染 HTML 邮件体。',
'[
"修复 multipart/alternative 邮件详情页只展示纯文本 fallback 的问题;现在详情页优先保存和渲染 HTML 邮件体。",
"邮件列表摘要、搜索预览和微信通知继续使用纯文本正文;没有纯文本正文时会从 HTML 中提取简要文本,避免标签污染摘要。",
"邮件详情 iframe 继续禁止脚本和表单,但允许加载 http/https 图片,使 Facebook 等 HTML 邮件更接近邮箱客户端渲染效果。",
"无数据库结构变化;已缓存的旧邮件若此前只保存了纯文本,需要重新抓取或刷新缓存后才能显示 HTML 版本。"
]'::jsonb,
'{
"ias-common": "0.1.0",
"ias-ai": "0.1.2",
"ias-wechat": "0.1.0",
"ias-context": "0.1.1",
"ias-http": "0.1.4",
"ias-mail": "0.1.12",
"ias-service": "0.1.7",
"ias-main": "0.2.7"
}'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes,
modules = EXCLUDED.modules;
@@ -0,0 +1,22 @@
UPDATE ias_upgrade_logs
SET
title = '修复 HTML 邮件详情渲染',
description = '修复 multipart 邮件详情页只展示纯文本 fallback 的问题,优先保存和渲染 HTML 邮件体,并刷新最近重复邮件缓存。',
changes = '[
"修复 multipart/alternative 邮件详情页只展示纯文本 fallback 的问题;现在详情页优先保存和渲染 HTML 邮件体。",
"邮件列表摘要、搜索预览和微信通知继续使用纯文本正文;没有纯文本正文时会从 HTML 中提取简要文本,避免标签污染摘要。",
"邮件轮询或手动拉取遇到已缓存的重复邮件时,会刷新缓存正文和摘要,使最近抓取范围内的旧纯文本缓存可自动更新为 HTML 正文。",
"邮件详情 iframe 继续禁止脚本和表单,但允许加载 http/https 图片,使 Facebook 等 HTML 邮件更接近邮箱客户端渲染效果。",
"无数据库结构变化;不在最近抓取范围内的旧邮件仍需要扩大抓取范围或重新入库后才能显示 HTML 版本。"
]'::jsonb,
modules = '{
"ias-common": "0.1.0",
"ias-ai": "0.1.2",
"ias-wechat": "0.1.0",
"ias-context": "0.1.1",
"ias-http": "0.1.4",
"ias-mail": "0.1.12",
"ias-service": "0.1.7",
"ias-main": "0.2.7"
}'::jsonb
WHERE version = '0.2.7';
@@ -0,0 +1,41 @@
ALTER TABLE ias_mail_messages
ADD COLUMN IF NOT EXISTS seen BOOLEAN NOT NULL DEFAULT FALSE;
CREATE INDEX IF NOT EXISTS idx_ias_mail_messages_seen
ON ias_mail_messages(seen, received_at DESC, id DESC);
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes,
modules
) VALUES (
'0.2.8',
'2026-07-07T00:00:00+08:00',
'新增邮件已读未读状态',
'保存 IMAP 已读状态并在邮件列表、详情页和 API 中展示与筛选。',
'[
"新增邮件已读/未读状态持久化,保存 IMAP \\Seen 标记到 ias_mail_messages.seen。",
"邮件列表页面新增状态筛选,支持查看全部、未读或已读邮件。",
"邮件列表卡片和详情页显示已读/未读状态,未读邮件在列表中使用更醒目的样式。",
"邮件 API GET /v1/mail/messages 支持 seen=true/false 过滤,并在返回的邮件对象中包含 seen 字段。",
"数据库新增 ias_mail_messages.seen 字段;已有历史邮件默认标记为未读,后续拉取最近邮件时会刷新为 IMAP 当前状态。"
]'::jsonb,
'{
"ias-common": "0.1.0",
"ias-ai": "0.1.2",
"ias-wechat": "0.1.0",
"ias-context": "0.1.1",
"ias-http": "0.1.4",
"ias-mail": "0.1.13",
"ias-service": "0.1.7",
"ias-main": "0.2.8"
}'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes,
modules = EXCLUDED.modules;
@@ -0,0 +1,33 @@
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes,
modules
) VALUES (
'0.2.9',
'2026-07-07T00:00:00+08:00',
'HTTP 页面迁移至 Askama 模板引擎',
'更新日志页面和 Web 内容页面从手写字符串拼接改为 Askama 编译期模板渲染,提升可维护性。',
'[
"HTTP 页面迁移至 Askama 模板引擎:更新日志页面(/updates)和 Web 内容页面(/web/{type}/{slug}、/mail/{slug})从手写 format!() 字符串拼接改为 Askama 编译期模板渲染。",
"新增 ias-http/templates/ 目录,包含 base.html(共享布局)、error.html(错误页)、updates/ 和 pages/ 模板,支持模板继承和自动 HTML 转义。",
"ias-http 新增 askama 依赖;shell.rs 新增 ErrorPage Askama 模板,保留 html_shell() 和 escape_html() 供 ias-mail、ias-service 兼容使用。"
]'::jsonb,
'{
"ias-common": "0.1.0",
"ias-ai": "0.1.2",
"ias-wechat": "0.1.0",
"ias-context": "0.1.1",
"ias-http": "0.1.5",
"ias-mail": "0.1.13",
"ias-service": "0.1.7",
"ias-main": "0.2.9"
}'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes,
modules = EXCLUDED.modules;
@@ -0,0 +1,34 @@
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes,
modules
) VALUES (
'0.2.10',
'2026-07-07T00:00:00+08:00',
'修复邮件已读状态和详情页返回上下文',
'修复邮件详情页自动标记已读后可能被后续轮询回滚的问题,并保留邮件详情页的列表查询上下文。',
'[
"修复邮件详情页自动标记已读后,下一轮 IMAP 轮询可能把本地已读状态回滚为未读的问题;本地已读状态现在不会被远端未读标记降级。",
"修复从筛选后的邮件列表进入详情页时丢失搜索、已读筛选和分页参数的问题;详情页返回列表、上一封和下一封会保留当前查询上下文。",
"说明:SMTP 只负责发信,不能设置收件箱邮件已读状态;已读同步需要 IMAP \\Seen 标记或服务端本地状态维护。",
"无数据库结构变化;ias-mail 升级至 0.1.14。"
]'::jsonb,
'{
"ias-common": "0.1.0",
"ias-ai": "0.1.2",
"ias-wechat": "0.1.0",
"ias-context": "0.1.1",
"ias-http": "0.1.5",
"ias-mail": "0.1.14",
"ias-service": "0.1.7",
"ias-main": "0.2.10"
}'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes,
modules = EXCLUDED.modules;
@@ -0,0 +1,36 @@
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes,
modules
) VALUES (
'0.2.11',
'2026-07-07T00:00:00+08:00',
'新增上下文组装模拟器页面',
'新增 /context 页面,可查询指定 scope 下的会话、消息、长期记忆、摘要和工具,便于分析排错。',
'[
"新增上下文组装模拟器页面(/context),支持按 scopechannel + account_id + from_user_id)查询。",
"页面展示:活跃会话详情及消息列表、组装后的系统上下文预览、历史会话表、长期记忆、会话摘要(上次会话/近一周)和可用工具列表。",
"导航栏新增「上下文」入口链接。",
"ias-service 新增 context_simulator 和 context_simulator_data 模块,升级至 0.1.8。",
"ias-http 更新 shell.rs 和 base.html 导航栏,升级至 0.1.6。",
"无数据库结构变化。"
]'::jsonb,
'{
"ias-common": "0.1.0",
"ias-ai": "0.1.2",
"ias-wechat": "0.1.0",
"ias-context": "0.1.1",
"ias-http": "0.1.6",
"ias-mail": "0.1.14",
"ias-service": "0.1.8",
"ias-main": "0.2.11"
}'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes,
modules = EXCLUDED.modules;
@@ -0,0 +1,59 @@
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes,
modules
) VALUES (
'0.2.12',
'2026-07-07T00:00:00+08:00',
'修复 React 页面路由与静态资源托管安全问题',
'修复 SPA 静态资源路径安全、动态内容页路由、HTML 邮件展示和上下文模拟器查询参数问题,并保持 ias-http 只负责 API 与静态资源托管。',
'[
"修复 React SPA 静态资源托管的路径安全问题,改用 tower-http 静态文件服务,避免手写路径拼接读取 web/dist 之外的文件。",
"React 新增动态内容页路由 /web/{page_type}/{slug} 与 /mail/{slug},通过 /v1/web/... API 拉取数据并渲染,保持 ias-http 只负责 API 与静态资源托管的边界。",
"邮件详情页恢复 HTML 邮件的沙箱 iframe 渲染,纯文本邮件继续按文本展示。",
"修复上下文组装模拟器提交表单后首次查询仍使用旧 URL 参数的问题。",
"邮件详情 API 在自动标记已读成功后,会在响应中同步返回 seen=true。",
"无数据库结构变化;ias-http 升级至 0.1.7ias-mail 升级至 0.1.15ias-service 升级至 0.1.9。"
]'::jsonb,
'{
"ias-common": "0.1.0",
"ias-ai": "0.1.2",
"ias-wechat": "0.1.0",
"ias-context": "0.1.1",
"ias-http": "0.1.7",
"ias-mail": "0.1.15",
"ias-service": "0.1.9",
"ias-main": "0.2.12"
}'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes,
modules = EXCLUDED.modules;
UPDATE ias_upgrade_logs
SET
title = 'HTTP 页面职责迁移到 React SPA',
description = 'HTTP 模块保留 JSON API 与静态资源托管,页面渲染职责迁移到 React 前端。',
changes = '[
"HTTP 页面职责迁移到 React SPAias-http 保留 /v1/updates、/v1/web/... 等 JSON API,页面渲染由 web/ 前端负责。",
"新增 React 前端工程,提供更新日志、邮件、工具、配置等页面入口。",
"ias-http 删除旧的服务端 HTML shell 与字符串拼接页面渲染代码,新增 React 静态资源托管入口。"
]'::jsonb
WHERE version = '0.2.9';
UPDATE ias_upgrade_logs
SET
changes = '[
"新增上下文组装模拟器页面(/context),支持按 scopechannel + account_id + from_user_id)查询。",
"页面展示:活跃会话详情及消息列表、组装后的系统上下文预览、历史会话表、长期记忆、会话摘要(上次会话/近一周)和可用工具列表。",
"导航栏新增「上下文」入口链接。",
"ias-service 新增 context_simulator 和 context_simulator_data 模块,升级至 0.1.8。",
"React 导航栏新增上下文入口;ias-http 升级至 0.1.6。",
"无数据库结构变化。"
]'::jsonb
WHERE version = '0.2.11';
@@ -0,0 +1,35 @@
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes,
modules
) VALUES (
'0.2.13',
'2026-07-07T00:00:00+08:00',
'修复多工具调用上下文消息序列',
'修复模型返回多个 tool_calls 时上下文中缺少部分 tool 消息导致 LLM 请求 400 的问题,并过滤历史不完整工具调用片段。',
'[
"修复模型返回多个 tool_calls 时只执行第一个工具导致后续请求缺少 tool 消息的问题;现在会为每个 tool_call_id 生成对应工具结果。",
"服务队列新增批量工具结果处理,所有 tool 消息都写入上下文后才触发下一次 LLM 请求,避免中间态消息序列发送给模型。",
"上下文快照会过滤历史中不完整的 assistant/tool 调用片段,防止已存在的坏数据继续触发 insufficient tool messages following tool_calls message。",
"新增单元测试覆盖完整多工具调用保留、缺失工具结果过滤,以及 /v1 API catch-all 路由构建。",
"无数据库结构变化;ias-common 升级至 0.1.1ias-ai 升级至 0.1.3ias-context 升级至 0.1.2ias-service 升级至 0.1.10。"
]'::jsonb,
'{
"ias-common": "0.1.1",
"ias-ai": "0.1.3",
"ias-wechat": "0.1.0",
"ias-context": "0.1.2",
"ias-http": "0.1.7",
"ias-mail": "0.1.15",
"ias-service": "0.1.10",
"ias-main": "0.2.13"
}'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes,
modules = EXCLUDED.modules;
@@ -0,0 +1,36 @@
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes,
modules
) VALUES (
'0.2.14',
'2026-07-07T00:00:00+08:00',
'新增 API 文档查询能力',
'新增结构化 HTTP API 文档接口和 AI 查询工具,使模型可以查询当前服务 API 的路径、参数、请求体、响应示例和备注后回复用户。',
'[
"新增 HTTP API 文档接口 /v1/api-docs 与 /v1/api-docs/{id},支持按关键词、分组和接口 id 查询当前服务的结构化 API 文档。",
"新增内置 AI 工具 query_api_docs,模型可查询 API 路径、参数、请求体、响应示例和备注,并据此用中文回复用户。",
"新增内置 AI 工具 query_web_pages,模型可按页面类型和关键词查询已创建的动态页面,并把相关 /web/... 或 /mail/... 链接发给用户。",
"动态页面列表 API GET /v1/web/pages 新增 q 关键词搜索,可匹配标题、摘要、正文、来源和 metadata,便于查找已创建页面。",
"API 文档覆盖健康检查、升级日志、动态页面、邮件、控制台工具、用户配置、长期记忆和上下文模拟器接口;HTTP 模块继续只负责 JSON API 与静态资源托管,页面展示仍由 React 前端负责。",
"无数据库结构变化;ias-ai 升级至 0.1.4ias-http 升级至 0.1.8ias-service 升级至 0.1.11。"
]'::jsonb,
'{
"ias-common": "0.1.1",
"ias-ai": "0.1.4",
"ias-wechat": "0.1.0",
"ias-context": "0.1.2",
"ias-http": "0.1.8",
"ias-mail": "0.1.15",
"ias-service": "0.1.11",
"ias-main": "0.2.14"
}'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes,
modules = EXCLUDED.modules;
@@ -0,0 +1,35 @@
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes,
modules
) VALUES (
'0.2.15',
'2026-07-07T00:00:00+08:00',
'新增 Markdown 预览页面能力',
'新增 Markdown 预览页面和 AI 创建工具,使模型可以把长篇 Markdown 内容生成可分享链接,避免聊天平台消息长度限制。',
'[
"新增 Markdown 预览页面能力:page_type=markdown 的动态页面公开路径为 /md/{slug}React 前端按 Markdown/GFM 渲染标题、列表、表格、引用、链接和代码块。",
"新增内置 AI 工具 create_markdown_preview,模型可把长篇 Markdown、报告、代码说明或结构化答复创建成预览页,并只在聊天中发送简短摘要和链接,避免平台消息长度限制。",
"query_web_pages 支持查询 markdown 页面类型,并会返回 /md/{slug} 链接;普通动态页面和邮件页面路径保持不变。",
"API 文档补充 Markdown 动态页面说明;HTTP 模块仍只负责 JSON API 与静态资源托管,Markdown 渲染由 React 前端负责。",
"无数据库结构变化;ias-ai 升级至 0.1.5ias-http 升级至 0.1.9ias-service 升级至 0.1.12。"
]'::jsonb,
'{
"ias-common": "0.1.1",
"ias-ai": "0.1.5",
"ias-wechat": "0.1.0",
"ias-context": "0.1.2",
"ias-http": "0.1.9",
"ias-mail": "0.1.15",
"ias-service": "0.1.12",
"ias-main": "0.2.15"
}'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes,
modules = EXCLUDED.modules;
@@ -0,0 +1,34 @@
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes,
modules
) VALUES (
'0.2.16',
'2026-07-07T00:00:00+08:00',
'修复 AI 页面工具内部访问地址',
'修复 AI 内置页面工具在公开地址受 NetBird 鉴权保护时无法创建 Markdown 预览页的问题,内部请求改为优先使用内部地址或本地服务地址。',
'[
"修复 AI 内置页面工具在配置了受保护公开地址时,请求 /v1/web/pages 命中 NetBird 鉴权页并返回 401 的问题。",
"create_web_page、create_markdown_preview、query_web_pages 的内部 HTTP 请求现在优先使用 IA_WEB_INTERNAL_URL 或本地 HOST;返回给用户的页面链接仍优先使用 IA_WEB_BASE_URL。",
"query_api_docs 同步优先使用内部地址查询 API 文档,避免内部工具调用被公开入口鉴权拦截。",
"无数据库结构变化;ias-ai 升级至 0.1.6ias-service 升级至 0.1.13。"
]'::jsonb,
'{
"ias-common": "0.1.1",
"ias-ai": "0.1.6",
"ias-wechat": "0.1.0",
"ias-context": "0.1.2",
"ias-http": "0.1.9",
"ias-mail": "0.1.15",
"ias-service": "0.1.13",
"ias-main": "0.2.16"
}'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes,
modules = EXCLUDED.modules;
@@ -0,0 +1,36 @@
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes,
modules
) VALUES (
'0.2.17',
'2026-07-08T00:00:00+08:00',
'收束 AI 工具入口并归类能力',
'模型初始工具列表收束为查询能力、能力详情和调用能力三个入口,底层能力按 API 与 HTTP 两类管理,避免模型直接调用具体工具。',
'[
"收束模型初始工具列表,仅暴露 query_capabilities、query_capability_detail 和 call_capability 三个入口工具;其它能力直接调用会被拒绝,必须通过 call_capability 按名称和参数调用。",
"query_capabilities 改为只返回能力名称和描述,并按 api / http 分类;query_capability_detail 返回单个能力的完整参数、来源、分类和 JSON Schemacall_capability 支持 name、parameters 和可选提示语 text。",
"API 能力统一表示通过 iAs HTTP API 封装的能力,新增 API 版 query_mail_messages;删除重复的 YAML 邮件查询能力。",
"HTTP 能力统一表示 tools/*.yaml 定义的外部 API 调用能力;删除天气测试 YAMLYAML 扫描只加载 .yaml/.yml 并保持排序。",
"控制台工具页和上下文模拟器同步展示入口工具、API 能力和 HTTP 能力分组,避免调试请求继续直接暴露底层能力。",
"无数据库结构变化;已验证 cargo test 和 bun run build。"
]'::jsonb,
'{
"ias-common": "0.1.1",
"ias-ai": "0.1.7",
"ias-wechat": "0.1.0",
"ias-context": "0.1.2",
"ias-http": "0.1.10",
"ias-mail": "0.1.15",
"ias-service": "0.1.14",
"ias-main": "0.2.17"
}'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes,
modules = EXCLUDED.modules;
@@ -0,0 +1,16 @@
CREATE TABLE ias_additional_system_prompts (
id BIGSERIAL PRIMARY KEY,
scope_key VARCHAR(768) NOT NULL UNIQUE,
channel VARCHAR(64) NOT NULL,
account_id VARCHAR(255) NOT NULL,
from_user_id VARCHAR(255) NOT NULL,
context_label TEXT,
content TEXT NOT NULL DEFAULT '',
last_reason TEXT,
updated_by VARCHAR(64) NOT NULL DEFAULT 'ai',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_ias_additional_system_prompts_scope_updated
ON ias_additional_system_prompts(scope_key, updated_at DESC);
@@ -0,0 +1,36 @@
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes,
modules
) VALUES (
'0.2.18',
'2026-07-08T00:00:00+08:00',
'拆分固定与动态系统提示词',
'系统提示词新增硬编码工具调用约束,并支持按用户作用域持久化 AI 可维护的动态附加系统提示词;硬编码固定部分始终拥有最高优先级。',
'[
"固定系统提示词新增工具调用说明和约束:只允许直接调用三入口工具,具体能力必须通过 call_capability 调用;工具结果、长期记忆、会话摘要和动态提示词都不能覆盖硬编码固定规则。",
"系统提示词拆分为硬编码固定部分和 AI 可维护的动态附加部分;固定部分在代码中生成且优先级最高,动态部分只允许补充偏好和工作方式。",
"新增 API 能力 update_additional_system_promptAI 可在当前用户作用域内更新或清空动态附加系统提示词,并记录修改原因。",
"新增内部接口 PATCH /v1/context/system-prompt 和数据表 ias_additional_system_prompts,按 scope 持久化动态附加系统提示词。",
"上下文组装和上下文模拟器会读取动态附加系统提示词,并在固定系统提示词之后、长期记忆和摘要之前插入。",
"数据库新增 ias_additional_system_prompts 表;已验证 cargo test 和 bun run build。"
]'::jsonb,
'{
"ias-common": "0.1.1",
"ias-ai": "0.1.8",
"ias-wechat": "0.1.0",
"ias-context": "0.1.3",
"ias-http": "0.1.11",
"ias-mail": "0.1.15",
"ias-service": "0.1.15",
"ias-main": "0.2.18"
}'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes,
modules = EXCLUDED.modules;
@@ -0,0 +1,76 @@
ALTER TABLE ias_context_messages
ADD COLUMN IF NOT EXISTS raw_message JSONB;
ALTER TABLE ias_context_messages
ADD COLUMN IF NOT EXISTS external_message_id VARCHAR(255);
CREATE UNIQUE INDEX IF NOT EXISTS idx_ias_context_messages_external_unique
ON ias_context_messages(session_id, role, external_message_id)
WHERE external_message_id IS NOT NULL;
WITH ranked AS (
SELECT
id,
ROW_NUMBER() OVER (
PARTITION BY scope_key
ORDER BY started_at DESC, id DESC
) AS rank
FROM ias_context_sessions
WHERE ended_at IS NULL
)
UPDATE ias_context_sessions AS session
SET
ended_at = last_message_at,
end_reason = 'active_session_deduplicated',
summary = COALESCE(summary, '创建活跃会话唯一约束时自动结束的重复活跃上下文。')
FROM ranked
WHERE session.id = ranked.id
AND ranked.rank > 1;
CREATE UNIQUE INDEX IF NOT EXISTS idx_ias_context_sessions_one_active
ON ias_context_sessions(scope_key)
WHERE ended_at IS NULL;
CREATE TABLE IF NOT EXISTS ias_tool_call_instructions (
id BIGSERIAL PRIMARY KEY,
scope_key VARCHAR(768) NOT NULL UNIQUE,
channel VARCHAR(64) NOT NULL,
account_id VARCHAR(255) NOT NULL,
from_user_id VARCHAR(255) NOT NULL,
context_label TEXT,
content TEXT NOT NULL DEFAULT '',
last_reason TEXT,
updated_by VARCHAR(64) NOT NULL DEFAULT 'ai',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_ias_tool_call_instructions_scope_updated
ON ias_tool_call_instructions(scope_key, updated_at DESC);
INSERT INTO ias_tool_call_instructions (
scope_key,
channel,
account_id,
from_user_id,
context_label,
content,
last_reason,
updated_by,
created_at,
updated_at
)
SELECT
scope_key,
channel,
account_id,
from_user_id,
context_label,
content,
last_reason,
updated_by,
created_at,
updated_at
FROM ias_additional_system_prompts
WHERE content <> ''
ON CONFLICT (scope_key) DO NOTHING;
@@ -0,0 +1,40 @@
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes,
modules
) VALUES (
'0.2.19',
'2026-07-08T00:00:00+08:00',
'上下文 raw 记录与工具调用说明',
'上下文消息支持 raw JSON 朔源入库,/new 可打断当前工具操作;动态附加系统提示词改为随能力列表展示的工具调用说明。',
'[
"/new 现在会取消同一用户作用域正在运行的 LLM/工具任务,清除当前 turn,并丢弃迟到的 assistant、工具提醒或工具结果。",
"上下文消息新增 raw_message JSONB 原文记录;读取上下文时优先从 raw JSON 重建 content、tool_calls 和 tool_call_id,旧字段继续兼容历史数据。",
"微信入口会把 message_id、session_id、group_id 和 create_time_ms 带入 raw 记录;同一 session 内相同外部消息 ID 的重复用户消息会自动去重。",
"群聊上下文 scope 现在优先使用 group_id,避免同一发送者在私聊和不同群聊之间串上下文;私聊继续保持原有 channel:account_id:from_user_id 形式。",
"数据库会先自动结束同一 scope 中较旧的重复活跃会话,再新增同一 scope 仅允许一个活跃上下文会话的唯一约束,避免异常重试或多实例情况下产生隐性多活会话。",
"模型响应兼容 content=null 的纯工具调用场景,请求消息不再序列化空 tool_call_id/tool_calls 字段。",
"动态附加系统提示词改为工具调用说明,不再进入系统提示词,只随 query_capabilities 展示。",
"新增内部接口 POST/PATCH /v1/context/tool-instructions 和 API 能力 record_tool_call_instruction,用于记录用户提供的工具调用规则、稳定调用顺序和 AI 总结出的调用经验。",
"上下文模拟器展示独立工具调用说明,并在 messages 预览中保留 tool_calls、tool_call_id 和 raw_message,便于观察上下文组成。",
"数据库新增 ias_context_messages.raw_message、ias_context_messages.external_message_id、ias_context_sessions 活跃会话唯一约束和 ias_tool_call_instructions 表,并迁移旧 ias_additional_system_prompts 中已有内容。"
]'::jsonb,
'{
"ias-common": "0.1.2",
"ias-ai": "0.1.9",
"ias-wechat": "0.1.0",
"ias-context": "0.1.4",
"ias-http": "0.1.12",
"ias-mail": "0.1.15",
"ias-service": "0.1.16",
"ias-main": "0.2.19"
}'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes,
modules = EXCLUDED.modules;
@@ -0,0 +1,34 @@
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes,
modules
) VALUES (
'0.2.20',
'2026-07-08T00:00:00+08:00',
'微信输入中状态修复',
'微信输入中状态改用官方 sendtyping 流程,并在回复完成或上下文打断时显式取消。',
'[
"微信输入中状态改用 getconfig + sendtyping 流程:按用户获取并缓存 typing_ticket,再发送 typing/cancel 状态,不再依赖 sendmessage 的 message_state=1 中间态。",
"服务队列在最终回复发出后、空回复结束时和 /new 打断当前 turn 时会显式取消微信输入中状态,避免提示残留。",
"微信 sendmessage 请求补齐 base_info;所有微信 API 请求默认带 iLink-App-Id=bot,并可通过 WEIXIN_APP_ID 覆盖。",
"数据库无结构变化。"
]'::jsonb,
'{
"ias-common": "0.1.2",
"ias-ai": "0.1.9",
"ias-wechat": "0.1.1",
"ias-context": "0.1.4",
"ias-http": "0.1.12",
"ias-mail": "0.1.15",
"ias-service": "0.1.17",
"ias-main": "0.2.20"
}'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes,
modules = EXCLUDED.modules;
@@ -0,0 +1,34 @@
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes,
modules
) VALUES (
'0.2.21',
'2026-07-08T00:00:00+08:00',
'HTTP API 功能分组说明',
'HTTP API 文档新增功能分组索引,AI 可先查看 API 面向,再按关键词或 endpoint id 查询具体接口。',
'[
"HTTP API 文档新增功能分组说明,/v1/api-docs 会返回 groups,描述 system、updates、api-docs、web、mail、console、context 等 API 面向及主要功能。",
"API 文档搜索会把功能分组的标题、说明、功能面和关键词纳入匹配;例如查询未读或邮箱目录可直接命中邮件相关接口。",
"query_api_docs 的列表输出会先展示 API 功能分组,并在每个 endpoint 简表中补充完整说明;查询单个 endpoint 时会附带所属功能分组说明。",
"数据库无结构变化。"
]'::jsonb,
'{
"ias-common": "0.1.2",
"ias-ai": "0.1.10",
"ias-wechat": "0.1.1",
"ias-context": "0.1.4",
"ias-http": "0.1.13",
"ias-mail": "0.1.15",
"ias-service": "0.1.18",
"ias-main": "0.2.21"
}'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes,
modules = EXCLUDED.modules;
@@ -0,0 +1,35 @@
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes,
modules
) VALUES (
'0.2.22',
'2026-07-08T00:00:00+08:00',
'AI API 能力去重',
'移除与 HTTP endpoint 一一重复的 AI 专用 API 能力,统一通过 API 文档发现和通用 HTTP API 调用能力执行。',
'[
"AI API 能力去重:移除与 HTTP endpoint 一一重复的专用能力,如 web 页面、邮件查询、长期记忆和工具调用说明写入包装。",
"新增通用 API 能力 call_http_api,模型先通过 query_api_docs 查询 endpoint_id、参数和请求体,再按 endpoint_id 调用具体内部 HTTP API。",
"call_http_api 会根据 /v1/api-docs/{endpoint_id} 校验接口路径和方法,支持 path_params、query、body,并为 context 分组接口自动补当前用户作用域身份和内部鉴权。",
"删除不再编译的 AI 专用 API 包装文件,保留 query_tool_call_instruction 作为 query_capabilities 内部读取工具调用说明的实现。",
"数据库无结构变化。"
]'::jsonb,
'{
"ias-common": "0.1.2",
"ias-ai": "0.1.11",
"ias-wechat": "0.1.1",
"ias-context": "0.1.4",
"ias-http": "0.1.13",
"ias-mail": "0.1.15",
"ias-service": "0.1.19",
"ias-main": "0.2.22"
}'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes,
modules = EXCLUDED.modules;
@@ -0,0 +1,34 @@
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes,
modules
) VALUES (
'0.2.23',
'2026-07-08T00:00:00+08:00',
'修复 HTTP 能力加载与工具说明措辞',
'修复高德地理编码 YAML 查询参数格式导致 HTTP 能力列表加载失败的问题,并同步修正工具调用说明中已过期的能力名称。',
'[
"修复 amap_geocode HTTP 能力 YAML 的 query 配置格式,改为工具链引擎支持的二元组列表,避免 query_capabilities 因解析失败导致所有 HTTP 能力为空。",
"query_capabilities 在没有额外动态工具调用说明时,会明确提示固定工具调用约束已在系统提示词中生效,避免误解为缺少系统级约束。",
"修正工具调用说明动态段中的过期能力名,不再引用已移除的 record_tool_call_instruction,改为提示通过 query_api_docs 查询 context.tool-instructions.update 后使用 call_http_api 维护。",
"数据库无结构变化;已验证 cargo fmt 和 cargo test。"
]'::jsonb,
'{
"ias-common": "0.1.2",
"ias-ai": "0.1.12",
"ias-wechat": "0.1.1",
"ias-context": "0.1.5",
"ias-http": "0.1.13",
"ias-mail": "0.1.15",
"ias-service": "0.1.20",
"ias-main": "0.2.23"
}'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes,
modules = EXCLUDED.modules;
@@ -0,0 +1,34 @@
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes,
modules
) VALUES (
'0.2.24',
'2026-07-08T00:00:00+08:00',
'HTTP YAML 能力加载文件级容错',
'HTTP YAML 能力加载改为文件级容错,单个配置解析失败只会跳过该文件并返回 warning,不再影响其它 HTTP 能力展示和调用。',
'[
"HTTP YAML 能力加载改为文件级容错:单个 YAML 配置读取或解析失败时只跳过该文件并返回 warning,不再导致其它 HTTP 能力全部为空。",
"query_capabilities 会继续展示成功加载的 HTTP 能力,并在 warnings 中标明被跳过的配置文件和错误原因,便于定位坏配置。",
"call_capability 调用 HTTP YAML 能力时同样继承文件级容错,坏配置不会影响其它可用 YAML 能力执行。",
"数据库无结构变化;已验证 cargo fmt 和 cargo test。"
]'::jsonb,
'{
"ias-common": "0.1.2",
"ias-ai": "0.1.13",
"ias-wechat": "0.1.1",
"ias-context": "0.1.5",
"ias-http": "0.1.13",
"ias-mail": "0.1.15",
"ias-service": "0.1.21",
"ias-main": "0.2.24"
}'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes,
modules = EXCLUDED.modules;
@@ -0,0 +1,41 @@
CREATE TABLE ias_operation_logs (
id BIGSERIAL PRIMARY KEY,
level VARCHAR(32) NOT NULL DEFAULT 'info',
module VARCHAR(128) NOT NULL DEFAULT '',
message TEXT NOT NULL DEFAULT '',
metadata JSONB NOT NULL DEFAULT '{}',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_ias_operation_logs_created
ON ias_operation_logs(created_at DESC);
CREATE INDEX idx_ias_operation_logs_level
ON ias_operation_logs(level);
CREATE INDEX idx_ias_operation_logs_module
ON ias_operation_logs(module);
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes
) VALUES (
'0.2.25',
'2026-07-08T00:00:00+08:00',
'操作日志入库与查询',
'新增操作日志表 ias_operation_logs,支持按级别、模块和关键词筛选日志,并新增日志查询页面。',
'[
"新增 ias_operation_logs 数据库表,支持记录操作日志(级别、模块、消息、元数据和时间戳)。",
"新增 GET /v1/logs 日志查询接口,支持按 level、module、q 筛选和分页。",
"新增 GET /v1/logs/{id} 单条日志查询接口。",
"新增 React 日志查询页面,支持按级别、模块和关键词筛选,分页浏览。",
"导航栏新增「日志」入口。"
]'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes;
@@ -0,0 +1,34 @@
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes,
modules
) VALUES (
'0.2.26',
'2026-07-08T00:00:00+08:00',
'修复操作日志预览 UTF-8 截断',
'修复服务队列记录 LLM 响应和工具结果预览时按字节直接切片导致中文等多字节字符触发 char boundary panic 的问题。',
'[
"修复服务队列记录 LLM 响应和工具结果预览时按字节直接切片的问题,避免中文、emoji 等多字节字符刚好落在预览上限时触发 char boundary panic。",
"操作日志预览现在仍按字节上限控制长度,但会回退到合法 UTF-8 字符边界,保证写入 metadata 前不会破坏字符串。",
"新增单元测试覆盖中文字符跨越 500 字节边界的预览场景。",
"数据库无结构变化;已验证 cargo fmt --check 和 cargo test -p ias-service。"
]'::jsonb,
'{
"ias-common": "0.1.2",
"ias-ai": "0.1.13",
"ias-wechat": "0.1.1",
"ias-context": "0.1.5",
"ias-http": "0.1.14",
"ias-mail": "0.1.15",
"ias-service": "0.1.23",
"ias-main": "0.2.26"
}'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes,
modules = EXCLUDED.modules;
@@ -0,0 +1,42 @@
INSERT INTO ias_upgrade_logs (
version,
released_at,
title,
description,
changes,
modules
) VALUES (
'0.2.27',
'2026-07-08T00:00:00+08:00',
'抽离数据库基础设施模块',
'新增 ias-db 模块集中负责数据库连接和 SQLx 迁移执行,并将原 ias-service/migrations 整体迁移到 ias-db/migrations。同时新增操作日志查询接口与前端页面,邮件远程已读同步,以及多处改进。',
'[
"新增 ias-db crate,集中负责 PostgreSQL 连接池创建、DATABASE_URL 读取和 SQLx 迁移执行。",
"将原 ias-service/migrations 整体迁移到 ias-db/migrations,迁移嵌入和变更监听由 ias-db/build.rs 负责。",
"ias-main 改为直接调用 ias_db::connect_db()ias-service 只接收已建立的 PgPool 并运行服务,数据库生命周期边界更清晰。",
"新增操作日志 HTTP 查询接口 GET /v1/logs 和 GET /v1/logs/{id},支持按级别、模块和关键词筛选,API 文档新增 logs 功能分组。",
"新增操作日志 Web 前端页面,支持按级别、模块和关键词筛选查看日志,便于排障和审计。",
"消息队列关键节点增加操作日志写入:LLM 响应、渠道消息接收、上下文记录错误、消息发送失败等场景自动记录到 ias_operation_logs。",
"ias-mail 新增 mark_remote_message_seen 函数,通过 IMAP 设置邮件已读并验证服务器确认,抽取 connect_imap_session 复用连接逻辑。",
"工作流配置加载改为文件级容错:ias-ai 新增 load_configs_with_warnings,返回 LoadedFlowConfigs 包含 warnings 信息,单个 YAML 配置损坏时不会导致所有 HTTP 能力为空。",
"ias-context 工具调用说明中的能力引用路径从已废弃的 record_tool_call_instruction 更新为 query_api_docs + call_http_api,并新增单元测试验证。",
"新增高德地图地理编码/逆地理编码 HTTP 能力配置文件 tools/amap_geocode.yaml。",
"数据库无结构变化;已验证 cargo fmt --check 和 cargo check。"
]'::jsonb,
'{
"ias-common": "0.1.2",
"ias-ai": "0.1.14",
"ias-wechat": "0.1.1",
"ias-context": "0.1.6",
"ias-http": "0.1.15",
"ias-mail": "0.1.16",
"ias-db": "0.1.0",
"ias-service": "0.1.25",
"ias-main": "0.2.27"
}'::jsonb
) ON CONFLICT (version) DO UPDATE SET
released_at = EXCLUDED.released_at,
title = EXCLUDED.title,
description = EXCLUDED.description,
changes = EXCLUDED.changes,
modules = EXCLUDED.modules;
+27
View File
@@ -0,0 +1,27 @@
use sqlx::{PgPool, postgres::PgPoolOptions};
use std::env;
include!(concat!(env!("OUT_DIR"), "/migration_stamp.rs"));
pub type DbPool = PgPool;
const DEFAULT_MAX_CONNECTIONS: u32 = 10;
pub async fn connect_db() -> anyhow::Result<DbPool> {
let db_url = env::var("DATABASE_URL").map_err(|_| anyhow::anyhow!("必须设置DATABASE_URL"))?;
connect_db_url(&db_url).await
}
pub async fn connect_db_url(db_url: &str) -> anyhow::Result<DbPool> {
let pool = PgPoolOptions::new()
.max_connections(DEFAULT_MAX_CONNECTIONS)
.connect(db_url)
.await?;
run_migrations(&pool).await?;
Ok(pool)
}
pub async fn run_migrations(pool: &PgPool) -> anyhow::Result<()> {
sqlx::migrate!("./migrations").run(pool).await?;
Ok(())
}