mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-20 02:17:24 +00:00
- Add RoleOwner ("owner") to permissions hierarchy (above admin)
- Replace all IsCrossTenant/client.IsCrossTenant() permission guards with
IsOwnerRole/client.IsOwner() role-based checks (20+ locations)
- Remove cross-tenant bypass from tenantClauseN SQL helper — always adds
AND tenant_id = $N (fail-closed)
- Add explicit unscoped store methods for startup: ListAllProviders,
ListAllInstances, ListAllEnabled, GetByIDUnscoped, GetTeamUnscoped
- Remove CrossTenant from auth layer: owner users get role "owner" with
concrete tenant ID (MasterTenantID fallback), no bypass flag
- Remove crossTenant field from WS Client; connect response sends
is_owner instead of cross_tenant
- Simplify event filter from 3-mode to 2-mode (tenant-scoped + owner sees system events)
- Fix channel instance loader to use inst.TenantID for agent lookup
- Fix agent embedding goroutine to capture tenant from caller context
- Fix provider cache invalidation to use event.TenantID
- Separate skills store: ListAllSystemSkills (system only) vs
ListAllSkills (system + tenant-scoped), StoreMissingDeps restricted
to system skills only
- Frontend: isCrossTenant → isOwner, add "owner" to UserRole type,
RequireAdmin uses hasMinRole for owner > admin hierarchy
60 lines
2.1 KiB
Go
60 lines
2.1 KiB
Go
package store
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"strings"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
// ChannelInstanceData represents a channel instance in the database.
|
|
type ChannelInstanceData struct {
|
|
BaseModel
|
|
TenantID uuid.UUID `json:"tenant_id,omitempty"`
|
|
Name string `json:"name"`
|
|
DisplayName string `json:"display_name"`
|
|
ChannelType string `json:"channel_type"`
|
|
AgentID uuid.UUID `json:"agent_id"`
|
|
Credentials []byte `json:"-"` // encrypted, never serialized to API
|
|
Config json.RawMessage `json:"config"`
|
|
Enabled bool `json:"enabled"`
|
|
CreatedBy string `json:"created_by"`
|
|
}
|
|
|
|
// IsDefaultChannelInstance returns true if the instance name matches a default/seeded channel.
|
|
// Default instances use either the bare channel type ("telegram") or "{channelType}/default".
|
|
func IsDefaultChannelInstance(name string) bool {
|
|
if strings.HasSuffix(name, "/default") {
|
|
return true
|
|
}
|
|
// Legacy Telegram default uses bare name "telegram"
|
|
switch name {
|
|
case "telegram", "discord", "feishu", "zalo_oa", "whatsapp":
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// ChannelInstanceListOpts configures channel instance listing with optional pagination and filtering.
|
|
type ChannelInstanceListOpts struct {
|
|
Search string
|
|
Limit int
|
|
Offset int
|
|
}
|
|
|
|
// ChannelInstanceStore manages channel instance definitions.
|
|
type ChannelInstanceStore interface {
|
|
Create(ctx context.Context, inst *ChannelInstanceData) error
|
|
Get(ctx context.Context, id uuid.UUID) (*ChannelInstanceData, error)
|
|
GetByName(ctx context.Context, name string) (*ChannelInstanceData, error)
|
|
Update(ctx context.Context, id uuid.UUID, updates map[string]any) error
|
|
Delete(ctx context.Context, id uuid.UUID) error
|
|
ListEnabled(ctx context.Context) ([]ChannelInstanceData, error)
|
|
ListAll(ctx context.Context) ([]ChannelInstanceData, error)
|
|
ListAllInstances(ctx context.Context) ([]ChannelInstanceData, error)
|
|
ListAllEnabled(ctx context.Context) ([]ChannelInstanceData, error)
|
|
ListPaged(ctx context.Context, opts ChannelInstanceListOpts) ([]ChannelInstanceData, error)
|
|
CountInstances(ctx context.Context, opts ChannelInstanceListOpts) (int, error)
|
|
}
|