CI / changes (push) Successful in 6s
CI / openapi (push) Successful in 23s
CI / go (push) Successful in 45s
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, , evobgp-web) (push) Successful in 1m14s
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, evobgp-all, evobgp-web-all) (push) Successful in 1m7s
CI / docker-bird (push) Has been skipped
CI / bird2 (push) Successful in 16s
CI / docker-go-prime (push) Successful in 23s
CI / docker-go (deploy/docker/evobgp-agent/Dockerfile, , evobgp-agent) (push) Successful in 1m2s
CI / docker-go (evobgp-all, 1, deploy/docker/gobinary/Dockerfile, , evobgp-all) (push) Successful in 6m21s
CI / docker-go (evobgp-api, 1, deploy/docker/gobinary/Dockerfile, , evobgp-api) (push) Successful in 1m32s
CI / docker-go (evobgp-deploy, 0, deploy/docker/gobinary/Dockerfile, , evobgp-deploy) (push) Successful in 1m26s
CI / docker-go (evobgp-ingest, 0, deploy/docker/gobinary/Dockerfile, , evobgp-ingest) (push) Successful in 1m26s
CI / docker-go (evobgp-node, 0, deploy/docker/gobinary/Dockerfile, , evobgp-node) (push) Successful in 1m8s
CI / docker-go (evobgp-render, 0, deploy/docker/gobinary/Dockerfile, , evobgp-render) (push) Successful in 1m25s
CI / docker-go (evobgp-scheduler, 0, deploy/docker/gobinary/Dockerfile, , evobgp-scheduler) (push) Successful in 1m28s
Introduced a new field, last_refreshed_at, to track the last successful update time for modules and CDN sources. Updated the database schema, API responses, and internal logic to support this feature. Enhanced the frontend to display the last refreshed timestamp, improving visibility into the data update status for users.
215 lines
8.9 KiB
Go
215 lines
8.9 KiB
Go
package store
|
|
|
|
import "time"
|
|
|
|
// Backend is the persistence abstraction for the control plane (memory, PostgreSQL, SQLite).
|
|
type Backend interface {
|
|
MaterializedPrefixStats() (max int, sum int)
|
|
PeerCount() int
|
|
PeerSessionCountsByState() map[string]int
|
|
// DemoIDs is non-empty only after SeedDemo (memory or seeded SQL).
|
|
DemoIDs() (tenant, moduleCDN, moduleIP, revision, speaker string)
|
|
|
|
// ListTenantIDs returns distinct tenant identifiers (for background workers).
|
|
ListTenantIDs() ([]string, error)
|
|
|
|
ListModules(tenantID string) []*Module
|
|
GetModule(tenantID, moduleID string) (*Module, error)
|
|
CreateModule(tenantID string, in *Module) (*Module, error)
|
|
UpdateModule(tenantID, moduleID string, patch *ModulePatch) (*Module, error)
|
|
SoftDeleteModule(tenantID, moduleID string) error
|
|
|
|
ListCDNSources(tenantID, moduleID string) ([]*CDNSource, error)
|
|
CreateCDNSource(tenantID, moduleID string, in *CDNSource) (*CDNSource, error)
|
|
UpdateCDNSource(tenantID, moduleID, sourceID string, patch *CDNSourcePatch) (*CDNSource, error)
|
|
DeleteCDNSource(tenantID, moduleID, sourceID string) error
|
|
|
|
ListASEntries(tenantID, moduleID string) ([]*ASEntry, error)
|
|
CreateASEntry(tenantID, moduleID string, in *ASEntry) (*ASEntry, error)
|
|
UpdateASEntry(tenantID, moduleID, entryID string, patch *ASEntryPatch) (*ASEntry, error)
|
|
DeleteASEntry(tenantID, moduleID, entryID string) error
|
|
// UpdateASEntryResolveMeta записывает имя AS, число объявленных префиксов и время успешного резолва (pipeline).
|
|
UpdateASEntryResolveMeta(tenantID, moduleID, entryID string, asnName string, prefixCount int64, resolvedAt time.Time) error
|
|
|
|
ListDomainEntries(tenantID, moduleID string) ([]*DomainEntry, error)
|
|
CreateDomainEntry(tenantID, moduleID string, in *DomainEntry) (*DomainEntry, error)
|
|
UpdateDomainEntry(tenantID, moduleID, entryID string, patch *DomainEntryPatch) (*DomainEntry, error)
|
|
DeleteDomainEntry(tenantID, moduleID, entryID string) error
|
|
|
|
ListIPRangeEntries(tenantID, moduleID string) ([]*IPRangeEntry, error)
|
|
CreateIPRangeEntry(tenantID, moduleID string, in *IPRangeEntry) (*IPRangeEntry, error)
|
|
UpdateIPRangeEntry(tenantID, moduleID, entryID string, patch *IPRangePatch) (*IPRangeEntry, error)
|
|
DeleteIPRangeEntry(tenantID, moduleID, entryID string) error
|
|
|
|
ListDohProfiles(tenantID string) ([]*DohProfile, error)
|
|
GetDohProfile(tenantID, id string) (*DohProfile, error)
|
|
CreateDohProfile(tenantID string, in *DohProfile) (*DohProfile, error)
|
|
UpdateDohProfile(tenantID, id string, patch *DohProfilePatch) (*DohProfile, error)
|
|
DeleteDohProfile(tenantID, id string) error
|
|
|
|
ListCommunities(tenantID string) ([]*Community, error)
|
|
GetCommunity(tenantID, id string) (*Community, error)
|
|
CreateCommunity(tenantID string, in *Community) (*Community, error)
|
|
UpdateCommunity(tenantID, id string, patch *CommunityPatch) (*Community, error)
|
|
DeleteCommunity(tenantID, id string) error
|
|
|
|
ListPeers(tenantID string) []*BGPPeer
|
|
GetPeer(tenantID, id string) (*BGPPeer, error)
|
|
CreatePeer(tenantID string, in *BGPPeer) (*BGPPeer, error)
|
|
UpdatePeer(tenantID, id string, patch *PeerPatch) (*BGPPeer, error)
|
|
DeletePeer(tenantID, id string) error
|
|
|
|
ListSpeakersForTenant(tenantID string) []*Speaker
|
|
GetSpeaker(tenantID, speakerID string) (*Speaker, error)
|
|
GetSpeakerAnyTenant(speakerID string) (*Speaker, error)
|
|
CreateSpeaker(tenantID string, in *Speaker) (*Speaker, error)
|
|
UpdateSpeaker(tenantID, id string, patch *SpeakerPatch) (*Speaker, error)
|
|
|
|
GetRevision(tenantID, revisionID string) (*Revision, error)
|
|
ListRevisions(tenantID, moduleID string, cursor string, limit int) (items []*Revision, nextCursor string, hasMore bool)
|
|
ListRevisionPrefixes(tenantID, revisionID string, cursor string, limit int) (prefixes []PrefixRow, next string, more bool)
|
|
CreateRollbackRevision(tenantID, sourceRevisionID string) (newID string, err error)
|
|
// CreateRenderRevision inserts a new config_revision (revID must be unique) with materialized prefixes and preview fragments.
|
|
CreateRenderRevision(revisionID, tenantID, moduleID string, parentRevisionID *string, contentHash string, previewFragments map[string]string, prefixes []PrefixRow) error
|
|
RevisionDiff(tenantID, aID, bID string) (map[string]any, error)
|
|
PruneRevisionsBefore(tenantID string, cutoff time.Time) (deleted int, err error)
|
|
SetLastAppliedRevision(tenantID, speakerID, revisionID string) error
|
|
PublishRevisionForSpeaker(speakerID, revisionID string) error
|
|
LatestPublishedRevision(speakerID string) (revisionID string, publishedAt time.Time, err error)
|
|
|
|
ListGlobalSettings(tenantID string) (map[string]any, error)
|
|
PatchGlobalSettings(tenantID string, patch map[string]any) error
|
|
}
|
|
|
|
// ModulePatch is a partial update for module.
|
|
type ModulePatch struct {
|
|
Name *string `json:"name,omitempty"`
|
|
Enabled *bool `json:"enabled,omitempty"`
|
|
Priority *int `json:"priority,omitempty"`
|
|
RefreshIntervalSec *int `json:"refresh_interval_sec,omitempty"`
|
|
CronExpr *string `json:"cron_expr,omitempty"`
|
|
DefaultCommunityID *string `json:"default_community_id,omitempty"`
|
|
DohProfileID *string `json:"doh_profile_id,omitempty"`
|
|
LastRefreshedAt *time.Time `json:"-"`
|
|
}
|
|
|
|
// CDNSource is a row under a CDN module.
|
|
type CDNSource struct {
|
|
ID string `json:"id,omitempty"`
|
|
ModuleID string `json:"module_id,omitempty"`
|
|
SourceKind string `json:"source_kind"`
|
|
URL string `json:"url"`
|
|
PrefixPath string `json:"prefix_path,omitempty"`
|
|
Etag string `json:"etag"`
|
|
RefreshIntervalSec *int `json:"refresh_interval_sec"`
|
|
CommunityID *string `json:"community_id"`
|
|
LastRefreshedAt *time.Time `json:"-"`
|
|
}
|
|
|
|
type CDNSourcePatch struct {
|
|
SourceKind *string `json:"source_kind,omitempty"`
|
|
URL *string `json:"url,omitempty"`
|
|
PrefixPath *string `json:"prefix_path,omitempty"`
|
|
Etag *string `json:"etag,omitempty"`
|
|
RefreshIntervalSec *int `json:"refresh_interval_sec,omitempty"`
|
|
CommunityID *string `json:"community_id,omitempty"`
|
|
LastRefreshedAt *time.Time `json:"-"`
|
|
}
|
|
|
|
type ASEntry struct {
|
|
ID string `json:"id,omitempty"`
|
|
ModuleID string `json:"module_id,omitempty"`
|
|
ASN int64 `json:"asn"`
|
|
CommunityID *string `json:"community_id"`
|
|
ASNName string `json:"asn_name,omitempty"`
|
|
PrefixCount *int64 `json:"prefix_count,omitempty"`
|
|
ASNResolvedAt *time.Time `json:"asn_resolved_at,omitempty"`
|
|
}
|
|
|
|
type ASEntryPatch struct {
|
|
ASN *int64 `json:"asn,omitempty"`
|
|
CommunityID *string `json:"community_id,omitempty"`
|
|
}
|
|
|
|
// ValidASN reports whether n is a usable BGP ASN (1..4294967295).
|
|
func ValidASN(n int64) bool {
|
|
return n >= 1 && n <= 4294967295
|
|
}
|
|
|
|
type DomainEntry struct {
|
|
ID string `json:"id,omitempty"`
|
|
ModuleID string `json:"module_id,omitempty"`
|
|
FQDN string `json:"fqdn"`
|
|
CommunityID *string `json:"community_id"`
|
|
}
|
|
|
|
type DomainEntryPatch struct {
|
|
FQDN *string `json:"fqdn,omitempty"`
|
|
CommunityID *string `json:"community_id,omitempty"`
|
|
}
|
|
|
|
type IPRangeEntry struct {
|
|
ID string `json:"id,omitempty"`
|
|
ModuleID string `json:"module_id,omitempty"`
|
|
Prefix string `json:"prefix"`
|
|
CommunityID *string `json:"community_id"`
|
|
}
|
|
|
|
type IPRangePatch struct {
|
|
Prefix *string `json:"prefix,omitempty"`
|
|
CommunityID *string `json:"community_id,omitempty"`
|
|
}
|
|
|
|
type DohProfile struct {
|
|
ID string `json:"id,omitempty"`
|
|
TenantID string `json:"tenant_id,omitempty"`
|
|
Name string `json:"name"`
|
|
URL string `json:"url"`
|
|
TimeoutMs *int `json:"timeout_ms"`
|
|
SecretRef *string `json:"vault_secret_ref"`
|
|
}
|
|
|
|
type DohProfilePatch struct {
|
|
Name *string `json:"name,omitempty"`
|
|
URL *string `json:"url,omitempty"`
|
|
TimeoutMs *int `json:"timeout_ms,omitempty"`
|
|
SecretRef *string `json:"vault_secret_ref,omitempty"`
|
|
}
|
|
|
|
type Community struct {
|
|
ID string `json:"id,omitempty"`
|
|
TenantID string `json:"tenant_id,omitempty"`
|
|
Community string `json:"community"`
|
|
Title string `json:"title"`
|
|
ValueJSON string `json:"value_json"`
|
|
}
|
|
|
|
type CommunityPatch struct {
|
|
Community *string `json:"community,omitempty"`
|
|
Title *string `json:"title,omitempty"`
|
|
ValueJSON *string `json:"value_json,omitempty"`
|
|
}
|
|
|
|
type PeerPatch struct {
|
|
Neighbor *string `json:"neighbor,omitempty"`
|
|
RemoteASN *int64 `json:"remote_asn,omitempty"`
|
|
SpeakerID *string `json:"bgp_speaker_id,omitempty"`
|
|
Enabled *bool `json:"enabled,omitempty"`
|
|
Name *string `json:"name,omitempty"`
|
|
SessionState *string `json:"session_state,omitempty"`
|
|
PoliciesJSON *string `json:"policies_json,omitempty"`
|
|
}
|
|
|
|
type SpeakerPatch struct {
|
|
Role *string `json:"role,omitempty"`
|
|
Endpoint *string `json:"endpoint,omitempty"`
|
|
MetaJSON *string `json:"meta_json,omitempty"`
|
|
}
|
|
|
|
// PrefixRow is one materialized prefix for GET /revisions/.../prefixes.
|
|
type PrefixRow struct {
|
|
Prefix string
|
|
CommunityID *string
|
|
Source string
|
|
}
|