mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-23 17:26:43 +02:00
Replace 21 separate NestJS Matrix bot processes (~2.1 GB RAM, ~4.2 GB Docker images) with a single Go binary using plugin architecture (8.6 MB binary, ~30 MB RAM). New services: - services/mana-matrix-bot/ — Go Matrix bot with 21 plugins (mautrix-go, Redis sessions) - services/mana-api-gateway-go/ — Go API gateway (rate limiting, API keys, credit billing) Deleted: - 21 services/matrix-*-bot/ directories - packages/bot-services/ and packages/matrix-bot-common/ - Legacy deploy scripts and CI build jobs Updated: - docker-compose.macmini.yml: new Go services, legacy bots removed - CI/CD: change detection + build jobs for Go services - Root package.json: new dev:matrix, build:matrix, test:matrix scripts Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
62 lines
1.8 KiB
Go
62 lines
1.8 KiB
Go
package services
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log/slog"
|
|
)
|
|
|
|
// CreditClient handles credit balance and consumption via mana-core-auth.
|
|
type CreditClient struct {
|
|
backend *BackendClient
|
|
}
|
|
|
|
// CreditBalance holds a user's credit balance.
|
|
type CreditBalance struct {
|
|
Balance float64 `json:"balance"`
|
|
Used float64 `json:"used"`
|
|
}
|
|
|
|
// NewCreditClient creates a new credit service client.
|
|
func NewCreditClient(authURL, serviceKey string) *CreditClient {
|
|
client := NewBackendClient(authURL)
|
|
// Service key is used for internal API calls
|
|
_ = serviceKey
|
|
return &CreditClient{backend: client}
|
|
}
|
|
|
|
// GetBalance returns the user's current credit balance.
|
|
func (c *CreditClient) GetBalance(ctx context.Context, token string) (*CreditBalance, error) {
|
|
var balance CreditBalance
|
|
if err := c.backend.Get(ctx, "/api/v1/credits/balance", token, &balance); err != nil {
|
|
return nil, fmt.Errorf("get balance: %w", err)
|
|
}
|
|
return &balance, nil
|
|
}
|
|
|
|
// Consume deducts credits for an operation.
|
|
func (c *CreditClient) Consume(ctx context.Context, token string, amount float64, description string) error {
|
|
body := map[string]any{
|
|
"amount": amount,
|
|
"description": description,
|
|
}
|
|
if err := c.backend.Post(ctx, "/api/v1/credits/use", token, body, nil); err != nil {
|
|
return fmt.Errorf("consume credits: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// HasEnough checks if the user has enough credits for an operation.
|
|
func (c *CreditClient) HasEnough(ctx context.Context, token string, required float64) (bool, float64) {
|
|
balance, err := c.GetBalance(ctx, token)
|
|
if err != nil {
|
|
slog.Debug("credit check failed", "error", err)
|
|
return true, 0 // Allow on error (fail open)
|
|
}
|
|
return balance.Balance >= required, balance.Balance
|
|
}
|
|
|
|
// FormatBalance returns a formatted credit balance string.
|
|
func FormatBalance(balance float64) string {
|
|
return fmt.Sprintf("%.2f", balance)
|
|
}
|