feat: 增加 SafeW 已监控群列表接口
通过 getUpdates 将群写入 Redis,供创建/编辑渠道时选择 chat_id,避免前端重复传递 token。
This commit is contained in:
Vendored
+94
@@ -0,0 +1,94 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"aiaa-notification-service/internal/adapter"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
func TokenHash(token string) string {
|
||||
sum := sha256.Sum256([]byte(token))
|
||||
return hex.EncodeToString(sum[:])
|
||||
}
|
||||
|
||||
func safewChatsKey(hash string) string { return "safew:chats:" + hash }
|
||||
func safewOffsetKey(hash string) string { return "safew:offset:" + hash }
|
||||
func safewPollKey(hash string) string { return "safew:poll:" + hash }
|
||||
|
||||
func (c *Cache) MergeSafewChats(ctx context.Context, token string, chats []adapter.SafewChat) error {
|
||||
if c == nil || c.rdb == nil {
|
||||
return fmt.Errorf("redis unavailable")
|
||||
}
|
||||
if len(chats) == 0 {
|
||||
return nil
|
||||
}
|
||||
hash := TokenHash(token)
|
||||
vals := make([]any, 0, len(chats)*2)
|
||||
for _, ch := range chats {
|
||||
b, err := json.Marshal(ch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
vals = append(vals, ch.ID, b)
|
||||
}
|
||||
return c.rdb.HSet(ctx, safewChatsKey(hash), vals...).Err()
|
||||
}
|
||||
|
||||
func (c *Cache) ListSafewChats(ctx context.Context, token string) ([]adapter.SafewChat, error) {
|
||||
if c == nil || c.rdb == nil {
|
||||
return nil, fmt.Errorf("redis unavailable")
|
||||
}
|
||||
m, err := c.rdb.HGetAll(ctx, safewChatsKey(TokenHash(token))).Result()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]adapter.SafewChat, 0, len(m))
|
||||
for _, raw := range m {
|
||||
var ch adapter.SafewChat
|
||||
if err := json.Unmarshal([]byte(raw), &ch); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, ch)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *Cache) GetSafewOffset(ctx context.Context, token string) (int64, error) {
|
||||
if c == nil || c.rdb == nil {
|
||||
return 0, fmt.Errorf("redis unavailable")
|
||||
}
|
||||
n, err := c.rdb.Get(ctx, safewOffsetKey(TokenHash(token))).Int64()
|
||||
if err == redis.Nil {
|
||||
return 0, nil
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (c *Cache) SetSafewOffset(ctx context.Context, token string, offset int64) error {
|
||||
if c == nil || c.rdb == nil {
|
||||
return fmt.Errorf("redis unavailable")
|
||||
}
|
||||
return c.rdb.Set(ctx, safewOffsetKey(TokenHash(token)), offset, 0).Err()
|
||||
}
|
||||
|
||||
func (c *Cache) TrySafewPollLock(ctx context.Context, token string, ttl time.Duration) (bool, error) {
|
||||
if c == nil || c.rdb == nil {
|
||||
return false, fmt.Errorf("redis unavailable")
|
||||
}
|
||||
ok, err := c.rdb.SetNX(ctx, safewPollKey(TokenHash(token)), "1", ttl).Result()
|
||||
return ok, err
|
||||
}
|
||||
|
||||
func (c *Cache) UnlockSafewPoll(ctx context.Context, token string) error {
|
||||
if c == nil || c.rdb == nil {
|
||||
return nil
|
||||
}
|
||||
return c.rdb.Del(ctx, safewPollKey(TokenHash(token))).Err()
|
||||
}
|
||||
Vendored
+22
@@ -0,0 +1,22 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTokenHashStableAndNotPlainToken(t *testing.T) {
|
||||
h := TokenHash("secret-token")
|
||||
if h == "secret-token" || h == "" {
|
||||
t.Fatalf("hash=%q", h)
|
||||
}
|
||||
if _, err := hex.DecodeString(h); err != nil {
|
||||
t.Fatalf("not hex: %v", err)
|
||||
}
|
||||
if TokenHash("secret-token") != h {
|
||||
t.Fatal("not stable")
|
||||
}
|
||||
if TokenHash("other") == h {
|
||||
t.Fatal("collision")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user