web

package
v1.1.1 Latest Latest
Warning

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

Go to latest
Published: Sep 20, 2026 License: AGPL-3.0 Imports: 60 Imported by: 0

Documentation

Overview

Dev build: the static Vue frontend is read straight from disk (relative to the process's working directory), so rerunning `npm run build` takes effect without a Go rebuild.

Web backend for factum

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AuthenticateUser

func AuthenticateUser(db *gorm.DB, username string, password string) (*models.User, error)

AuthenticateUser checks username and password, trying local auth first and falling back to LDAP/AD per the rules below. Returns the user object on success, (nil, nil) on any kind of "bad credentials" (mirrors the pre-LDAP behavior other callers already depend on), and (nil, err) only for a real infrastructure problem (DB error, or LDAP server unreachable/misconfigured).

  1. Local user found, PasswordHash == ldapSentinelPassword: authenticate against LDAP/AD instead of bcrypt (authenticateExistingLDAPUser).
  2. Local user found, PasswordHash is a normal bcrypt hash: unchanged bcrypt check.
  3. No local user found: try LDAP/AD; on success, auto-provision a local user with PasswordHash = ldapSentinelPassword so subsequent logins take path 1 (authenticateNewLDAPUser).

func CheckPasswordHash

func CheckPasswordHash(password, hash string) bool

CheckPasswordHash compares a plain text password with a stored hash Returns true if they are the same

func CreateAdmin

func CreateAdmin(p *CreateAdminParams) error

CreateAdmin seeds roles and (re)creates the admin user.

func GUI

func GUI(p *GuiParams) error

----- GUI -----

func GenerateJWT

func GenerateJWT(userID uint) (string, error)

func GetCurrentUser

func GetCurrentUser(DB *gorm.DB, c *echo.Context) *models.User

GetCurrentUser retrieves the authenticated user from the context. Returns nil if the user is not authenticated or not found in the context.

func GetUserByID

func GetUserByID(db *gorm.DB, userID uint) (*models.User, error)

GetUserByID fetches a user by ID, preloading roles

func HashPassword

func HashPassword(password string) (string, error)

HashPassword generates a bcrypt hash of the password

Types

type ApiServiceElinePushResult

type ApiServiceElinePushResult struct {
	Device string `json:"device"`
	Error  string `json:"error,omitempty"`
}

ApiServiceElinePushResult is one endpoint device's outcome, returned per-device since a cross-device push can partially fail with no way to roll back the other side.

type ApiWorkerRunRequest

type ApiWorkerRunRequest struct {
	Command string   `json:"command"`
	Args    []string `json:"args,omitempty"`
}

ApiWorkerRunRequest is the body "factum2-worker run" posts - see internal/worker/run_client.go's RunRemote, its client-side counterpart.

type ConnectionListDTO added in v1.0.5

type ConnectionListDTO struct {
	ID   uint   `json:"id"`
	Name string `json:"name"`
}

ConnectionListDTO is a cable for typeahead pickers (maintenance fibers).

type Controller

type Controller struct {
	DB            *gorm.DB
	LogHub        *LogHub
	RemoteManager *worker.RemoteManager
	// contains filtered or unexported fields
}

func (*Controller) APIServiceList

func (ctrl *Controller) APIServiceList(c *echo.Context) error

func (*Controller) ApiBranding added in v1.1.1

func (ctrl *Controller) ApiBranding(c *echo.Context) error

ApiBranding is unauthenticated so the login page and other apps (e.g. AbundoPortal) can show the operator-configured logo and text next to the Factum wordmark. It returns only those two fields.

func (*Controller) ApiCertAccountCreate added in v1.1.0

func (ctrl *Controller) ApiCertAccountCreate(c *echo.Context) error

func (*Controller) ApiCertAccountDelete added in v1.1.0

func (ctrl *Controller) ApiCertAccountDelete(c *echo.Context) error

func (*Controller) ApiCertAccountGet added in v1.1.0

func (ctrl *Controller) ApiCertAccountGet(c *echo.Context) error

func (*Controller) ApiCertAccountList added in v1.1.0

func (ctrl *Controller) ApiCertAccountList(c *echo.Context) error

func (*Controller) ApiCertAccountUpdate added in v1.1.0

func (ctrl *Controller) ApiCertAccountUpdate(c *echo.Context) error

func (*Controller) ApiCertChallengeCreate added in v1.1.0

func (ctrl *Controller) ApiCertChallengeCreate(c *echo.Context) error

func (*Controller) ApiCertChallengeDelete added in v1.1.0

func (ctrl *Controller) ApiCertChallengeDelete(c *echo.Context) error

func (*Controller) ApiCertChallengeGet added in v1.1.0

func (ctrl *Controller) ApiCertChallengeGet(c *echo.Context) error

func (*Controller) ApiCertChallengeList added in v1.1.0

func (ctrl *Controller) ApiCertChallengeList(c *echo.Context) error

func (*Controller) ApiCertChallengeUpdate added in v1.1.0

func (ctrl *Controller) ApiCertChallengeUpdate(c *echo.Context) error

func (*Controller) ApiCertificateCreate added in v1.1.0

func (ctrl *Controller) ApiCertificateCreate(c *echo.Context) error

func (*Controller) ApiCertificateDelete added in v1.1.0

func (ctrl *Controller) ApiCertificateDelete(c *echo.Context) error

func (*Controller) ApiCertificateGet added in v1.1.0

func (ctrl *Controller) ApiCertificateGet(c *echo.Context) error

func (*Controller) ApiCertificateList added in v1.1.0

func (ctrl *Controller) ApiCertificateList(c *echo.Context) error

func (*Controller) ApiCertificateUpdate added in v1.1.0

func (ctrl *Controller) ApiCertificateUpdate(c *echo.Context) error

func (*Controller) ApiCertsConfig added in v1.1.0

func (ctrl *Controller) ApiCertsConfig(c *echo.Context) error

func (*Controller) ApiCommonConfig

func (ctrl *Controller) ApiCommonConfig(c *echo.Context) error

ApiCommonConfig returns util.CommonConfig - settings shared by every remote-config-fetching CLI tool, not tied to any one service. Tools that don't need any service-specific config (currently just factum2-worker agents, via internal/worker.FetchRemoteConfig) fetch this directly instead of one of the per-service endpoints (ApiDNSConfig, ApiIcingaConfig, etc.), which already embed the same util.CommonConfig for tools that do need service-specific config too.

func (*Controller) ApiConfigAssignmentDelete added in v1.0.2

func (ctrl *Controller) ApiConfigAssignmentDelete(c *echo.Context) error

func (*Controller) ApiConfigAssignmentList added in v1.0.2

func (ctrl *Controller) ApiConfigAssignmentList(c *echo.Context) error

func (*Controller) ApiConfigAssignmentUpsert added in v1.0.2

func (ctrl *Controller) ApiConfigAssignmentUpsert(c *echo.Context) error

func (*Controller) ApiConfigConnectionTypeImageGet added in v1.1.0

func (ctrl *Controller) ApiConfigConnectionTypeImageGet(c *echo.Context) error

func (*Controller) ApiConfigConnectionTypeImagePut added in v1.1.0

func (ctrl *Controller) ApiConfigConnectionTypeImagePut(c *echo.Context) error

func (*Controller) ApiConfigFeatureCreate added in v1.0.5

func (ctrl *Controller) ApiConfigFeatureCreate(c *echo.Context) error

func (*Controller) ApiConfigFeatureDelete added in v1.0.5

func (ctrl *Controller) ApiConfigFeatureDelete(c *echo.Context) error

func (*Controller) ApiConfigFeatureList added in v1.0.5

func (ctrl *Controller) ApiConfigFeatureList(c *echo.Context) error

func (*Controller) ApiConfigFeatureUpdate added in v1.0.5

func (ctrl *Controller) ApiConfigFeatureUpdate(c *echo.Context) error

func (*Controller) ApiConfigLegacyGone added in v1.0.5

func (ctrl *Controller) ApiConfigLegacyGone(c *echo.Context) error

func (*Controller) ApiConfigMacroCreate added in v1.0.2

func (ctrl *Controller) ApiConfigMacroCreate(c *echo.Context) error

func (*Controller) ApiConfigMacroDelete added in v1.0.2

func (ctrl *Controller) ApiConfigMacroDelete(c *echo.Context) error

func (*Controller) ApiConfigMacroGet added in v1.0.2

func (ctrl *Controller) ApiConfigMacroGet(c *echo.Context) error

func (*Controller) ApiConfigMacroList added in v1.0.2

func (ctrl *Controller) ApiConfigMacroList(c *echo.Context) error

func (*Controller) ApiConfigMacroUpdate added in v1.0.2

func (ctrl *Controller) ApiConfigMacroUpdate(c *echo.Context) error

func (*Controller) ApiConfigMatrix added in v1.0.2

func (ctrl *Controller) ApiConfigMatrix(c *echo.Context) error

func (*Controller) ApiConfigRender added in v1.0.2

func (ctrl *Controller) ApiConfigRender(c *echo.Context) error

func (*Controller) ApiConfigResolve added in v1.0.2

func (ctrl *Controller) ApiConfigResolve(c *echo.Context) error

func (*Controller) ApiConfigResourcesFree added in v1.1.0

func (ctrl *Controller) ApiConfigResourcesFree(c *echo.Context) error

func (*Controller) ApiConfigScopeCreate added in v1.0.2

func (ctrl *Controller) ApiConfigScopeCreate(c *echo.Context) error

func (*Controller) ApiConfigScopeDelete added in v1.0.2

func (ctrl *Controller) ApiConfigScopeDelete(c *echo.Context) error

func (*Controller) ApiConfigScopeDetach added in v1.0.5

func (ctrl *Controller) ApiConfigScopeDetach(c *echo.Context) error

func (*Controller) ApiConfigScopeList added in v1.0.2

func (ctrl *Controller) ApiConfigScopeList(c *echo.Context) error

func (*Controller) ApiConfigScopeMove added in v1.0.5

func (ctrl *Controller) ApiConfigScopeMove(c *echo.Context) error

func (*Controller) ApiConfigScopeTree added in v1.0.2

func (ctrl *Controller) ApiConfigScopeTree(c *echo.Context) error

func (*Controller) ApiConfigScopeUpdate added in v1.0.2

func (ctrl *Controller) ApiConfigScopeUpdate(c *echo.Context) error

func (*Controller) ApiConfigServiceTypeCreate added in v1.0.2

func (ctrl *Controller) ApiConfigServiceTypeCreate(c *echo.Context) error

func (*Controller) ApiConfigServiceTypeDelete added in v1.0.2

func (ctrl *Controller) ApiConfigServiceTypeDelete(c *echo.Context) error

func (*Controller) ApiConfigServiceTypeGet added in v1.0.2

func (ctrl *Controller) ApiConfigServiceTypeGet(c *echo.Context) error

func (*Controller) ApiConfigServiceTypeList added in v1.0.2

func (ctrl *Controller) ApiConfigServiceTypeList(c *echo.Context) error

func (*Controller) ApiConfigServiceTypeUpdate added in v1.0.2

func (ctrl *Controller) ApiConfigServiceTypeUpdate(c *echo.Context) error

func (*Controller) ApiConfigVariableCreate added in v1.0.2

func (ctrl *Controller) ApiConfigVariableCreate(c *echo.Context) error

func (*Controller) ApiConfigVariableDelete added in v1.0.2

func (ctrl *Controller) ApiConfigVariableDelete(c *echo.Context) error

func (*Controller) ApiConfigVariableGet added in v1.0.2

func (ctrl *Controller) ApiConfigVariableGet(c *echo.Context) error

func (*Controller) ApiConfigVariableList added in v1.0.2

func (ctrl *Controller) ApiConfigVariableList(c *echo.Context) error

func (*Controller) ApiConfigVariableUpdate added in v1.0.2

func (ctrl *Controller) ApiConfigVariableUpdate(c *echo.Context) error

func (*Controller) ApiContact

func (ctrl *Controller) ApiContact(c *echo.Context) error

Fetch all contacts, with related company names attached for the list view.

func (*Controller) ApiContactByID

func (ctrl *Controller) ApiContactByID(c *echo.Context) error

func (*Controller) ApiContactCustomers

func (ctrl *Controller) ApiContactCustomers(c *echo.Context) error

func (*Controller) ApiContactCustomersPut

func (ctrl *Controller) ApiContactCustomersPut(c *echo.Context) error

func (*Controller) ApiContactDelete

func (ctrl *Controller) ApiContactDelete(c *echo.Context) error

ApiContactDelete removes a locally-created contact. Lime-synced rows are rejected (they'd just reappear on the next person-sync). CustomerContact join rows are not independent records, so they're removed in the same transaction rather than treated as a blocker. MaintenanceNotification rows keep the sent email and have ContactID cleared so the send log is not deleted with the person.

func (*Controller) ApiContactUpdate

func (ctrl *Controller) ApiContactUpdate(contacts *SecureCRUDHandler[models.Contact, models.ContactDTO]) echo.HandlerFunc

ApiContactUpdate rejects Lime-owned field edits on contacts synced from Lime (name/email/phone get overwritten by the next person-sync in internal/lime/lime.go). NotifyMaintenance is factum-owned, so a Lime row may still toggle that flag - same idea as ApiServiceTypeUpdate on a Lime-sourced service. Everything else delegates to the generic CRUD handler.

func (*Controller) ApiCreateDCIMAddress added in v1.1.0

func (ctrl *Controller) ApiCreateDCIMAddress(c *echo.Context) error

func (*Controller) ApiCreateDCIMInterface added in v1.1.0

func (ctrl *Controller) ApiCreateDCIMInterface(c *echo.Context) error

func (*Controller) ApiCreateInterfaceTemplate added in v1.1.0

func (ctrl *Controller) ApiCreateInterfaceTemplate(c *echo.Context) error

func (*Controller) ApiCreateInterfaceType added in v1.1.1

func (ctrl *Controller) ApiCreateInterfaceType(c *echo.Context) error

func (*Controller) ApiCustomer

func (ctrl *Controller) ApiCustomer(c *echo.Context) error

Fetch all companies, with a per-customer service count attached for the list view.

func (*Controller) ApiCustomerByID

func (ctrl *Controller) ApiCustomerByID(c *echo.Context) error

func (*Controller) ApiCustomerContacts

func (ctrl *Controller) ApiCustomerContacts(c *echo.Context) error

func (*Controller) ApiCustomerDelete

func (ctrl *Controller) ApiCustomerDelete(c *echo.Context) error

ApiCustomerDelete removes a locally-created customer. Lime-synced rows are rejected the same way as updates (they'd just reappear on the next Lime sync). A customer that still has services is also rejected: those rows would be left pointing at a missing company. CustomerContact join rows are not independent records, so they're removed in the same transaction rather than treated as a blocker.

func (*Controller) ApiCustomerUpdate

func (ctrl *Controller) ApiCustomerUpdate(customers *SecureCRUDHandler[models.Customer, models.CustomerDTO]) echo.HandlerFunc

ApiCustomerUpdate rejects edits to customers synced from Lime (the existing row's Source == "lime") before delegating to the generic CRUD handler for everything else. Lime-sourced fields get overwritten wholesale on the next sync run (SaveCustomer in internal/lime/lime.go), so letting an edit through the API would just have it silently discarded on the next sync - better to reject it upfront than have an edit mysteriously disappear.

func (*Controller) ApiDCIMConnectionCreate added in v1.1.1

func (ctrl *Controller) ApiDCIMConnectionCreate(c *echo.Context) error

func (*Controller) ApiDCIMConnectionDelete added in v1.1.1

func (ctrl *Controller) ApiDCIMConnectionDelete(c *echo.Context) error

func (*Controller) ApiDCIMConnectionGraph added in v1.1.1

func (ctrl *Controller) ApiDCIMConnectionGraph(c *echo.Context) error

func (*Controller) ApiDCIMConnectionLayoutGet added in v1.1.1

func (ctrl *Controller) ApiDCIMConnectionLayoutGet(c *echo.Context) error

func (*Controller) ApiDCIMConnectionLayoutPut added in v1.1.1

func (ctrl *Controller) ApiDCIMConnectionLayoutPut(c *echo.Context) error

func (*Controller) ApiDCIMConnectionPair added in v1.1.1

func (ctrl *Controller) ApiDCIMConnectionPair(c *echo.Context) error

func (*Controller) ApiDCIMConnectionUpdate added in v1.1.1

func (ctrl *Controller) ApiDCIMConnectionUpdate(c *echo.Context) error

func (*Controller) ApiDCIMDevicePlace added in v1.1.1

func (ctrl *Controller) ApiDCIMDevicePlace(c *echo.Context) error

func (*Controller) ApiDCIMDeviceUnmount added in v1.1.1

func (ctrl *Controller) ApiDCIMDeviceUnmount(c *echo.Context) error

func (*Controller) ApiDCIMFloorPlanCreate added in v1.1.1

func (ctrl *Controller) ApiDCIMFloorPlanCreate(c *echo.Context) error

func (*Controller) ApiDCIMFloorPlanGet added in v1.1.1

func (ctrl *Controller) ApiDCIMFloorPlanGet(c *echo.Context) error

func (*Controller) ApiDCIMFloorPlanLayout added in v1.1.1

func (ctrl *Controller) ApiDCIMFloorPlanLayout(c *echo.Context) error

func (*Controller) ApiDCIMFloorPlanList added in v1.1.1

func (ctrl *Controller) ApiDCIMFloorPlanList(c *echo.Context) error

func (*Controller) ApiDCIMFloorPlanRename added in v1.1.1

func (ctrl *Controller) ApiDCIMFloorPlanRename(c *echo.Context) error

func (*Controller) ApiDCIMRackCreate added in v1.1.1

func (ctrl *Controller) ApiDCIMRackCreate(c *echo.Context) error

func (*Controller) ApiDCIMRackDelete added in v1.1.1

func (ctrl *Controller) ApiDCIMRackDelete(c *echo.Context) error

func (*Controller) ApiDCIMRackElevation added in v1.1.1

func (ctrl *Controller) ApiDCIMRackElevation(c *echo.Context) error

func (*Controller) ApiDCIMRackUpdate added in v1.1.1

func (ctrl *Controller) ApiDCIMRackUpdate(c *echo.Context) error

func (*Controller) ApiDCIMRacksList added in v1.1.1

func (ctrl *Controller) ApiDCIMRacksList(c *echo.Context) error

func (*Controller) ApiDNSConfig

func (ctrl *Controller) ApiDNSConfig(c *echo.Context) error

ApiDNSConfig returns the DNS sync settings from the database-backed Settings row, so factum2-dns - which typically runs on a different host than the primary - doesn't need a direct Postgres connection just to read these. When the zone editor is enabled it also includes zones and records so the CLI can write dnsmgr2 zone/prefix includes and the JSON records file.

func (*Controller) ApiDeleteDCIMAddress added in v1.1.0

func (ctrl *Controller) ApiDeleteDCIMAddress(c *echo.Context) error

func (*Controller) ApiDeleteDCIMInterface added in v1.1.0

func (ctrl *Controller) ApiDeleteDCIMInterface(c *echo.Context) error

func (*Controller) ApiDeleteInterfaceTemplate added in v1.1.0

func (ctrl *Controller) ApiDeleteInterfaceTemplate(c *echo.Context) error

func (*Controller) ApiDeleteInterfaceType added in v1.1.1

func (ctrl *Controller) ApiDeleteInterfaceType(c *echo.Context) error

func (*Controller) ApiDeviceCreate added in v1.1.0

func (ctrl *Controller) ApiDeviceCreate(c *echo.Context) error

ApiDeviceCreate inserts a Factum-local device (NetboxID=0, CfSource=factum). Manufacturer/model/platform names are copied from catalog rows so the existing denormalized Device columns stay populated for list/detail views.

func (*Controller) ApiDeviceDelete added in v1.1.0

func (ctrl *Controller) ApiDeviceDelete(c *echo.Context) error

ApiDeviceDelete removes a Factum-local device. NetBox-synced rows are rejected — they would come back on the next sync.

func (*Controller) ApiDeviceImpact

func (ctrl *Controller) ApiDeviceImpact(c *echo.Context) error

func (*Controller) ApiDeviceInterfacesRefresh

func (ctrl *Controller) ApiDeviceInterfacesRefresh(c *echo.Context) error

ApiDeviceInterfacesRefresh fetches live interfaces from the device and reconciles factum/Netbox against that list: descriptions of matching names are overwritten with what the device reports, and interfaces that no longer exist on the device are deleted (device-type template ports are kept, matching internal/device-sync's interfacesDelete).

func (*Controller) ApiDeviceInterfacesUpdate

func (ctrl *Controller) ApiDeviceInterfacesUpdate(c *echo.Context) error

ApiDeviceInterfacesUpdate pushes edited interface descriptions out to the device itself (via the EOS driver), Netbox, and factum's own interface table.

func (*Controller) ApiDeviceInterfacesUpdateVlans

func (ctrl *Controller) ApiDeviceInterfacesUpdateVlans(c *echo.Context) error

ApiDeviceInterfacesUpdateVlans pushes edited switchport/VLAN config out to the device itself (EOS/VRP/Cisco SMB drivers only - see globalVlanPlatforms) and Netbox (creating any new VLAN in Settings.DeviceSyncVlanGroupName's Netbox VLAN group as needed, via the same NetboxMgr internal/device-sync uses for its own VLAN sync), then refreshes factum's own interface/VLAN cache for the device by running the single-device netbox.SyncDB used by the Netbox webhook. Unlike ApiDeviceInterfacesUpdate's description path, this fails the whole request if any part of it fails - there's no useful "partially applied" VLAN edit to leave in place.

func (*Controller) ApiDeviceOpticalPorts

func (ctrl *Controller) ApiDeviceOpticalPorts(c *echo.Context) error

func (*Controller) ApiDeviceSyncAuthCreate

func (ctrl *Controller) ApiDeviceSyncAuthCreate(c *echo.Context) error

ApiDeviceSyncAuthCreate/ApiDeviceSyncAuthUpdate replace the generic SecureCRUDHandler for DeviceSyncAuth's write side: Password is json:"-" on the model (so GetAll/GetOne never leak it), but that same tag makes it invisible to SecureCRUDHandler's DTO<->model JSON round-trip in both directions - same reasoning as ApiWorkerNodeCreate/Update for WorkerNode.Token.

func (*Controller) ApiDeviceSyncAuthPassword

func (ctrl *Controller) ApiDeviceSyncAuthPassword(c *echo.Context) error

ApiDeviceSyncAuthToken returns one DeviceSyncAuth row's stored password. Kept separate from GetAll/GetOne (models.DeviceSyncAuth.Password is json:"-", so neither ever includes it) so a bulk list/detail fetch never carries secrets - used to prefill the admin UI's password field so its own eye icon can show it, same pattern as ApiWorkerNodeToken.

func (*Controller) ApiDeviceSyncAuthUpdate

func (ctrl *Controller) ApiDeviceSyncAuthUpdate(c *echo.Context) error

func (*Controller) ApiDeviceSyncConfig

func (ctrl *Controller) ApiDeviceSyncConfig(c *echo.Context) error

ApiDeviceSyncConfig returns everything factum2-device-sync-cli needs to run: the default domain and VRFInGlobal/DeviceStates/DeviceIgnore lists from the database-backed Settings row, per-device credentials from the models.DeviceSyncAuth table, and cfgmgmt inventory maps (sync_source → netbox_type). Device-sync talks to this over REST (util.WithoutHubSocket), not /api/config/service-types. The Netbox client itself isn't served here - that's /api/netbox-config (web.ApiNetboxConfig), which device-sync fetches separately.

func (*Controller) ApiDeviceTypeUpdateChecked added in v1.1.1

func (ctrl *Controller) ApiDeviceTypeUpdateChecked(c *echo.Context) error

func (*Controller) ApiDeviceUpdate added in v1.1.0

func (ctrl *Controller) ApiDeviceUpdate(c *echo.Context) error

ApiDeviceUpdate edits a Factum-local device. NetBox-synced rows are rejected — the next sync would overwrite them.

func (*Controller) ApiDnsDhcpLeases added in v1.1.1

func (ctrl *Controller) ApiDnsDhcpLeases(c *echo.Context) error

ApiDnsDhcpLeases is GET /api/dns/leases. The zone-editor MAC picker lists current Kea IPv4 and IPv6 leases (hostname, IP, MAC). DHCP must be on. Leases are read from local Kea sockets when present, otherwise from the DNS dest worker via the hub.

func (*Controller) ApiDnsPolicyCreate added in v1.0.6

func (ctrl *Controller) ApiDnsPolicyCreate(c *echo.Context) error

func (*Controller) ApiDnsPolicyDelete added in v1.0.6

func (ctrl *Controller) ApiDnsPolicyDelete(c *echo.Context) error

func (*Controller) ApiDnsPolicyGet added in v1.0.6

func (ctrl *Controller) ApiDnsPolicyGet(c *echo.Context) error

func (*Controller) ApiDnsPolicyList added in v1.0.6

func (ctrl *Controller) ApiDnsPolicyList(c *echo.Context) error

func (*Controller) ApiDnsPolicyUpdate added in v1.0.6

func (ctrl *Controller) ApiDnsPolicyUpdate(c *echo.Context) error

func (*Controller) ApiDnsSOACreate added in v1.0.6

func (ctrl *Controller) ApiDnsSOACreate(c *echo.Context) error

func (*Controller) ApiDnsSOADelete added in v1.0.6

func (ctrl *Controller) ApiDnsSOADelete(c *echo.Context) error

func (*Controller) ApiDnsSOAGet added in v1.0.6

func (ctrl *Controller) ApiDnsSOAGet(c *echo.Context) error

func (*Controller) ApiDnsSOAList added in v1.0.6

func (ctrl *Controller) ApiDnsSOAList(c *echo.Context) error

func (*Controller) ApiDnsSOAUpdate added in v1.0.6

func (ctrl *Controller) ApiDnsSOAUpdate(c *echo.Context) error

func (*Controller) ApiDnsTemplateCreate added in v1.0.6

func (ctrl *Controller) ApiDnsTemplateCreate(c *echo.Context) error

func (*Controller) ApiDnsTemplateDelete added in v1.0.6

func (ctrl *Controller) ApiDnsTemplateDelete(c *echo.Context) error

func (*Controller) ApiDnsTemplateGet added in v1.0.6

func (ctrl *Controller) ApiDnsTemplateGet(c *echo.Context) error

func (*Controller) ApiDnsTemplateList added in v1.0.6

func (ctrl *Controller) ApiDnsTemplateList(c *echo.Context) error

func (*Controller) ApiDnsTemplateUpdate added in v1.0.6

func (ctrl *Controller) ApiDnsTemplateUpdate(c *echo.Context) error

func (*Controller) ApiDnsZoneCreate added in v1.0.6

func (ctrl *Controller) ApiDnsZoneCreate(c *echo.Context) error

func (*Controller) ApiDnsZoneDelete added in v1.0.6

func (ctrl *Controller) ApiDnsZoneDelete(c *echo.Context) error

func (*Controller) ApiDnsZoneGet added in v1.0.6

func (ctrl *Controller) ApiDnsZoneGet(c *echo.Context) error

func (*Controller) ApiDnsZoneList added in v1.0.6

func (ctrl *Controller) ApiDnsZoneList(c *echo.Context) error

func (*Controller) ApiDnsZoneUpdate added in v1.0.6

func (ctrl *Controller) ApiDnsZoneUpdate(c *echo.Context) error

func (*Controller) ApiDocsGet added in v1.0.5

func (ctrl *Controller) ApiDocsGet(c *echo.Context) error

ApiDocsGet returns one docs/user/<slug>.md page. Invalid slugs are 400 (never a filesystem lookup); unknown pages are 404.

func (*Controller) ApiDocsList added in v1.0.5

func (ctrl *Controller) ApiDocsList(c *echo.Context) error

ApiDocsList returns the operator doc catalog (slug + title, no bodies). Authenticated so it matches the GUI /doc page; any logged-in user can read these — they are the same Markdown GitHub Pages publishes.

func (*Controller) ApiForgotPassword

func (ctrl *Controller) ApiForgotPassword(c *echo.Context) error

ApiForgotPassword always responds 200 {"ok":true} regardless of whether the email matches an account, whether that account can actually be reset, or whether sending the email succeeded - anything else would let a caller enumerate valid accounts by probing this endpoint.

func (*Controller) ApiGetConnections added in v1.0.5

func (ctrl *Controller) ApiGetConnections(c *echo.Context) error

func (*Controller) ApiGetDCIMAddresses added in v1.1.0

func (ctrl *Controller) ApiGetDCIMAddresses(c *echo.Context) error

func (*Controller) ApiGetDCIMInterfaces added in v1.1.0

func (ctrl *Controller) ApiGetDCIMInterfaces(c *echo.Context) error

func (*Controller) ApiGetDeviceByID

func (ctrl *Controller) ApiGetDeviceByID(c *echo.Context) error

func (*Controller) ApiGetDeviceByName

func (ctrl *Controller) ApiGetDeviceByName(c *echo.Context) error

ApiGetDeviceByName returns the device with a given name as a one-element array (empty if there's no such device), not as a bare object - the same shape as /api/device, so internal/factum.FactumClient parses both with the same []*models.Device. Used by tools that only know a device by name and have no access to the primary's Postgres (currently factum2-driver-cli, via internal/drivers.NewDriverName).

func (*Controller) ApiGetDevices

func (ctrl *Controller) ApiGetDevices(c *echo.Context) error

Fetch all devices. Default is the shallow list (no nested interfaces/addresses) used by the device-list UI. ?include=interfaces loads the full snapshot instead - DNS sync (internal/factum.GetDevicesWithInterfaces) needs per-interface addresses and cannot use the shallow list.

func (*Controller) ApiGetInterfaceTemplates added in v1.1.0

func (ctrl *Controller) ApiGetInterfaceTemplates(c *echo.Context) error

func (*Controller) ApiGetInterfaceType added in v1.1.1

func (ctrl *Controller) ApiGetInterfaceType(c *echo.Context) error

func (*Controller) ApiGetInterfaceTypes added in v1.1.1

func (ctrl *Controller) ApiGetInterfaceTypes(c *echo.Context) error

func (*Controller) ApiGetTopology

func (ctrl *Controller) ApiGetTopology(c *echo.Context) error

ApiGetTopology returns every mappable device and the connections between them, for the 3D network map view.

func (*Controller) ApiGetTopologyDevices added in v1.0.5

func (ctrl *Controller) ApiGetTopologyDevices(c *echo.Context) error

ApiGetTopologyDevices returns every physical device (placed or not) for the map's location-assignment panel. Virtual machines are omitted: they have no coordinates of their own, only the host they run on does. The main /topology payload still omits unlocated devices because the map has nowhere to plot them.

func (*Controller) ApiIcingaConfig

func (ctrl *Controller) ApiIcingaConfig(c *echo.Context) error

ApiIcingaConfig returns the Icinga API connection settings from the database-backed Settings row, so factum2-icinga - which typically runs on a different host than the primary - doesn't need its own copy of these credentials in a local YAML config file.

func (*Controller) ApiIpamForest

func (ctrl *Controller) ApiIpamForest(c *echo.Context) error

ApiIpamForest is the single-tree API: no parent → namespaces; parent is a node key (ns:1, vrf:2, pool:3, pfx:4) → that node's children.

func (*Controller) ApiIpamNamespaceCreate

func (ctrl *Controller) ApiIpamNamespaceCreate(c *echo.Context) error

func (*Controller) ApiIpamNamespaceDelete

func (ctrl *Controller) ApiIpamNamespaceDelete(c *echo.Context) error

func (*Controller) ApiIpamNamespaceGet

func (ctrl *Controller) ApiIpamNamespaceGet(c *echo.Context) error

func (*Controller) ApiIpamNamespaceList

func (ctrl *Controller) ApiIpamNamespaceList(c *echo.Context) error

func (*Controller) ApiIpamNamespaceUpdate

func (ctrl *Controller) ApiIpamNamespaceUpdate(c *echo.Context) error

func (*Controller) ApiIpamPoolCreate

func (ctrl *Controller) ApiIpamPoolCreate(c *echo.Context) error

func (*Controller) ApiIpamPoolDelete

func (ctrl *Controller) ApiIpamPoolDelete(c *echo.Context) error

func (*Controller) ApiIpamPoolList

func (ctrl *Controller) ApiIpamPoolList(c *echo.Context) error

func (*Controller) ApiIpamPrefixCreate

func (ctrl *Controller) ApiIpamPrefixCreate(c *echo.Context) error

func (*Controller) ApiIpamPrefixDelete

func (ctrl *Controller) ApiIpamPrefixDelete(c *echo.Context) error

func (*Controller) ApiIpamPrefixHosts added in v1.1.0

func (ctrl *Controller) ApiIpamPrefixHosts(c *echo.Context) error

func (*Controller) ApiIpamPrefixList

func (ctrl *Controller) ApiIpamPrefixList(c *echo.Context) error

func (*Controller) ApiIpamPrefixUpdate

func (ctrl *Controller) ApiIpamPrefixUpdate(c *echo.Context) error

func (*Controller) ApiIpamTree

func (ctrl *Controller) ApiIpamTree(c *echo.Context) error

func (*Controller) ApiIpamVRFCreate

func (ctrl *Controller) ApiIpamVRFCreate(c *echo.Context) error

func (*Controller) ApiIpamVRFDelete

func (ctrl *Controller) ApiIpamVRFDelete(c *echo.Context) error

func (*Controller) ApiIpamVRFList

func (ctrl *Controller) ApiIpamVRFList(c *echo.Context) error

func (*Controller) ApiIpamVRFListAll added in v1.1.0

func (ctrl *Controller) ApiIpamVRFListAll(c *echo.Context) error

func (*Controller) ApiIpamVRFUpdate

func (ctrl *Controller) ApiIpamVRFUpdate(c *echo.Context) error

func (*Controller) ApiJobTaskEvents

func (ctrl *Controller) ApiJobTaskEvents(c *echo.Context) error

ApiJobTaskEvents lists a single JobTask's JobTaskEvent rows in chronological order - the job-history table's per-subjob drill-in view.

func (*Controller) ApiJobs

func (ctrl *Controller) ApiJobs(c *echo.Context) error

ApiJobs lists the most recent Job rows, newest first, with their JobTasks preloaded in the same query (no N+1 when the frontend opens a job's detail modal) - the job status page's job-history table.

func (*Controller) ApiLdapBrowse

func (ctrl *Controller) ApiLdapBrowse(c *echo.Context) error

ApiLdapBrowse lists the immediate children of the ?dn= query param (or the configured LdapBaseDN if omitted), for the "browse the directory" picker on the LDAP/AD group -> role mapping dialog. Uses the saved Settings connection details (unlike ApiLdapTestConnection, which tests possibly- unsaved form values) since browsing only makes sense once LDAP is actually configured.

func (*Controller) ApiLdapTestConnection

func (ctrl *Controller) ApiLdapTestConnection(c *echo.Context) error

ApiLdapTestConnection dials+binds with the submitted (or saved) settings and reports ok/error as a 200 JSON body rather than a 4xx/5xx status - a failed directory bind isn't an application error, it's the answer to the question the admin is asking. Each configured server is tested on its own (no failover) so a broken secondary isn't hidden by a working primary. `ok` is true only if every configured server succeeded.

func (*Controller) ApiLibrenmsConfig

func (ctrl *Controller) ApiLibrenmsConfig(c *echo.Context) error

ApiLibrenmsConfig returns everything factum2-librenms-cli needs to run - the default domain, LibreNMS REST API settings, and the Sync regex-filter lists, all from the database-backed Settings row - so factum2-librenms-cli, which typically runs on a different host than the primary, doesn't need any local librenms config of its own. LibreNMS's own MySQL credentials aren't included here - factum2-librenms-cli reads those directly from LibreNMS's .env file, since it assumes co-location with the LibreNMS server.

func (*Controller) ApiLibrenmsPendingDeleteList

func (ctrl *Controller) ApiLibrenmsPendingDeleteList(c *echo.Context) error

ApiLibrenmsPendingDeleteList is GET /api/librenms/pending-deletes - the UI's pending-deletion table and factum2-librenms-cli's delete pass.

func (*Controller) ApiLibrenmsPendingDeleteNextSync

func (ctrl *Controller) ApiLibrenmsPendingDeleteNextSync(c *echo.Context) error

ApiLibrenmsPendingDeleteNextSync is POST /api/librenms/pending-deletes/:device_id/delete-next-sync - the UI's "delete on next sync" action. The next factum2-librenms-cli run deletes the LibreNMS device even if the delay has not elapsed (and even if delayed deletion is currently disabled).

func (*Controller) ApiLibrenmsPendingDeleteRemove

func (ctrl *Controller) ApiLibrenmsPendingDeleteRemove(c *echo.Context) error

ApiLibrenmsPendingDeleteRemove is DELETE /api/librenms/pending-deletes/:device_id - factum2-librenms-cli drops the row after a real LibreNMS delete or a restore.

func (*Controller) ApiLibrenmsPendingDeleteUpsert

func (ctrl *Controller) ApiLibrenmsPendingDeleteUpsert(c *echo.Context) error

ApiLibrenmsPendingDeleteUpsert is PUT /api/librenms/pending-deletes/:device_id - factum2-librenms-cli records a newly quarantined device. An existing row keeps its ScheduledAt and ForceDelete so a later sync cannot reset the clock or clear a user's "delete next run" flag.

func (*Controller) ApiLinkCreate

func (ctrl *Controller) ApiLinkCreate(c *echo.Context) error

ApiLinkCreate replaces the generic handler's Create so a new link is always appended after the current highest Position instead of trusting a client-supplied one - drag-and-drop reordering (ApiLinksReorder) is the only path allowed to move an existing row's Position.

func (ctrl *Controller) ApiLinksList(c *echo.Context) error

ApiLinksList returns every dashboard link ordered by Position, then ID as a tiebreaker - shared by the admin CRUD page (GET /api/admin/links) and the dashboard itself (GET /api/links, any authenticated user: the list is meant to be seen by everyone, only edited by admins).

func (*Controller) ApiLinksReorder

func (ctrl *Controller) ApiLinksReorder(c *echo.Context) error

ApiLinksReorder assigns each link's Position from its index in the dropped order.

func (*Controller) ApiListRoles

func (ctrl *Controller) ApiListRoles(c *echo.Context) error

Fetch all roles

func (*Controller) ApiListUsers

func (ctrl *Controller) ApiListUsers(c *echo.Context) error

Fetch all users

func (*Controller) ApiLogin

func (ctrl *Controller) ApiLogin(c *echo.Context) error

ApiLogin authenticates the SPA: on success it issues the "token" cookie via setAuthCookie; on failure it returns a JSON error body with a 401.

func (*Controller) ApiLogout

func (ctrl *Controller) ApiLogout(c *echo.Context) error

func (*Controller) ApiLogsWebSocket

func (ctrl *Controller) ApiLogsWebSocket(c *echo.Context) error

ApiLogsWebSocket streams live slog records (published via hubHandler in logstream.go) to the frontend's log window. Gated by RequireAdmin in web.go: log lines can carry details (hostnames, internal error text) not meant for a non-admin user.

func (*Controller) ApiMaintenanceCreate

func (ctrl *Controller) ApiMaintenanceCreate(c *echo.Context) error

func (*Controller) ApiMaintenanceGet

func (ctrl *Controller) ApiMaintenanceGet(c *echo.Context) error

func (*Controller) ApiMaintenanceImpact

func (ctrl *Controller) ApiMaintenanceImpact(c *echo.Context) error

func (*Controller) ApiMaintenanceList

func (ctrl *Controller) ApiMaintenanceList(c *echo.Context) error

func (*Controller) ApiMaintenanceNotify

func (ctrl *Controller) ApiMaintenanceNotify(c *echo.Context) error

func (*Controller) ApiMaintenanceUpdate

func (ctrl *Controller) ApiMaintenanceUpdate(c *echo.Context) error

func (*Controller) ApiMeGet

func (ctrl *Controller) ApiMeGet(c *echo.Context) error

func (*Controller) ApiMeUpdate

func (ctrl *Controller) ApiMeUpdate(c *echo.Context) error

func (*Controller) ApiNetboxConfig

func (ctrl *Controller) ApiNetboxConfig(c *echo.Context) error

ApiNetboxConfig returns the Netbox API connection settings from the database-backed Settings row, so callers that can't reach the primary's Postgres DB directly - currently factum2-librenms-cli's Sync(), which typically runs on the LibreNMS host, not the primary - can fetch them over REST instead.

func (*Controller) ApiNetboxWebhook

func (ctrl *Controller) ApiNetboxWebhook(c *echo.Context) error

ApiNetboxWebhook receives change-event webhooks from Netbox.

Device / interface / IP: create/update (and interface/IP delete) re-fetches the named device and upserts it. Device delete removes the matching netbox-sourced factum row by the payload's id — GetDevice would return nil once Netbox has already removed the object.

Cable / site / region / location: create/update re-fetches that one object and upserts the Connection or hierarchical Site row; delete removes it by the payload's netbox_id. These are not "resync one named device" — they have no name lookup, and a deleted object cannot be re-fetched.

Tenants and contacts are not applied here: customer→tenant and contact→contact sync are factum→Netbox.

Netbox has no session/token auth for outgoing webhooks; instead it signs the request body with HMAC-SHA512 and sends the hex digest in "X-Hook-Signature", keyed by a shared secret configured on both sides (Settings.NetboxWebhookSecret here, the webhook's "secret" field on Netbox) - verified the same constant-time-compare way as checkServiceToken, and likewise fails closed if the secret isn't configured.

func (*Controller) ApiOpticalDeviceInventoryPut added in v1.0.4

func (ctrl *Controller) ApiOpticalDeviceInventoryPut(c *echo.Context) error

func (*Controller) ApiOpticalKindMapCreate

func (ctrl *Controller) ApiOpticalKindMapCreate(c *echo.Context) error

func (*Controller) ApiOpticalKindMapDelete

func (ctrl *Controller) ApiOpticalKindMapDelete(c *echo.Context) error

func (*Controller) ApiOpticalKindMapList

func (ctrl *Controller) ApiOpticalKindMapList(c *echo.Context) error

func (*Controller) ApiOpticalKindMapUpdate

func (ctrl *Controller) ApiOpticalKindMapUpdate(c *echo.Context) error

func (*Controller) ApiOpticalPortDelete

func (ctrl *Controller) ApiOpticalPortDelete(c *echo.Context) error

func (*Controller) ApiOpticalPortGet

func (ctrl *Controller) ApiOpticalPortGet(c *echo.Context) error

func (*Controller) ApiOpticalPortPut

func (ctrl *Controller) ApiOpticalPortPut(c *echo.Context) error

func (*Controller) ApiOpticalRetraceStale

func (ctrl *Controller) ApiOpticalRetraceStale(c *echo.Context) error

func (*Controller) ApiOpticalTrace

func (ctrl *Controller) ApiOpticalTrace(c *echo.Context) error

func (*Controller) ApiOpticalXConnectCreate

func (ctrl *Controller) ApiOpticalXConnectCreate(c *echo.Context) error

func (*Controller) ApiOpticalXConnectDelete

func (ctrl *Controller) ApiOpticalXConnectDelete(c *echo.Context) error

func (*Controller) ApiOpticalXConnectList

func (ctrl *Controller) ApiOpticalXConnectList(c *echo.Context) error

func (*Controller) ApiOxidizedConfig

func (ctrl *Controller) ApiOxidizedConfig(c *echo.Context) error

ApiOxidizedConfig returns the Oxidized API connection settings from the database-backed Settings row, so factum2-oxidized - which typically runs on a different host than the primary - doesn't need its own copy of these credentials in a local YAML config file.

func (*Controller) ApiOxidizedNodeConfig added in v1.0.3

func (ctrl *Controller) ApiOxidizedNodeConfig(c *echo.Context) error

ApiOxidizedNodeConfig is GET /api/oxidized/node/config?node_full=.

func (*Controller) ApiOxidizedNodeDiff added in v1.0.3

func (ctrl *Controller) ApiOxidizedNodeDiff(c *echo.Context) error

ApiOxidizedNodeDiff is GET /api/oxidized/node/diff?node_full=&oid=&oid2=.

func (*Controller) ApiOxidizedNodeVersion added in v1.0.3

func (ctrl *Controller) ApiOxidizedNodeVersion(c *echo.Context) error

ApiOxidizedNodeVersion is GET /api/oxidized/node/version?node_full=&oid=.

func (*Controller) ApiOxidizedNodeVersions added in v1.0.3

func (ctrl *Controller) ApiOxidizedNodeVersions(c *echo.Context) error

ApiOxidizedNodeVersions is GET /api/oxidized/node/versions?node_full=.

func (*Controller) ApiOxidizedNodes added in v1.0.3

func (ctrl *Controller) ApiOxidizedNodes(c *echo.Context) error

ApiOxidizedNodes is GET /api/oxidized/nodes - the GUI device browser. Proxies oxidized-web GET /nodes.json. Deleted devices are not included; see internal/oxidized/api.go.

func (*Controller) ApiPrometheusConfig added in v1.0.3

func (ctrl *Controller) ApiPrometheusConfig(c *echo.Context) error

ApiPrometheusConfig returns the Prometheus/snmp_exporter sync settings from the database-backed Settings row, so factum2-prometheus - which typically runs on a different host than the primary - doesn't need its own copy of these in a local YAML config file.

func (*Controller) ApiResetPassword

func (ctrl *Controller) ApiResetPassword(c *echo.Context) error

func (*Controller) ApiScheduleCreate

func (ctrl *Controller) ApiScheduleCreate(c *echo.Context) error

ApiScheduleCreate inserts a JobSchedule and, if enabled, stamps NextRunAt from the cron expression so the in-process scheduler can pick it up without waiting for an edit.

func (*Controller) ApiScheduleDelete

func (ctrl *Controller) ApiScheduleDelete(c *echo.Context) error

ApiScheduleDelete removes a JobSchedule. In-flight jobs it already started keep running - the row is only the trigger, not the Job.

func (*Controller) ApiScheduleGet

func (ctrl *Controller) ApiScheduleGet(c *echo.Context) error

ApiScheduleGet returns one JobSchedule by ID.

func (*Controller) ApiScheduleList

func (ctrl *Controller) ApiScheduleList(c *echo.Context) error

ApiScheduleList returns every JobSchedule, enabled first then by name - the Scheduler page's table.

func (*Controller) ApiScheduleUpdate

func (ctrl *Controller) ApiScheduleUpdate(c *echo.Context) error

ApiScheduleUpdate replaces the writable fields and recomputes NextRunAt from now (or clears it when disabled). LastRunAt/LastError/CreatedBy stay as the scheduler last left them.

func (*Controller) ApiServiceByID

func (ctrl *Controller) ApiServiceByID(c *echo.Context) error

func (*Controller) ApiServiceCreate

func (ctrl *Controller) ApiServiceCreate(c *echo.Context) error

ApiServiceCreate creates a service in a single step - the create wizard no longer reserves a service ID before the rest of the form is filled in. If the caller leaves ServiceID blank, the next available <category><5-digit> number for Category is auto-assigned (derived from existing rows, max + 1); otherwise the caller-supplied ID is used as-is after checking it isn't already taken.

func (*Controller) ApiServiceDelete

func (ctrl *Controller) ApiServiceDelete(services *SecureCRUDHandler[models.Service, models.ServiceDTO]) echo.HandlerFunc

ApiServiceDelete refuses Lime-sourced rows (use unrealize to drop realization). Optional RemoveFromDevice / RemoveFromNetbox apply to any definition. Cleanup failure aborts the delete.

func (*Controller) ApiServiceElinePush

func (ctrl *Controller) ApiServiceElinePush(c *echo.Context) error

ApiServiceElinePush is gone; use POST /api/service/:id/push.

func (*Controller) ApiServiceElineUpdate

func (ctrl *Controller) ApiServiceElineUpdate(c *echo.Context) error

ApiServiceElineUpdate is gone; use PUT /api/service/:id/endpoints.

func (*Controller) ApiServiceEndpointsGet added in v1.0.2

func (ctrl *Controller) ApiServiceEndpointsGet(c *echo.Context) error

func (*Controller) ApiServiceEndpointsPut added in v1.0.2

func (ctrl *Controller) ApiServiceEndpointsPut(c *echo.Context) error

ApiServiceEndpointsPut replaces the service's endpoints. When a previous push snapshot exists, rebind/remove talks to devices using DeviceSyncAuth (same credentials as factum2-device-sync), not per-request username/password.

func (*Controller) ApiServicePathGet

func (ctrl *Controller) ApiServicePathGet(c *echo.Context) error

func (*Controller) ApiServicePathPut

func (ctrl *Controller) ApiServicePathPut(c *echo.Context) error

func (*Controller) ApiServicePush added in v1.0.2

func (ctrl *Controller) ApiServicePush(c *echo.Context) error

ApiServicePush renders the translation CLI object and applies CLI sessions. Device login uses DeviceSyncAuth (same credentials as factum2-device-sync). EOS records the operator on the configure-session description; SR OS and IOS-XR put it on `commit comment`.

func (*Controller) ApiServiceTypeUpdate

func (ctrl *Controller) ApiServiceTypeUpdate(c *echo.Context) error

ApiServiceTypeUpdate lets the network GUI attach a service type, bandwidth and max MAC address count to a service - including one synced from Lime, which is otherwise read-only (ApiServiceUpdate) since Lime doesn't supply these fields itself and SaveDelivery now preserves whatever's set here across future syncs. Deliberately not routed through the generic SecureCRUDHandler/ServiceDTO update: that DTO also carries Lime-owned fields (company, delivery points, product, service, comment, service_id, agreement_status), which must stay off-limits for a Lime-sourced row.

func (*Controller) ApiServiceUnrealize added in v1.1.0

func (ctrl *Controller) ApiServiceUnrealize(c *echo.Context) error

ApiServiceUnrealize drops realization (type, endpoints, tree node, cfgmgmt fields) but keeps the commercial row. Allowed on Lime. Honors the same cleanup flags as delete.

func (*Controller) ApiServiceUpdate

func (ctrl *Controller) ApiServiceUpdate(services *SecureCRUDHandler[models.Service, models.ServiceDTO]) echo.HandlerFunc

ApiServiceUpdate rejects edits to services synced from Lime (the existing row's Source == "lime") before delegating to the generic CRUD handler for everything else. Lime-sourced fields get overwritten wholesale on the next sync run (SaveDelivery in internal/lime/lime.go), so letting an edit through the API would just have it silently discarded on the next sync - better to reject it upfront than have an edit mysteriously disappear.

func (*Controller) ApiSettingsGet

func (ctrl *Controller) ApiSettingsGet(c *echo.Context) error

func (*Controller) ApiSettingsTestEmail

func (ctrl *Controller) ApiSettingsTestEmail(c *echo.Context) error

ApiSettingsTestEmail sends a test email using the submitted (or saved) SMTP settings and reports ok/error as a 200 JSON body rather than a 4xx/5xx status - a failed send isn't an application error, it's the answer to the question the admin is asking. Same shape as ApiLdapTestConnection.

func (*Controller) ApiSettingsUpdate

func (ctrl *Controller) ApiSettingsUpdate(c *echo.Context) error

func (*Controller) ApiSiteCreate added in v1.1.0

func (ctrl *Controller) ApiSiteCreate(c *echo.Context) error

func (*Controller) ApiSiteDelete added in v1.1.0

func (ctrl *Controller) ApiSiteDelete(c *echo.Context) error

func (*Controller) ApiSiteGet added in v1.1.0

func (ctrl *Controller) ApiSiteGet(c *echo.Context) error

func (*Controller) ApiSiteList added in v1.1.0

func (ctrl *Controller) ApiSiteList(c *echo.Context) error

func (*Controller) ApiSiteTree added in v1.1.0

func (ctrl *Controller) ApiSiteTree(c *echo.Context) error

func (*Controller) ApiSiteUpdate added in v1.1.0

func (ctrl *Controller) ApiSiteUpdate(c *echo.Context) error

func (*Controller) ApiSoftwareCopy added in v1.1.1

func (ctrl *Controller) ApiSoftwareCopy(c *echo.Context) error

func (*Controller) ApiSoftwareDelete added in v1.1.1

func (ctrl *Controller) ApiSoftwareDelete(c *echo.Context) error

func (*Controller) ApiSoftwareList added in v1.1.1

func (ctrl *Controller) ApiSoftwareList(c *echo.Context) error

func (*Controller) ApiSoftwareMkdir added in v1.1.1

func (ctrl *Controller) ApiSoftwareMkdir(c *echo.Context) error

func (*Controller) ApiSoftwareMove added in v1.1.1

func (ctrl *Controller) ApiSoftwareMove(c *echo.Context) error

func (*Controller) ApiSoftwareUpload added in v1.1.1

func (ctrl *Controller) ApiSoftwareUpload(c *echo.Context) error

func (*Controller) ApiStorageConfig added in v1.1.1

func (ctrl *Controller) ApiStorageConfig(c *echo.Context) error

func (*Controller) ApiSyncTargets

func (ctrl *Controller) ApiSyncTargets(c *echo.Context) error

ApiSyncTargets returns the systems that can be synced, so the frontend can render one button per target without hardcoding the list - filtered down to those enabled in Settings (worker.EnabledSyncTargets), so a source/ destination an admin hasn't activated doesn't show a Sync button at all. Housekeeping is always appended: it is not a Settings-gated sync and is not part of "Sync all", but the overview still offers a Run button.

func (*Controller) ApiSyncTrigger

func (ctrl *Controller) ApiSyncTrigger(c *echo.Context) error

ApiSyncTrigger dispatches :target to exactly one connected worker node activated for it (see internal/worker/hub.go's RemoteManager.StartJob - deliberately single-node per target, not a fan-out like SendCommand/ "factum2-worker run", so a target maps to exactly one execution) and creates a Job row (with one JobTask child) tracking it. Unlike the old rabbitmq-based PublishSync, which silently "succeeded" even with nobody listening, matched==0 is now an observable fact and reported as an error instead - the job/task rows still get created and immediately marked failed, so they show up in history rather than just vanishing. StartJob also refuses to dispatch a target that already has a task in flight (worker.ErrSyncAlreadyRunning), reported here as 409 Conflict - the frontend's Sync buttons are expected to already be disabled in this case, but the server is the actual enforcement point.

func (*Controller) ApiSyncTriggerAll

func (ctrl *Controller) ApiSyncTriggerAll(c *echo.Context) error

ApiSyncTriggerAll dispatches every enabled sync target as one Job with a JobTask per target (RemoteManager.StartJob) - the server-side successor of the old client-side "trigger targets one at a time, waiting for each" loop (SyncOverviewPage.vue's syncAll): targets are dispatched one at a time, each waited on to finish before the next starts (RemoteManager.dispatchRemainingSequentially), in the sources-then- destinations order worker.SequencedSyncAllTargets produces, so a destination sync never runs while (or ahead of) the source sync that feeds it. Unlike ApiSyncTrigger, a single target's conflict/dispatch failure doesn't fail the whole request - it's recorded against that target's JobTask row instead (see worker.RemoteManager.startOneTask), so the response is 200 as long as the parent Job row itself was created. The response's per-target "tasks" only ever has (at most) one entry now - the first target, dispatched synchronously - since the rest are dispatched later from a background goroutine; nothing currently reads that field besides job_id (see SyncOverviewPage.vue's syncAll and factum.FactumClient.TriggerSyncAll), so its shape was kept rather than removed.

func (*Controller) ApiTopologyDeviceLocation added in v1.0.5

func (ctrl *Controller) ApiTopologyDeviceLocation(c *echo.Context) error

ApiTopologyDeviceLocation writes GPS to Netbox: onto the named site (creating it if needed) when site_name is set, or onto the device itself when site_name is empty.

func (*Controller) ApiTopologyGeocode added in v1.0.5

func (ctrl *Controller) ApiTopologyGeocode(c *echo.Context) error

ApiTopologyGeocode reverse-geocodes a map click into a physical address (OSM Nominatim) so the assign-locations panel can show it and, when a site is being written, POST it onto the Netbox site. Missing / failed lookups return an empty address, not an error — the coordinates are still usable on their own.

func (*Controller) ApiTopologySiteLocation added in v1.1.0

func (ctrl *Controller) ApiTopologySiteLocation(c *echo.Context) error

ApiTopologySiteLocation writes GPS onto a Factum-created site and mirrors it onto devices that inherit that site's coordinates.

func (*Controller) ApiUpdateDCIMAddress added in v1.1.0

func (ctrl *Controller) ApiUpdateDCIMAddress(c *echo.Context) error

func (*Controller) ApiUpdateDCIMInterface added in v1.1.0

func (ctrl *Controller) ApiUpdateDCIMInterface(c *echo.Context) error

func (*Controller) ApiUpdateInterfaceTemplate added in v1.1.0

func (ctrl *Controller) ApiUpdateInterfaceTemplate(c *echo.Context) error

func (*Controller) ApiUpdateInterfaceType added in v1.1.1

func (ctrl *Controller) ApiUpdateInterfaceType(c *echo.Context) error

func (*Controller) ApiUserCreate

func (ctrl *Controller) ApiUserCreate(c *echo.Context) error

func (*Controller) ApiUserDelete

func (ctrl *Controller) ApiUserDelete(c *echo.Context) error

ApiUserDelete refuses to delete a user holding the "admin" role, so the last admin account can never be removed through this endpoint (there's no other way to grant that role back once nobody holds it).

func (*Controller) ApiUserGetOne

func (ctrl *Controller) ApiUserGetOne(c *echo.Context) error

func (*Controller) ApiUserList

func (ctrl *Controller) ApiUserList(c *echo.Context) error

func (*Controller) ApiUserUpdate

func (ctrl *Controller) ApiUserUpdate(c *echo.Context) error

func (*Controller) ApiVersion added in v1.0.2

func (ctrl *Controller) ApiVersion(c *echo.Context) error

ApiVersion returns the running binary's release identity (ldflags-stamped version/commit/date, plus Go version). Unauthenticated so the login page can show it. `go run` builds stay at the unstamped defaults.

func (*Controller) ApiWorkerNodeCreate

func (ctrl *Controller) ApiWorkerNodeCreate(c *echo.Context) error

ApiWorkerNodeCreate/ApiWorkerNodeUpdate replace the generic SecureCRUDHandler for WorkerNode's write side: Token is json:"-" on the model (so GetAll/GetOne never leak it), but that same tag makes it invisible to SecureCRUDHandler's DTO<->model JSON round-trip in both directions - without a custom handler, a submitted token would silently never be written to the DB. Same reasoning as ApiUserCreate/ApiUserUpdate for PasswordHash (see handle_user_api.go).

func (*Controller) ApiWorkerNodeToken

func (ctrl *Controller) ApiWorkerNodeToken(c *echo.Context) error

ApiWorkerNodeToken returns a single WorkerNode's stored token. Kept separate from GetAll/GetOne (models.WorkerNode.Token is json:"-", so neither ever includes it) so a bulk list/detail fetch never carries secrets - this is the one explicit, single-node reveal path, used to prefill the admin UI's token field so its own eye icon can show it.

func (*Controller) ApiWorkerNodeUpdate

func (ctrl *Controller) ApiWorkerNodeUpdate(c *echo.Context) error

func (*Controller) ApiWorkerRun

func (ctrl *Controller) ApiWorkerRun(c *echo.Context) error

ApiWorkerRun dispatches a predefined command to every connected node handling the given role (ctrl.RemoteManager.RunAndWait) and streams each LogMsg back as one NDJSON line, flushed immediately - the "factum2-worker run" CLI's replacement for dialing rabbitmq directly, now that only the primary holds the hub connections a command can actually be dispatched over.

func (*Controller) ApiWorkerStatus

func (ctrl *Controller) ApiWorkerStatus(c *echo.Context) error

ApiWorkerStatus lists every configured worker node with its live connection status - the sync status page's main table. Replaces ApiWorkerPing's broadcast-and-wait for nodes managed via ctrl.RemoteManager: status here is a plain read of already-known state.

func (*Controller) RequireAPIAuth

func (ctrl *Controller) RequireAPIAuth(next echo.HandlerFunc) echo.HandlerFunc

RequireAPIAuth identifies the caller either from the "token" cookie (sets "user" in the context) or a service token (see checkServiceToken; sets "auth_method" to "token" instead, since there's no user behind it), responding with a 401 JSON body on failure - API clients can't follow a redirect into an HTML page, which is why this doesn't redirect to /login like the legacy form-POST handlers do.

func (*Controller) RequireAdmin

func (ctrl *Controller) RequireAdmin(next echo.HandlerFunc) echo.HandlerFunc

RequireAdmin rejects the request unless the authenticated user (set by RequireAPIAuth) has the "admin" role. Must run after RequireAPIAuth.

func (*Controller) RequireAdminOrServiceToken

func (ctrl *Controller) RequireAdminOrServiceToken(next echo.HandlerFunc) echo.HandlerFunc

RequireAdminOrServiceToken allows either an admin user (session cookie) or a valid service token through - for endpoints a remote CLI needs to reach non-interactively (see checkServiceToken) but which still carry admin-level secrets (DB credentials, API tokens), so a plain logged-in non-admin user mustn't see them either. Must run after RequireAPIAuth.

func (*Controller) RequireCertsEnabled added in v1.1.0

func (ctrl *Controller) RequireCertsEnabled(next echo.HandlerFunc) echo.HandlerFunc

func (*Controller) RequireDnsZonesEnabled added in v1.0.6

func (ctrl *Controller) RequireDnsZonesEnabled(next echo.HandlerFunc) echo.HandlerFunc

func (*Controller) RequireIpamEnabled

func (ctrl *Controller) RequireIpamEnabled(next echo.HandlerFunc) echo.HandlerFunc

RequireIpamEnabled 404s IPAM routes when the Factum setting is off. Rows are left untouched — this is a visibility gate, not a delete.

func (*Controller) RequireOpticalEnabled

func (ctrl *Controller) RequireOpticalEnabled(next echo.HandlerFunc) echo.HandlerFunc

RequireOpticalEnabled 404s optical routes when the Factum setting is off.

func (*Controller) RequireRead

func (ctrl *Controller) RequireRead(next echo.HandlerFunc) echo.HandlerFunc

RequireRead allows the "admin", "operator" or "viewer" role - any authenticated user granted at least read access to non-admin resources. A user with no role at all is rejected with 403, leaving them only the routes that don't use RequireRead/RequireWrite: the dashboard (GET /api/links) and their own profile (GET/PUT /api/me). A valid service token is also allowed (same rationale as RequireWrite - e.g. FactumClient.GetDeviceByName). Must run after RequireAPIAuth.

func (*Controller) RequireStorageEnabled added in v1.1.1

func (ctrl *Controller) RequireStorageEnabled(next echo.HandlerFunc) echo.HandlerFunc

func (*Controller) RequireWrite

func (ctrl *Controller) RequireWrite(next echo.HandlerFunc) echo.HandlerFunc

RequireWrite allows the "admin" or "operator" role - the only roles permitted to create/update/delete non-admin resources (customer, contact, service, device interfaces, sync triggers, ...). A user with no role, or only "viewer", is rejected with 403. A valid service token (auth_method "token", set by RequireAPIAuth) is also allowed - remote CLIs such as factum2-device-sync have no session user. Must run after RequireAPIAuth.

func (*Controller) SaveUserSetings

func (ctrl *Controller) SaveUserSetings(c *echo.Context) error

SaveUserSetings is unrouted (superseded by ApiMeUpdate) but left in place; it doesn't touch templates so it's out of scope for this cleanup.

type CreateAdminParams added in v1.0.6

type CreateAdminParams struct {
	cmdbase.Params
	Password string `short:"p" optional:"true" descr:"Admin password (FACTUM_ADMIN_PASSWORD or prompt if omitted)"`
}

CreateAdminParams is the createadmin CLI. Password may come from -p, FACTUM_ADMIN_PASSWORD, or an interactive prompt (in that order).

type DCIMAddressDTO added in v1.1.0

type DCIMAddressDTO struct {
	ID            uint   `json:"id"`
	Address       string `json:"address"`
	DNSName       string `json:"dns_name"`
	VRF           string `json:"vrf"`
	Role          string `json:"role"`
	PrefixID      *uint  `json:"prefix_id"`
	Prefix        string `json:"prefix"`
	InterfaceID   uint   `json:"interface_id"`
	InterfaceName string `json:"interface_name"`
	DeviceID      uint   `json:"device_id"`
	DeviceName    string `json:"device_name"`
	NetboxID      uint   `json:"netbox_id"`
	Source        string `json:"source"`
}

type DCIMAddressListDTO added in v1.1.0

type DCIMAddressListDTO struct {
	Items  []DCIMAddressDTO `json:"items"`
	Total  int64            `json:"total"`
	Limit  int              `json:"limit"`
	Offset int              `json:"offset"`
}

type DCIMInterfaceDTO added in v1.1.0

type DCIMInterfaceDTO struct {
	ID          uint   `json:"id"`
	DeviceID    uint   `json:"device_id"`
	DeviceName  string `json:"device_name"`
	Site        string `json:"site"`
	Name        string `json:"name"`
	Type        string `json:"type"`
	Label       string `json:"label"`
	Description string `json:"description"`
	Enabled     bool   `json:"enabled"`
	VRF         string `json:"vrf"`
	NetboxID    uint   `json:"netbox_id"`
	Source      string `json:"source"`
}

type DCIMInterfaceListDTO added in v1.1.0

type DCIMInterfaceListDTO struct {
	Items  []DCIMInterfaceDTO `json:"items"`
	Total  int64              `json:"total"`
	Limit  int                `json:"limit"`
	Offset int                `json:"offset"`
}

type DNSConfigResponse

type DNSConfigResponse struct {
	util.CommonConfig
	DestFile        string              `json:"dest_file"`
	IgnoreModels    string              `json:"ignore_models"`
	IgnorePlatforms string              `json:"ignore_platforms"`
	ZonesEnabled    bool                `json:"zones_enabled"`
	DhcpEnabled     bool                `json:"dhcp_enabled"`
	ZonesFile       string              `json:"zones_file"`
	PrefixesFile    string              `json:"prefixes_file"`
	Zones           []dns.ConfigDNSZone `json:"zones"`
	DHCP            dns.ConfigDHCP      `json:"dhcp"`
}

DNSConfigResponse is what factum2-dns (internal/dns's FetchRemoteConfig) parses - keep the JSON tags in sync with that type.

type DeviceSyncAuthEntry

type DeviceSyncAuthEntry struct {
	Username string `json:"username"`
	Password string `json:"password"`
}

DeviceSyncAuthEntry is one entry of DeviceSyncConfigResponse.Auth - the REST-facing shape of models.DeviceSyncAuth (minus Name, which is the map key instead).

type DeviceSyncConfigResponse

type DeviceSyncConfigResponse struct {
	util.CommonConfig
	VRFInGlobal   []string                       `json:"vrf_in_global"`
	DeviceStates  []string                       `json:"device_states"`
	DeviceIgnore  []string                       `json:"device_ignore"`
	VlanGroupName string                         `json:"vlan_group_name"`
	InventoryMaps map[string]string              `json:"inventory_maps"`
	Auth          map[string]DeviceSyncAuthEntry `json:"auth"`
}

DeviceSyncConfigResponse is what internal/device-sync's FetchRemoteConfig parses - keep the JSON tags in sync with that type.

type ForgotPasswordRequest

type ForgotPasswordRequest struct {
	Email string `json:"email"`
}

type GuiParams

type GuiParams struct {
	cmdbase.Params
	Bind string `default:":8090"`
}

type IcingaConfigResponse

type IcingaConfigResponse struct {
	util.CommonConfig
	URL      string `json:"url"`
	Username string `json:"username"`
	Password string `json:"password"`

	HostsFile           string                  `json:"hosts_file"`
	UsersFile           string                  `json:"users_file"`
	CertsFile           string                  `json:"certs_file"`
	IgnoreDevices       string                  `json:"ignore_devices"`
	DefaultNotification string                  `json:"default_notification"`
	HostTemplate        string                  `json:"host_template"`
	DependencyTemplate  string                  `json:"dependency_template"`
	UserTemplate        string                  `json:"user_template"`
	CertTemplate        string                  `json:"cert_template"`
	Certificates        []util.ConfigIcingaCert `json:"certificates"`
}

IcingaConfigResponse is what factum2-icinga (internal/icinga's FetchRemoteConfig) parses - keep the JSON tags in sync with that type.

type LdapBrowseEntryDTO

type LdapBrowseEntryDTO struct {
	DN      string `json:"dn"`
	Name    string `json:"name"`
	IsGroup bool   `json:"is_group"`
}

LdapBrowseEntryDTO is one node in the tree-browser response.

type LdapTestRequest

type LdapTestRequest struct {
	Host          string `json:"ldap_host"`
	Port          uint16 `json:"ldap_port"`
	Host2         string `json:"ldap_host2"`
	Port2         uint16 `json:"ldap_port2"`
	TLSMode       string `json:"ldap_tls_mode"`
	SkipTLSVerify bool   `json:"ldap_skip_tls_verify"`
	BindDN        string `json:"ldap_bind_dn"`
	BindPassword  string `json:"ldap_bind_password,omitempty"`
	BaseDN        string `json:"ldap_base_dn"`
}

LdapTestRequest carries the (possibly-unsaved, currently-in-form) LDAP connection fields for the admin "Test Connection" button. BindPassword is optional: if blank AND BindDN is set, the currently-saved Settings.LdapBindPassword is used instead, so the admin doesn't have to retype a secret just to test other field changes - same "omit to keep existing value" pattern as WorkerNodeDTO.Token/UserDTO.Password. An empty BindDN is an anonymous bind: do not fill in a leftover saved password.

type LdapTestServerDTO added in v1.0.2

type LdapTestServerDTO struct {
	Host  string `json:"host"`
	Port  uint16 `json:"port"`
	OK    bool   `json:"ok"`
	Error string `json:"error,omitempty"`
}

LdapTestServerDTO is one host's result in ApiLdapTestConnection's response, so a dual-server setup can show which replica answered.

type LibrenmsConfigResponse

type LibrenmsConfigResponse struct {
	util.CommonConfig
	URL                  string `json:"url"`
	Key                  string `json:"key"`
	PersistentDevices    string `json:"persistent_devices"`
	DelayedDeleteEnabled bool   `json:"delayed_delete_enabled"`
	DelayedDeleteDays    int    `json:"delayed_delete_days"`
	RolesEnabled         string `json:"roles_enabled"`
	InterfacesDisabled   string `json:"interfaces_disabled"`
	SNMPVersion          string `json:"snmp_version"`
	SNMPCommunities      string `json:"snmp_communities"`
}

LibrenmsConfigResponse is what factum2-librenms-cli (internal/librenms's FetchRemoteConfig) parses - keep the JSON tags in sync with that type.

type LinksReorderRequest

type LinksReorderRequest struct {
	IDs []uint `json:"ids"`
}

LinksReorderRequest is the drag-and-drop result from the admin page: every link ID in its new display order.

type LogEvent

type LogEvent struct {
	Time    time.Time `json:"time"`
	Level   string    `json:"level"`
	Message string    `json:"message"`
	// Source is the subsystem that produced the line (a sync target like
	// "librenms"/"netbox", "hub", "job", or "web") so the log window can
	// label mixed streams. Derived from slog attrs, not a separate logger.
	Source string            `json:"source,omitempty"`
	Attrs  map[string]string `json:"attrs,omitempty"`
}

LogEvent is the JSON shape streamed to the frontend log window - one per slog record emitted anywhere in the process (see hubHandler below).

type LogHub

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

LogHub fans out LogEvents to every connected websocket client and keeps a bounded ring buffer so newly-subscribed clients get recent history.

func NewLogHub

func NewLogHub() *LogHub

func (*LogHub) Publish

func (h *LogHub) Publish(e LogEvent)

Publish fans out an event to all subscribers and appends it to history. A subscriber whose channel is full is skipped rather than blocked on - log production must never stall waiting on a slow/stuck browser tab.

func (*LogHub) Subscribe

func (h *LogHub) Subscribe() (ch chan LogEvent, history []LogEvent, unsubscribe func())

Subscribe registers a new client, returning its event channel, a snapshot of recent history to replay, and an unsubscribe func the caller must run (typically deferred) once it's done reading.

type LoginRequest

type LoginRequest struct {
	Username   string `json:"username"`
	Password   string `json:"password"`
	RememberMe bool   `json:"remember_me"`
}

type MeUpdateRequest

type MeUpdateRequest struct {
	Name            string `json:"name"`
	Email           string `json:"email"`
	Mobile          string `json:"mobile"`
	CurrentPassword string `json:"current_password,omitempty"`
	NewPassword     string `json:"new_password,omitempty"`
}

type NetboxConfigResponse

type NetboxConfigResponse struct {
	util.CommonConfig
	URL   string `json:"url"`
	Token string `json:"token"`
}

NetboxConfigResponse is what internal/netbox's FetchRemoteConfig parses - keep the JSON tags in sync with that type.

type NetboxWebhookPayload

type NetboxWebhookPayload struct {
	Event      string         `json:"event"`
	ObjectType string         `json:"object_type"`
	Data       map[string]any `json:"data"`
}

NetboxWebhookPayload is the JSON body Netbox posts for its default webhook body template (https://netboxlabs.com/docs/netbox/integrations/webhooks/): event/object_type/etc plus the changed object's serialized representation in Data - object_type is "app_label.model_name" (e.g. "dcim.device"), per Netbox's send_webhook() (extras/webhooks.py), which builds the context dict from object_type.natural_key(). Only the fields ApiNetboxWebhook needs are declared.

type OxidizedConfigResponse

type OxidizedConfigResponse struct {
	util.CommonConfig
	URL  string `json:"url"`
	User string `json:"user"`
	Pass string `json:"pass"`

	DestFile            string `json:"dest_file"`
	IgnoreDevices       string `json:"ignore_devices"`
	IgnoreManufacturers string `json:"ignore_manufacturers"`
	IgnoreModels        string `json:"ignore_models"`
	IgnorePlatforms     string `json:"ignore_platforms"`
}

OxidizedConfigResponse is what factum2-oxidized (internal/oxidized's FetchRemoteConfig) parses - keep the JSON tags in sync with that type.

type PrometheusConfigResponse added in v1.0.3

type PrometheusConfigResponse struct {
	util.CommonConfig
	DestFile            string `json:"dest_file"`
	ReloadURL           string `json:"reload_url"`
	Module              string `json:"module"`
	Auth                string `json:"auth"`
	IgnoreDevices       string `json:"ignore_devices"`
	IgnoreManufacturers string `json:"ignore_manufacturers"`
	IgnoreModels        string `json:"ignore_models"`
	IgnorePlatforms     string `json:"ignore_platforms"`
}

PrometheusConfigResponse is what factum2-prometheus (internal/prometheus's FetchRemoteConfig) parses - keep the JSON tags in sync with that type.

type ResetPasswordRequest

type ResetPasswordRequest struct {
	Token       string `json:"token,omitempty"`
	Email       string `json:"email,omitempty"`
	Code        string `json:"code,omitempty"`
	NewPassword string `json:"new_password"`
}

type SecureCRUDHandler

type SecureCRUDHandler[M any, Req any] struct {
	DB *gorm.DB
}

M = Database Model, Req = Request DTO

func NewSecureCRUDHandler

func NewSecureCRUDHandler[M any, Req any](db *gorm.DB) *SecureCRUDHandler[M, Req]

func (*SecureCRUDHandler[M, Req]) Create

func (h *SecureCRUDHandler[M, Req]) Create(c *echo.Context) error

3. CREATE: POST /api/resource Safety: Prevents Mass Assignment by binding to the DTO first.

func (*SecureCRUDHandler[M, Req]) Delete

func (h *SecureCRUDHandler[M, Req]) Delete(c *echo.Context) error

5. DELETE: DELETE /api/resource/:id

func (*SecureCRUDHandler[M, Req]) GetAll

func (h *SecureCRUDHandler[M, Req]) GetAll(c *echo.Context) error

1. GET ALL: GET /api/resource

func (*SecureCRUDHandler[M, Req]) GetOne

func (h *SecureCRUDHandler[M, Req]) GetOne(c *echo.Context) error

2. GET ONE: GET /api/resource/:id Safety: Safe by default if your Model utilizes `json:"-"` tags on private fields. The path id is parsed as uint before it reaches GORM: a non-numeric string cond is treated as a raw SQL fragment (Statement.BuildCondition), so First(&item, c.Param("id")) is a SQL-injection hole.

func (*SecureCRUDHandler[M, Req]) Update

func (h *SecureCRUDHandler[M, Req]) Update(c *echo.Context) error

4. UPDATE: PUT /api/resource/:id

type ServiceDTO

type ServiceDTO struct {
	ID       uint   `json:"id"`
	Name     string `json:"name"`
	Customer string `json:"company"`

	ServiceID     string `json:"service_id"`
	Category      string `json:"category"`
	ServiceType   string `json:"service_type"`
	BandwidthMbps int    `json:"bandwidth_mbps"`

	Deliverypoint1  string `json:"deliverypoint1"`
	Deliverypoint2  string `json:"deliverypoint2"`
	Product         string `json:"product"`
	Service         string `json:"service"`
	AgreementStatus string `json:"agreement_status"`
	Source          string `json:"source"`
}

type ServiceDeleteRequest

type ServiceDeleteRequest struct {
	RemoveFromNetbox bool `json:"remove_from_netbox"`
	RemoveFromDevice bool `json:"remove_from_device"`
}

ServiceDeleteRequest is the optional body for ApiServiceDelete and ApiServiceUnrealize. Device login uses DeviceSyncAuth (same credentials as factum2-device-sync), not per-request username/password.

type ServiceDetailResponse

type ServiceDetailResponse struct {
	models.Service
	AppliedToDevice bool                     `json:"applied_to_device"`
	Endpoints       []models.ServiceEndpoint `json:"endpoints,omitempty"`
}

ServiceDetailResponse adds AppliedToDevice to the raw Service model - a derived signal for whether the service has live config on a device (service_endpoints.applied_device_id). Used by the delete dialog (ServiceList.vue) to decide whether to offer a "remove from device" cleanup option.

type ServiceElineDTO

type ServiceElineDTO struct {
	EndpointADeviceID    uint `json:"endpoint_a_device_id"`
	EndpointAInterfaceID uint `json:"endpoint_a_interface_id"`
	EndpointAVlan        int  `json:"endpoint_a_vlan"`

	EndpointBDeviceID    uint `json:"endpoint_b_device_id"`
	EndpointBInterfaceID uint `json:"endpoint_b_interface_id"`
	EndpointBVlan        int  `json:"endpoint_b_vlan"`
}

type ServiceTypeDTO

type ServiceTypeDTO struct {
	ServiceType      string          `json:"service_type"`
	BandwidthMbps    int             `json:"bandwidth_mbps"`
	MaxMacAddresses  int             `json:"max_mac_addresses"`
	Fields           json.RawMessage `json:"fields"`
	ConnectionTypeID *uint           `json:"connection_type_id"`
}

ServiceTypeDTO is the request shape for ApiServiceTypeUpdate - the one slice of a service's data that's editable regardless of Source, since SaveDelivery (internal/lime/lime.go) now preserves these specific fields across a Lime resync instead of overwriting them.

type SettingsTestEmailRequest

type SettingsTestEmailRequest struct {
	To          string `json:"to"`
	SmtpHost    string `json:"smtp_host"`
	SmtpPort    uint16 `json:"smtp_port"`
	SmtpUser    string `json:"smtp_user"`
	SmtpPass    string `json:"smtp_pass,omitempty"`
	SmtpTLSMode string `json:"smtp_tls_mode"`
	EmailSender string `json:"email_sender"`
}

SettingsTestEmailRequest carries the (possibly-unsaved, currently-in-form) SMTP fields plus the recipient address(es) for the admin "Send test email" button. To is free-form textarea content, one address (or comma-separated addresses) per line - same "one item per line" convention as the Ignore*/Roles* textareas elsewhere on this page. SmtpPass is optional: if blank, the currently-saved Settings.SmtpPass is used instead, same "omit to keep existing value" pattern as LdapTestRequest.BindPassword.

type TopologyDTO

type TopologyDTO struct {
	Devices []TopologyDeviceDTO `json:"devices"`
	Edges   []TopologyEdgeDTO   `json:"edges"`
	Sites   []TopologySiteDTO   `json:"sites"`
}

type TopologyDeviceDTO

type TopologyDeviceDTO struct {
	ID           uint    `json:"id"`
	Name         string  `json:"name"`
	Site         string  `json:"site"`
	Role         string  `json:"role"`
	Status       string  `json:"status"`
	Manufacturer string  `json:"manufacturer"`
	ModelName    string  `json:"model_name"`
	OpticalKind  string  `json:"optical_kind"`
	Latitude     float64 `json:"latitude"`
	Longitude    float64 `json:"longitude"`
}

TopologyDeviceDTO is one device positioned on the network map. Only devices with resolved coordinates (see models.Device.Latitude/Longitude) are ever included - the map has nowhere to place the rest.

type TopologyDeviceListDTO added in v1.0.5

type TopologyDeviceListDTO struct {
	ID           uint     `json:"id"`
	Name         string   `json:"name"`
	Site         string   `json:"site"`
	SiteID       uint     `json:"site_id"`
	Role         string   `json:"role"`
	Status       string   `json:"status"`
	Manufacturer string   `json:"manufacturer"`
	ModelName    string   `json:"model_name"`
	NetboxID     uint     `json:"netbox_id"`
	VM           bool     `json:"vm"`
	Latitude     *float64 `json:"latitude"`
	Longitude    *float64 `json:"longitude"`
}

TopologyDeviceListDTO is one device for the map's location-assignment panel. Unlike TopologyDeviceDTO it includes devices with no coordinates, so the operator can find unplaced devices and pin them.

type TopologyDevicesDTO added in v1.0.5

type TopologyDevicesDTO struct {
	Devices []TopologyDeviceListDTO `json:"devices"`
	Sites   []TopologySiteListDTO   `json:"sites"`
}

type TopologyEdgeDTO

type TopologyEdgeDTO struct {
	ID                    uint   `json:"id"`
	DeviceAID             uint   `json:"device_a_id"`
	InterfaceA            string `json:"interface_a"`
	InterfaceADescription string `json:"interface_a_description"`
	DeviceBID             uint   `json:"device_b_id"`
	InterfaceB            string `json:"interface_b"`
	InterfaceBDescription string `json:"interface_b_description"`
	Label                 string `json:"label"`
}

TopologyEdgeDTO is one Connection (Netbox cable) between two devices already present in the same response's Devices list.

type TopologyGeocodeDTO added in v1.0.5

type TopologyGeocodeDTO struct {
	Address string `json:"address"`
}

type TopologyLocationResponse added in v1.0.5

type TopologyLocationResponse struct {
	Device TopologyDeviceListDTO `json:"device"`
	Site   *TopologySiteDTO      `json:"site,omitempty"`
}

type TopologySiteDTO

type TopologySiteDTO struct {
	ID        uint    `json:"id"`
	Name      string  `json:"name"`
	Source    string  `json:"source"`
	Latitude  float64 `json:"latitude"`
	Longitude float64 `json:"longitude"`
}

TopologySiteDTO is a Netbox site plotted on the map independently of any device - see models.Site, synced by internal/netbox.syncSites - so a site with no devices of its own still shows up.

type TopologySiteListDTO added in v1.1.0

type TopologySiteListDTO struct {
	ID        uint     `json:"id"`
	Name      string   `json:"name"`
	Source    string   `json:"source"`
	Latitude  *float64 `json:"latitude"`
	Longitude *float64 `json:"longitude"`
}

TopologySiteListDTO is one site for the map's location-assignment panel, including sites that have no GPS yet.

type TopologySiteLocationResponse added in v1.1.0

type TopologySiteLocationResponse struct {
	Site TopologySiteDTO `json:"site"`
}

type WorkerStatusEntry

type WorkerStatusEntry struct {
	Name      string    `json:"name"`
	Address   string    `json:"address"`
	Enabled   bool      `json:"enabled"`
	Connected bool      `json:"connected"`
	Hostname  string    `json:"hostname"`
	Roles     []string  `json:"roles"`
	Version   string    `json:"version,omitempty"`
	LastSeen  time.Time `json:"last_seen"`
	LastError string    `json:"last_error,omitempty"`
}

WorkerStatusEntry is one configured models.WorkerNode merged with its live connection state from ctrl.RemoteManager - unlike ApiWorkerPing, this always lists every configured node (connected or not), since RemoteManager already holds a live-or-retrying connection to each one rather than needing a broadcast round trip to find out.

Jump to

Keyboard shortcuts

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