store

package
v0.2.10 Latest Latest
Warning

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

Go to latest
Published: Sep 5, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package store 是 SQLite 持久层(modernc.org/sqlite,纯 Go 零 CGO)。 schema 内嵌,启动时按 PRAGMA user_version 自动逐级迁移。

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrPluginInstanceNotFound 表示 (tenant, edge, instance) 期望态行不存在 → api.PluginErrNotFound。
	ErrPluginInstanceNotFound = errors.New("store: plugin instance not found")
	// ErrPluginInstanceConflict 表示 Create 目标实例已存在 → api.PluginErrConflict。
	ErrPluginInstanceConflict = errors.New("store: plugin instance already exists")
	// ErrPluginQuotaExceeded 表示租户 plugin instance 配额已满:写在事务内被原子拒绝,
	// revision 未推进、无任何半状态 → api.PluginErrQuota(tenant-security-policy.md §4.1、§6.2)。
	ErrPluginQuotaExceeded = errors.New("store: plugin instance quota exceeded")
	// ErrPluginIdentityIncomplete 表示 edge_id / instance_id / plugin_id / boot_id 缺失,fail-closed。
	ErrPluginIdentityIncomplete = errors.New("store: plugin identity incomplete")
	// ErrPluginConfigInvalid 表示 config_json / secret_refs 不是契约要求的 JSON 形状
	// → api.PluginErrInvalidConfig。
	ErrPluginConfigInvalid = errors.New("store: plugin config or secret refs invalid")
	// ErrPluginImmutableField 表示试图改写不可变字段(实例的 plugin_id 归属);
	// 换插件是新实例语义,必须走 Create,不得由 Update 静默改写。
	ErrPluginImmutableField = errors.New("store: plugin instance immutable field changed")
)

插件控制面稳定 sentinel 错误:调用方用 errors.Is 判断并映射到 api.PluginErr* 稳定码, 不要依赖错误文本。

View Source
var (
	// ErrUsernameTaken 表示 username 已存在(租户内唯一,handler 回 409)。
	ErrUsernameTaken = errors.New("store: username already taken")
	// ErrLastAdmin 表示目标用户是最后一个可用 admin,禁止禁用/降级(handler 回 409)。
	ErrLastAdmin = errors.New("store: cannot disable or demote the last admin")
	// ErrInvalidRole 表示角色不在 admin|operator|viewer 内。
	ErrInvalidRole = errors.New("store: invalid role")
)

用户/令牌管理的哨兵错误(docs/api.md §3.2/§3.3 语义)。

View Source
var ErrDeviceTenantMismatch = errors.New("store: device identity bound to another tenant")

ErrDeviceTenantMismatch 表示设备身份已绑定其他租户,写入必须 fail-closed。

View Source
var ErrEdgeTenantMismatch = errors.New("store: edge identity bound to another tenant")

ErrEdgeTenantMismatch 表示 edge 身份已绑定其他租户,写入必须 fail-closed。

Functions

This section is empty.

Types

type AppDomainRecordRow added in v0.2.3

type AppDomainRecordRow struct {
	TenantID   int64
	InstanceID string
	RecordType string
	RecordID   string
	DataJSON   string
	Version    string
	UpdatedAt  int64
}

AppDomainRecordRow 是 app_domain_records 一行(Application Plugin 的领域记录)。

type AuditEvent

type AuditEvent struct {
	ID           int64
	TenantID     int64
	ActorType    string
	ActorID      int64
	ActorName    string
	Action       string
	TargetType   string
	TargetID     string
	Outcome      string
	RequestID    string
	RemoteIP     string
	MetadataJSON string
	CreatedAt    int64
}

AuditEvent 是 audit_events 表一行。append-only:本包只暴露 INSERT 与 tenant-scoped 查询, 不暴露任何 update/delete API(安全红线由代码结构锁定)。

type AuthUser

type AuthUser struct {
	UserRow
	TenantSlug      string
	SessionLastSeen int64 // 仅 UserBySession 填充
}

AuthUser 是鉴权查询结果:用户 + 所属租户 slug + 会话最近活跃时间。

type CommandRow

type CommandRow struct {
	ID        int64
	DeviceID  string
	Cmd       string
	Args      string
	Status    string
	CreatedAt int64
	AckedAt   sql.NullInt64
	Result    string
}

CommandRow 是 commands 一行。

type DeviceMetaInput

type DeviceMetaInput struct {
	ID      string
	Adapter string
	Name    string
	Port    string
}

DeviceMetaInput 是批量设备注册的输入(ID 为完整 "<edge>/<dev>" 键)。

type DeviceRow

type DeviceRow struct {
	ID         string
	EdgeID     string
	Adapter    string
	Name       string
	Port       string
	FirstSeen  int64
	LastSeen   int64
	TenantID   int64
	TenantSlug string
}

DeviceRow 是 devices 表一行。

type EventRow

type EventRow struct {
	ID       int64
	DeviceID string
	Ts       int64
	Type     string
	Payload  string
}

EventRow 是 events 一行。

type PluginEdgeRevisionRow

type PluginEdgeRevisionRow struct {
	TenantID        int64
	EdgeID          string
	DesiredRevision uint64
	AppliedRevision uint64
	BootID          string
	LastSequence    uint64
	LastReportAt    int64
	LastAckAt       int64
}

PluginEdgeRevisionRow 是 plugin_edge_revisions 一行:每 (tenant, edge) 的同步游标。

type PluginInstallationRow

type PluginInstallationRow struct {
	api.PluginInstallationStatusData
	TenantID   int64
	EdgeID     string
	ReportedAt int64
}

PluginInstallationRow 是 plugin_installations 投影一行:Edge 上报的已安装插件公开事实。 内嵌 api.PluginInstallationStatusData。本表只存公开 manifest 字段, 永不含本地路径、启动参数、环境变量或 secret 值(§7、tenant-security-policy.md §2.4)。

type PluginInstanceRow

type PluginInstanceRow struct {
	TenantID   int64
	EdgeID     string
	InstanceID string
	PluginID   string
	Version    string
	Enabled    bool
	Isolation  string
	ConfigJSON string
	SecretRefs string
	Revision   uint64
	CreatedAt  int64
	UpdatedAt  int64
}

PluginInstanceRow 是 plugin_desired_instances 一行(Server 权威期望态)。 ConfigJSON 是 map[string]string 的 JSON,值只含非敏感标量或 secret:// handle; SecretRefs 是 JSON []string,只含 handle 名称。Revision 是该行最后一次写入时所属 (tenant_id, edge_id) 的 desired revision(写入时由 store 赋值,调用方传入值被忽略)。

type PluginObservationRow

type PluginObservationRow struct {
	api.PluginObservedInstanceData
	TenantID   int64
	EdgeID     string
	ReportedAt int64
}

PluginObservationRow 是 plugin_observations 投影一行:Edge 上报的实例实际态 + 归属坐标。 内嵌 api.PluginObservedInstanceData(instance/plugin/version/host_online/state/health/ detail/restart_count/last_healthy/message_rate),读侧可直接复用 DTO 组装 api.PluginInstanceObservedView。boot/sequence/applied_revision 不在此重复存储—— 它们是 per-edge 事实,唯一来源是 plugin_edge_revisions(GetPluginEdgeRevision)。

type ScheduledJobRow added in v0.2.8

type ScheduledJobRow struct {
	TenantID     int64
	InstanceID   string
	ScheduleID   string
	Cron         string
	Timezone     string
	PayloadJSON  string
	MissedPolicy string // skip | run_once
	NextRunAt    int64  // 计划触发时刻(unix 秒)
	LastRunAt    int64
	LastDispatch string
	State        string // active | cancelled
	Revision     int64
	CreatedAt    int64
	UpdatedAt    int64
}

ScheduledJobRow 是 scheduled_jobs 一行(Durable Scheduler 的持久态)。

type SessionRow

type SessionRow struct {
	ID         string
	UserID     int64
	CreatedAt  int64
	ExpiresAt  int64
	LastSeenAt int64
}

SessionRow 是 sessions 表一行。

type StateRow

type StateRow struct {
	DeviceID  string
	State     string
	Online    bool
	UpdatedAt int64
}

StateRow 是 device_state 一行。

type Stats

type Stats struct {
	Devices     int64 `json:"devices"`
	Events      int64 `json:"events"`
	Commands    int64 `json:"commands"`
	OldestEvent int64 `json:"oldest_event"`
	SchemaVer   int   `json:"schema_version"`
}

Stats 是存储侧计数(系统页展示用)。

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store 持有数据库连接。所有方法并发安全(database/sql 连接池)。

func Open

func Open(path string) (*Store, error)

Open 打开(或创建)数据库并执行迁移。path 为 ":memory:" 时用内存库(测试)。

func (*Store) AddEvent

func (s *Store) AddEvent(deviceID, typ, payload string, ts int64) (int64, error)

AddEvent 写入一条事件,返回行 ID;tenant_id 由 devices 表继承,设备缺失时回落 default。

func (*Store) AddEventTenant

func (s *Store) AddEventTenant(tenantID int64, deviceID, typ, payload string, ts int64) (int64, error)

AddEventTenant 只在设备确属指定租户时写事件(fail-closed,不回退 default)。

func (*Store) CancelScheduledJob added in v0.2.8

func (s *Store) CancelScheduledJob(tenantID int64, instanceID, scheduleID string) error

CancelScheduledJob 撤销一条定时任务(cancel_job 效果落点)。幂等:不存在或 已取消均不报错(删除语义的收敛写)。

func (*Store) ClaimScheduledJobRun added in v0.2.8

func (s *Store) ClaimScheduledJobRun(tenantID int64, instanceID, scheduleID string, nextRunAt, lastRunAt int64, dispatchKey string) error

ClaimScheduledJobRun 领取一次运行:先持久化推进 next_run_at / last_run_at / last_dispatch,再由调用方派发(claim-then-dispatch)。崩溃发生在 claim 之后、 派发之前 → 该次运行静默跳过(at-most-once,符合平台「不回放过期副作用」 哲学);崩溃不可能造成重复派发(next_run_at 已推进,同 run 不会被再次选中)。 dispatchKey 为空表示本次不派发(skip 策略只推进)。

func (*Store) Close

func (s *Store) Close() error

Close 关闭数据库。

func (*Store) CountEnabledAdmins

func (s *Store) CountEnabledAdmins(tenantID int64) (int64, error)

CountEnabledAdmins 返回租户内启用中的 admin 数(保护与测试用)。

func (*Store) CountPluginInstances

func (s *Store) CountPluginInstances(tenantID int64) (int, error)

CountPluginInstances 返回本租户期望态实例数(配额准入与 UI 计数共用同一口径: 只数 Server 权威 desired,不数 Edge 上报投影)。

func (*Store) CountUsers

func (s *Store) CountUsers() (int64, error)

CountUsers 返回用户总数(判定账号模式/首装状态)。

func (*Store) CreateCommand

func (s *Store) CreateCommand(deviceID, cmd, args string) (int64, error)

CreateCommand 建命令行(pending),返回命令 ID;tenant_id 由 devices 表继承,设备缺失时回落 default。

func (*Store) CreateCommandTenant

func (s *Store) CreateCommandTenant(deviceID, cmd, args string, tenantID int64) (int64, error)

CreateCommandTenant 在指定租户下为已存在设备建命令行;设备不存在或跨租户返回 sql.ErrNoRows。

func (*Store) CreateInitialAdmin

func (s *Store) CreateInitialAdmin(username, name, passwordHash string) (AuthUser, bool, error)

CreateInitialAdmin 原子首装:确保 default 租户存在,且仅当用户数为 0 时创建首个 admin。 created=false 表示已有用户(调用方回 409)。事务保证并发首装只会成功一次。

func (*Store) CreatePluginInstance

func (s *Store) CreatePluginInstance(row PluginInstanceRow) (uint64, error)

CreatePluginInstance 新建期望态实例并在同一事务内原子推进 (tenant,edge) desired revision, 返回新 revision。row.Revision / CreatedAt / UpdatedAt 由 store 赋值(CreatedAt<=0 用 now())。

拒绝路径(全部整体回滚,revision 不消费、无半状态):

  • 身份段缺失 → ErrPluginIdentityIncomplete
  • config/secret_refs 形状非法 → ErrPluginConfigInvalid
  • edge 已绑定其他租户 → ErrEdgeTenantMismatch
  • 配额已满 → ErrPluginQuotaExceeded(计数与写入同事务,杜绝「先 COUNT 后 INSERT」竞态)
  • 实例已存在 → ErrPluginInstanceConflict

func (*Store) CreateSession

func (s *Store) CreateSession(id string, userID, expiresAt int64) error

CreateSession 落一条服务端会话(登录成功调用;每次登录都新 ID,天然防会话固定)。

func (*Store) CreateTenant

func (s *Store) CreateTenant(slug, name string) (int64, error)

CreateTenant 创建新租户(P2 测试/后续租户管理使用),返回其 id。

func (*Store) CreateTenantToken

func (s *Store) CreateTenantToken(tenantID int64, name, hash, prefix, scopesJSON string, expiresAt *int64) (TokenRow, error)

CreateTenantToken 落一条租户服务令牌(只存 hash/prefix/scopes)。

func (*Store) CreateUser

func (s *Store) CreateUser(tenantID int64, username, name, role, passwordHash string) (AuthUser, error)

CreateUser 在租户内创建用户;username 冲突返回 ErrUsernameTaken。

func (*Store) DeletePluginInstance

func (s *Store) DeletePluginInstance(tenantID int64, edgeID, instanceID string, purge bool) (uint64, error)

DeletePluginInstance 删除期望态实例并原子推进 revision(Edge 需靠新 revision 收敛「实例已移除」), 返回新 revision。不存在 → ErrPluginInstanceNotFound(不消费 revision)。

purge=false(默认):只删期望态,保留 Edge 上报的 observed 投影与插件安装事实。 purge=true:额外删除该实例在 plugin_observations 的投影行(API 语义「显式 purge 才删数据」)。 两种情况都**绝不**删除 audit_events(契约:删除期望态不删审计),也不删 plugin_installations ——安装物是 per-plugin 事实,可能被同 edge 的其他实例共享。

func (*Store) DeleteSession

func (s *Store) DeleteSession(id string) error

DeleteSession 删除会话(登出;幂等,不存在也返回 nil)。

func (*Store) DeleteSessionsByUser

func (s *Store) DeleteSessionsByUser(userID int64) (int64, error)

DeleteSessionsByUser 撤销用户全部会话,返回删除行数(幂等)。

func (*Store) EnsureDefaultTenant

func (s *Store) EnsureDefaultTenant() (int64, error)

EnsureDefaultTenant 幂等创建 default 租户(首装自动创建),返回其 id。

func (*Store) GetAppDomainRecord added in v0.2.3

func (s *Store) GetAppDomainRecord(tenantID int64, instanceID, recordType, recordID string) (AppDomainRecordRow, error)

GetAppDomainRecord 取单条领域记录;不存在返回 sql.ErrNoRows。

func (*Store) GetPluginEdgeRevision

func (s *Store) GetPluginEdgeRevision(tenantID int64, edgeID string) (PluginEdgeRevisionRow, error)

GetPluginEdgeRevision 读 (tenant, edge) 同步游标。从未同步过返回全零行(TenantID/EdgeID 已填) 且 error 为 nil,调用方无需处理 sql.ErrNoRows。

func (*Store) GetPluginInstance

func (s *Store) GetPluginInstance(tenantID int64, edgeID, instanceID string) (PluginInstanceRow, bool, error)

GetPluginInstance 读单个期望态实例;不存在返回 (zero, false, nil)。

func (*Store) GetStates

func (s *Store) GetStates() (map[string]StateRow, error)

GetStates 返回全部设备最新状态(启动水合用)。

func (*Store) GetTenantByID

func (s *Store) GetTenantByID(id int64) (TenantRow, error)

GetTenantByID 按 id 读租户;不存在返回 sql.ErrNoRows。

func (*Store) GetTenantBySlug

func (s *Store) GetTenantBySlug(slug string) (TenantRow, error)

GetTenantBySlug 按 slug 读租户;不存在返回 sql.ErrNoRows。

func (*Store) GetTenantPolicy

func (s *Store) GetTenantPolicy(tenantID int64) (TenantPolicyRow, error)

GetTenantPolicy 读本租户策略行。无策略行时返回全 0 行(= 全部继承默认)且 error 为 nil, 调用方无需处理 sql.ErrNoRows;用 Resolve() 取得可执行策略。

func (*Store) GetTenantToken

func (s *Store) GetTenantToken(id, tenantID int64) (TokenRow, error)

GetTenantToken 按 id+租户读令牌(handler 校验归属用)。

func (*Store) GetTenantTokenByHash

func (s *Store) GetTenantTokenByHash(hash string) (TokenRow, error)

GetTenantTokenByHash 按 hash 读令牌(鉴权路径);不存在返回 sql.ErrNoRows。

func (*Store) GetUserByID

func (s *Store) GetUserByID(id int64) (AuthUser, error)

GetUserByID 按 id 读用户(含租户 slug);不存在返回 sql.ErrNoRows。

func (*Store) GetUserByUsername

func (s *Store) GetUserByUsername(username string) (AuthUser, error)

GetUserByUsername 按用户名读用户(含租户 slug);不存在返回 sql.ErrNoRows。

func (*Store) InsertAuditEvent

func (s *Store) InsertAuditEvent(ev AuditEvent) error

InsertAuditEvent 追加一条审计事件。写失败由调用方消化(audit 失败不得改变业务结果)。

func (*Store) ListAppDomainRecords added in v0.2.3

func (s *Store) ListAppDomainRecords(tenantID int64, instanceID string, limit int) ([]AppDomainRecordRow, error)

ListAppDomainRecords 按实例列领域记录(按更新时间倒序,limit<=0 默认 100,上限 1000)。

func (*Store) ListAppDomainRecordsFiltered added in v0.2.7

func (s *Store) ListAppDomainRecordsFiltered(tenantID int64, instanceID, recordType string, limit, offset int) ([]AppDomainRecordRow, error)

ListAppDomainRecordsFiltered 按实例列领域记录,支持 record_type 过滤与 limit/offset 分页(按更新时间倒序)。recordType 为空 = 不过滤;limit<=0 默认 100、上限 1000;offset<0 归零。offset 分页在本表规模(单实例数百条) 下语义足够,不引入游标。

func (*Store) ListAuditEvents

func (s *Store) ListAuditEvents(tenantID int64, since int64, action string, limit int) ([]AuditEvent, error)

ListAuditEvents 查询本租户审计事件:since>0 只取 created_at>=since;action 非空精确过滤; limit 经 clampLimit 归一(<=0 或 >1000 → 100)。恒按 tenant_id 过滤,跨租户不可见。

func (*Store) ListCommands

func (s *Store) ListCommands(deviceID, status string, limit int) ([]CommandRow, error)

ListCommands 查询命令。deviceID/status 为空表示不过滤。

func (*Store) ListCommandsTenant

func (s *Store) ListCommandsTenant(tenantID int64, deviceID, status string, limit int) ([]CommandRow, error)

ListCommandsTenant 查询指定租户的命令(账号模式)。

func (*Store) ListDevices

func (s *Store) ListDevices() ([]DeviceRow, error)

ListDevices 返回全部注册设备(无账号开发模式沿用)。

func (*Store) ListDevicesTenant

func (s *Store) ListDevicesTenant(tenantID int64) ([]DeviceRow, error)

ListDevicesTenant 返回指定租户的设备(账号模式)。

func (*Store) ListEvents

func (s *Store) ListEvents(deviceID string, since int64, limit int) ([]EventRow, error)

ListEvents 查询事件历史。deviceID 为空查全部;since>0 只取 ts>=since;limit<=0 默认 100(上限 1000)。

func (*Store) ListEventsTenant

func (s *Store) ListEventsTenant(tenantID int64, deviceID string, since int64, limit int) ([]EventRow, error)

ListEventsTenant 查询指定租户的事件历史(账号模式)。

func (*Store) ListPluginInstallationsTenant

func (s *Store) ListPluginInstallationsTenant(tenantID int64) ([]PluginInstallationRow, error)

ListPluginInstallationsTenant 返回本租户全部安装物投影,按 (edge_id, plugin_id) 有序。

func (*Store) ListPluginInstancesAll added in v0.2.3

func (s *Store) ListPluginInstancesAll() ([]PluginInstanceRow, error)

ListPluginInstancesAll 返回全部租户的全部期望态实例(Server 侧 AppHost reconcile 用; 按 tenant/edge/instance 有序)。Edge 侧读面禁止使用本方法(Edge 必须走租户过滤版本)。

func (*Store) ListPluginInstancesTenant

func (s *Store) ListPluginInstancesTenant(tenantID int64) ([]PluginInstanceRow, error)

ListPluginInstancesTenant 返回本租户全部期望态实例,按 (edge_id, instance_id) 有序。 恒带 tenant predicate;tenantID<=0 解析为 default 租户,绝不当作「不过滤」。

func (*Store) ListPluginObservationsTenant

func (s *Store) ListPluginObservationsTenant(tenantID int64) ([]PluginObservationRow, error)

ListPluginObservationsTenant 返回本租户全部 observed 投影,按 (edge_id, instance_id) 有序。 恒带 tenant predicate;tenantID<=0 解析为 default 租户,绝不当作「不过滤」。

func (*Store) ListScheduledJobs added in v0.2.8

func (s *Store) ListScheduledJobs(tenantID int64, instanceID string) ([]ScheduledJobRow, error)

ListScheduledJobs 按实例列定时任务(D1 jobs 读面;含 cancelled,state 自述)。

func (*Store) ListScheduledJobsDue added in v0.2.8

func (s *Store) ListScheduledJobsDue(now int64) ([]ScheduledJobRow, error)

ListScheduledJobsDue 列出到期任务(state=active AND next_run_at<=now), 按计划时刻升序(错过最久的先被处理)。

func (*Store) ListTenantTokens

func (s *Store) ListTenantTokens(tenantID int64) ([]TokenRow, error)

ListTenantTokens 返回租户全部令牌元数据(无明文)。

func (*Store) ListUsersTenant

func (s *Store) ListUsersTenant(tenantID int64) ([]AuthUser, error)

ListUsersTenant 返回指定租户的全部用户(不含密码哈希之外的敏感信息;handler 只透出视图)。

func (*Store) PluginDesiredRevision

func (s *Store) PluginDesiredRevision(tenantID int64, edgeID string) (uint64, error)

PluginDesiredRevision 返回 (tenant, edge) 当前 desired revision;从未写过返回 0。

func (*Store) PruneCommands

func (s *Store) PruneCommands(before int64) (int64, error)

PruneCommands 删除 ts < before 的终态命令(保留期清理),返回删除行数。 仅清理已结算状态,避免误删仍在等待回执的命令行。

func (*Store) PruneEvents

func (s *Store) PruneEvents(before int64) (int64, error)

PruneEvents 删除 ts < before 的事件(保留期清理),返回删除行数。

func (*Store) PrunePluginObservations

func (s *Store) PrunePluginObservations(tenantID int64, before int64) (int64, error)

PrunePluginObservations 删除本租户 reported_at < before 的 observed 投影行,返回删除行数。 这是 §3.1「plugin observed projection 默认 30 天」的执行面,供 Server sweeper 按租户分批调用: 恒带 tenant predicate(清理 A 绝不读取/删除 B),且**只清投影**——desired 期望态与 audit 审计都不随 observed 清理(§5「删除期望态不删审计」、§3.1「desired 不随 observed 清理」)。 本方法是 §3 契约之外的补充(契约未列保留期执行面),Captain 可选择接线或忽略。

func (*Store) PruneSessions

func (s *Store) PruneSessions(before int64) (int64, error)

PruneSessions 删除过期会话,返回删除行数(保留期维护复用)。

func (*Store) RevokeTenantToken

func (s *Store) RevokeTenantToken(id, tenantID int64) (bool, error)

RevokeTenantToken 吊销租户内令牌(幂等语义由 handler 决定 404/204),返回是否命中。

func (*Store) SetPluginEdgeApplied

func (s *Store) SetPluginEdgeApplied(tenantID int64, edgeID, bootID string, seq, appliedRevision uint64, at int64) error

SetPluginEdgeApplied 在 Edge ack=applied 后推进 applied_revision 并记 last_ack_at。 除 boot/sequence 规则外再加两条单调守卫(不满足即幂等忽略,不写任何列):

  • applied_revision 不得回退(Server 侧记录只增,避免 UI/下发出现回归);
  • applied_revision 不得超过本 (tenant,edge) 的 desired_revision(Edge 不能声称应用了 Server 从未下发过的 revision,control-plane-sync.md §2 不变量 2、3)。

func (*Store) SetPluginEdgeReport

func (s *Store) SetPluginEdgeReport(tenantID int64, edgeID, bootID string, seq uint64, at int64) error

SetPluginEdgeReport 记录 Edge 一次 plugin_status 上报(boot 身份 + sequence + 时间), 不改 applied_revision。重复/倒序/旧 boot 迟到消息按契约幂等忽略(返回 nil,不写任何列)。

func (*Store) SetState

func (s *Store) SetState(deviceID, stateJSON string, online bool, updatedAt int64) error

SetState 覆盖设备最新状态;tenant_id 由 devices 表继承(不信任调用方自报)。

func (*Store) SetTenantPolicy

func (s *Store) SetTenantPolicy(tenantID int64, p TenantPolicyRow) error

SetTenantPolicy 写(upsert)本租户策略行。0 字段存 NULL(继承默认)。 先用 tenantpolicy 的冻结校验规则验解析结果(单一校验来源,不与 store 各写一份范围), 非法值 → tenantpolicy.ErrInvalidPolicy 包装错误,且**不写入任何列**; DB CHECK 是第二道防线(手工 SQL 也绕不过)。row.TenantID 被忽略,归属恒取 tenantID 参数。

func (*Store) Stats

func (s *Store) Stats() (Stats, error)

Stats 返回全局行数统计与最早事件时间(无账号开发模式沿用)。

func (*Store) StatsTenant

func (s *Store) StatsTenant(tenantID int64) (Stats, error)

StatsTenant 返回指定租户的行数统计与最早事件时间(账号模式)。

func (*Store) TenantIDBySlug

func (s *Store) TenantIDBySlug(slug string) (int64, error)

TenantIDBySlug 按租户 slug 读 id;不存在返回 sql.ErrNoRows。

func (*Store) TimeoutStaleCommands

func (s *Store) TimeoutStaleCommands(ttl time.Duration) (int64, error)

TimeoutStaleCommands 把超过 ttl 仍 pending/sent 的命令标记 timeout,返回受影响行。

func (*Store) TouchSession

func (s *Store) TouchSession(id string, lastSeen int64) error

TouchSession 刷新会话最近活跃时间(节流调用,避免每请求一次写)。

func (*Store) TouchTenantToken

func (s *Store) TouchTenantToken(id, at int64) error

TouchTenantToken 节流刷新 last_used_at(避免每请求一次写库)。

func (*Store) UpdateCommandStatus

func (s *Store) UpdateCommandStatus(id int64, status, result string) error

UpdateCommandStatus 更新命令状态与回执(default 兼容包装)。

func (*Store) UpdateCommandStatusScoped

func (s *Store) UpdateCommandStatusScoped(id int64, deviceID string, tenantID int64, status, result string) (bool, error)

UpdateCommandStatusScoped 只允许更新同租户、同设备的命令行,返回是否命中(防止跨租户 ack)。

func (*Store) UpdatePluginInstance

func (s *Store) UpdatePluginInstance(row PluginInstanceRow) (uint64, error)

UpdatePluginInstance 更新既有实例并原子推进 revision,返回新 revision。 不可变字段:tenant_id(结构性不可改)、created_at(不改写历史)、plugin_id(换插件属新实例, 传空表示保留,传不同值 → ErrPluginImmutableField)。不存在 → ErrPluginInstanceNotFound。 更新不占配额(没有新增实例),因此不做配额准入。

func (*Store) UpdateUser

func (s *Store) UpdateUser(tenantID, id int64, patch UserPatch) (AuthUser, error)

UpdateUser 修改用户字段并在同一事务内做「最后一个可用 admin」保护。 禁用或重置密码会撤销该用户全部会话。目标用户不存在或跨租户返回 sql.ErrNoRows。

func (*Store) UpsertAppDomainRecord added in v0.2.3

func (s *Store) UpsertAppDomainRecord(tenantID int64, instanceID, recordType, recordID, dataJSON, version string, updatedAt int64) error

UpsertAppDomainRecord 写入/覆盖一条应用领域记录(create_domain_record 效果落点)。 tenantID 由调用方(Server,来自实例行)传入;记录键为 (tenant, instance, record_type, record_id),同一键重复上报即覆盖—— 与 Application Protocol 的 UpsertDomainRecord 语义一致。

func (*Store) UpsertDevice

func (s *Store) UpsertDevice(id, edgeID, adapter, name, port string) error

UpsertDevice 注册/刷新设备元信息(default 兼容包装,无账号开发模式沿用)。

func (*Store) UpsertDeviceTenant

func (s *Store) UpsertDeviceTenant(id, edgeID, adapter, name, port string, tenantID int64) error

UpsertDeviceTenant 注册/刷新设备元信息并绑定租户(账号模式使用)。

func (*Store) UpsertDevicesTenant

func (s *Store) UpsertDevicesTenant(edgeID string, metas []DeviceMetaInput, tenantID int64) error

UpsertDevicesTenant 在单个 IMMEDIATE 写事务里绑定 edge 及其全部设备到租户。 任一设备或 edge 已绑定其他租户时整体回滚并返回对应 sentinel error(fail-closed, 不留半绑定行),避免跨租户同名注册污染。

func (*Store) UpsertPluginInstallations

func (s *Store) UpsertPluginInstallations(tenantID int64, edgeID string, rows []api.PluginInstallationStatusData) error

UpsertPluginInstallations 写入某 (tenant, edge) 的已安装插件全量快照(语义同上)。 时间戳取 now()(契约未传 reportedAt);per-edge 的 last_report_at 由 SetPluginEdgeReport 记录。

func (*Store) UpsertPluginObservations

func (s *Store) UpsertPluginObservations(tenantID int64, edgeID string, rows []api.PluginObservedInstanceData, reportedAt int64) error

UpsertPluginObservations 写入某 (tenant, edge) 的 observed 实例全量快照。 reportedAt<=0 时用 now()。rows 为该 edge 的完整快照:nil/空表示该 edge 当前无实例运行, 会清空该 (tenant,edge) 的投影(不影响其他 edge / 其他租户)。 edge 已绑定其他租户 → ErrEdgeTenantMismatch 且整批不写入(暗卷:伪造上报不污染任一租户投影)。

func (*Store) UpsertScheduledJob added in v0.2.8

func (s *Store) UpsertScheduledJob(row ScheduledJobRow) (ScheduledJobRow, error)

UpsertScheduledJob 写入/更新一条声明式定时任务(schedule_job 效果落点)。 同 (tenant, instance, schedule_id) 重声明 = 更新(cron/payload/策略/next_run_at 重算);state 一律回到 active(cancel 后重声明 = 重新生效,与 UpsertDomainRecord 的「最终态收敛」同一哲学)。revision 递增。

func (*Store) UserBySession

func (s *Store) UserBySession(id string, at int64) (AuthUser, error)

UserBySession 按会话 ID 读有效用户;过期/不存在/用户被禁用一律返回 sql.ErrNoRows。

func (*Store) Version

func (s *Store) Version() int

Version 返回当前 schema 版本(迁移后)。

type TenantPolicyRow

type TenantPolicyRow struct {
	TenantID int64

	// 保留期(天)
	RetentionEventsDays         int
	RetentionCommandsDays       int
	RetentionAuditDays          int
	RetentionRevokedTokensDays  int
	RetentionPluginObservedDays int

	// 配额(硬上限)
	QuotaDevices         int
	QuotaEdges           int
	QuotaBrowserWS       int
	QuotaTokens          int
	QuotaUsers           int
	QuotaEventsPerMinute int
	QuotaPluginInstances int

	UpdatedAt int64
}

TenantPolicyRow 是 tenant_policies 一行。所有天数/上限字段 0 表示继承默认(存 NULL)。 字段集与 tenantpolicy.RetentionDays / tenantpolicy.Quotas 一一对应。

func (TenantPolicyRow) Resolve

func (r TenantPolicyRow) Resolve() tenantpolicy.Policy

Resolve 把行解析成 tenantpolicy.Policy:0/NULL 字段填入 tenantpolicy.Defaults()。 解析结果恒为可直接执行的正数上限与天数(永不返回 0,因此 0 不可能被当成「无限」)。

type TenantRow

type TenantRow struct {
	ID        int64
	Slug      string
	Name      string
	CreatedAt int64
}

TenantRow 是 tenant 表一行。

type TokenRow

type TokenRow struct {
	ID         int64
	TenantID   int64
	Name       string
	Prefix     string
	Hash       string
	Scopes     string // JSON 数组
	ExpiresAt  sql.NullInt64
	LastUsedAt sql.NullInt64
	RevokedAt  sql.NullInt64
	CreatedAt  int64
}

TokenRow 是 tenant_tokens 表一行。明文只在创建响应返回,本结构不含明文。

type UserPatch

type UserPatch struct {
	Name     *string
	Role     *string
	Password *string // 已哈希的 argon2id;设置即重置并撤销全部会话
	Disabled *bool
}

UserPatch 是 PATCH /api/users/{id} 的可选字段集(nil=不改)。

type UserRow

type UserRow struct {
	ID           int64
	TenantID     int64
	Username     string
	Name         string
	Role         string // admin|operator|viewer
	PasswordHash string
	CreatedAt    int64
	Disabled     bool
}

UserRow 是 users 表一行。密码只存 argon2id 哈希(永不落明文)。

Jump to

Keyboard shortcuts

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