Show WeCom machine name after brand title on shell and login.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -98,16 +98,27 @@ async def login(
|
||||
)
|
||||
|
||||
|
||||
@router.get("/branding")
|
||||
async def branding() -> dict:
|
||||
"""登录页/顶栏展示用(无敏感信息):与企微通知同一机器名。"""
|
||||
from ..notify import wecom
|
||||
|
||||
return {"machine_name": wecom.wecom_machine_name() or ""}
|
||||
|
||||
|
||||
@router.get("/me")
|
||||
async def me(
|
||||
username: Annotated[str, Depends(require_user)],
|
||||
settings: Annotated[Settings, Depends(get_settings)],
|
||||
) -> dict:
|
||||
from ..notify import wecom
|
||||
|
||||
return {
|
||||
"username": username,
|
||||
"env_name": settings.env_name,
|
||||
"mode": settings.mode,
|
||||
"sim": settings.is_sim,
|
||||
"machine_name": wecom.wecom_machine_name() or "",
|
||||
}
|
||||
|
||||
|
||||
|
||||
+25
-3
@@ -1,7 +1,8 @@
|
||||
import { type ReactNode, useEffect } from "react";
|
||||
import { type ReactNode, useEffect, useState } from "react";
|
||||
import { NavLink, Navigate, Route, Routes } from "react-router-dom";
|
||||
import {
|
||||
clearSession,
|
||||
fetchMachineName,
|
||||
getToken,
|
||||
getUsername,
|
||||
startAuthTokenAutoRefresh,
|
||||
@@ -22,6 +23,17 @@ const NAV = [
|
||||
|
||||
function Shell({ children }: { children: ReactNode }) {
|
||||
const user = getUsername();
|
||||
const [machineName, setMachineName] = useState("");
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
void fetchMachineName().then((name) => {
|
||||
if (!cancelled) setMachineName(name);
|
||||
});
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, []);
|
||||
const machineTag = machineName ? `【${machineName}】` : "";
|
||||
return (
|
||||
<div className="app-shell">
|
||||
<div className="app-container">
|
||||
@@ -35,8 +47,18 @@ function Shell({ children }: { children: ReactNode }) {
|
||||
width={28}
|
||||
height={28}
|
||||
/>
|
||||
<span className="brand-full">比特骆驼自动化对冲系统</span>
|
||||
<span className="brand-short">比特骆驼</span>
|
||||
<span className="brand-full">
|
||||
比特骆驼自动化对冲系统
|
||||
{machineTag ? (
|
||||
<span className="brand-machine">{machineTag}</span>
|
||||
) : null}
|
||||
</span>
|
||||
<span className="brand-short">
|
||||
比特骆驼
|
||||
{machineTag ? (
|
||||
<span className="brand-machine">{machineTag}</span>
|
||||
) : null}
|
||||
</span>
|
||||
</div>
|
||||
<div className="brand-actions">
|
||||
<span className="meta mono topnav-user">{user}</span>
|
||||
|
||||
@@ -32,6 +32,18 @@ export function getUsername(): string | null {
|
||||
return localStorage.getItem(USER_KEY);
|
||||
}
|
||||
|
||||
/** 与企微通知同一机器名(公开接口,登录前可用)。 */
|
||||
export async function fetchMachineName(): Promise<string> {
|
||||
try {
|
||||
const res = await fetch(`${getApiBase()}/api/auth/branding`);
|
||||
if (!res.ok) return "";
|
||||
const data = (await res.json()) as { machine_name?: string };
|
||||
return String(data.machine_name || "").trim();
|
||||
} catch {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
function tokenExpiresAtMs(): number | null {
|
||||
const raw = localStorage.getItem(TOKEN_EXP_KEY);
|
||||
if (!raw) return null;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { FormEvent, useEffect, useState } from "react";
|
||||
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||
import { login, setSession } from "../api/client";
|
||||
import { fetchMachineName, login, setSession } from "../api/client";
|
||||
|
||||
type ExchangeRes = {
|
||||
token: string;
|
||||
@@ -16,6 +16,17 @@ export default function LoginPage() {
|
||||
const [err, setErr] = useState("");
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [ticketBusy, setTicketBusy] = useState(false);
|
||||
const [machineName, setMachineName] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
void fetchMachineName().then((name) => {
|
||||
if (!cancelled) setMachineName(name);
|
||||
});
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const ticket = (params.get("ticket") || "").trim();
|
||||
@@ -91,7 +102,12 @@ export default function LoginPage() {
|
||||
width={48}
|
||||
height={48}
|
||||
/>
|
||||
<h1>比特骆驼自动化对冲系统</h1>
|
||||
<h1>
|
||||
比特骆驼自动化对冲系统
|
||||
{machineName ? (
|
||||
<span className="brand-machine">【{machineName}】</span>
|
||||
) : null}
|
||||
</h1>
|
||||
</div>
|
||||
{err ? <div className="err">{err}</div> : null}
|
||||
<div className="field">
|
||||
|
||||
@@ -124,6 +124,12 @@ input {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.brand-machine {
|
||||
margin-left: 0.15em;
|
||||
font-weight: 700;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.brand-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
Reference in New Issue
Block a user