Parcourir la source

docs: 添加登录 Widget 设计文档

纯前端登录表单 Widget,通过 <script> 标签嵌入到第三方页面,
调用现有 /api/user/login 接口,零后端改动。
master
fengsilin il y a 3 semaines
Parent
révision
08b89e6252
1 fichiers modifiés avec 139 ajouts et 0 suppressions
  1. +139
    -0
      docs/superpowers/specs/2026-05-20-login-widget-design.md

+ 139
- 0
docs/superpowers/specs/2026-05-20-login-widget-design.md Voir le fichier

@@ -0,0 +1,139 @@
# 登录 Widget 设计文档

> 日期:2026-05-20 | 分支:master | 状态:已批准

## 概述

将现有前端登录表单封装为独立的 JS Widget,嵌入方通过 `<script>` 标签引入后渲染登录表单,调用现有 `/api/user/login` 接口完成认证,登录成功后通过回调返回用户数据。

**核心原则**:零后端改动,纯前端实现。

## 嵌入方使用方式

```html
<div id="login-container"></div>
<script src="https://new-api.example.com/static/login-widget.js"></script>
<script>
const widget = NewApiLoginWidget.init({
container: '#login-container',
apiUrl: 'https://new-api.example.com',
onSuccess: (data) => {
// data = { id, username, display_name, role, status, group }
console.log('登录成功:', data);
},
onError: (err) => {
console.log('登录失败:', err.message);
}
});

// 销毁
widget.destroy();
</script>
```

## Widget API

### `NewApiLoginWidget.init(options)`

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `container` | `string \| Element` | 是 | 挂载点,CSS 选择器或 DOM 元素 |
| `apiUrl` | `string` | 是 | new-api 服务地址 |
| `onSuccess` | `(data: UserData) => void` | 否 | 登录成功回调 |
| `onError` | `(err: { message: string }) => void` | 否 | 登录失败回调 |
| `theme` | `'light' \| 'dark'` | 否 | 主题,默认 `'light'` |

### 返回值

`{ destroy: () => void }` — 调用 `destroy()` 卸载 Widget 并清理 DOM。

### 回调数据格式

```typescript
// onSuccess 回调参数
interface UserData {
id: number;
username: string;
display_name: string;
role: number;
status: number;
group: string;
}
```

## 技术方案

### 构建方式

在 `web/widget/` 目录下创建独立的 Vite 入口,打包成 UMD 格式。

```
web/widget/
index.jsx # 入口:init() API,创建 Shadow DOM 并挂载 React 组件
LoginForm.jsx # 登录表单组件(参考 web/src/components/auth/LoginForm.jsx)
styles.css # Widget 样式
vite.config.js # UMD 打包配置
```

### 样式隔离

使用 Shadow DOM 隔离 Widget 样式,防止与嵌入方 CSS 冲突。

### API 调用

直接调用现有 `POST /api/user/login` 接口,跨域 CORS 已开启(`AllowAllOrigins + AllowCredentials`)。

请求:
```json
POST {apiUrl}/api/user/login
{ "username": "xxx", "password": "xxx" }
```

响应(成功):
```json
{
"success": true,
"data": {
"id": 1,
"username": "user",
"display_name": "User",
"role": 1,
"status": 1,
"group": "default"
}
}
```

响应(失败):
```json
{ "success": false, "message": "用户名或密码错误" }
```

### 打包产物

- 输出:`web/build/static/login-widget.js`
- 格式:UMD(全局暴露 `NewApiLoginWidget`)
- CSS 内联到 JS bundle 中,通过 Shadow DOM 注入
- React 和 ReactDOM 打包进 bundle(避免要求嵌入方提供 React)

### 托管

由 new-api 后端的静态文件服务自动托管。构建流程:
1. `cd web && bun run build` — 构建主前端
2. `cd web/widget && bun run build` — 构建 Widget(输出到 `web/build/static/`)
3. 最终 `web/build/static/login-widget.js` 随 new-api 部署

## 改动范围

| 位置 | 改动 |
|------|------|
| `web/widget/` | 新增目录,Widget 源码 |
| 后端 | **无改动** |
| 构建流程 | `Makefile` 或 `web/package.json` 添加 Widget 构建步骤 |

## 范围限定(当前迭代)

- 仅支持**密码登录**
- 不处理 2FA
- 不处理 OAuth/Passkey 等其他登录方式
- 不管理嵌入方的认证状态(通过回调交由嵌入方处理)

Chargement…
Annuler
Enregistrer