entity

package
v0.5.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 2, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Index

Constants

View Source
const (
	VisibilityPublic  = pkgentity.VisibilityPublic
	VisibilityPrivate = pkgentity.VisibilityPrivate
)
View Source
const (
	HomeViewCompact  = "compact"
	HomeViewDetailed = "detailed"
)
View Source
const SSOProviderGoogle = "google"

SSOProviderGoogle is the provider key for Google OAuth.

Variables

View Source
var StructToConfigs = pkgentity.StructToConfigs

StructToConfigs is re-exported from pkg/entity — see that package for the tag grammar and reflection rules.

Functions

This section is empty.

Types

type Bookmark

type Bookmark struct {
	UserID    string `gorm:"primaryKey;type:varchar(36)"`
	ToolPath  string `gorm:"primaryKey;type:varchar(255)"`
	CreatedAt time.Time
}

Bookmark marks a tool as a favorite for a specific user. Bookmarked tools appear in a dedicated "Bookmarks" group on the home page in addition to any group/category they already belong to.

type Config

type Config = pkgentity.Config

Config is re-exported from pkg/entity so existing internal callers keep working. Module authors should import pkg/entity directly.

type Connector added in v0.4.0

type Connector struct {
	ID        string `gorm:"type:varchar(36);primaryKey"`
	Key       string `gorm:"type:varchar(100);index;not null"`
	Label     string `gorm:"type:varchar(255);not null"`
	Configs   string `gorm:"type:text"`
	Disabled  bool   `gorm:"default:false"`
	CreatedBy string `gorm:"type:varchar(36)"`
	CreatedAt time.Time
	UpdatedAt time.Time
}

Connector is one row per running connector — the runtime pairing of a code-registered connector definition (identified by Key) with a credential set, label, and creator.

Connector definitions live in code (see pkg/connector and the internal/connectors registry); this entity is what the admin UI reads, writes, and duplicates. MCP exposes one tool per Connector row per enabled operation.

Key references the code definition's slug (e.g. "loki", "github") — it is NOT unique on this table. Multiple Connector rows share the same Key when admins duplicate a definition into multiple instances (Loki Prod, Loki Staging, Loki Dev). Uniqueness lives on ID; the admin UI distinguishes siblings by Label.

Code-registered Modules survive deletion: when bootstrap runs and finds zero rows for a registered Key, it auto-creates a fresh row (empty Configs, Label = Meta.Name). Admins who delete every row for a connector therefore get an empty-but-working row back on restart; duplicates and edits to existing rows are untouched.

Configs holds the credential / endpoint values as a JSON-encoded map[string]string keyed by the field names declared on the connector's Creds struct. Secret-marked fields are stored plaintext (matching the wick `configs` table convention) and masked in the UI render layer; if at-rest encryption becomes a requirement it applies to both this column and the legacy configs table together. The jsonb shape is preferred to a row-per-field table because cred sets are tiny, always read together, and never need cross-instance queries.

Disabled hides the row from MCP tools/list and the admin UI list view (admins can re-enable from the manager). The tag-filter system (the existing ToolTag table, addressed by path "/connectors/{id}", joined against UserTag) gates which authenticated users see this row at all — Disabled is the orthogonal "off switch" for the whole row.

Tag association reuses ToolTag (with ToolPath = "/connectors/{id}") rather than introducing a connector-specific link table; jobs do the same with "/jobs/{path}". A future rename of ToolTag/SetToolTags into a generic entity-tag API is tracked separately.

func (*Connector) BeforeCreate added in v0.4.0

func (c *Connector) BeforeCreate(tx *gorm.DB) error

type ConnectorOperation added in v0.4.0

type ConnectorOperation struct {
	ConnectorID  string `gorm:"primaryKey;type:varchar(36)"`
	OperationKey string `gorm:"primaryKey;type:varchar(100)"`
	Enabled      bool   `gorm:"default:true"`
	UpdatedAt    time.Time
}

ConnectorOperation stores the enable state of one operation on one connector row. Operations are declared in code (Module.Operations); this table records whether the admin opted them in (or out) for a specific connector row.

Default rule applied when a connector row is created:

  • Operation.Destructive == false → Enabled = true
  • Operation.Destructive == true → Enabled = false (admin opt-in)

Rows for ops the admin has not touched yet may be missing; readers fall back to the default rule above when no row exists. Toggling in the UI inserts or updates a row.

type ConnectorRun added in v0.4.0

type ConnectorRun struct {
	ID           string             `gorm:"type:varchar(36);primaryKey"`
	ConnectorID  string             `gorm:"type:varchar(36);not null;index:idx_run_connector_started,priority:1"`
	OperationKey string             `gorm:"type:varchar(100);not null"`
	UserID       string             `gorm:"type:varchar(36);index:idx_run_user_started,priority:1"`
	Source       ConnectorRunSource `gorm:"type:varchar(20);not null"`
	RequestJSON  string             `gorm:"type:text"`
	ResponseJSON string             `gorm:"type:text"`
	Status       ConnectorRunStatus `gorm:"type:varchar(20);not null;index:idx_run_status_started,priority:1"`
	ErrorMsg     string             `gorm:"type:text"`
	LatencyMs    int
	HTTPStatus   int
	IPAddress    string    `gorm:"type:varchar(45);index:idx_run_ip_started,priority:1"`
	UserAgent    string    `gorm:"type:varchar(512)"`
	ParentRunID  *string   `gorm:"type:varchar(36);index"`
	StartedAt    time.Time `` /* 218-byte string literal not displayed */
	EndedAt      *time.Time
	CreatedAt    time.Time
}

ConnectorRun records one execution of one operation on one connector row. Written once per MCP tools/call, panel-test click, or retry, so admins can audit traffic, debug failures, and replay buggy calls.

RequestJSON stores the input arguments the caller (LLM or admin) passed in. Credentials are NOT in this column — they live on the Connector row itself, joined at exec time. Replaying a run rebuilds the call from RequestJSON + the current Connector.Configs, so a retry honors any cred edits the admin has made since the original.

ResponseJSON is the JSON-marshaled return value of ExecuteFunc. Large responses may be truncated by the writer (defense in depth); readers should treat the value as opaque.

IPAddress and UserAgent capture the calling client's network identity at the time of the run. These are recorded for security observability — feeding a future allowlist/blocklist surface — and for incident triage. They are best-effort: behind a proxy the IP is whatever X-Forwarded-For policy the deploy resolves to, and a PAT-using script may not send a recognizable UA at all.

ParentRunID is non-nil only when Source == ConnectorRunSourceRetry, pointing to the run this one re-played. There is no FK constraint — the parent may be deleted by retention, leaving the lineage dangling (the UI tolerates the gap).

Retention: rows older than the configured retention window are purged by a scheduled cleanup job (default 7 days). The single-column index on StartedAt keeps the purge query cheap.

Index strategy (composite, listed by query they serve):

  • (connector_id, started_at DESC) → "recent runs for this connector"
  • (user_id, started_at DESC) → "user activity timeline"
  • (status, started_at DESC) → "recent errors" filter
  • (ip_address, started_at DESC) → "activity from this IP" (future allow/block UX)
  • started_at → retention purge
  • parent_run_id → retry lineage trace

func (*ConnectorRun) BeforeCreate added in v0.4.0

func (r *ConnectorRun) BeforeCreate(tx *gorm.DB) error

type ConnectorRunSource added in v0.4.0

type ConnectorRunSource string

ConnectorRunSource describes how a ConnectorRun was triggered.

const (
	// ConnectorRunSourceMCP marks runs initiated by an LLM client through
	// the /mcp endpoint.
	ConnectorRunSourceMCP ConnectorRunSource = "mcp"
	// ConnectorRunSourceTest marks runs initiated from the panel-test
	// view in the admin UI (Postman-style manual exec).
	ConnectorRunSourceTest ConnectorRunSource = "test"
	// ConnectorRunSourceRetry marks runs that replay the request payload
	// of a previous run, identified by ConnectorRun.ParentRunID.
	ConnectorRunSourceRetry ConnectorRunSource = "retry"
)

type ConnectorRunStatus added in v0.4.0

type ConnectorRunStatus string

ConnectorRunStatus describes the outcome of a ConnectorRun.

const (
	ConnectorRunStatusRunning ConnectorRunStatus = "running"
	ConnectorRunStatusSuccess ConnectorRunStatus = "success"
	ConnectorRunStatusError   ConnectorRunStatus = "error"
)

type HealthComponent

type HealthComponent struct {
	Database HealthState `json:"database"`
}

type HealthState

type HealthState string
const (
	HealthStateOK   HealthState = "ok"
	HealthStateFail HealthState = "fail"
)

type Job

type Job struct {
	ID          string    `gorm:"type:varchar(36);primaryKey"`
	Key         string    `gorm:"type:varchar(100);uniqueIndex;not null"`
	Name        string    `gorm:"type:varchar(255);not null"`
	Description string    `gorm:"type:text"`
	Icon        string    `gorm:"type:varchar(10)"`
	Schedule    string    `gorm:"type:varchar(100)"` // cron expression
	Enabled     bool      `gorm:"default:false"`
	MaxRuns     int       `gorm:"default:0"` // 0 = unlimited, admin-managed
	TotalRuns   int       `gorm:"default:0"`
	LastStatus  JobStatus `gorm:"type:varchar(20);default:'idle'"`
	LastRunAt   *time.Time
	CreatedBy   string `gorm:"type:varchar(36)"`
	CreatedAt   time.Time
	UpdatedAt   time.Time
}

Job is a background job definition whose schedule and lifecycle are managed via the DB. Code-defined jobs bootstrap a row on startup; admins can tweak the cron expression, enable/disable, and cap the run count.

func (*Job) BeforeCreate

func (j *Job) BeforeCreate(tx *gorm.DB) error

type JobRun

type JobRun struct {
	ID          string     `gorm:"type:varchar(36);primaryKey"`
	JobID       string     `gorm:"type:varchar(36);index;not null"`
	Status      RunStatus  `gorm:"type:varchar(20);not null"`
	Result      string     `gorm:"type:text"`
	TriggeredBy RunTrigger `gorm:"type:varchar(20);not null"`
	UserID      string     `gorm:"type:varchar(36)"`
	StartedAt   time.Time
	EndedAt     *time.Time
	CreatedAt   time.Time
}

JobRun stores the result of a single execution of a Job.

func (*JobRun) BeforeCreate

func (r *JobRun) BeforeCreate(tx *gorm.DB) error

type JobStatus

type JobStatus string

JobStatus represents the current state of a job.

const (
	JobStatusIdle    JobStatus = "idle"
	JobStatusRunning JobStatus = "running"
)

type OAuthAuthorizationCode added in v0.4.0

type OAuthAuthorizationCode struct {
	ID                  string `gorm:"type:varchar(36);primaryKey"`
	Code                string `gorm:"type:varchar(64);uniqueIndex;not null"`
	ClientID            string `gorm:"type:varchar(64);index;not null"`
	UserID              string `gorm:"type:varchar(36);not null"`
	RedirectURI         string `gorm:"type:varchar(512);not null"`
	Scope               string `gorm:"type:varchar(255)"`
	CodeChallenge       string `gorm:"type:varchar(128);not null"`
	CodeChallengeMethod string `gorm:"type:varchar(10);not null"`
	Used                bool   `gorm:"default:false"`
	ExpiresAt           time.Time
	CreatedAt           time.Time
}

OAuthAuthorizationCode is the short-lived PKCE authorization code minted at /oauth/authorize and consumed at /oauth/token.

Code is the opaque string sent in the redirect query string. It's indexed for the lookup at /token but not unique — we let the database flag duplicates if two requests collide (statistically near impossible with 32 random bytes).

CodeChallenge / Method come from the original /authorize request; /token verifies the client's code_verifier against them per RFC 7636.

Used flips to true on first /token redemption to prevent replay. We keep the row for audit instead of deleting; PurgeExpired sweeps later.

func (*OAuthAuthorizationCode) BeforeCreate added in v0.4.0

func (c *OAuthAuthorizationCode) BeforeCreate(tx *gorm.DB) error

func (OAuthAuthorizationCode) TableName added in v0.4.0

func (OAuthAuthorizationCode) TableName() string

type OAuthClient added in v0.4.0

type OAuthClient struct {
	ID           string `gorm:"type:varchar(36);primaryKey"`
	ClientID     string `gorm:"type:varchar(64);uniqueIndex;not null"`
	Name         string `gorm:"type:varchar(255)"`
	RedirectURIs string `gorm:"type:text;not null"` // JSON array
	CreatedBy    string `gorm:"type:varchar(36)"`
	CreatedAt    time.Time
}

OAuthClient is one Dynamic Client Registration record (RFC 7591).

MCP clients (Claude.ai web, Claude Desktop, Cursor) call POST /oauth/register without prior coordination, hand wick a name + redirect_uris, and receive back the ClientID. There is no client secret in this flow — every client is treated as a public client using PKCE per RFC 7636 (which the MCP authorization spec mandates).

RedirectURIs is a JSON-encoded array of allowed redirect URIs; /oauth/authorize verifies the requested URI is one of them. We store as JSON to keep gorm migrations simple — the list is small and read whole every time.

CreatedBy is non-nil only when the registration happened while a user was logged in (rare — DCR usually fires before any wick session exists). Useful for admin auditing.

func (*OAuthClient) BeforeCreate added in v0.4.0

func (c *OAuthClient) BeforeCreate(tx *gorm.DB) error

func (OAuthClient) TableName added in v0.4.0

func (OAuthClient) TableName() string

TableName pins the table name. GORM's default naming would lower- case + snake_case "OAuth" into "o_auth", giving "o_auth_clients". We override so raw SQL in oauth.Repo.ListGrantsByUser stays readable ("oauth_clients", not "o_auth_clients").

type OAuthToken added in v0.4.0

type OAuthToken struct {
	ID            string  `gorm:"type:varchar(36);primaryKey"`
	TokenHash     string  `gorm:"type:varchar(64);uniqueIndex;not null"`
	Kind          string  `gorm:"type:varchar(10);not null"` // access | refresh
	ClientID      string  `gorm:"type:varchar(64);index;not null"`
	UserID        string  `gorm:"type:varchar(36);index;not null"`
	Scope         string  `gorm:"type:varchar(255)"`
	ParentTokenID *string `gorm:"type:varchar(36);index"` // refresh chain ancestor
	ExpiresAt     time.Time
	RevokedAt     *time.Time `gorm:"index"`
	LastUsedAt    *time.Time
	CreatedAt     time.Time
}

OAuthToken is one issued access or refresh token. Stored opaque (32 hex chars), hashed at rest just like PersonalAccessToken — the plaintext only crosses the wire on the /token response.

Kind is "access" or "refresh". A code redemption mints both: the access has a short TTL (~1h), the refresh a long one (~30d) and is rotated on every use (RFC 6749 §6 + best-current-practice).

ParentTokenID chains refresh-token rotation: when a refresh redeems, the new refresh row carries the previous row's ID here. RevokedAt stamps a row when its child is minted, so reuse of an old refresh is detectable (and the whole chain should be revoked — a sign the token was leaked).

func (*OAuthToken) BeforeCreate added in v0.4.0

func (t *OAuthToken) BeforeCreate(tx *gorm.DB) error

func (OAuthToken) TableName added in v0.4.0

func (OAuthToken) TableName() string

type PersonalAccessToken added in v0.4.0

type PersonalAccessToken struct {
	ID         string `gorm:"type:varchar(36);primaryKey"`
	UserID     string `gorm:"type:varchar(36);not null;index"`
	Name       string `gorm:"type:varchar(120);not null"`
	TokenHash  string `gorm:"type:varchar(64);not null;uniqueIndex"`
	Last4      string `gorm:"type:varchar(8);not null"`
	CreatedAt  time.Time
	LastUsedAt *time.Time
	RevokedAt  *time.Time `gorm:"index"`
}

PersonalAccessToken is a static bearer credential a user generates from /profile/mcp. The plaintext token is shown to the user exactly once at creation time; only the SHA-256 hash is persisted so it can be looked up on incoming MCP requests but never reconstructed.

Token wire format: "wick_pat_" + 32 hex chars. Last4 stores the last 4 characters of the random suffix so the UI can render a stable "wick_pat_****abcd" preview without keeping the secret around.

LastUsedAt is best-effort — written by the MCP middleware on successful auth. RevokedAt nil means active; non-nil hides the row from active-token queries while keeping the audit trail intact.

func (*PersonalAccessToken) BeforeCreate added in v0.4.0

func (t *PersonalAccessToken) BeforeCreate(tx *gorm.DB) error

func (*PersonalAccessToken) Masked added in v0.4.0

func (t *PersonalAccessToken) Masked() string

Masked returns the display form for list views: prefix + asterisks + last 4 characters. Never exposes the secret.

type RunStatus

type RunStatus string

RunStatus represents the outcome of a single job execution.

const (
	RunStatusRunning RunStatus = "running"
	RunStatusSuccess RunStatus = "success"
	RunStatusError   RunStatus = "error"
)

type RunTrigger

type RunTrigger string

RunTrigger describes how a run was initiated.

const (
	RunTriggerManual RunTrigger = "manual"
	RunTriggerCron   RunTrigger = "cron"
)

type SSOProvider

type SSOProvider struct {
	ID           uint   `gorm:"primaryKey"`
	Provider     string `gorm:"uniqueIndex;type:varchar(32);not null"` // "google"
	ClientID     string `gorm:"type:varchar(255)"`
	ClientSecret string `gorm:"type:varchar(255)"`
	Enabled      bool   `gorm:"default:false"`
	// AllowedDomains is a comma-separated list of email domains allowed
	// to sign in through this provider (e.g. "abc.com,abc.net").
	// Empty string means no restriction — any email from the provider is
	// accepted. Matching is case-insensitive.
	AllowedDomains string `gorm:"type:text"`
	CreatedAt      time.Time
	UpdatedAt      time.Time
}

SSOProvider holds one OAuth/SSO provider's configuration. The callback URL is never stored — it's derived at runtime from app_variables.app_url + "/auth/callback".

func (SSOProvider) TableName

func (SSOProvider) TableName() string

type Session

type Session struct {
	Token     string    `gorm:"primaryKey;type:varchar(64)"`
	UserID    string    `gorm:"type:uuid;not null;index"`
	User      User      `gorm:"foreignKey:UserID"`
	ExpiresAt time.Time `gorm:"not null"`
	CreatedAt time.Time
}

type Tag

type Tag struct {
	ID          string `gorm:"type:varchar(36);primaryKey"`
	Name        string `gorm:"uniqueIndex;type:varchar(100);not null"`
	Description string `gorm:"type:varchar(500)"`
	IsGroup     bool   `gorm:"default:false"`
	IsFilter    bool   `gorm:"default:false"`
	IsSystem    bool   `gorm:"default:false"`
	SortOrder   int    `gorm:"default:0"`
	CreatedAt   time.Time
}

Tag is a first-class label that can be attached to users and tools. Renaming a Tag propagates automatically because associations store TagID, not the name.

A Tag has two orthogonal-but-combinable purposes:

  • Access filter: when IsFilter is true and the tag is attached to a Private tool, only users who carry the same tag may access it. Tool-tags without IsFilter are purely cosmetic for access (they don't restrict who can enter).
  • Group on home: when IsGroup is true, tools carrying the tag are rendered together on the home page under Name. A tool with multiple group tags appears in each group.

A tag can set any combination of IsGroup and IsFilter independently.

IsSystem marks a tag as code-owned: it can only be assigned to tool/job/connector entities by code (via DefaultTags seeding), never by an admin from the UI to a user. The intent is to gate built-in maintenance items (e.g. the connector-runs-purge job) behind a tag no end user can carry — combined with IsFilter=true, this hides the item from /manager/* for everyone except admin (who bypasses the tag-filter rule wholesale).

func (*Tag) BeforeCreate

func (t *Tag) BeforeCreate(tx *gorm.DB) error

type ToolPermission

type ToolPermission struct {
	ToolPath   string         `gorm:"primaryKey;type:varchar(255)"`
	Visibility ToolVisibility `gorm:"type:varchar(50);default:'private'"`
	// Disabled hides the tool from every user (including admins) and makes
	// direct hits to /tools/{slug}/* return 404. Admins re-enable from
	// /admin/tools.
	Disabled  bool `gorm:"default:false"`
	UpdatedAt time.Time
}

ToolPermission stores the per-tool visibility override set by an admin. If no row exists for a tool path the code falls back to the tool's declared default visibility.

type ToolTag

type ToolTag struct {
	ToolPath string `gorm:"primaryKey;type:varchar(255)"`
	TagID    string `gorm:"primaryKey;type:varchar(36);index"`
}

ToolTag links a tool to a Tag. When a tool is Private and at least one ToolTag exists, only users carrying one of those tags may access it.

type ToolVisibility

type ToolVisibility = pkgentity.ToolVisibility

ToolVisibility is re-exported from pkg/entity so existing callers continue to work after the public contract moved out of internal.

type User

type User struct {
	ID           string `gorm:"type:varchar(36);primaryKey"`
	Email        string `gorm:"uniqueIndex;not null"`
	Name         string `gorm:"not null"`
	Avatar       string
	Role         UserRole     `gorm:"type:varchar(50);default:'user'"`
	Approved     bool         `gorm:"default:false"`
	PasswordHash string       `gorm:"type:varchar(255)"`
	Metadata     UserMetadata `gorm:"type:jsonb"`
	CreatedAt    time.Time
	UpdatedAt    time.Time
}

func (*User) BeforeCreate

func (u *User) BeforeCreate(tx *gorm.DB) error

func (*User) IsAdmin

func (u *User) IsAdmin() bool

type UserMetadata

type UserMetadata struct {
	// HomeView picks the tool grid density: "compact" (icon+name) or
	// "detailed" (wider cards with description). Empty means compact.
	HomeView string `json:"home_view,omitempty"`

	// Theme picks the UI color palette. Values are Theme.ID from
	// internal/pkg/ui/theme.go ("light", "dark", "dracula", …).
	// Empty means "no preference" — guests follow the device
	// `prefers-color-scheme`, logged-in users can pick in the navbar.
	Theme string `json:"theme,omitempty"`

	// LightTheme / DarkTheme remember the last light- and dark-mode
	// theme the user picked from the dropdown, so the navbar toggle
	// can switch straight back to that variant instead of the generic
	// "light"/"dark" defaults. Values are Theme.ID.
	LightTheme string `json:"light_theme,omitempty"`
	DarkTheme  string `json:"dark_theme,omitempty"`
}

UserMetadata is the free-form preferences bag stored as JSON on the user row. Add fields here when a new per-user preference is needed — all consumers should default to the zero value when a field is unset so existing rows (NULL metadata) keep working without a backfill.

func (UserMetadata) HomeViewOrDefault

func (m UserMetadata) HomeViewOrDefault() string

HomeViewOrDefault returns a valid HomeView value, falling back to compact when unset or unrecognized.

func (*UserMetadata) Scan

func (m *UserMetadata) Scan(value any) error

func (UserMetadata) Value

func (m UserMetadata) Value() (driver.Value, error)

type UserRole

type UserRole string
const (
	RoleAdmin UserRole = "admin"
	RoleUser  UserRole = "user"
)

type UserTag

type UserTag struct {
	UserID string `gorm:"primaryKey;type:uuid"`
	TagID  string `gorm:"primaryKey;type:varchar(36);index"`
}

UserTag assigns a Tag to a user. Only approved users may carry tags.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL