Something went wrong. Try again.
small gleam coding and persistent agent daemon with a detachable cli and a canvas ui forked from okami.mom/albedo
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140package webui
import ( "encoding/json" "errors" "net" "net/http" "net/url" "os" "path/filepath" "slices" "syscall" "time"
"albedo/cli/internal/config")
// Options is what a web listener serves and how it is published: the flags a// running server must match, and what --new-link resumes. A field here is a// flag in newWeb, a line in serveArgs, and a condition in backgroundWeb's// changed-options check.type Options struct { // Port is what the command was asked to serve on (0 picks a free one), // and in a manifest the port it bound. Port int `json:"port"` Allow []string `json:"allow,omitempty"` Local bool `json:"local,omitempty"` // Public is the origin a proxy serves this listener through, in the form // ParseOrigin returns, and empty when it is not behind a proxy. Public string `json:"public,omitempty"` TailnetHTTPS bool `json:"tailnet_https,omitempty"`}
// PublicOrigin is the origin a proxy serves this listener through, and nil when// it is not behind a proxy. A recorded origin that no longer parses counts as// none: the links then stay on loopback.func (o Options) PublicOrigin() *url.URL { origin, err := ParseOrigin(o.Public) if err != nil { return nil } return origin}
// SameAllow reports whether other allows the same networks. The flags are a// set: order, CIDR or address spelling, and a repeated network do not make a// different server.func (o Options) SameAllow(other Options) bool { return slices.Equal(canonicalNetworks(o.Allow), canonicalNetworks(other.Allow))}
func canonicalNetworks(networks []string) []string { out := make([]string, len(networks)) for i, text := range networks { network, err := ParseNetwork(text) if err != nil { out[i] = text continue } out[i] = network.String() } slices.Sort(out) return slices.Compact(out)}
// Manifest describes a running albedo web listener: what it serves, and the// process and addresses a later command needs to find it.type Manifest struct { Options Names []string `json:"names,omitempty"` Addresses []string `json:"addresses,omitempty"` PID int `json:"pid"` URL string `json:"url"`}
// ManifestPath returns the shared web listener manifest path.func ManifestPath() string { return filepath.Join(config.HomeDir(), "web.json") }
// ReadManifest reads and decodes a web listener manifest.func ReadManifest(path string) (Manifest, error) { data, err := os.ReadFile(path) if err != nil { return Manifest{}, err } var manifest Manifest if err := json.Unmarshal(data, &manifest); err != nil { return Manifest{}, err } return manifest, nil}
// WriteManifest atomically replaces path with a private web listener manifest.func WriteManifest(path string, manifest Manifest) error { if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil { return err } data, err := json.Marshal(manifest) if err != nil { return err } tmp, err := os.CreateTemp(filepath.Dir(path), ".web.json-") if err != nil { return err } name := tmp.Name() defer os.Remove(name) if err := tmp.Chmod(0o600); err != nil { tmp.Close() return err } if _, err := tmp.Write(data); err != nil { tmp.Close() return err } if err := tmp.Close(); err != nil { return err } return os.Rename(name, path)}
// ManifestAlive reports whether the recorded process exists and listens on its// URL. A server that takes its connection but answers late is busy, not dead.func ManifestAlive(manifest Manifest) bool { if manifest.PID <= 0 || manifest.URL == "" { return false } process, err := os.FindProcess(manifest.PID) if err != nil || process == nil || process.Signal(os.Signal(syscall.Signal(0))) != nil { return false } client := &http.Client{Timeout: 250 * time.Millisecond} response, err := client.Get(manifest.URL) if err != nil { var netErr net.Error return errors.As(err, &netErr) && netErr.Timeout() } response.Body.Close() return true}