fix: use internal URL for AI HTTP tools

Prefer IA_WEB_INTERNAL_URL or local HOST for AI tool requests while preserving public page links for users.
This commit is contained in:
2026-07-08 00:03:14 +08:00
parent 640ba9e7d2
commit 279d89b87a
9 changed files with 62 additions and 13 deletions
+7
View File
@@ -1,5 +1,12 @@
# 更新日志 # 更新日志
## 0.2.16 - 2026-07-07
- 修复 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 文档,避免内部工具调用被公开入口鉴权拦截。
- 无数据库结构变化;新增 `0.2.16` 升级日志记录,`ias-ai` 升级至 `0.1.6``ias-service` 升级至 `0.1.13`
## 0.2.15 - 2026-07-07 ## 0.2.15 - 2026-07-07
- 新增 Markdown 预览页面能力:`page_type=markdown` 的动态页面公开路径为 `/md/{slug}`React 前端按 Markdown/GFM 渲染标题、列表、表格、引用、链接和代码块。 - 新增 Markdown 预览页面能力:`page_type=markdown` 的动态页面公开路径为 `/md/{slug}`React 前端按 Markdown/GFM 渲染标题、列表、表格、引用、链接和代码块。
Generated
+3 -3
View File
@@ -1042,7 +1042,7 @@ dependencies = [
[[package]] [[package]]
name = "ias-ai" name = "ias-ai"
version = "0.1.5" version = "0.1.6"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",
@@ -1120,7 +1120,7 @@ dependencies = [
[[package]] [[package]]
name = "ias-main" name = "ias-main"
version = "0.2.15" version = "0.2.16"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"clap", "clap",
@@ -1135,7 +1135,7 @@ dependencies = [
[[package]] [[package]]
name = "ias-service" name = "ias-service"
version = "0.1.12" version = "0.1.13"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"axum", "axum",
+1 -1
View File
@@ -1 +1 @@
0.2.15 0.2.16
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "ias-ai" name = "ias-ai"
version = "0.1.5" version = "0.1.6"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
+1 -2
View File
@@ -196,8 +196,7 @@ fn validate_endpoint_id(value: &str) -> anyhow::Result<String> {
fn api_docs_base_url() -> String { fn api_docs_base_url() -> String {
std::env::var("IA_API_DOCS_BASE_URL") std::env::var("IA_API_DOCS_BASE_URL")
.or_else(|_| std::env::var("IA_WEB_BASE_URL")) .or_else(|_| std::env::var("IA_WEB_INTERNAL_URL"))
.or_else(|_| std::env::var("WEB_BASE_URL"))
.or_else(|_| std::env::var("HOST").map(|host| format!("http://{host}"))) .or_else(|_| std::env::var("HOST").map(|host| format!("http://{host}")))
.unwrap_or_else(|_| "http://127.0.0.1:9003".to_string()) .unwrap_or_else(|_| "http://127.0.0.1:9003".to_string())
.trim() .trim()
+13 -4
View File
@@ -129,7 +129,7 @@ async fn create_markdown_preview_inner(arguments: &str) -> anyhow::Result<String
} }
async fn create_page_request(payload: Value) -> anyhow::Result<String> { async fn create_page_request(payload: Value) -> anyhow::Result<String> {
let base_url = web_base_url(); let base_url = web_request_base_url();
let endpoint = format!("{base_url}/v1/web/pages"); let endpoint = format!("{base_url}/v1/web/pages");
let client = reqwest::Client::new(); let client = reqwest::Client::new();
let response = client let response = client
@@ -184,7 +184,7 @@ async fn query_web_pages_inner(arguments: &str) -> anyhow::Result<String> {
let page = args.page.unwrap_or(1).max(1); let page = args.page.unwrap_or(1).max(1);
let per_page = args.per_page.unwrap_or(10).clamp(1, 20); let per_page = args.per_page.unwrap_or(10).clamp(1, 20);
let mut url = reqwest::Url::parse(&format!("{}/v1/web/pages", web_base_url())) let mut url = reqwest::Url::parse(&format!("{}/v1/web/pages", web_request_base_url()))
.context("构造 web 页面查询地址失败")?; .context("构造 web 页面查询地址失败")?;
{ {
let mut query_pairs = url.query_pairs_mut(); let mut query_pairs = url.query_pairs_mut();
@@ -266,7 +266,7 @@ fn web_page_url(page: &WebPageItem) -> String {
} else { } else {
format!("/web/{}/{}", page.page_type, page.slug) format!("/web/{}/{}", page.page_type, page.slug)
}; };
format!("{}{}", web_base_url(), path) format!("{}{}", web_public_base_url(), path)
} }
fn optional_text(value: Option<String>) -> Option<String> { fn optional_text(value: Option<String>) -> Option<String> {
@@ -275,7 +275,16 @@ fn optional_text(value: Option<String>) -> Option<String> {
.filter(|value| !value.is_empty()) .filter(|value| !value.is_empty())
} }
fn web_base_url() -> String { fn web_request_base_url() -> String {
std::env::var("IA_WEB_INTERNAL_URL")
.or_else(|_| std::env::var("HOST").map(|host| format!("http://{host}")))
.unwrap_or_else(|_| "http://127.0.0.1:9003".to_string())
.trim()
.trim_end_matches('/')
.replace("0.0.0.0", "127.0.0.1")
}
fn web_public_base_url() -> String {
std::env::var("IA_WEB_BASE_URL") std::env::var("IA_WEB_BASE_URL")
.or_else(|_| std::env::var("WEB_BASE_URL")) .or_else(|_| std::env::var("WEB_BASE_URL"))
.or_else(|_| std::env::var("HOST").map(|host| format!("http://{host}"))) .or_else(|_| std::env::var("HOST").map(|host| format!("http://{host}")))
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "ias-main" name = "ias-main"
version = "0.2.15" version = "0.2.16"
edition = "2024" edition = "2024"
[[bin]] [[bin]]
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "ias-service" name = "ias-service"
version = "0.1.12" version = "0.1.13"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
@@ -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;