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:
@@ -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 反查日志)
|
||||
@@ -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)` | 落地节奏与现状 |
|
||||
|
||||
|
||||
## 边界速览
|
||||
|
||||
@@ -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)`。
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -137,3 +137,14 @@ sequenceDiagram
|
||||
- Token 长生命周期使用前提:Manager 端可吊销且具备审计;不要在跨设备粘贴中传播
|
||||
- 出现安全事件时,Manager 应能批量吊销名为 `HeiCode` 的 Token
|
||||
|
||||
## 八、界面截图(docs/images)
|
||||
|
||||
> 下列截图来自 `docs/images/`,用于辅助理解登录链路与界面落位。
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
|
||||
@@ -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 = ""
|
||||
|
||||
Vendored
+3
-3
@@ -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
@@ -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>
|
||||
)
|
||||
|
||||
+13
-50
@@ -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) {
|
||||
© {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>
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ type WorkspaceSwitcherProps = {
|
||||
*/
|
||||
export function WorkspaceSwitcher({
|
||||
workspaces,
|
||||
defaultName = 'New API',
|
||||
defaultName = 'Heicode Manager',
|
||||
defaultVersion,
|
||||
}: WorkspaceSwitcherProps) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
+61
-60
@@ -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: '',
|
||||
|
||||
+2
-2
@@ -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
@@ -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
@@ -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
@@ -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' },
|
||||
})
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user