Documentation
¶
Overview ¶
Package api exposes the app over HTTP: a small REST surface plus a WebSocket stream under /api, and the embedded SPA everywhere else.
This file is the router and the two pieces of middleware every request passes through. The endpoints themselves live in routes_*.go, one file per subsystem, and every one of them registers through the table in routes.go — see the note there for why nothing may attach a handler to the mux by hand.
Index ¶
Constants ¶
const AnyMethod = ""
AnyMethod is the method of a route that answers whatever it is sent, because it forwards the request somewhere else and the method is part of what it forwards. It is not a way to avoid deciding: a route that acts on this instance names its method, so that a GET can never be made to do a POST's job.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type ConnectInfo ¶
type ConnectInfo struct {
// Active is whether this instance has a connection secret at all.
Active bool `json:"active"`
// Connected is whether the relay socket is actually up right now, which
// is a different question: a stored secret with a relay that cannot be
// reached is configured but not working, and collapsing the two is what
// made the old relay card unable to say which was wrong.
Connected bool `json:"connected"`
// PasswordSet mirrors GET /api/auth, so the page can warn - before
// anything is generated - that the phrase it is about to hand out
// reaches every instance in the group, and that this one is unprotected.
PasswordSet bool `json:"passwordSet"`
// RelayURL is which relay this instance dials: the compiled-in default,
// or an override somebody set to point at their own.
RelayURL string `json:"relayUrl"`
// SelfHosted is whether that is an override rather than the default -
// the one bit of the address a person actually needs to see.
SelfHosted bool `json:"selfHosted"`
}
ConnectInfo is what GET /api/connect answers with. Deliberately never the phrase itself - that needs the password (see the reveal route).
type DeploymentInfo ¶
type DeploymentInfo struct {
// Deployment is "container" (cmd/knightloader, the default) or
// "desktop" (the Wails build) — see buildinfo.Deployment.
Deployment string `json:"deployment"`
// CanQuit and CanRestart report whether this process actually has a way
// to act on the two routes below at all — false wherever RequestExit was
// never wired. Both read the same field today because the mechanism is
// identical either way (see RequestExit's own doc comment); they are two
// fields rather than one so a build that ever wants to offer only one of
// the two can say so without changing the response shape.
CanQuit bool `json:"canQuit"`
CanRestart bool `json:"canRestart"`
// Note is plain English from the server about what quit and restart
// actually do in THIS build — the same "a fact about this build, not a
// translation key" convention Feature.Reason already uses (routes_
// features.go), for the same reason: what matters here (does something
// outside this process bring it back) is true or false about the
// deployment, not about the button, and 38 locale files should not have
// to gain a key every time that sentence is reworded.
Note string `json:"note"`
}
DeploymentInfo is what the interface needs to label a quit/restart control honestly instead of offering the same two buttons everywhere.
type Diagnostics ¶
type Diagnostics struct {
GeneratedAt time.Time `json:"generatedAt"`
Version string `json:"version"`
// Deployment is "container" or "desktop" (internal/buildinfo) - the same
// fact 10D's quit/restart route reports, worth repeating here because a
// bug report attached from a browser gives no other way to tell which
// binary produced it.
Deployment string `json:"deployment"`
GoVersion string `json:"goVersion"`
OS string `json:"os"`
Arch string `json:"arch"`
Goroutines int `json:"goroutines"`
// Settings is what GET /api/settings sends, with one further redaction
// on top: Settings.Redacted() covers the router and proxy passwords (see
// its own doc comment, internal/settings/settings_network.go) but was
// never meant to cover ArchivePasswords - that field is ordinary,
// visible config on the Archives settings page, where a user is editing
// their own passwords and needs to see them. This bundle is a different
// exposure: a file meant to be attached to a PUBLIC bug report, where
// the same values have no business appearing. Cleared here rather than
// by changing Settings.Redacted() itself, which would incorrectly blank
// the Archives page too. ArchivePasswordCount below keeps the one
// diagnostically useful fact (is anything configured at all) without
// the values themselves.
Settings settings.Settings `json:"settings"`
ArchivePasswordCount int `json:"archivePasswordCount"`
// LogLines is the ring's own recent tail, oldest first - see
// internal/logring for what feeds it and why capturing it costs no
// changes anywhere else in the tree.
LogLines []string `json:"logLines"`
// LogCapacity is how many lines the ring keeps at most, so the page can
// say "showing the last N" without a second copy of that number.
LogCapacity int `json:"logCapacity"`
}
Diagnostics is everything GET /api/diagnostics answers. The frontend's live preview and its "download bundle" button read the same document, so there is nothing the page shows that the saved file does not also carry.
type Feature ¶
type Feature struct {
// ID is stable and is what the interface looks a label up by. The label is
// deliberately not here: a server string cannot be translated by the browser,
// and this instance does not know which of the 42 locales is looking at it.
ID string `json:"id"`
Verdict FeatureVerdict `json:"verdict"`
// Page is the settings sub-page this module is configured on, empty when it
// has none. It is what lets a page with nothing shipped behind it explain
// itself out of this table rather than inventing its own excuse.
Page string `json:"page"`
// Enabled is computed from live state on every request. See the file comment.
Enabled bool `json:"enabled"`
Switch FeatureSwitch `json:"switch"`
// Parked is whether a SwitchParked module has a value waiting to come back.
//
// It is the difference between "somebody switched this off" and "this was
// never set up", and without it the two are the same row. That collapse is a
// deadlock on a fresh install: the page that configures the module disables
// its field because the module reads off, and the switch refuses to turn on
// because nothing is configured, so there is no way in from either end.
Parked bool `json:"parked"`
// Reason is why the verdict is what it is, or why there is no switch. It is
// English prose from the server for the same reason a Go error is: it is a
// fact about this build, and inventing a translation key per build fact means
// 42 files change every time a subsystem lands.
Reason string `json:"reason,omitempty"`
// Detail is one line of live state — the folder being watched, the port, how
// many rules there are — so the row says something even when the switch does
// not apply.
Detail string `json:"detail,omitempty"`
}
Feature is one subsystem as this build has it.
type FeaturePage ¶
type FeaturePage struct {
ID string `json:"id"`
// Modules are the module ids configured on this page, so the page can render
// the registry's reason for what is missing instead of writing its own.
Modules []string `json:"modules"`
}
FeaturePage is one settings sub-page as registered. Every page is listed even when it is empty: a later wave then fills a page that already exists, with a route people may already have bookmarked, instead of inventing one and deciding its name and place all over again.
type FeatureState ¶
type FeatureState struct {
Modules []Feature `json:"modules"`
Pages []FeaturePage `json:"pages"`
}
FeatureState is the whole registry as one document, because the modules page and the rail both need all of it and two requests would let them disagree for as long as the second one is in flight.
type FeatureSwitch ¶
type FeatureSwitch string
FeatureSwitch is how a module is switched, and — the part that matters — a declaration of whether it can be switched from here at all.
const ( // SwitchNone: there is no switch. Reason says why, and the interface renders // the control disabled carrying that reason. A switch that stores a boolean // nothing reads is worse than no switch, because it looks like it worked. SwitchNone FeatureSwitch = "none" // SwitchSetting: a boolean the subsystem re-reads every time it is about to // act, so clearing it stops the next action. Nothing already in flight is // killed, and none of these subsystems holds a goroutine open between // actions, so there is nothing left running to leak. SwitchSetting FeatureSwitch = "setting" // SwitchParked: the subsystem is configured by a value rather than by a flag, // so "off" means clearing that value — which genuinely tears it down, because // the app applies the cleared value the same way it applies any other save. // The old value is parked so switching back on restores it instead of handing // the user an empty field and a shrug. SwitchParked FeatureSwitch = "parked" )
type FeatureVerdict ¶
type FeatureVerdict string
FeatureVerdict is what this build can honestly say about a subsystem. Three answers, because "not there" and "not there in this build" are different facts and a user who reads the second one as the first files a bug.
const ( // VerdictShipped: the code is in this binary and reachable from the server. VerdictShipped FeatureVerdict = "shipped" // VerdictDesktop: built, but only reachable in the desktop bundle. The // container has no desktop session to put it in. VerdictDesktop FeatureVerdict = "desktop" // VerdictNotBuilt: absent, and Reason says why. Never an empty section // labelled "not installed", which reads as a broken page rather than a // decision — Go has no portable dynamic plugin loading, so the set of // modules is fixed when the binary is built and the page says so. VerdictNotBuilt FeatureVerdict = "not-built" )
type HelpIndex ¶
type HelpIndex struct {
Version string `json:"version"`
Deployment string `json:"deployment"`
// About is one paragraph of orientation for somebody who has never seen
// this API before: how it is guarded, and what "open" below means.
About string `json:"about"`
// Vocabulary is why every route name here is KnightLoader's own, never
// JDownloader's Deprecated API or My.JDownloader's remote namespaces.
// This is the field section 8's Wave 11 amendment asks for by name, so a
// future contributor reads a decision here instead of an absence and
// does not go half-build a compatibility shim that buys nothing.
Vocabulary string `json:"vocabulary"`
// RemoteAccess is why there is no hosted relay and no pairing route, and
// points at what this build offers instead: GET /api/remote-access and
// POST /api/tokens.
RemoteAccess string `json:"remoteAccess"`
// Routes is the full table, sorted by path then method, the same slice
// TestOnlyTheseRoutesAreOpen and TestEveryRouteDescribesItself already
// hold every route in this build to.
Routes []Route `json:"routes"`
}
HelpIndex is what GET /api/help answers with.
type QRMatrix ¶
type QRMatrix struct {
Size int `json:"size"`
// Bits is one string per row, '1' for a dark module and '0' for a light
// one: a fixed-width string per row rather than size squared individual
// booleans, because the JSON is a quarter the size and there is nothing
// to interpret beyond "index into this string".
Bits []string `json:"bits"`
}
QRMatrix is a QR code as the plain module grid rsc.io/qr computed, not a rendered image. The frontend draws it as inline SVG (web/src/components/ QRCode.tsx) deliberately: a matrix has no format to disagree about between light and dark theme framing, no caching semantics of its own to get wrong, and no second render path (an <img> endpoint) that could fall out of sync with the address list this same response already carries.
type ReachableAddress ¶
type ReachableAddress struct {
// Label names where this address came from: "this connection" for the
// one the request itself arrived on, "known" for one remembered or typed
// in by hand (see rememberDomain below), otherwise the interface's own
// IP.
Label string `json:"label"`
URL string `json:"url"`
// Loopback is true for 127.0.0.1/localhost/::1: reachable only from this
// same machine, never a phone on the LAN, and never what the QR code
// should encode.
Loopback bool `json:"loopback"`
// Domain is true when URL's host is a real hostname rather than a bare
// IP - a domain behind a reverse proxy or VPN is what actually lets
// pairing and the QR code work from outside this LAN, so it outranks a
// LAN IP the moment one is known (see preferredAddress below), and the
// Access tab uses this to tell a remembered domain apart from a plain
// interface IP in the same list.
Domain bool `json:"domain"`
}
ReachableAddress is one URL this instance might answer on.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry collects the routes as each subsystem registers them.
func (*Registry) Add ¶
func (reg *Registry) Add(method, path, summary string, h http.HandlerFunc)
Add registers a route that needs a session once a password is set, which is everything except the handful the login flow itself depends on.
func (*Registry) AddOpen ¶
func (reg *Registry) AddOpen(method, path, summary string, h http.HandlerFunc)
AddOpen registers a route reachable without a session. Use it only for a route that is either how somebody gets a session in the first place, or one whose own credential is in the request — and say which in the summary.
type RemoteAccessInfo ¶
type RemoteAccessInfo struct {
// Deployment is "container" or "desktop" (buildinfo.Deployment). The
// desktop build never opens a TCP port at all, see main's own comment on
// AssetServer, so everything below is reported empty/false for it rather
// than guessed at from request fields that mean nothing there.
Deployment string `json:"deployment"`
// PasswordSet mirrors GET /api/auth's own "enabled", repeated here so
// the page has the one fact its warning banner turns on without a second
// request racing this one.
PasswordSet bool `json:"passwordSet"`
// Addresses is every address this build can confirm or infer this
// instance answers on, the one the request itself arrived on always
// first (see remoteAddresses' own comment).
Addresses []ReachableAddress `json:"addresses"`
// Exposed is the loud warning's own condition: no password, and EITHER
// this very request just proved this instance is reachable from
// somewhere other than this machine itself (see requestIsNonLoopback),
// OR the listener itself is bound wider than loopback on a machine that
// has a real non-loopback interface to be reached on
// (buildinfo.ListensWidely - see its own doc comment). The second half
// exists because the first half alone can never be true for the one
// person best placed to act on the warning: an admin looking at their
// own Access page from 127.0.0.1 never generates a non-loopback
// request, no matter how exposed the instance actually is - reproduced
// live before this fix: a LAN-reachable, password-less instance showed
// the warning to a visitor from another machine and never to the admin
// sitting at the box itself.
Exposed bool `json:"exposed"`
// QR renders the primary address (Addresses[0] when there is one) as a
// scannable code, nil when there is nothing to encode.
QR *QRMatrix `json:"qr,omitempty"`
}
RemoteAccessInfo is what GET /api/remote-access answers with.
type Route ¶
type Route struct {
// Method is the HTTP method, exactly as net/http's pattern syntax wants it,
// or AnyMethod.
Method string `json:"method"`
// Path is the pattern, wildcards included ("/api/tasks/{id}").
Path string `json:"path"`
// Summary is one line saying what the route does, in the language the rest of
// the app is written in. It is what the index shows.
Summary string `json:"summary"`
// Open is a route reachable without a session on a password-protected
// instance. It is false unless there is a reason, and the reason belongs in
// the summary: these are the only doors in the building that are not locked.
Open bool `json:"open"`
// contains filtered or unexported fields
}
Route is one endpoint as registered: enough to attach it, and enough to describe it to somebody who has never seen the source.
Source Files
¶
- api.go
- peertokens.go
- routes.go
- routes_accounts.go
- routes_backup.go
- routes_browsertools.go
- routes_bulk.go
- routes_captcha.go
- routes_captcha_skip.go
- routes_captcha_widget.go
- routes_connect.go
- routes_connections.go
- routes_containers.go
- routes_controls.go
- routes_diagnostics.go
- routes_discovery.go
- routes_extract.go
- routes_features.go
- routes_federation.go
- routes_files.go
- routes_folders.go
- routes_help.go
- routes_history.go
- routes_hosterauth.go
- routes_idleaction.go
- routes_lifecycle.go
- routes_links.go
- routes_portmap.go
- routes_queue.go
- routes_reconnect.go
- routes_relay.go
- routes_remote.go
- routes_resolvers.go
- routes_rules.go
- routes_schedule.go
- routes_scripts.go
- routes_settings.go
- routes_system.go
- routes_tasks.go
- routes_tokens.go
- routes_torrents.go
- routes_uistate.go