feat(telemetry): integrate OpenTelemetry observability stack with health metrics
- Add OpenTelemetry SDK, OTLP exporter, Prometheus integration - Implement connection tracking with active/total/disconnection metrics - Add health endpoint with uptime and connection counts - Integrate tracing spans for socket events and engine messages - Add metrics collection for event handling duration - Update health endpoint to include live runtime state - Add graceful telemetry shutdown in main function - Implement engine session active metrics tracking - Add namespace-specific attributes to connection metrics - Introduce message edit history retrieval endpoint - Add scheduled message CRUD operations and dispatcher - Update Socket.IO event registration with observability - Refactor component update to remove dead code allowance - Add comprehensive environment variables documentation - Implement detailed development guidelines in AGENTS.md
This commit is contained in:
+718
@@ -0,0 +1,718 @@
|
||||
# Auth 认证方案
|
||||
|
||||
## 架构总览
|
||||
|
||||
```
|
||||
┌─────────┐ ┌──────────┐ ┌──────────┐
|
||||
│ Client │ │ appks │ │ imks │
|
||||
│ (浏览器/ │ │ (core) │ │ (IM服务) │
|
||||
│ APP) │ │ │ │ │
|
||||
└────┬─────┘ └────┬─────┘ └────┬─────┘
|
||||
│ │ │
|
||||
│ 1. POST /api/v1/auth/login │
|
||||
│───────────────────▶│ │
|
||||
│ 2. {access_token, refresh_token} │
|
||||
│◀───────────────────│ │
|
||||
│ │ │
|
||||
│ 3. WS/gRPC/HTTP 携带 JWT │
|
||||
│──────────────────────────────────────▶│
|
||||
│ │ │
|
||||
│ │ 4a. VerifyToken RPC (RPC模式)
|
||||
│ │◀─────────────────│
|
||||
│ │ 4b. GetSigningKeys (本地模式)
|
||||
│ │◀─────────────────│
|
||||
│ │ │
|
||||
│ │ 5. TokenClaims / SigningKeys
|
||||
│ │─────────────────▶│
|
||||
│ │ │
|
||||
│ 6. 业务响应 │ │
|
||||
│◀─────────────────────────────────────│
|
||||
```
|
||||
|
||||
**角色分工:**
|
||||
|
||||
| 服务 | 职责 |
|
||||
|------------------|----------------------------------------------------|
|
||||
| **appks** (core) | 颁发 JWT、刷新 JWT、撤销 JWT、管理签名密钥、提供 `TokenService` gRPC |
|
||||
| **imks** (IM) | 接收客户端 JWT,通过 RPC 或本地密钥验证用户身份 |
|
||||
|
||||
## Proto 契约
|
||||
|
||||
定义在 `proto/core/auth.proto`,package `appks.core.v1`。
|
||||
|
||||
appks 和 imks 各自维护一份相同的 proto 文件:
|
||||
- appks 编译为 **server** stub(提供服务)
|
||||
- imks 编译为 **client** stub(调用服务)
|
||||
|
||||
### TokenService RPC
|
||||
|
||||
```protobuf
|
||||
service TokenService {
|
||||
// 令牌生命周期 (appks 内部调用)
|
||||
rpc IssueToken(IssueTokenRequest) returns (IssueTokenResponse);
|
||||
rpc RefreshToken(RefreshTokenRequest) returns (RefreshTokenResponse);
|
||||
rpc RevokeToken(RevokeTokenRequest) returns (RevokeTokenResponse);
|
||||
|
||||
// imks 验证 (RPC 模式)
|
||||
rpc VerifyToken(VerifyTokenRequest) returns (VerifyTokenResponse);
|
||||
|
||||
// imks 密钥拉取 (本地验证模式)
|
||||
rpc GetSigningKeys(GetSigningKeysRequest) returns (GetSigningKeysResponse);
|
||||
}
|
||||
```
|
||||
|
||||
## JWT 令牌
|
||||
|
||||
### 结构
|
||||
|
||||
JWT Header:
|
||||
```json
|
||||
{
|
||||
"alg": "HS256",
|
||||
"typ": "JWT",
|
||||
"kid": "01909a..." // 签名密钥 ID,用于匹配 SigningKey
|
||||
}
|
||||
```
|
||||
|
||||
JWT Payload (`TokenClaims`):
|
||||
```json
|
||||
{
|
||||
"sub": "user-uuid",
|
||||
"iss": "appks",
|
||||
"iat": 1718000000,
|
||||
"exp": 1718003600,
|
||||
"jti": "01909b...",
|
||||
"scope": "im:read im:write",
|
||||
"extra": {
|
||||
"workspace_id": "..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 令牌类型
|
||||
|
||||
| 类型 | 格式 | 存储 | 用途 |
|
||||
|-------------------|----------------------|----------------------------------------------|---------------------------------------|
|
||||
| **access_token** | JWT (HS256) | 无状态,客户端持有 | 每次请求携带,验证用户身份 |
|
||||
| **refresh_token** | `rt_{UUIDv7}` 不透明字符串 | Redis `core:token:refresh:{token}` → user_id | 换取新的 access_token + refresh_token(旋转) |
|
||||
|
||||
## 双模式验证
|
||||
|
||||
imks 可选择以下任一模式验证客户端 JWT:
|
||||
|
||||
### 模式 A:RPC 验证(`VerifyToken`)
|
||||
|
||||
```
|
||||
imks → appks TokenService.VerifyToken(jwt) → {valid, claims}
|
||||
```
|
||||
|
||||
- **优点**:实时权威,能感知撤销
|
||||
- **缺点**:每次请求增加一次 RPC 往返
|
||||
- **适用场景**:高安全要求操作(管理员操作、敏感数据)
|
||||
|
||||
### 模式 B:本地验证(`GetSigningKeys`)
|
||||
|
||||
```
|
||||
imks 启动时 → appks TokenService.GetSigningKeys() → 缓存密钥到本地
|
||||
后续请求 → imks 用本地密钥解码 JWT(HS256 验签)
|
||||
定期刷新 → 根据 next_rotation_at 拉取新密钥
|
||||
```
|
||||
|
||||
- **优点**:零 RPC 延迟,appks 不可用时仍能验证
|
||||
- **缺点**:撤销有最多一个密钥窗口(3h)的延迟
|
||||
- **适用场景**:高频低延迟操作(消息收发、实时通信)
|
||||
|
||||
### 推荐策略
|
||||
|
||||
混合使用:
|
||||
- 普通操作(发消息、读频道)→ 本地验证
|
||||
- 敏感操作(踢人、删频道、改权限)→ RPC 验证
|
||||
|
||||
## 签名密钥管理
|
||||
|
||||
### 密钥窗口
|
||||
|
||||
```
|
||||
时间轴:
|
||||
─────────┬──────────┬──────────┬────────
|
||||
│ key A │ key B │ key C
|
||||
│ (过期) │ (活跃) │ (未来)
|
||||
└──────────┴──────────┴────────
|
||||
issued_at issued_at issued_at
|
||||
+3h +3h +3h
|
||||
```
|
||||
|
||||
- 每个签名密钥有效期 **3 小时**
|
||||
- 同一时刻可能有 **2 个有效密钥**(滚动窗口,平滑过渡)
|
||||
- JWT header 的 `kid` 字段标识使用哪个密钥签名
|
||||
|
||||
### 密钥轮换流程
|
||||
|
||||
```
|
||||
1. 当前密钥到达 3h → TokenService.rotate_if_needed()
|
||||
2. Redis 分布式锁 (core:token:rotation_lock, 10s TTL) 防止多实例竞争
|
||||
3. 旧密钥标记 active=false,仍保留在 Redis 用于验证旧 token
|
||||
4. 生成新密钥,active=true
|
||||
5. ArcSwap 原子替换当前签名密钥
|
||||
6. 旧密钥 TTL = 6h (2× window) 后从 Redis 自动清除
|
||||
```
|
||||
|
||||
### 密钥存储(Redis)
|
||||
|
||||
```
|
||||
core:token:active_key → kid (当前活跃密钥 ID)
|
||||
core:token:key:{kid} → SigningKey JSON (TTL = 6h)
|
||||
core:token:rotation_lock → "1" (TTL = 10s, 分布式锁)
|
||||
```
|
||||
|
||||
### SigningKey 结构
|
||||
|
||||
```rust
|
||||
pub struct SigningKeyInfo {
|
||||
pub kid: String, // UUIDv7
|
||||
pub algorithm: String, // "HS256"
|
||||
pub key_material: String, // base64(32 bytes random)
|
||||
pub issued_at: i64,
|
||||
pub expires_at: i64, // issued_at + 3h
|
||||
pub active: bool,
|
||||
}
|
||||
```
|
||||
|
||||
## 撤销机制
|
||||
|
||||
### Redis 布局
|
||||
|
||||
```
|
||||
core:token:revoked:{jti} → "1" (TTL = token 剩余有效期)
|
||||
core:token:refresh:{token} → user_id (TTL = 7d)
|
||||
```
|
||||
|
||||
### 撤销方式
|
||||
|
||||
| 操作 | RPC | 效果 |
|
||||
|--------------|------------------------|-----------------------|
|
||||
| 撤销单个 token | `RevokeToken(jti)` | 将 jti 加入撤销列表 |
|
||||
| 撤销用户所有 token | `RevokeToken(user_id)` | 删除该用户所有 refresh token |
|
||||
|
||||
### 撤销感知延迟
|
||||
|
||||
| 验证模式 | 延迟 |
|
||||
|-----------------------|------------------------------|
|
||||
| RPC (`VerifyToken`) | **实时** — 每次检查撤销列表 |
|
||||
| 本地 (`GetSigningKeys`) | **最多 3h** — 密钥过期前无法感知 jti 撤销 |
|
||||
|
||||
## appks 实现
|
||||
|
||||
### 模块结构
|
||||
|
||||
```
|
||||
service/internal_auth.rs → TokenService (业务逻辑)
|
||||
grpc/auth.rs → TokenGrpcService (gRPC handler)
|
||||
grpc/mod.rs → TokenServiceServer 注册到 tonic server
|
||||
api/internal/issue_api_key.rs → REST: POST /api/v1/internal/tokens
|
||||
```
|
||||
|
||||
### TokenService 核心
|
||||
|
||||
```rust
|
||||
pub struct TokenService {
|
||||
redis: AppRedis,
|
||||
current_key: Arc<ArcSwap<SigningKeyInfo>>, // 无锁读
|
||||
}
|
||||
```
|
||||
|
||||
- 启动时从 Redis 加载活跃密钥,无则生成
|
||||
- 签名使用 `jsonwebtoken` crate (HS256)
|
||||
- 密钥轮换使用 Redis 分布式锁,支持多实例部署
|
||||
- `ArcSwap` 保证签名密钥读取无锁、写入原子
|
||||
|
||||
## imks 实现指南
|
||||
|
||||
### 启动流程
|
||||
|
||||
```rust
|
||||
// 1. 连接 appks TokenService
|
||||
let mut token_client = TokenServiceClient::connect(appks_addr).await?;
|
||||
|
||||
// 2. 拉取签名密钥
|
||||
let resp = token_client.get_signing_keys(GetSigningKeysRequest { kid: "" }).await?;
|
||||
let keys = resp.keys;
|
||||
let next_rotation = resp.next_rotation_at;
|
||||
|
||||
// 3. 缓存密钥到本地 (HashMap<kid, SigningKey>)
|
||||
key_store.insert_all(keys);
|
||||
|
||||
// 4. 安排定时刷新
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
let delay = next_rotation - now();
|
||||
tokio::time::sleep(Duration::from_secs(delay as u64)).await;
|
||||
let resp = token_client.get_signing_keys(...).await;
|
||||
key_store.update(resp.keys);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### 连接时验证
|
||||
|
||||
```rust
|
||||
// 客户端建立 WebSocket/gRPC 连接时携带 JWT
|
||||
fn on_connect(headers: &Headers) -> Result<TokenClaims, AuthError> {
|
||||
let token = headers.get("Authorization")
|
||||
.and_then(|v| v.strip_prefix("Bearer "))
|
||||
.ok_or(AuthError::MissingToken)?;
|
||||
|
||||
// 本地验证 (快速路径)
|
||||
let header = decode_header(token)?;
|
||||
let kid = header.kid.ok_or(AuthError::MissingKid)?;
|
||||
let key = key_store.get(&kid).ok_or(AuthError::UnknownKey)?;
|
||||
|
||||
let mut validation = Validation::new(Algorithm::HS256);
|
||||
validation.set_issuer(&["appks"]);
|
||||
validation.validate_exp = true;
|
||||
|
||||
let data = decode::<TokenClaims>(token, &key, &validation)?;
|
||||
Ok(data.claims)
|
||||
}
|
||||
```
|
||||
|
||||
### 敏感操作验证
|
||||
|
||||
```rust
|
||||
// 敏感操作走 RPC 验证 (权威路径)
|
||||
async fn on_sensitive_action(token: &str) -> Result<TokenClaims, AuthError> {
|
||||
let resp = token_client.verify_token(VerifyTokenRequest {
|
||||
token: token.to_string(),
|
||||
}).await?;
|
||||
|
||||
if resp.valid {
|
||||
Ok(resp.claims.unwrap())
|
||||
} else {
|
||||
Err(AuthError::from(resp.reason))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 安全考虑
|
||||
|
||||
1. **密钥传输**:appks → imks 的 gRPC 连接应使用 mTLS,防止密钥在传输中被截获
|
||||
2. **密钥生命周期**:3h 窗口平衡了安全性和可用性;缩短窗口可减少撤销延迟但增加轮换频率
|
||||
3. **HS256 vs 非对称**:当前使用 HS256(对称密钥),imks 拿到的密钥可以伪造 token。如果 imks 不可完全信任,应改用 RS256/EdDSA,imks 只持有公钥
|
||||
4. **Refresh Token 安全**:每次刷新都旋转(旧 token 立即失效),防止重放
|
||||
5. **撤销列表 TTL**:与 token 剩余有效期对齐,过期 token 无需保留撤销记录
|
||||
|
||||
---
|
||||
|
||||
# IM 服务 Proto 说明书
|
||||
|
||||
以下是 imks 侧 `proto/core/` 下各 gRPC 服务的完整说明。所有 IM 服务定义在 `appks.im.v1` 包下,由 appks 提供 server 端,imks 消费 client 端。
|
||||
|
||||
## 服务总览
|
||||
|
||||
| Proto 文件 | 服务 | RPC 数量 | 职责 |
|
||||
|---|---|---|---|
|
||||
| `auth.proto` | TokenService | 5 | JWT 令牌生命周期 + 验证 + 密钥分发 |
|
||||
| `channel.proto` | ChannelService | 10 | 频道/分类 CRUD + 统计 |
|
||||
| `member.proto` | MemberService | 7 | 成员邀请/踢出/加入/离开/查询 |
|
||||
| `permission.proto` | PermissionService | 7 | 权限检查 + 覆盖规则 + 频道解析 |
|
||||
| `channel_settings.proto` | ChannelRoleService | 4 | 频道自定义角色 |
|
||||
| | ChannelInvitationService | 4 | 邀请生命周期 |
|
||||
| | ChannelWebhookService | 4 | Webhook CRUD |
|
||||
| | ChannelSlashCommandService | 4 | 斜杠命令注册 |
|
||||
| | ChannelRepoLinkService | 3 | 频道 ↔ 代码仓库关联 |
|
||||
| | ImIntegrationService | 4 | 外部平台集成(Slack/Discord 等) |
|
||||
| | CustomEmojiService | 3 | 工作区自定义表情 |
|
||||
| | ForumTagService | 4 | 论坛频道标签 |
|
||||
| | VoiceService | 2 | 语音频道参与者状态 |
|
||||
| | StageService | 4 | 舞台频道管理 |
|
||||
| | ChannelAuditService | 1 | 频道审计日志查询 |
|
||||
|
||||
---
|
||||
|
||||
## ChannelService(`channel.proto`)
|
||||
|
||||
频道和分类的 CRUD 管理,以及频道统计。
|
||||
|
||||
### 枚举
|
||||
|
||||
**ChannelType** — 频道类型:
|
||||
|
||||
| 值 | 含义 |
|
||||
|---|---|
|
||||
| `PUBLIC` | 公开频道,workspace 内所有人可见 |
|
||||
| `PRIVATE` | 私有频道,仅被邀请成员可见 |
|
||||
| `DIRECT` | 私聊(一对一) |
|
||||
| `GROUP` | 群聊(多人私聊) |
|
||||
| `REPO` | 仓库关联频道(自动与 git repo 绑定) |
|
||||
| `SYSTEM` | 系统频道(公告、通知等,只读) |
|
||||
|
||||
**ChannelKind** — 频道形态:
|
||||
|
||||
| 值 | 含义 |
|
||||
|---|---|
|
||||
| `TEXT` | 文本频道 |
|
||||
| `VOICE` | 语音频道 |
|
||||
| `STAGE` | 舞台频道(主持人+观众模式) |
|
||||
| `FORUM` | 论坛频道(帖子/主题式讨论) |
|
||||
| `ANNOUNCEMENT` | 公告频道(仅管理员可发消息) |
|
||||
|
||||
**Visibility** — 可见性级别(从低到高):
|
||||
|
||||
| 值 | 含义 |
|
||||
|---|---|
|
||||
| `PUBLIC` | 所有人可见(含未登录用户) |
|
||||
| `WORKSPACE` | workspace 成员可见 |
|
||||
| `INTERNAL` | 内部可见(组织成员) |
|
||||
| `PRIVATE` | 仅频道成员可见 |
|
||||
| `PROTECTED` | 受保护(不可被搜索/索引) |
|
||||
| `HIDDEN` | 隐藏(不显示在频道列表中) |
|
||||
| `SECRET` | 机密(仅通过直链访问) |
|
||||
|
||||
### RPC 列表
|
||||
|
||||
```
|
||||
GetChannel(channel_id) → Channel 获取频道详情
|
||||
ListChannels(workspace, ...) → [Channel], total 列出频道(支持分类/类型/形态过滤)
|
||||
CreateChannel(workspace, name) → Channel 创建频道
|
||||
UpdateChannel(channel_id, ...) → Channel 更新频道属性
|
||||
DeleteChannel(channel_id) → {} 删除频道
|
||||
GetChannelStats(channel_id) → ChannelStats 获取频道统计(成员/消息/线程/反应数)
|
||||
|
||||
ListCategories(workspace) → [ChannelCategory] 列出分类
|
||||
CreateCategory(workspace, name) → ChannelCategory 创建分类
|
||||
UpdateCategory(category_id, ...) → ChannelCategory 更新分类
|
||||
DeleteCategory(category_id) → {} 删除分类
|
||||
```
|
||||
|
||||
### 核心消息
|
||||
|
||||
**Channel** — 频道主体:
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|---|---|---|
|
||||
| `id` | UUID | 频道 ID |
|
||||
| `workspace_id` | UUID | 所属 workspace |
|
||||
| `category_id` | UUID? | 所属分类(可选) |
|
||||
| `parent_channel_id` | UUID? | 父频道(用于子频道/线程) |
|
||||
| `name` | string | 频道名称 |
|
||||
| `topic` / `description` | string? | 主题 / 描述 |
|
||||
| `channel_type` | ChannelType | 频道类型 |
|
||||
| `channel_kind` | ChannelKind | 频道形态 |
|
||||
| `visibility` | Visibility | 可见性 |
|
||||
| `position` | int32 | 排序位置 |
|
||||
| `nsfw` | bool | NSFW 标记 |
|
||||
| `read_only` | bool | 只读(仅管理员可发消息) |
|
||||
| `archived` | bool | 已归档 |
|
||||
| `rate_limit_per_user` | int32? | 慢速模式(秒/消息) |
|
||||
| `last_message_id` / `last_message_at` | — | 最后一条消息信息 |
|
||||
|
||||
**ChannelStats** — 频道统计:
|
||||
|
||||
| 字段 | 说明 |
|
||||
|---|---|
|
||||
| `members_count` | 成员数 |
|
||||
| `messages_count` | 消息数 |
|
||||
| `threads_count` | 线程数 |
|
||||
| `reactions_count` | 反应数 |
|
||||
| `mentions_count` | @提及数 |
|
||||
| `files_count` | 文件数 |
|
||||
| `last_activity_at` | 最后活跃时间 |
|
||||
|
||||
---
|
||||
|
||||
## MemberService(`member.proto`)
|
||||
|
||||
频道成员管理。
|
||||
|
||||
### 枚举
|
||||
|
||||
**Role** — 角色层级(从高到低):
|
||||
|
||||
| 值 | 含义 |
|
||||
|---|---|
|
||||
| `OWNER` | 频道所有者 |
|
||||
| `ADMIN` | 管理员(全部权限) |
|
||||
| `MAINTAINER` | 维护者(管理频道设置、成员) |
|
||||
| `MODERATOR` | 版主(管理消息、踢人) |
|
||||
| `MEMBER` | 普通成员 |
|
||||
| `CONTRIBUTOR` | 贡献者(可发消息,部分限制) |
|
||||
| `VIEWER` | 观察者(只读) |
|
||||
| `GUEST` | 访客(临时访问) |
|
||||
| `BOT` | 机器人 |
|
||||
|
||||
**MemberStatus** — 成员状态:
|
||||
|
||||
| 值 | 含义 |
|
||||
|---|---|
|
||||
| `ACTIVE` | 活跃成员 |
|
||||
| `INVITED` | 已邀请(尚未加入) |
|
||||
| `LEFT` | 已离开 |
|
||||
| `KICKED` | 被踢出 |
|
||||
| `BANNED` | 被封禁 |
|
||||
|
||||
### RPC 列表
|
||||
|
||||
```
|
||||
ListMembers(channel_id, ...) → [ChannelMember], total 列出成员(支持状态过滤)
|
||||
InviteMember(channel_id, user_id) → ChannelMember 邀请用户加入频道
|
||||
UpdateMember(channel_id, user_id) → ChannelMember 更新成员(角色/禁言/置顶)
|
||||
KickMember(channel_id, user_id) → {} 踢出成员
|
||||
JoinChannel(channel_id, user_id) → ChannelMember 用户主动加入
|
||||
LeaveChannel(channel_id, user_id) → {} 用户主动离开
|
||||
IsMember(channel_id, user_id) → is_member, role 检查是否为成员
|
||||
```
|
||||
|
||||
### 核心消息
|
||||
|
||||
**ChannelMember** — 频道成员:
|
||||
|
||||
| 字段 | 说明 |
|
||||
|---|---|
|
||||
| `channel_id` / `user_id` | 频道 + 用户 |
|
||||
| `role` | 角色(Role 枚举值字符串) |
|
||||
| `status` | 状态(MemberStatus 枚举值字符串) |
|
||||
| `muted` | 是否被禁言 |
|
||||
| `pinned` | 是否被置顶(频道侧标记) |
|
||||
| `last_read_message_id` / `last_read_at` | 已读进度 |
|
||||
| `joined_at` / `left_at` | 加入/离开时间 |
|
||||
|
||||
---
|
||||
|
||||
## PermissionService(`permission.proto`)
|
||||
|
||||
频道级权限系统,独立于 workspace/repo 的通用权限。
|
||||
|
||||
### 权限枚举(ImPermission)
|
||||
|
||||
| 权限 | 说明 |
|
||||
|---|---|
|
||||
| `READ_CHANNEL` | 查看频道 |
|
||||
| `SEND_MESSAGE` | 发送消息 |
|
||||
| `MANAGE_THREADS` | 管理线程 |
|
||||
| `MANAGE_REACTIONS` | 管理反应 |
|
||||
| `MANAGE_PINS` | 管理置顶消息 |
|
||||
| `INVITE_MEMBERS` | 邀请成员 |
|
||||
| `KICK_MEMBERS` | 踢出成员 |
|
||||
| `MANAGE_CHANNEL` | 管理频道设置 |
|
||||
| `MANAGE_ROLES` | 管理角色 |
|
||||
| `MANAGE_WEBHOOKS` | 管理 Webhook |
|
||||
| `MANAGE_EMOJIS` | 管理自定义表情 |
|
||||
| `VIEW_AUDIT_LOG` | 查看审计日志 |
|
||||
| `MANAGE_INTEGRATIONS` | 管理外部集成 |
|
||||
| `SEND_TTS` | 发送 TTS 消息 |
|
||||
| `USE_SLASH_COMMANDS` | 使用斜杠命令 |
|
||||
| `ATTACH_FILES` | 上传文件 |
|
||||
| `MENTION_EVERYONE` | @所有人 |
|
||||
| `MANAGE_MESSAGES` | 管理消息(删除他人消息) |
|
||||
| `ADMIN` | 管理员(拥有所有权限) |
|
||||
|
||||
### RPC 列表
|
||||
|
||||
```
|
||||
CheckPermission(channel, user, perm) → allowed, role 检查单项权限
|
||||
GetPermissions(channel, user) → [ImPermission] 获取用户全部权限
|
||||
SetPermissionOverwrite(channel, target) → Overwrite 设置权限覆盖
|
||||
GetPermissionOverwrites(channel) → [Overwrite] 获取覆盖列表
|
||||
DeletePermissionOverwrite(channel, target) → {} 删除覆盖
|
||||
|
||||
ResolveChannel(channel_id) → 频道摘要信息 解析频道元数据
|
||||
EnsureReadable(channel, user) → allowed 确保用户可读(快速检查)
|
||||
```
|
||||
|
||||
### 权限覆盖(PermissionOverwrite)
|
||||
|
||||
权限覆盖允许对特定用户/角色在特定频道上覆盖默认权限:
|
||||
|
||||
| 字段 | 说明 |
|
||||
|---|---|
|
||||
| `target_type` | `"user"` 或 `"role"` |
|
||||
| `target_id` | 用户 ID 或角色 ID |
|
||||
| `allow` | 显式允许的权限列表 |
|
||||
| `deny` | 显式拒绝的权限列表 |
|
||||
|
||||
权限解析优先级:`deny 覆盖 > allow 覆盖 > 角色权限`
|
||||
|
||||
---
|
||||
|
||||
## ChannelSettings 服务组(`channel_settings.proto`)
|
||||
|
||||
所有频道配置相关的服务定义在同一个 proto 文件中。
|
||||
|
||||
### ChannelRoleService — 频道自定义角色
|
||||
|
||||
频道级别的自定义角色(不同于 `member.proto` 中的全局 Role 枚举)。
|
||||
|
||||
```
|
||||
ListChannelRoles(channel_id) → [ChannelRole]
|
||||
CreateChannelRole(channel, name) → ChannelRole
|
||||
UpdateChannelRole(role_id, ...) → ChannelRole
|
||||
DeleteChannelRole(role_id) → {}
|
||||
```
|
||||
|
||||
**ChannelRole** 字段:`name`, `permissions[]`(ImPermission 字符串列表), `assignable`(是否可被普通成员分配)
|
||||
|
||||
### ChannelInvitationService — 邀请管理
|
||||
|
||||
```
|
||||
ListInvitations(channel_id) → [ChannelInvitation]
|
||||
CreateInvitation(channel, user) → ChannelInvitation
|
||||
AcceptInvitation(invitation_id) → ChannelInvitation
|
||||
RevokeInvitation(invitation_id) → {}
|
||||
```
|
||||
|
||||
**ChannelInvitation** 字段:`invited_by`, `invited_user_id`, `role`(预设角色), `status`
|
||||
|
||||
### ChannelWebhookService — Webhook 管理
|
||||
|
||||
```
|
||||
ListWebhooks(channel_id) → [ChannelWebhook]
|
||||
CreateWebhook(channel, name, url) → ChannelWebhook
|
||||
UpdateWebhook(webhook_id, ...) → ChannelWebhook
|
||||
DeleteWebhook(webhook_id) → {}
|
||||
```
|
||||
|
||||
**ChannelWebhook** 字段:`name`, `url`, `secret`(签名验证用), `events[]`(订阅事件列表), `active`
|
||||
|
||||
### ChannelSlashCommandService — 斜杠命令注册
|
||||
|
||||
```
|
||||
ListSlashCommands(channel_id) → [ChannelSlashCommand]
|
||||
CreateSlashCommand(channel, cmd, url) → ChannelSlashCommand
|
||||
UpdateSlashCommand(command_id, ...) → ChannelSlashCommand
|
||||
DeleteSlashCommand(command_id) → {}
|
||||
```
|
||||
|
||||
**ChannelSlashCommand** 字段:`command`(命令名如 `/deploy`), `description`, `request_url`(回调地址), `scopes[]`
|
||||
|
||||
### ChannelRepoLinkService — 仓库关联
|
||||
|
||||
将频道与代码仓库关联,自动推送仓库事件到频道。
|
||||
|
||||
```
|
||||
ListRepoLinks(channel_id) → [ChannelRepoLink]
|
||||
CreateRepoLink(channel, repo, type) → ChannelRepoLink
|
||||
DeleteRepoLink(link_id) → {}
|
||||
```
|
||||
|
||||
**ChannelRepoLink** 字段:`repo_id`, `link_type`, `events[]`(订阅的仓库事件:push、pr、issue 等)
|
||||
|
||||
### ImIntegrationService — 外部平台集成
|
||||
|
||||
与 Slack、Discord 等外部平台的消息同步。
|
||||
|
||||
```
|
||||
ListIntegrations(channel_id) → [ImIntegration]
|
||||
CreateIntegration(channel, provider, ...)→ ImIntegration
|
||||
UpdateIntegration(integration_id, ...) → ImIntegration
|
||||
DeleteIntegration(integration_id) → {}
|
||||
```
|
||||
|
||||
**ImIntegration** 字段:`provider`(平台名), `external_channel_id`(外部频道 ID), `sync_direction`(`inbound`/`outbound`/`bidirectional`), `active`
|
||||
|
||||
### CustomEmojiService — 自定义表情
|
||||
|
||||
工作区级别的自定义表情管理。
|
||||
|
||||
```
|
||||
ListCustomEmojis(workspace_id) → [CustomEmoji]
|
||||
CreateCustomEmoji(workspace, name, url) → CustomEmoji
|
||||
DeleteCustomEmoji(emoji_id) → {}
|
||||
```
|
||||
|
||||
**CustomEmoji** 字段:`workspace_id`, `name`(表情名如 `:appks:`), `image_url`
|
||||
|
||||
### ForumTagService — 论坛标签
|
||||
|
||||
论坛频道(`ChannelKind::FORUM`)的帖子分类标签。
|
||||
|
||||
```
|
||||
ListForumTags(channel_id) → [ForumTag]
|
||||
CreateForumTag(channel, name, ...) → ForumTag
|
||||
UpdateForumTag(tag_id, ...) → ForumTag
|
||||
DeleteForumTag(tag_id) → {}
|
||||
```
|
||||
|
||||
**ForumTag** 字段:`name`, `moderated`(是否需要管理员审核), `position`
|
||||
|
||||
### VoiceService — 语音频道
|
||||
|
||||
语音频道的参与者状态管理。
|
||||
|
||||
```
|
||||
ListVoiceParticipants(channel_id) → [VoiceParticipant]
|
||||
UpdateVoiceState(channel, user, ...) → VoiceParticipant
|
||||
```
|
||||
|
||||
**VoiceParticipant** 字段:`user_id`, `muted`(静音), `deafened`(屏蔽音频), `joined_at`
|
||||
|
||||
### StageService — 舞台频道
|
||||
|
||||
舞台频道(`ChannelKind::STAGE`)的管理。主持人说话,观众收听。
|
||||
|
||||
```
|
||||
GetStage(channel_id) → Stage
|
||||
CreateStage(channel, topic, ...) → Stage
|
||||
UpdateStage(stage_id, ...) → Stage
|
||||
DeleteStage(stage_id) → {}
|
||||
```
|
||||
|
||||
**Stage** 字段:`topic`(当前话题), `privacy_level`, `discoverable`(是否可被发现), `started_at` / `ended_at`
|
||||
|
||||
### ChannelAuditService — 审计日志
|
||||
|
||||
频道操作审计日志查询(只读)。
|
||||
|
||||
```
|
||||
ListChannelEvents(channel_id, ...) → [ChannelAuditEvent], total
|
||||
```
|
||||
|
||||
**ChannelAuditEvent** 字段:`actor_id`(操作者), `event_type`(事件类型字符串), `target_type` / `target_id`(操作对象), `old_value` / `new_value`(变更前后值)
|
||||
|
||||
---
|
||||
|
||||
## imks 与 appks 的调用关系
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────────────┐
|
||||
│ imks │
|
||||
│ │
|
||||
│ Socket.IO / WebSocket / WebTransport │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ 连接握手 ──→ TokenService.VerifyToken() 或 本地密钥验证 │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ 消息收发 ──→ ChannelService + MemberService │
|
||||
│ │ PermissionService.EnsureReadable() │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ 频道管理 ──→ ChannelService CRUD │
|
||||
│ │ ChannelRoleService │
|
||||
│ │ ChannelInvitationService │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ 语音/舞台 ──→ VoiceService + StageService │
|
||||
│ │
|
||||
│ 集成/扩展 ──→ WebhookService + SlashCommandService │
|
||||
│ RepoLinkService + ImIntegrationService │
|
||||
│ │
|
||||
│ 审计查询 ──→ ChannelAuditService │
|
||||
└────────────────────────┬───────────────────────────────────┘
|
||||
│ gRPC
|
||||
▼
|
||||
┌────────────────────────────────────────────────────────────┐
|
||||
│ appks │
|
||||
│ TokenService server │ Channel/Member/Permission server │
|
||||
│ Redis (JWT keys) │ Postgres (channel data) │
|
||||
└────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### imks 本地缓存建议
|
||||
|
||||
| 数据 | 缓存策略 | 刷新时机 |
|
||||
|--------------------------|--------------|--------------------------|
|
||||
| 签名密钥 (`SigningKey[]`) | 内存 HashMap | `next_rotation_at` 到达时拉取 |
|
||||
| 频道信息 (`Channel`) | LRU + TTL | 频道更新事件 (NATS) |
|
||||
| 成员列表 (`ChannelMember[]`) | LRU + TTL | 成员变更事件 (NATS) |
|
||||
| 权限缓存 | 短期 TTL (30s) | 权限变更事件 (NATS) |
|
||||
| 自定义表情 | 全量加载 + 事件增量 | emoji 增删事件 (NATS) |
|
||||
Reference in New Issue
Block a user