syncGroupForUser 同步指定用户在指定组的 inbound 记录: 先删除旧的组记录,再按 inboundIDs 重建,返回受影响的 nodeIDs。 若用户已通过其他途径持有相同 inbound,复用其凭据保持密码不变。
(groupID, userID string, inboundIDs []string)
| 334 | // 先删除旧的组记录,再按 inboundIDs 重建,返回受影响的 nodeIDs。 |
| 335 | // 若用户已通过其他途径持有相同 inbound,复用其凭据保持密码不变。 |
| 336 | func (a *userGroupAPI) syncGroupForUser(groupID, userID string, inboundIDs []string) ([]string, error) { |
| 337 | // 先获取现有 user_inbounds,用于复用凭据(保证切换来源时密码不变) |
| 338 | existing, _ := a.userStore.ListUserInboundsByUser(userID) |
| 339 | credsByInbound := make(map[string]users.UserInbound, len(existing)) |
| 340 | for _, acc := range existing { |
| 341 | if _, ok := credsByInbound[acc.InboundID]; !ok { |
| 342 | credsByInbound[acc.InboundID] = acc // 优先保留先找到的(直接分配排在前) |
| 343 | } |
| 344 | } |
| 345 | |
| 346 | // 删除旧的组记录 |
| 347 | if err := a.userStore.DeleteGroupUserInbounds(userID, groupID); err != nil { |
| 348 | return nil, err |
| 349 | } |
| 350 | affectedNodes := make(map[string]struct{}) |
| 351 | for _, ibID := range inboundIDs { |
| 352 | ib, err := a.ibStore.GetInbound(ibID) |
| 353 | if err != nil { |
| 354 | continue // inbound 不存在则跳过 |
| 355 | } |
| 356 | uuid := randomUUID() |
| 357 | secret := generateGroupSecret(ib.Protocol, ib.Method) |
| 358 | if prev, ok := credsByInbound[ibID]; ok { |
| 359 | uuid = prev.UUID |
| 360 | secret = prev.Secret |
| 361 | } |
| 362 | acc := users.UserInbound{ |
| 363 | ID: idgen.NextString(), |
| 364 | UserID: userID, |
| 365 | InboundID: ibID, |
| 366 | NodeID: ib.NodeID, |
| 367 | UUID: uuid, |
| 368 | Secret: secret, |
| 369 | GroupID: groupID, |
| 370 | CreatedAt: time.Now().UTC(), |
| 371 | } |
| 372 | if _, err := a.userStore.UpsertGroupUserInbound(acc); err != nil { |
| 373 | return nil, err |
| 374 | } |
| 375 | affectedNodes[ib.NodeID] = struct{}{} |
| 376 | } |
| 377 | out := make([]string, 0, len(affectedNodes)) |
| 378 | for nid := range affectedNodes { |
| 379 | out = append(out, nid) |
| 380 | } |
| 381 | return out, nil |
| 382 | } |
| 383 | |
| 384 | // generateGroupSecret 根据协议和方法生成适当的密钥。 |
| 385 | func generateGroupSecret(protocol, method string) string { |
no test coverage detected