refactor progress
This commit is contained in:
272
fluxer_devops/livekitctl/cmd/bootstrap.go
Normal file
272
fluxer_devops/livekitctl/cmd/bootstrap.go
Normal file
@@ -0,0 +1,272 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/fluxerapp/fluxer/fluxer_devops/livekitctl/internal/constants"
|
||||
"github.com/fluxerapp/fluxer/fluxer_devops/livekitctl/internal/dnswait"
|
||||
"github.com/fluxerapp/fluxer/fluxer_devops/livekitctl/internal/errors"
|
||||
"github.com/fluxerapp/fluxer/fluxer_devops/livekitctl/internal/firewall"
|
||||
"github.com/fluxerapp/fluxer/fluxer_devops/livekitctl/internal/install"
|
||||
"github.com/fluxerapp/fluxer/fluxer_devops/livekitctl/internal/netutil"
|
||||
"github.com/fluxerapp/fluxer/fluxer_devops/livekitctl/internal/ops"
|
||||
"github.com/fluxerapp/fluxer/fluxer_devops/livekitctl/internal/platform"
|
||||
"github.com/fluxerapp/fluxer/fluxer_devops/livekitctl/internal/secrets"
|
||||
"github.com/fluxerapp/fluxer/fluxer_devops/livekitctl/internal/state"
|
||||
"github.com/fluxerapp/fluxer/fluxer_devops/livekitctl/internal/util"
|
||||
"github.com/fluxerapp/fluxer/fluxer_devops/livekitctl/internal/validate"
|
||||
)
|
||||
|
||||
var bootstrapCmd = &cobra.Command{
|
||||
Use: "bootstrap",
|
||||
Short: "Install and configure LiveKit, Caddy (l4), coturn, and KV store",
|
||||
Run: runBootstrap,
|
||||
}
|
||||
|
||||
var (
|
||||
livekitDomain string
|
||||
turnDomain string
|
||||
email string
|
||||
livekitVersion string
|
||||
caddyVersion string
|
||||
caddyL4Version string
|
||||
xcaddyVersion string
|
||||
installDir string
|
||||
enableFirewall bool
|
||||
kvPort int
|
||||
kvPortAuto bool
|
||||
webhookURLs []string
|
||||
webhookURLsFile string
|
||||
allowHTTPWebhooks bool
|
||||
dnsTimeout int
|
||||
dnsInterval int
|
||||
printSecrets bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(bootstrapCmd)
|
||||
|
||||
bootstrapCmd.Flags().StringVar(&livekitDomain, "livekit-domain", "", "LiveKit domain (required)")
|
||||
bootstrapCmd.Flags().StringVar(&turnDomain, "turn-domain", "", "TURN domain (required)")
|
||||
bootstrapCmd.Flags().StringVar(&email, "email", "", "ACME email (required)")
|
||||
|
||||
bootstrapCmd.Flags().StringVar(&livekitVersion, "livekit-version", constants.DefaultLiveKitVersion, "LiveKit version")
|
||||
bootstrapCmd.Flags().StringVar(&caddyVersion, "caddy-version", constants.DefaultCaddyVersion, "Caddy version")
|
||||
bootstrapCmd.Flags().StringVar(&caddyL4Version, "caddy-l4-version", constants.DefaultCaddyL4Version, "Caddy L4 version")
|
||||
bootstrapCmd.Flags().StringVar(&xcaddyVersion, "xcaddy-version", constants.DefaultXcaddyVersion, "xcaddy version")
|
||||
|
||||
bootstrapCmd.Flags().StringVar(&installDir, "install-dir", "", "Override LiveKit install dir (default: /opt/livekit)")
|
||||
bootstrapCmd.Flags().BoolVar(&enableFirewall, "firewall", false, "Configure detected firewall tool")
|
||||
bootstrapCmd.Flags().IntVar(&kvPort, "kv-port", 0, "KV port (default: 6379)")
|
||||
bootstrapCmd.Flags().BoolVar(&kvPortAuto, "kv-port-auto", false, "Pick a free KV port from 6379-6382")
|
||||
bootstrapCmd.Flags().StringArrayVar(&webhookURLs, "webhook-url", nil, "Webhook URL (repeatable)")
|
||||
bootstrapCmd.Flags().StringVar(&webhookURLsFile, "webhook-urls-file", "", "File with webhook URLs (one per line)")
|
||||
bootstrapCmd.Flags().BoolVar(&allowHTTPWebhooks, "allow-http-webhooks", false, "Allow http:// webhook URLs")
|
||||
|
||||
bootstrapCmd.Flags().IntVar(&dnsTimeout, "dns-timeout", 900, "DNS wait timeout in seconds")
|
||||
bootstrapCmd.Flags().IntVar(&dnsInterval, "dns-interval", 10, "DNS check interval in seconds")
|
||||
|
||||
bootstrapCmd.Flags().BoolVar(&printSecrets, "print-secrets", false, "Print secrets JSON to stdout")
|
||||
|
||||
bootstrapCmd.MarkFlagRequired("livekit-domain")
|
||||
bootstrapCmd.MarkFlagRequired("turn-domain")
|
||||
bootstrapCmd.MarkFlagRequired("email")
|
||||
}
|
||||
|
||||
func runBootstrap(cmd *cobra.Command, args []string) {
|
||||
exitOnError(ops.EnsureLinuxRoot())
|
||||
|
||||
livekitDomainValidated, err := validate.RequireDomain(livekitDomain, "livekit domain")
|
||||
exitOnError(err)
|
||||
|
||||
turnDomainValidated, err := validate.RequireDomain(turnDomain, "turn domain")
|
||||
exitOnError(err)
|
||||
|
||||
acmeEmail, err := validate.RequireEmail(email)
|
||||
exitOnError(err)
|
||||
|
||||
ports := constants.DefaultPorts()
|
||||
if kvPort > 0 {
|
||||
ports.KVPort = kvPort
|
||||
}
|
||||
|
||||
livekitVersionValidated, err := validate.NormaliseVersionTag(livekitVersion)
|
||||
exitOnError(err)
|
||||
|
||||
caddyVersionValidated, err := validate.NormaliseVersionTag(caddyVersion)
|
||||
exitOnError(err)
|
||||
|
||||
caddyL4VersionValidated, err := validate.NormaliseVersionTag(caddyL4Version)
|
||||
exitOnError(err)
|
||||
|
||||
xcaddyVersionValidated, err := validate.NormaliseVersionTag(xcaddyVersion)
|
||||
exitOnError(err)
|
||||
|
||||
var webhooks []string
|
||||
for _, u := range webhookURLs {
|
||||
validated, err := validate.RequireWebhookURL(u, allowHTTPWebhooks)
|
||||
exitOnError(err)
|
||||
webhooks = append(webhooks, validated)
|
||||
}
|
||||
|
||||
if webhookURLsFile != "" {
|
||||
lines, err := ops.ReadLinesFile(webhookURLsFile)
|
||||
exitOnError(err)
|
||||
for _, u := range lines {
|
||||
validated, err := validate.RequireWebhookURL(u, allowHTTPWebhooks)
|
||||
exitOnError(err)
|
||||
webhooks = append(webhooks, validated)
|
||||
}
|
||||
}
|
||||
|
||||
pm := platform.DetectPackageManager()
|
||||
if pm == nil {
|
||||
exitOnError(errors.NewPlatformError("No supported package manager detected."))
|
||||
}
|
||||
|
||||
exitOnError(install.InstallBasePackages(pm))
|
||||
exitOnError(install.EnsureUsers())
|
||||
|
||||
kvBin, err := install.InstallKVBinary(pm)
|
||||
exitOnError(err)
|
||||
|
||||
paths := state.DefaultPaths()
|
||||
if installDir != "" {
|
||||
paths.LiveKitInstallDir = installDir
|
||||
paths.LiveKitBinDir = filepath.Join(installDir, "bin")
|
||||
}
|
||||
|
||||
if kvPortAuto {
|
||||
for _, cand := range []int{6379, 6380, 6381, 6382} {
|
||||
output, exitCode := util.RunCaptureNoCheck([]string{"bash", "-lc", fmt.Sprintf("ss -lnt | awk '{print $4}' | grep -q ':%d$'", cand)})
|
||||
_ = output
|
||||
if exitCode != 0 {
|
||||
ports.KVPort = cand
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fwTool := firewall.DetectFirewallTool()
|
||||
firewallCfg := state.FirewallConfig{Enabled: enableFirewall, Tool: fwTool.Name}
|
||||
|
||||
st := state.NewState(state.NewStateParams{
|
||||
ACMEEmail: acmeEmail,
|
||||
Domains: state.Domains{
|
||||
LiveKit: livekitDomainValidated,
|
||||
TURN: turnDomainValidated,
|
||||
},
|
||||
Ports: ports,
|
||||
Versions: state.Versions{
|
||||
LiveKit: livekitVersionValidated,
|
||||
Caddy: caddyVersionValidated,
|
||||
CaddyL4: caddyL4VersionValidated,
|
||||
Xcaddy: xcaddyVersionValidated,
|
||||
},
|
||||
KV: state.KVConfig{
|
||||
BindHost: ports.KVBindHost,
|
||||
Port: ports.KVPort,
|
||||
},
|
||||
Webhooks: webhooks,
|
||||
Firewall: firewallCfg,
|
||||
Paths: &paths,
|
||||
})
|
||||
|
||||
exitOnError(os.MkdirAll(st.Paths.ConfigDir, 0755))
|
||||
|
||||
sec := secrets.GenerateNewSecrets()
|
||||
exitOnError(ops.SaveSecrets(st, sec))
|
||||
|
||||
pub4 := netutil.DetectPublicIP("4")
|
||||
if pub4 == "" {
|
||||
exitOnError(errors.NewPlatformError("Could not detect public IPv4."))
|
||||
}
|
||||
|
||||
var pub6 string
|
||||
if netutil.HasGlobalIPv6() {
|
||||
pub6 = netutil.DetectPublicIP("6")
|
||||
}
|
||||
|
||||
priv4 := netutil.PrimaryPrivateIPv4()
|
||||
|
||||
util.Log("")
|
||||
util.Log("DNS records needed before TLS issuance:")
|
||||
util.Logf("A %s -> %s", livekitDomainValidated, pub4)
|
||||
util.Logf("A %s -> %s", turnDomainValidated, pub4)
|
||||
if pub6 != "" {
|
||||
util.Logf("AAAA %s -> %s", livekitDomainValidated, pub6)
|
||||
util.Logf("AAAA %s -> %s", turnDomainValidated, pub6)
|
||||
}
|
||||
util.Log("")
|
||||
|
||||
okDNS := dnswait.WaitForDNS(livekitDomainValidated, turnDomainValidated, pub4, pub6, dnsTimeout, dnsInterval)
|
||||
if !okDNS {
|
||||
util.Log("DNS not verified yet. Continuing. ACME may fail until DNS is correct.")
|
||||
}
|
||||
|
||||
_, err = install.InstallLiveKitBinary(livekitVersionValidated, st.Paths.LiveKitInstallDir, "")
|
||||
exitOnError(err)
|
||||
|
||||
exitOnError(install.EnsureCaddyWithL4(
|
||||
"/tmp/livekitctl-caddy-build",
|
||||
caddyVersionValidated,
|
||||
caddyL4VersionValidated,
|
||||
xcaddyVersionValidated,
|
||||
st.Paths.CaddyBin,
|
||||
))
|
||||
|
||||
exitOnError(state.SaveState(st))
|
||||
|
||||
ops.StopConflictingServices()
|
||||
exitOnError(ops.ApplyConfigAndRestart(st, kvBin, pub4, priv4))
|
||||
|
||||
if st.Firewall.Enabled {
|
||||
msg, err := ops.ConfigureFirewallFromState(st)
|
||||
exitOnError(err)
|
||||
util.Log(msg)
|
||||
}
|
||||
|
||||
util.Log("")
|
||||
util.Log("Bootstrap completed.")
|
||||
util.Log("")
|
||||
util.Log("State:")
|
||||
util.Logf(" %s", st.Paths.StatePath)
|
||||
util.Log("Secrets:")
|
||||
util.Logf(" %s", st.Paths.SecretsPath)
|
||||
util.Log("")
|
||||
|
||||
if printSecrets {
|
||||
data, err := os.ReadFile(st.Paths.SecretsPath)
|
||||
if err == nil {
|
||||
util.Log("Secrets JSON:")
|
||||
util.Log(strings.TrimSpace(string(data)))
|
||||
util.Log("")
|
||||
}
|
||||
}
|
||||
|
||||
util.Log(ops.RunBasicHealthChecks(st))
|
||||
}
|
||||
56
fluxer_devops/livekitctl/cmd/logs.go
Normal file
56
fluxer_devops/livekitctl/cmd/logs.go
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/fluxerapp/fluxer/fluxer_devops/livekitctl/internal/ops"
|
||||
"github.com/fluxerapp/fluxer/fluxer_devops/livekitctl/internal/util"
|
||||
)
|
||||
|
||||
var logsCmd = &cobra.Command{
|
||||
Use: "logs",
|
||||
Short: "Show systemd logs",
|
||||
Run: runLogs,
|
||||
}
|
||||
|
||||
var (
|
||||
logsService string
|
||||
logsLines int
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(logsCmd)
|
||||
|
||||
logsCmd.Flags().StringVar(&logsService, "service", "", "systemd unit, eg livekit.service (required)")
|
||||
logsCmd.Flags().IntVar(&logsLines, "lines", 200, "Number of log lines")
|
||||
|
||||
logsCmd.MarkFlagRequired("service")
|
||||
}
|
||||
|
||||
func runLogs(cmd *cobra.Command, args []string) {
|
||||
exitOnError(ops.EnsureLinuxRoot())
|
||||
|
||||
st, err := ops.EnsureStateLoadedOrFail(statePath)
|
||||
exitOnError(err)
|
||||
|
||||
util.Log(ops.OpLogs(st, logsService, logsLines))
|
||||
}
|
||||
50
fluxer_devops/livekitctl/cmd/restart.go
Normal file
50
fluxer_devops/livekitctl/cmd/restart.go
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/fluxerapp/fluxer/fluxer_devops/livekitctl/internal/ops"
|
||||
"github.com/fluxerapp/fluxer/fluxer_devops/livekitctl/internal/util"
|
||||
)
|
||||
|
||||
var restartCmd = &cobra.Command{
|
||||
Use: "restart [services...]",
|
||||
Short: "Restart one or more services",
|
||||
Long: "Restart one or more services. If no services specified, restarts all managed services.",
|
||||
Run: runRestart,
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(restartCmd)
|
||||
}
|
||||
|
||||
func runRestart(cmd *cobra.Command, args []string) {
|
||||
exitOnError(ops.EnsureLinuxRoot())
|
||||
|
||||
services := args
|
||||
if len(services) == 0 {
|
||||
services = []string{"livekit-kv.service", "livekit-coturn.service", "livekit.service", "caddy.service"}
|
||||
}
|
||||
|
||||
exitOnError(ops.OpRestart(services))
|
||||
util.Log("Restart requested.")
|
||||
}
|
||||
50
fluxer_devops/livekitctl/cmd/root.go
Normal file
50
fluxer_devops/livekitctl/cmd/root.go
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var statePath string
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "livekitctl",
|
||||
Short: "LiveKit bootstrap and operations CLI",
|
||||
Long: "Self-hosted LiveKit bootstrap and operations CLI for installing and managing LiveKit servers.",
|
||||
}
|
||||
|
||||
func Execute() error {
|
||||
return rootCmd.Execute()
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.PersistentFlags().StringVar(&statePath, "state", "", "Path to state file (default: /etc/livekit/livekitctl-state.json)")
|
||||
}
|
||||
|
||||
func exitOnError(err error) {
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
46
fluxer_devops/livekitctl/cmd/status.go
Normal file
46
fluxer_devops/livekitctl/cmd/status.go
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/fluxerapp/fluxer/fluxer_devops/livekitctl/internal/ops"
|
||||
"github.com/fluxerapp/fluxer/fluxer_devops/livekitctl/internal/util"
|
||||
)
|
||||
|
||||
var statusCmd = &cobra.Command{
|
||||
Use: "status",
|
||||
Short: "Show systemd status for managed services",
|
||||
Run: runStatus,
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(statusCmd)
|
||||
}
|
||||
|
||||
func runStatus(cmd *cobra.Command, args []string) {
|
||||
exitOnError(ops.EnsureLinuxRoot())
|
||||
|
||||
st, err := ops.EnsureStateLoadedOrFail(statePath)
|
||||
exitOnError(err)
|
||||
|
||||
util.Log(ops.OpStatus(st))
|
||||
}
|
||||
172
fluxer_devops/livekitctl/cmd/webhook.go
Normal file
172
fluxer_devops/livekitctl/cmd/webhook.go
Normal file
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/fluxerapp/fluxer/fluxer_devops/livekitctl/internal/install"
|
||||
"github.com/fluxerapp/fluxer/fluxer_devops/livekitctl/internal/netutil"
|
||||
"github.com/fluxerapp/fluxer/fluxer_devops/livekitctl/internal/ops"
|
||||
"github.com/fluxerapp/fluxer/fluxer_devops/livekitctl/internal/platform"
|
||||
"github.com/fluxerapp/fluxer/fluxer_devops/livekitctl/internal/state"
|
||||
"github.com/fluxerapp/fluxer/fluxer_devops/livekitctl/internal/util"
|
||||
)
|
||||
|
||||
var webhookCmd = &cobra.Command{
|
||||
Use: "webhook",
|
||||
Short: "Manage LiveKit webhooks (writes config and restarts LiveKit)",
|
||||
}
|
||||
|
||||
var webhookListCmd = &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "List webhook URLs",
|
||||
Run: runWebhookList,
|
||||
}
|
||||
|
||||
var webhookAddCmd = &cobra.Command{
|
||||
Use: "add <url>",
|
||||
Short: "Add a webhook URL",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: runWebhookAdd,
|
||||
}
|
||||
|
||||
var webhookRemoveCmd = &cobra.Command{
|
||||
Use: "remove <url>",
|
||||
Short: "Remove a webhook URL",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: runWebhookRemove,
|
||||
}
|
||||
|
||||
var webhookSetCmd = &cobra.Command{
|
||||
Use: "set",
|
||||
Short: "Replace webhook URLs",
|
||||
Run: runWebhookSet,
|
||||
}
|
||||
|
||||
var (
|
||||
webhookAllowHTTP bool
|
||||
webhookSetURLs []string
|
||||
webhookSetFile string
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(webhookCmd)
|
||||
|
||||
webhookCmd.AddCommand(webhookListCmd)
|
||||
|
||||
webhookAddCmd.Flags().BoolVar(&webhookAllowHTTP, "allow-http-webhooks", false, "Allow http:// webhook URLs")
|
||||
webhookCmd.AddCommand(webhookAddCmd)
|
||||
|
||||
webhookCmd.AddCommand(webhookRemoveCmd)
|
||||
|
||||
webhookSetCmd.Flags().StringArrayVar(&webhookSetURLs, "url", nil, "Webhook URL (repeatable)")
|
||||
webhookSetCmd.Flags().StringVar(&webhookSetFile, "file", "", "File with webhook URLs (one per line)")
|
||||
webhookSetCmd.Flags().BoolVar(&webhookAllowHTTP, "allow-http-webhooks", false, "Allow http:// webhook URLs")
|
||||
webhookCmd.AddCommand(webhookSetCmd)
|
||||
}
|
||||
|
||||
func runWebhookList(cmd *cobra.Command, args []string) {
|
||||
exitOnError(ops.EnsureLinuxRoot())
|
||||
|
||||
st, err := ops.EnsureStateLoadedOrFail(statePath)
|
||||
exitOnError(err)
|
||||
|
||||
for _, u := range ops.WebhookList(st) {
|
||||
fmt.Println(u)
|
||||
}
|
||||
}
|
||||
|
||||
func runWebhookAdd(cmd *cobra.Command, args []string) {
|
||||
exitOnError(ops.EnsureLinuxRoot())
|
||||
|
||||
st, err := ops.EnsureStateLoadedOrFail(statePath)
|
||||
exitOnError(err)
|
||||
|
||||
changed, err := ops.WebhookAdd(st, args[0], webhookAllowHTTP)
|
||||
exitOnError(err)
|
||||
|
||||
if changed {
|
||||
exitOnError(applyAndRestart(st))
|
||||
util.Log("Webhook added and LiveKit restarted.")
|
||||
} else {
|
||||
util.Log("Webhook already present.")
|
||||
}
|
||||
}
|
||||
|
||||
func runWebhookRemove(cmd *cobra.Command, args []string) {
|
||||
exitOnError(ops.EnsureLinuxRoot())
|
||||
|
||||
st, err := ops.EnsureStateLoadedOrFail(statePath)
|
||||
exitOnError(err)
|
||||
|
||||
changed, err := ops.WebhookRemove(st, args[0])
|
||||
exitOnError(err)
|
||||
|
||||
if changed {
|
||||
exitOnError(applyAndRestart(st))
|
||||
util.Log("Webhook removed and LiveKit restarted.")
|
||||
} else {
|
||||
util.Log("Webhook not found.")
|
||||
}
|
||||
}
|
||||
|
||||
func runWebhookSet(cmd *cobra.Command, args []string) {
|
||||
exitOnError(ops.EnsureLinuxRoot())
|
||||
|
||||
st, err := ops.EnsureStateLoadedOrFail(statePath)
|
||||
exitOnError(err)
|
||||
|
||||
var urls []string
|
||||
urls = append(urls, webhookSetURLs...)
|
||||
|
||||
if webhookSetFile != "" {
|
||||
lines, err := ops.ReadLinesFile(webhookSetFile)
|
||||
exitOnError(err)
|
||||
urls = append(urls, lines...)
|
||||
}
|
||||
|
||||
exitOnError(ops.WebhookSet(st, urls, webhookAllowHTTP))
|
||||
exitOnError(applyAndRestart(st))
|
||||
util.Log("Webhooks updated and LiveKit restarted.")
|
||||
}
|
||||
|
||||
func applyAndRestart(st *state.BootstrapState) error {
|
||||
pm := platform.DetectPackageManager()
|
||||
if pm == nil {
|
||||
return fmt.Errorf("no supported package manager detected")
|
||||
}
|
||||
|
||||
kvBin, err := install.InstallKVBinary(pm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pub4 := netutil.DetectPublicIP("4")
|
||||
if pub4 == "" {
|
||||
util.Log("Warning: Could not detect public IPv4, using 0.0.0.0")
|
||||
pub4 = "0.0.0.0"
|
||||
}
|
||||
priv4 := netutil.PrimaryPrivateIPv4()
|
||||
|
||||
return ops.ApplyConfigAndRestart(st, kvBin, pub4, priv4)
|
||||
}
|
||||
Reference in New Issue
Block a user