llm-proxy(ui): 修 placeholder token 泄漏 + UI 重做 + λ favicon
deploy llm-proxy / build-and-deploy (push) Successful in 1m46s
deploy llm-proxy / build-and-deploy (push) Successful in 1m46s
- 修:token 输入框 placeholder 之前硬编码了真实 token (`e.g. famzheng-llm-2026`),等于明文泄露。改成 `your auth token` - UI 重做 — 100dvh 锁 viewport(处理移动软键盘)+ grid 布局 让 thread 永远占中间、footer 永远贴底 - textarea autogrow(最高 200px,超出内部滚) - 复制按钮 / smooth scroll-to-bottom (double rAF) / iOS momentum scroll / safe-area padding - 错误状态独立 row,monospace + 红底 - λ favicon(紫蓝渐变 + 绿色在线点 + glow)— SVG include_str! 进 binary,`/favicon.svg` + `/favicon.ico` 同源响应
This commit is contained in:
@@ -37,6 +37,8 @@ async fn main() -> std::io::Result<()> {
|
|||||||
.route("/healthz", get(|| async { "ok" }))
|
.route("/healthz", get(|| async { "ok" }))
|
||||||
.route("/", get(|| async { Redirect::permanent("/chat") }))
|
.route("/", get(|| async { Redirect::permanent("/chat") }))
|
||||||
.route("/chat", get(chat_ui))
|
.route("/chat", get(chat_ui))
|
||||||
|
.route("/favicon.svg", get(favicon))
|
||||||
|
.route("/favicon.ico", get(favicon)) // 浏览器默认会请求 .ico,让它共享同一 SVG
|
||||||
.merge(chat_api)
|
.merge(chat_api)
|
||||||
.layer(TraceLayer::new_for_http());
|
.layer(TraceLayer::new_for_http());
|
||||||
|
|
||||||
@@ -47,11 +49,22 @@ async fn main() -> std::io::Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const CHAT_HTML: &str = include_str!("../web/chat.html");
|
const CHAT_HTML: &str = include_str!("../web/chat.html");
|
||||||
|
const FAVICON_SVG: &str = include_str!("../web/favicon.svg");
|
||||||
|
|
||||||
async fn chat_ui() -> Html<&'static str> {
|
async fn chat_ui() -> Html<&'static str> {
|
||||||
Html(CHAT_HTML)
|
Html(CHAT_HTML)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn favicon() -> impl IntoResponse {
|
||||||
|
(
|
||||||
|
[
|
||||||
|
(axum::http::header::CONTENT_TYPE, "image/svg+xml"),
|
||||||
|
(axum::http::header::CACHE_CONTROL, "public, max-age=604800"),
|
||||||
|
],
|
||||||
|
FAVICON_SVG,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// 验 `Authorization: token <PROXY_AUTH_TOKEN>`,错的直接 401。
|
/// 验 `Authorization: token <PROXY_AUTH_TOKEN>`,错的直接 401。
|
||||||
async fn require_token(
|
async fn require_token(
|
||||||
State(cfg): State<Arc<proxy::Config>>,
|
State(cfg): State<Arc<proxy::Config>>,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
||||||
<meta name="theme-color" content="#0f1419" />
|
<meta name="theme-color" content="#0f1419" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
<title>llm.famzheng.me</title>
|
<title>llm.famzheng.me</title>
|
||||||
<style>
|
<style>
|
||||||
:root {
|
:root {
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
|
||||||
|
<stop offset="0%" stop-color="#7c3aed"/>
|
||||||
|
<stop offset="100%" stop-color="#06b6d4"/>
|
||||||
|
</linearGradient>
|
||||||
|
<filter id="glow">
|
||||||
|
<feGaussianBlur stdDeviation="1.2"/>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
<rect x="2" y="2" width="60" height="60" rx="14" fill="url(#bg)"/>
|
||||||
|
<text x="32" y="46" text-anchor="middle"
|
||||||
|
font-family="ui-serif, Georgia, 'Times New Roman', serif"
|
||||||
|
font-size="42" font-weight="700" fill="white"
|
||||||
|
style="font-style: italic;">λ</text>
|
||||||
|
<circle cx="49" cy="49" r="6.5" fill="#4ade80" filter="url(#glow)" opacity="0.5"/>
|
||||||
|
<circle cx="49" cy="49" r="4.5" fill="#4ade80"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 756 B |
Reference in New Issue
Block a user