mirror of
https://github.com/memohai/Memoh.git
synced 2026-04-27 07:16:19 +09:00
ca5c6a1866
- Rename chat module to conversation with flow-based architecture - Move channelidentities into channel/identities subpackage - Add channel/route for routing logic - Add message service with event hub - Add MCP providers: container, directory, schedule - Refactor Feishu/Telegram adapters with directory and stream support - Add platform management page and channel badges in web UI - Update database schema for conversations, messages and channel routes - Add @memoh/shared package for cross-package type definitions
109 lines
2.7 KiB
Go
109 lines
2.7 KiB
Go
package preauth
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/jackc/pgx/v5"
|
|
"github.com/jackc/pgx/v5/pgtype"
|
|
|
|
"github.com/memohai/memoh/internal/db"
|
|
"github.com/memohai/memoh/internal/db/sqlc"
|
|
)
|
|
|
|
var ErrKeyNotFound = errors.New("preauth key not found")
|
|
|
|
type Service struct {
|
|
queries *sqlc.Queries
|
|
}
|
|
|
|
func NewService(queries *sqlc.Queries) *Service {
|
|
return &Service{queries: queries}
|
|
}
|
|
|
|
// Issue creates a new preauth key for the given bot.
|
|
func (s *Service) Issue(ctx context.Context, botID, issuedByUserID string, ttl time.Duration) (Key, error) {
|
|
if s.queries == nil {
|
|
return Key{}, fmt.Errorf("preauth queries not configured")
|
|
}
|
|
if ttl <= 0 {
|
|
ttl = 24 * time.Hour
|
|
}
|
|
pgBotID, err := db.ParseUUID(botID)
|
|
if err != nil {
|
|
return Key{}, err
|
|
}
|
|
pgIssuedBy := pgtype.UUID{Valid: false}
|
|
if strings.TrimSpace(issuedByUserID) != "" {
|
|
parsed, err := db.ParseUUID(issuedByUserID)
|
|
if err != nil {
|
|
return Key{}, err
|
|
}
|
|
pgIssuedBy = parsed
|
|
}
|
|
token := strings.ReplaceAll(uuid.NewString(), "-", "")[:8]
|
|
expiresAt := time.Now().UTC().Add(ttl)
|
|
row, err := s.queries.CreateBotPreauthKey(ctx, sqlc.CreateBotPreauthKeyParams{
|
|
BotID: pgBotID,
|
|
Token: token,
|
|
IssuedByUserID: pgIssuedBy,
|
|
ExpiresAt: pgtype.Timestamptz{Time: expiresAt, Valid: true},
|
|
})
|
|
if err != nil {
|
|
return Key{}, err
|
|
}
|
|
return normalizeKey(row), nil
|
|
}
|
|
|
|
func (s *Service) Get(ctx context.Context, token string) (Key, error) {
|
|
if s.queries == nil {
|
|
return Key{}, fmt.Errorf("preauth queries not configured")
|
|
}
|
|
row, err := s.queries.GetBotPreauthKey(ctx, strings.TrimSpace(token))
|
|
if err != nil {
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
return Key{}, ErrKeyNotFound
|
|
}
|
|
return Key{}, err
|
|
}
|
|
return normalizeKey(row), nil
|
|
}
|
|
|
|
func (s *Service) MarkUsed(ctx context.Context, id string) (Key, error) {
|
|
if s.queries == nil {
|
|
return Key{}, fmt.Errorf("preauth queries not configured")
|
|
}
|
|
pgID, err := db.ParseUUID(id)
|
|
if err != nil {
|
|
return Key{}, err
|
|
}
|
|
row, err := s.queries.MarkBotPreauthKeyUsed(ctx, pgID)
|
|
if err != nil {
|
|
return Key{}, err
|
|
}
|
|
return normalizeKey(row), nil
|
|
}
|
|
|
|
func normalizeKey(row sqlc.BotPreauthKey) Key {
|
|
return Key{
|
|
ID: row.ID.String(),
|
|
BotID: row.BotID.String(),
|
|
Token: strings.TrimSpace(row.Token),
|
|
IssuedByChannelIdentityID: row.IssuedByUserID.String(),
|
|
ExpiresAt: timeFromPg(row.ExpiresAt),
|
|
UsedAt: timeFromPg(row.UsedAt),
|
|
CreatedAt: timeFromPg(row.CreatedAt),
|
|
}
|
|
}
|
|
|
|
func timeFromPg(value pgtype.Timestamptz) time.Time {
|
|
if value.Valid {
|
|
return value.Time
|
|
}
|
|
return time.Time{}
|
|
}
|