feat(new-api): rebrand manager UI and expose login integration assets

Rebrand the default console experience to Heicode Manager, reshape key navigation toward deployment/ops workflows, and surface login integration docs/screenshots directly in the app for implementation handoff.

Constraint: Keep runtime/module identifiers compatible while shipping user-visible brand and IA changes first
Confidence: medium
Scope-risk: moderate
Not-tested: Full browser regression run across all default/classic pages
Made-with: Cursor
This commit is contained in:
gongzhiyong
2026-04-30 15:37:33 +08:00
parent b3cbbd5199
commit aae31e7506
18 changed files with 564 additions and 204 deletions
@@ -0,0 +1,349 @@
# Heicode 客户端 — 登录接口对接文档
**版本**: v1.0
**生效日期**: 2026-04-30
**状态**: 已上线生产,已通过端到端测试
---
## 1. 概述
本文档描述 Heicode 客户端(桌面/CLI)与 Heicode Manager(即 mcp-server)之间的**登录认证接口**。共 4 个接口,覆盖完整登录生命周期:
| 接口 | 用途 |
|------|------|
| `POST /api/auth/login` | 账号密码登录,换取 token |
| `GET /api/auth/me` | 校验 token 有效性 + 获取当前用户资料 |
| `POST /api/auth/refresh` | access token 过期时换新的 |
| `POST /api/auth/logout` | 登出(token 加入黑名单) |
> 不在本期范围:注册、找回密码、改密码 — 这些走官网 web 端完成。
---
## 2. 接入信息
### 2.1 Base URL
生产环境通过 Azure APIM 网关接入:
```
https://apimtaiji.azure-api.net/api/mcp
```
完整路径示例:
```
POST https://apimtaiji.azure-api.net/api/mcp/api/auth/login
```
### 2.2 通用请求头
| Header | 必填 | 说明 |
|--------|------|------|
| `Content-Type: application/json` | 是(POST/PUT) | 请求体 JSON |
| `Authorization: Bearer <token>` | 受保护接口必填 | 见 §3 |
| `X-Request-Id: <uuid>` | 建议 | 全链路追踪 ID,客户端生成 |
### 2.3 Token 模型
登录成功返回两个 token:
| Token | 用途 | 有效期 |
|-------|------|--------|
| **Access Token** | 调业务接口(含 `/me`、`/logout`) | 24 小时 |
| **Refresh Token** | 仅用于 `/refresh` 换新 access | 7 天 |
JWT claims 包含:`sub`(user_id)、`email`、`role`、`channelId`、`type`(access/refresh)、`iat`、`exp`。
---
## 3. 接口详情
### 3.1 POST /api/auth/login — 登录
**请求**
```http
POST /api/auth/login HTTP/1.1
Content-Type: application/json
{
"email": "user@example.com",
"password": "YourPassword123",
"role": "user"
}
```
字段:
- `email` (string, 必填)
- `password` (string, 必填)
- `role` (string, 必填):Heicode 客户端**固定传 `"user"`**
**成功响应 200**
```json
{
"success": true,
"data": {
"token": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "eyJhbGciOiJIUzI1NiIs...",
"user": {
"id": "b00a7b8e-9e8b-463d-9593-a3b4d0006778",
"name": "张三",
"email": "user@example.com",
"role": "user",
"channelId": "6e6fc470-76f8-4bb1-8ea4-625dc5b12bc6"
}
}
}
```
**错误响应**
| HTTP | 含义 | 客户端处理建议 |
|------|------|----------------|
| 401 | 邮箱或密码错误 | 显示"账号或密码错误",让用户重新输入 |
| 403 | 账户已被禁用 | 提示用户联系管理员 |
| 429 | 登录尝试过于频繁(**每 IP 5 次/分钟**) | 显示倒计时;响应头 `Retry-After: 60` 表示秒数 |
| 422 | 请求体校验失败(邮箱格式不合法等) | 检查 `detail` 字段 |
| 500 | 服务异常 | 重试或提示稍后再试 |
**重要:限流规则**
- **每 IP 每分钟最多 5 次**登录尝试(不区分成功失败)
- 超出返回 **429 Too Many Requests**,含 `Retry-After` 头(秒)
- 计数滑动窗口,60 秒后自动恢复
---
### 3.2 GET /api/auth/me — 获取当前用户
客户端**启动时**应调用此接口校验本地缓存的 access token 是否仍有效,并刷新用户信息。
**请求**
```http
GET /api/auth/me HTTP/1.1
Authorization: Bearer <accessToken>
```
**成功响应 200**
```json
{
"success": true,
"data": {
"id": "b00a7b8e-9e8b-463d-9593-a3b4d0006778",
"email": "user@example.com",
"name": "张三",
"role": "user",
"channelId": "6e6fc470-76f8-4bb1-8ea4-625dc5b12bc6",
"status": "active",
"subscriptionTier": "free",
"lastLoginAt": "2026-04-30T06:38:14.765457"
}
}
```
**错误响应**
| HTTP | 含义 | 客户端处理建议 |
|------|------|----------------|
| 401 | Token 无效/过期/已登出/用户不存在 | 调 `/refresh` 换新 token;若 refresh 也 401,跳登录页 |
| 403 | 账户已被禁用 | 强制登出,提示联系管理员 |
---
### 3.3 POST /api/auth/refresh — 刷新 token
access token 接近或已过期时调用,使用 **refresh token** 换取新的 access + refresh token 对。
**请求**
```http
POST /api/auth/refresh HTTP/1.1
Authorization: Bearer <refreshToken>
```
> ⚠️ **必须传 refresh token**,传 access token 会被拒绝。
**成功响应 200**
```json
{
"success": true,
"data": {
"token": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "eyJhbGciOiJIUzI1NiIs..."
}
}
```
客户端收到新的 token 对后**应替换本地缓存**(包括 refresh token,旧的也作废)。
**错误响应**
| HTTP | 含义 | 客户端处理建议 |
|------|------|----------------|
| 401 | refresh token 无效 / 过期 / 错传了 access token | 跳登录页 |
实施细节:
- 服务端会校验 token claims `type == "refresh"`,否则拒绝
- 旧 refresh token 不会被立即吊销(容许并发换发期),但客户端应丢弃旧的
---
### 3.4 POST /api/auth/logout — 登出
将当前 access token 加入黑名单,使其立即失效。
**请求**
```http
POST /api/auth/logout HTTP/1.1
Authorization: Bearer <accessToken>
```
**成功响应 200**
```json
{
"success": true,
"data": null,
"message": "登出成功"
}
```
**错误响应**
logout 容错性较强,token 黑名单写入失败也会返回 200(前端清理本地 token 即可)。
**客户端登出流程**:
1. 调 `/api/auth/logout`
2. 清除本地存储的 access + refresh token
3. 清除当前用户资料缓存
4. 跳转到登录页
---
## 4. 完整登录流程(示例)
### 启动时
```
┌─ 本地有 access token ?
│
├─ 是 ─→ GET /me
│ ├─ 200 ─→ 进入主界面
│ └─ 401 ─→ 本地有 refresh token ?
│ ├─ 是 ─→ POST /refresh
│ │ ├─ 200 ─→ 替换 token,进入主界面
│ │ └─ 401 ─→ 跳登录页
│ └─ 否 ─→ 跳登录页
│
└─ 否 ─→ 跳登录页
```
### 登录页提交
```
POST /login
├─ 200 ─→ 存 token 对,进主界面
├─ 401 ─→ 显示"账号或密码错误"
├─ 429 ─→ 显示"尝试过于频繁,请 N 秒后重试"(N 取响应头 Retry-After)
└─ 其他 ─→ 显示通用错误
```
### 业务请求过程中 access token 过期
```
任意业务接口返回 401
└─→ POST /refresh (用 refresh token)
├─ 200 ─→ 替换 token,重试原请求
└─ 401 ─→ 清理 token,跳登录页
```
### 登出按钮
```
POST /logout
└─→ 不论结果都清理本地 token,跳登录页
```
---
## 5. 错误响应格式
当前为 FastAPI 默认格式(下个版本 `/api/v1/*` 路径会改为标准 envelope,本期保留兼容):
```json
{
"detail": "邮箱或密码错误"
}
```
422 校验错误格式(Pydantic):
```json
{
"detail": [
{
"type": "value_error",
"loc": ["body", "email"],
"msg": "value is not a valid email address: ...",
"input": "abc"
}
]
}
```
---
## 6. 安全注意事项
| 项 | 说明 |
|---|---|
| **token 存储** | 桌面应用建议存到 OS 安全凭据存储(Windows Credential Manager / macOS Keychain / Linux Secret Service) |
| **HTTPS 强制** | 生产 base URL 已是 HTTPS;客户端**禁止**回退 HTTP |
| **token 泄露应对** | 用户怀疑泄露时提示去官网 web 端改密码(改密会导致所有 session 黑名单) |
| **审计日志** | 所有 login 尝试(成功/失败)服务端均写审计 |
| **状态码不泄漏** | 错误信息已统一用"邮箱或密码错误",不区分账号是否存在,防爆破 |
---
## 7. 测试账号(仅供联调)
| 角色 | 邮箱 | 密码 |
|------|------|------|
| 普通用户 | `55@55.com` | `By@123456.` |
> ⚠️ 测试账号仅用于联调阶段,正式上线前请务必关闭。
---
## 8. 已上线生产验证清单
| 测试项 | 结果 |
|--------|------|
| login 200 + 返回 access/refresh token | ✅ |
| /me 用 access token → 200 + 完整 profile | ✅ |
| /refresh 用 refresh token → 200 + 新 token 对 | ✅ |
| /refresh 用 access token → 401 拒绝 | ✅ |
| logout → 200 | ✅ |
| logout 后旧 token 调 /me → 401(黑名单生效) | ✅ |
| 连续 7 次错密 → 第 6 次起 429(每 IP 5/min 限流) | ✅ |
| 服务器审计日志记录所有 login(含成功/失败) | ✅ |
镜像 digest: `sha256:339b64ae090dc81fa13cb29705958167e77fe0698e27ac227c05054ed5c42309`
镜像 tag: `taiji.azurecr.io/mcp-server:heicode-auth-fix2-20260430`
部署日期: 2026-04-30
---
## 9. 联系
如对接过程发现接口行为与本文档不一致,请联系 Heicode Manager 后端团队,附上:
- 请求完整 URL / Headers / Body
- 响应 HTTP 状态 + Body
- `X-Request-Id` 头值(便于服务端按 ID 反查日志)
+4 -3
View File
@@ -22,9 +22,10 @@
| 7 | `[./agnet-platform-api-design.md](./agnet-platform-api-design.md)` §5 | 一键部署、SK 绑定、控制面 API |
| 8 | `[./orchestration-plan-contract.md](./orchestration-plan-contract.md)` | 模型提案对象与平台裁决规则 |
| 9 | `[./agnet-platform-api-design.md](./agnet-platform-api-design.md)` §6–§7 | 运行态、聚合视图、事件流(双轨) |
| 10 | `[./heicode-oauth-flow.md](./heicode-oauth-flow.md)` | 客户端浏览器登录到 Manager 的完整流程 |
| 11 | `[./acceptance-matrix.md](./acceptance-matrix.md)` | 集成验收最小测试矩阵 |
| 12 | `[../milestones/README.md](../milestones/README.md)` + `[../milestones/STATUS.md](../milestones/STATUS.md)` | 落地节奏与现状 |
| 10 | `[./Heicode-登录接口对接文档.md](./Heicode-登录接口对接文档.md)` | 已上线认证接口契约(login / me / refresh / logout) |
| 11 | `[./heicode-oauth-flow.md](./heicode-oauth-flow.md)` | 客户端浏览器登录到 Manager 的完整流程 |
| 12 | `[./acceptance-matrix.md](./acceptance-matrix.md)` | 集成验收最小测试矩阵 |
| 13 | `[../milestones/README.md](../milestones/README.md)` + `[../milestones/STATUS.md](../milestones/STATUS.md)` | 落地节奏与现状 |
## 边界速览
+32 -28
View File
@@ -3,7 +3,7 @@
本文描述 **Agnet 平台**应向 **Heicode(Manager / 客户端 / 自动化服务)** 暴露的 **控制面、数据面隔离、权限模型与可视化/事件接口**。
路径、字段名为 **设计意图**;落地时可等价映射为 gRPC 或 GraphQL,但**语义与隔离边界**应保持一致。
**关联里程碑**:[`../milestones/`](../milestones/README.md) 中 M3~M5。
**关联里程碑**:`[../milestones/](../milestones/README.md)` 中 M3~M5。
---
@@ -79,7 +79,7 @@
- **控制面**(部署/改策略)与 **观测面**(读指标)可分角色授予。
- **凭据类写操作**(绑定 Git Token、云 SA)单独 scope:`agnet:credential:write`。
- **SK 正文写入**:仅允许经 **Heicode 客户端**身份或专用 **`heicode:sk:write`**(示例名)路径;**Agnet / Manager 控制台接口不得授予 SK 正文写权限**(与 §5.0「SK 仅在 Heicode 编辑」一致)。
- **SK 正文写入**:仅允许经 **Heicode 客户端**身份或专用 `**heicode:sk:write`**(示例名)路径;**Agnet / Manager 控制台接口不得授予 SK 正文写权限**(与 §5.0「SK 仅在 Heicode 编辑」一致)。
- **拒绝隐式升级**:只读 Token **不得**通过查询参数绕过 body 校验升格为写操作。
---
@@ -122,7 +122,7 @@ Tenant(租户)
### 5.0 Heicode Manager:一键部署 Agnet 团队与 SK 边界(产品契约)
下列条款为 **Heicode 与 Agnet 联合落地时必须写清** 的契约;API 形状可与 **`POST /deployments`** 合一或拆为 **`POST /teams/deployments`** 等聚合端点,但 **语义不得缩水**。
下列条款为 **Heicode 与 Agnet 联合落地时必须写清** 的契约;API 形状可与 `**POST /deployments`** 合一或拆为 `**POST /teams/deployments`** 等聚合端点,但 **语义不得缩水**。
| 契约项 | 要求 |
@@ -165,7 +165,7 @@ Tenant(租户)
}
```
- **`sk_sources`(推荐显式建模)**:替代或细化纯路径数组 `sk_file_refs`; 每个元素标明来源类型,便于 Agnet 实现拉取与快照。
- `**sk_sources`(推荐显式建模)**:替代或细化纯路径数组 `sk_file_refs`; 每个元素标明来源类型,便于 Agnet 实现拉取与快照。
**部署请求体中 SK 绑定扩展示意**
@@ -195,15 +195,15 @@ Tenant(租户)
| 类别 | 要求 |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **部署与编队** | 实现 **`POST /deployments`**(或 **`POST /teams/deployments`**)可接收 **成员、成员模型、子 agent 模板及 `sk_sources`**;返回 **`deployment_id`**、实例/子 agent 标识,供 Callback 与观测关联。 |
| **部署与编队** | 实现 `**POST /deployments`**(或 `**POST /teams/deployments`**)可接收 **成员、成员模型、子 agent 模板及 `sk_sources`**;返回 `**deployment_id**`、实例/子 agent 标识,供 Callback 与观测关联。 |
| **SK 快照只读** | 对每个 `deployment_id` / `sub_agent_id`,Agnet 须能记录 **已解析的 SK 快照**(Git:`commit_sha` + 路径哈希;Upload:`artifact_id` + 版本)。运行注入 **仅此快照**,不得在执行中「瞒报版本」拉未授权路径。 |
| **Git 拉取** | Agnet 须支持 **按租户注册 Git 凭据/连接**(`connection_id` 或等价),由用户在 **Heicode/Manager 流程**中授权;**Agnet 不提供 Git 写接口用于改 SK**——写操作发生在 Git 远端或经 Heicode 提交后,Agnet 仅 **fetch + checkout 指定 ref**。 |
| **上传制品** | 若支持 `type: "upload"`:Agnet(或与 Manager 分工)须提供 **`artifact_id`** 的只读获取(如 `GET /sk-artifacts/{artifact_id}/content` 或预签名 URL),**无 `PUT` 修改正文**于 Agnet 控制台;上传入口 **仅** Heicode 侧发起、Agnet 存只读副本。 |
| **刷新策略** | 约定 **何时重新解析 SK**(如新 commit、用户触发刷新、部署新版本);须可通过 API 或事件暴露 **`sk_snapshot_refreshed`**,便于 Heicode 提示「已用新版本 SK」。 |
| **上传制品** | 若支持 `type: "upload"`:Agnet(或与 Manager 分工)须提供 `**artifact_id`** 的只读获取(如 `GET /sk-artifacts/{artifact_id}/content` 或预签名 URL),**无 `PUT` 修改正文**于 Agnet 控制台;上传入口 **仅** Heicode 侧发起、Agnet 存只读副本。 |
| **刷新策略** | 约定 **何时重新解析 SK**(如新 commit、用户触发刷新、部署新版本);须可通过 API 或事件暴露 `**sk_snapshot_refreshed`**,便于 Heicode 提示「已用新版本 SK」。 |
| **禁止项** | **不得**提供面向 SK 正文的 **通用写 API**(与 §3.2 一致);子 agent **只读**绑定列表内的快照。 |
- **`sk_file_refs`**(若保留简化字段):视为 **相对某默认 Git 根**或 **由 Manager 展开为 `sk_sources`** 前的简写;联合 RFC 须声明展开规则。
- `**sk_file_refs`**(若保留简化字段):视为 **相对某默认 Git 根**或 **由 Manager 展开为 `sk_sources`** 前的简写;联合 RFC 须声明展开规则。
- **验收**:部署完成后,Manager 可展示「团队成员—模型—**Agnet 子 agent**—SK 源(Git ref / 上传件)—快照版本」;Git 更新或 Heicode 重新上传后,按刷新策略在后续运行使用新快照。
### 5.1 部署编队
@@ -345,7 +345,7 @@ Tenant(租户)
`WS /projects/{project_id}/stream`
- 首帧 **鉴权**(query token 或子协议);订阅主题:`deployment.*`、`instance.*`。
- 首帧 **鉴权**(query token 或子协议);订阅主题:`deployment.`*、`instance.`*。
### 7.3 重连与顺序
@@ -404,7 +404,7 @@ Tenant(租户)
- **平台裁决**:Agnet 对提案执行权限、租户、预算、模型授权校验后决定执行
- **Manager 入口**:Heicode Manager 仅作为调用入口与状态展示,不绕过裁决链路
详见:[`./orchestration-plan-contract.md`](./orchestration-plan-contract.md)。
详见:`[./orchestration-plan-contract.md](./orchestration-plan-contract.md)`。
最低校验项(平台必须执行):
@@ -420,14 +420,16 @@ Tenant(租户)
建议固定以下事件名,避免前后端各自命名造成协议漂移:
| event | 用途 |
|------|------|
| `deployment.accepted` | 部署请求被接受,进入编排队列 |
| `deployment.rejected` | 部署被策略拒绝 |
| `instance.phase_changed` | 执行单元阶段变化 |
| `instance.health_changed` | 执行单元健康状态变化 |
| `sub_agent.output_delta` | 子 agent 流式输出增量 |
| `sk_snapshot_refreshed` | SK 快照刷新完成 |
| event | 用途 |
| ------------------------- | -------------- |
| `deployment.accepted` | 部署请求被接受,进入编排队列 |
| `deployment.rejected` | 部署被策略拒绝 |
| `instance.phase_changed` | 执行单元阶段变化 |
| `instance.health_changed` | 执行单元健康状态变化 |
| `sub_agent.output_delta` | 子 agent 流式输出增量 |
| `sk_snapshot_refreshed` | SK 快照刷新完成 |
每个事件最小字段建议:
@@ -445,17 +447,19 @@ Tenant(租户)
除 HTTP 状态码外,建议统一错误码最小集合:
| code | 说明 |
|------|------|
| `POLICY_REJECTED` | 平台策略拒绝执行提案 |
| `MODEL_NOT_ALLOWED` | 模型未授权 |
| `SK_SOURCE_UNRESOLVABLE` | SK 源不可解析或不可读 |
| `BUDGET_EXCEEDED` | 超预算 |
| `FORBIDDEN_CROSS_TENANT` | 跨租户访问拒绝 |
| `DEPLOYMENT_CONFLICT` | 幂等或状态冲突 |
| `CALLBACK_SIGNATURE_INVALID` | 回调签名校验失败 |
验收用例集合见:[`./acceptance-matrix.md`](./acceptance-matrix.md)。
| code | 说明 |
| ---------------------------- | ------------ |
| `POLICY_REJECTED` | 平台策略拒绝执行提案 |
| `MODEL_NOT_ALLOWED` | 模型未授权 |
| `SK_SOURCE_UNRESOLVABLE` | SK 源不可解析或不可读 |
| `BUDGET_EXCEEDED` | 超预算 |
| `FORBIDDEN_CROSS_TENANT` | 跨租户访问拒绝 |
| `DEPLOYMENT_CONFLICT` | 幂等或状态冲突 |
| `CALLBACK_SIGNATURE_INVALID` | 回调签名校验失败 |
验收用例集合见:`[./acceptance-matrix.md](./acceptance-matrix.md)`。
---
+11
View File
@@ -137,3 +137,14 @@ sequenceDiagram
- Token 长生命周期使用前提:Manager 端可吊销且具备审计;不要在跨设备粘贴中传播
- 出现安全事件时,Manager 应能批量吊销名为 `HeiCode` 的 Token
## 八、界面截图(docs/images)
> 下列截图来自 `docs/images/`,用于辅助理解登录链路与界面落位。
![Heicode login flow screenshot 01](../images/wecom-screenshot-01.jpg)
![Heicode login flow screenshot 02](../images/wecom-screenshot-02.jpg)
![Heicode login flow screenshot 03](../images/wecom-screenshot-03.jpg)
![Heicode login flow screenshot 04](../images/wecom-screenshot-04.jpg)
![Heicode login flow screenshot 05](../images/wecom-screenshot-05.jpg)
![Heicode login flow screenshot 06](../images/wecom-screenshot-06.jpg)
+1 -1
View File
@@ -13,7 +13,7 @@ import (
var StartTime = time.Now().Unix() // unit: second
var Version = "v0.0.0" // this hard coding will be replaced automatically when building, no need to manually change
var SystemName = "New API"
var SystemName = "Heicode Manager"
var Footer = ""
var Logo = ""
var TopUpLink = ""
+3 -3
View File
@@ -6,11 +6,11 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- Primary Meta Tags -->
<title>New API</title>
<meta name="title" content="New API" />
<title>Heicode Manager</title>
<meta name="title" content="Heicode Manager" />
<meta
name="description"
content="Unified AI API gateway and admin dashboard."
content="Heicode Manager control plane for model gateway, deployments, and operations."
/>
<meta name="theme-color" content="#fff" />
+2 -2
View File
@@ -4,7 +4,7 @@ import { cn } from '@/lib/utils'
export function Logo({ className, ...props }: SVGProps<SVGSVGElement>) {
return (
<svg
id='newapi-logo'
id='heicode-manager-logo'
viewBox='0 0 24 24'
xmlns='http://www.w3.org/2000/svg'
height='24'
@@ -17,7 +17,7 @@ export function Logo({ className, ...props }: SVGProps<SVGSVGElement>) {
className={cn('size-6', className)}
{...props}
>
<title>New API</title>
<title>Heicode Manager</title>
<path d='M15 6v12a3 3 0 1 0 3-3H6a3 3 0 1 0 3 3V6a3 3 0 1 0-3 3h12a3 3 0 1 0-3-3' />
</svg>
)
@@ -60,7 +60,7 @@ export function Footer(props: FooterProps) {
} = useSystemConfig()
const displayLogo = systemLogo || props.logo || '/logo.png'
const displayName = systemName || props.name || 'New API'
const displayName = systemName || props.name || 'Heicode Manager'
const isDemoSiteMode = Boolean(demoSiteEnabled)
const currentYear = new Date().getFullYear()
@@ -69,52 +69,25 @@ export function Footer(props: FooterProps) {
{
title: t('footer.columns.about.title'),
links: [
{
text: t('footer.columns.about.links.aboutProject'),
href: 'https://docs.newapi.pro/wiki/project-introduction/',
},
{
text: t('footer.columns.about.links.contact'),
href: 'https://docs.newapi.pro/support/community-interaction/',
},
{
text: t('footer.columns.about.links.features'),
href: 'https://docs.newapi.pro/wiki/features-introduction/',
},
{ text: t('About'), href: '/about' },
{ text: t('Profile'), href: '/profile' },
{ text: t('System Settings'), href: '/system-settings/general' },
],
},
{
title: t('footer.columns.docs.title'),
links: [
{
text: t('footer.columns.docs.links.quickStart'),
href: 'https://docs.newapi.pro/getting-started/',
},
{
text: t('footer.columns.docs.links.installation'),
href: 'https://docs.newapi.pro/installation/',
},
{
text: t('footer.columns.docs.links.apiDocs'),
href: 'https://docs.newapi.pro/api/',
},
{ text: t('Deployments'), href: '/deployments' },
{ text: t('Events'), href: '/events' },
{ text: t('API Keys'), href: '/keys' },
],
},
{
title: t('footer.columns.related.title'),
links: [
{
text: t('footer.columns.related.links.oneApi'),
href: 'https://github.com/songquanpeng/one-api',
},
{
text: t('footer.columns.related.links.midjourney'),
href: 'https://github.com/novicezk/midjourney-proxy',
},
{
text: t('footer.columns.related.links.neko'),
href: 'https://github.com/Calcium-Ion/neko-api-key-tool',
},
{ text: t('Channels'), href: '/channels' },
{ text: t('Models'), href: '/models/metadata' },
{ text: t('Users'), href: '/users' },
],
},
],
@@ -182,19 +155,9 @@ export function Footer(props: FooterProps) {
&copy; {currentYear} {displayName}.{' '}
{props.copyright ?? t('footer.defaultCopyright')}
</p>
<div className='flex items-center gap-2'>
<span className='text-muted-foreground/40 text-xs'>
{t('Designed and Developed by')}{' '}
</span>
<a
href='https://github.com/QuantumNous/new-api'
target='_blank'
rel='noopener noreferrer'
className='text-primary text-xs font-medium hover:underline'
>
{t('New API')}
</a>
</div>
<span className='text-muted-foreground/40 text-xs'>
{t('Platform Console')}
</span>
</div>
</div>
</footer>
@@ -37,7 +37,7 @@ type WorkspaceSwitcherProps = {
*/
export function WorkspaceSwitcher({
workspaces,
defaultName = 'New API',
defaultName = 'Heicode Manager',
defaultVersion,
}: WorkspaceSwitcherProps) {
const { t } = useTranslation()
+61 -60
View File
@@ -1,6 +1,7 @@
import { useQuery } from '@tanstack/react-query'
import { Construction } from 'lucide-react'
import { useTranslation } from 'react-i18next'
import { Link } from '@tanstack/react-router'
import { Markdown } from '@/components/ui/markdown'
import { Skeleton } from '@/components/ui/skeleton'
import { PublicLayout } from '@/components/layout'
@@ -19,9 +20,61 @@ function isLikelyHtml(value: string) {
return /<\/?[a-z][\s\S]*>/i.test(value)
}
const HEICODE_INTEGRATION_IMAGES = [
'/docs/images/wecom-screenshot-01.jpg',
'/docs/images/wecom-screenshot-02.jpg',
'/docs/images/wecom-screenshot-03.jpg',
'/docs/images/wecom-screenshot-04.jpg',
'/docs/images/wecom-screenshot-05.jpg',
'/docs/images/wecom-screenshot-06.jpg',
]
function HeicodeIntegrationPanel() {
const { t } = useTranslation()
return (
<section className='bg-card mt-8 rounded-xl border p-5 md:p-6'>
<div className='mb-4 flex flex-wrap items-center justify-between gap-3'>
<div>
<h3 className='text-lg font-semibold'>{t('Heicode Manager Integration')}</h3>
<p className='text-muted-foreground mt-1 text-sm'>
{t('Client login API contract and integration screenshots')}
</p>
</div>
<a
href='/docs/integration/Heicode-登录接口对接文档.md'
target='_blank'
rel='noopener noreferrer'
className='text-primary text-sm font-medium hover:underline'
>
{t('Open Login Interface Doc')}
</a>
</div>
<div className='grid grid-cols-1 gap-4 md:grid-cols-2'>
{HEICODE_INTEGRATION_IMAGES.map((src, index) => (
<a
key={src}
href={src}
target='_blank'
rel='noopener noreferrer'
className='group block overflow-hidden rounded-lg border'
>
<img
src={src}
alt={`Heicode integration screenshot ${index + 1}`}
className='h-full w-full object-cover transition-transform duration-200 group-hover:scale-[1.02]'
loading='lazy'
/>
</a>
))}
</div>
</section>
)
}
function EmptyAboutState() {
const { t } = useTranslation()
const currentYear = new Date().getFullYear()
return (
<div className='flex min-h-[60vh] items-center justify-center p-8'>
@@ -37,66 +90,13 @@ function EmptyAboutState() {
)}
</p>
</div>
<div className='space-y-4 text-sm'>
<div className='text-muted-foreground space-y-2 text-sm'>
<p>{t('Heicode Manager console is ready for your organization branding.')}</p>
<p>{t('Set custom About HTML or URL in System Settings > General > About.')}</p>
<p>
{t('New API Project Repository:')}{' '}
<a
href='https://github.com/QuantumNous/new-api'
target='_blank'
rel='noopener noreferrer'
className='text-primary hover:underline'
>
{t('https://github.com/QuantumNous/new-api')}
</a>
</p>
<p className='text-muted-foreground'>
<a
href='https://github.com/QuantumNous/new-api'
target='_blank'
rel='noopener noreferrer'
className='text-primary hover:underline'
>
{t('NewAPI')}
</a>{' '}
© {currentYear}{' '}
<a
href='https://github.com/QuantumNous'
target='_blank'
rel='noopener noreferrer'
className='text-primary hover:underline'
>
{t('QuantumNous')}
</a>{' '}
{t('| Based on')}{' '}
<a
href='https://github.com/songquanpeng/one-api'
target='_blank'
rel='noopener noreferrer'
className='text-primary hover:underline'
>
{t('One API')}
</a>{' '}
© 2023{' '}
<a
href='https://github.com/songquanpeng'
target='_blank'
rel='noopener noreferrer'
className='text-primary hover:underline'
>
{t('JustSong')}
</a>
</p>
<p className='text-muted-foreground'>
{t('This project must be used in compliance with the')}{' '}
<a
href='https://github.com/QuantumNous/new-api/blob/main/LICENSE'
target='_blank'
rel='noopener noreferrer'
className='text-primary hover:underline'
>
{t('AGPL v3.0 License')}
</a>
.
<Link to='/about' className='text-primary hover:underline'>
{t('This page can also host integration runbooks and screenshots.')}
</Link>
</p>
</div>
</div>
@@ -162,6 +162,7 @@ export function About() {
{rawContent}
</Markdown>
)}
<HeicodeIntegrationPanel />
</div>
</PublicLayout>
)
@@ -11,7 +11,7 @@ import {
const defaultGeneralSettings: GeneralSettings = {
'theme.frontend': 'default',
Notice: '',
SystemName: 'New API',
SystemName: 'Heicode Manager',
Logo: '',
Footer: '',
About: '',
@@ -201,7 +201,7 @@ export function SystemInfoSection({ defaultValues }: SystemInfoSectionProps) {
<FormItem>
<FormLabel>{t('System Name')}</FormLabel>
<FormControl>
<Input placeholder={t('New API')} {...field} />
<Input placeholder={t('Heicode Manager')} {...field} />
</FormControl>
<FormDescription>
{t('The name displayed across the application')}
@@ -305,7 +305,7 @@ export function SystemInfoSection({ defaultValues }: SystemInfoSectionProps) {
<FormLabel>{t('Home Page Content')}</FormLabel>
<FormControl>
<Textarea
placeholder={t('Welcome to our New API...')}
placeholder={t('Welcome to Heicode Manager...')}
rows={6}
{...field}
/>
+34 -15
View File
@@ -25,11 +25,10 @@ const DEFAULT_SIDEBAR_MODULES: SidebarModulesAdminConfig = {
},
console: {
enabled: true,
detail: true,
deployments: true,
events: true,
audit: true,
token: true,
log: true,
midjourney: true,
task: true,
},
personal: {
enabled: true,
@@ -40,9 +39,7 @@ const DEFAULT_SIDEBAR_MODULES: SidebarModulesAdminConfig = {
enabled: true,
channel: true,
models: true,
redemption: true,
user: true,
setting: true,
subscription: true,
},
}
@@ -52,15 +49,19 @@ const DEFAULT_SIDEBAR_MODULES: SidebarModulesAdminConfig = {
*/
const URL_TO_CONFIG_MAP: Record<string, { section: string; module: string }> = {
'/playground': { section: 'chat', module: 'playground' },
'/dashboard': { section: 'console', module: 'detail' },
'/dashboard/overview': { section: 'console', module: 'detail' },
'/dashboard/models': { section: 'console', module: 'detail' },
'/dashboard/users': { section: 'console', module: 'detail' },
'/dashboard': { section: 'console', module: 'deployments' },
'/dashboard/overview': { section: 'console', module: 'deployments' },
'/dashboard/models': { section: 'console', module: 'deployments' },
'/dashboard/users': { section: 'console', module: 'deployments' },
'/deployments': { section: 'console', module: 'deployments' },
'/models/deployments': { section: 'console', module: 'deployments' },
'/events': { section: 'console', module: 'events' },
'/keys': { section: 'console', module: 'token' },
'/usage-logs': { section: 'console', module: 'log' },
'/usage-logs/common': { section: 'console', module: 'log' },
'/usage-logs/drawing': { section: 'console', module: 'midjourney' },
'/usage-logs/task': { section: 'console', module: 'task' },
'/audit': { section: 'console', module: 'audit' },
'/usage-logs': { section: 'console', module: 'events' },
'/usage-logs/common': { section: 'console', module: 'events' },
'/usage-logs/drawing': { section: 'console', module: 'audit' },
'/usage-logs/task': { section: 'console', module: 'audit' },
'/wallet': { section: 'personal', module: 'topup' },
'/profile': { section: 'personal', module: 'personal' },
'/channels': { section: 'admin', module: 'channel' },
@@ -68,7 +69,6 @@ const URL_TO_CONFIG_MAP: Record<string, { section: string; module: string }> = {
'/models/metadata': { section: 'admin', module: 'models' },
'/models/deployments': { section: 'admin', module: 'models' },
'/users': { section: 'admin', module: 'user' },
'/redemption-codes': { section: 'admin', module: 'redemption' },
'/subscriptions': { section: 'admin', module: 'subscription' },
}
@@ -93,6 +93,25 @@ function parseSidebarConfig(
if (parsed.chat.playground === undefined) parsed.chat.playground = true
if (parsed.chat.chat === undefined) parsed.chat.chat = true
}
if (!parsed.console) {
parsed.console = {
enabled: true,
deployments: true,
events: true,
audit: true,
token: true,
}
} else {
if (parsed.console.enabled === undefined) parsed.console.enabled = true
if (parsed.console.deployments === undefined)
parsed.console.deployments = parsed.console.detail ?? true
if (parsed.console.events === undefined)
parsed.console.events = parsed.console.log ?? true
if (parsed.console.audit === undefined)
parsed.console.audit =
parsed.console.task ?? parsed.console.midjourney ?? true
if (parsed.console.token === undefined) parsed.console.token = true
}
return parsed
} catch {
// eslint-disable-next-line no-console
+19 -37
View File
@@ -1,19 +1,16 @@
import {
LayoutDashboard,
Activity,
Key,
FileText,
Wallet,
ShieldCheck,
Box,
Users,
Ticket,
User,
Command,
Radio,
FlaskConical,
MessageSquare,
CreditCard,
ListTodo,
Rocket,
} from 'lucide-react'
import { useTranslation } from 'react-i18next'
import { WORKSPACE_IDS } from '@/components/layout/lib/workspace-registry'
@@ -50,40 +47,30 @@ export function useSidebarData(): SidebarData {
},
{
id: 'general',
title: t('General'),
title: t('Operations'),
items: [
{
title: t('Overview'),
url: '/dashboard/overview',
title: t('Deployments'),
url: '/deployments',
icon: Rocket,
},
{
title: t('Events'),
url: '/events',
icon: Activity,
},
{
title: t('Dashboard'),
url: '/dashboard/models',
icon: LayoutDashboard,
title: t('Audit'),
url: '/audit',
activeUrls: ['/usage-logs/drawing', '/usage-logs/task'],
configUrls: ['/audit', '/usage-logs/drawing', '/usage-logs/task'],
icon: ShieldCheck,
},
{
title: t('API Keys'),
url: '/keys',
icon: Key,
},
{
title: t('Usage Logs'),
url: '/usage-logs/common',
icon: FileText,
},
{
title: t('Task Logs'),
url: '/usage-logs/task',
activeUrls: ['/usage-logs/drawing'],
configUrls: ['/usage-logs/drawing', '/usage-logs/task'],
icon: ListTodo,
},
{
title: t('Wallet'),
url: '/wallet',
icon: Wallet,
},
{
title: t('Profile'),
url: '/profile',
@@ -93,15 +80,15 @@ export function useSidebarData(): SidebarData {
},
{
id: 'admin',
title: t('Admin'),
title: t('Control Plane'),
items: [
{
title: t('Channels'),
title: t('Provider Channels'),
url: '/channels',
icon: Radio,
},
{
title: t('Models'),
title: t('Model Catalog'),
url: '/models/metadata',
icon: Box,
},
@@ -111,12 +98,7 @@ export function useSidebarData(): SidebarData {
icon: Users,
},
{
title: t('Redemption Codes'),
url: '/redemption-codes',
icon: Ticket,
},
{
title: t('Subscription Management'),
title: t('Subscriptions'),
url: '/subscriptions',
icon: CreditCard,
},
+1 -1
View File
@@ -3,7 +3,7 @@
*/
// System Configuration Defaults
export const DEFAULT_SYSTEM_NAME = 'New API'
export const DEFAULT_SYSTEM_NAME = 'Heicode Manager'
export const DEFAULT_LOGO = '/logo.png'
// LocalStorage Keys
@@ -0,0 +1,10 @@
import { createFileRoute, redirect } from '@tanstack/react-router'
export const Route = createFileRoute('/_authenticated/audit/')({
beforeLoad: () => {
throw redirect({
to: '/usage-logs/$section',
params: { section: 'task' },
})
},
})
@@ -0,0 +1,10 @@
import { createFileRoute, redirect } from '@tanstack/react-router'
export const Route = createFileRoute('/_authenticated/deployments/')({
beforeLoad: () => {
throw redirect({
to: '/models/$section',
params: { section: 'deployments' },
})
},
})
@@ -0,0 +1,10 @@
import { createFileRoute, redirect } from '@tanstack/react-router'
export const Route = createFileRoute('/_authenticated/events/')({
beforeLoad: () => {
throw redirect({
to: '/usage-logs/$section',
params: { section: 'common' },
})
},
})