f580d88a14
gates / gates (push) Successful in 45s
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
87 lines
2.5 KiB
Go
87 lines
2.5 KiB
Go
package mediawire
|
|
|
|
import (
|
|
"database/sql"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
_ "modernc.org/sqlite"
|
|
|
|
"gitea.dooplex.hu/admin/felhom-controller/internal/appbackup"
|
|
"gitea.dooplex.hu/admin/felhom-controller/internal/dockerexec"
|
|
)
|
|
|
|
// DockerBox is the production Box: the docker CLI on this guest.
|
|
type DockerBox struct{}
|
|
|
|
func (DockerBox) Running(c string) bool {
|
|
out, err := dockerexec.Command("docker", "inspect", "-f", "{{.State.Running}}", c).Output()
|
|
return err == nil && strings.TrimSpace(string(out)) == "true"
|
|
}
|
|
|
|
func (DockerBox) Env(c, name string) (string, error) {
|
|
out, err := dockerexec.Command("docker", "exec", c, "printenv", name).Output()
|
|
if err != nil {
|
|
return "", fmt.Errorf("%s: reading %s: %w", c, name, err)
|
|
}
|
|
return strings.TrimRight(string(out), "\n"), nil
|
|
}
|
|
|
|
func (DockerBox) ReadFile(c, path string) ([]byte, error) {
|
|
out, err := dockerexec.Command("docker", "exec", c, "cat", path).Output()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%s: reading %s: %w", c, path, err)
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (DockerBox) EnsureDir(p string) error { return appbackup.EnsureUserdataDir(p) }
|
|
|
|
func (DockerBox) Mounts(c string) map[string]string {
|
|
out, err := dockerexec.Command("docker", "inspect", "-f", "{{json .Mounts}}", c).Output()
|
|
res := map[string]string{}
|
|
if err != nil {
|
|
return res
|
|
}
|
|
var ms []struct {
|
|
Source string `json:"Source"`
|
|
Destination string `json:"Destination"`
|
|
}
|
|
if json.Unmarshal(out, &ms) == nil {
|
|
for _, m := range ms {
|
|
res[filepath.Clean(m.Destination)] = m.Source
|
|
}
|
|
}
|
|
return res
|
|
}
|
|
|
|
// JellyfinKey copies Jellyfin's database (with its -wal/-shm, R-286's lesson) into a 0700 temp dir, reads the API key
|
|
// with this name read-only, and removes the copy at once. The key is returned to the caller only.
|
|
func (DockerBox) JellyfinKey(name string) (string, error) {
|
|
dir, err := os.MkdirTemp("", "felhom-jf-")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer os.RemoveAll(dir)
|
|
for _, f := range []string{"jellyfin.db", "jellyfin.db-wal", "jellyfin.db-shm"} {
|
|
err := dockerexec.Command("docker", "cp", "jellyfin:/config/data/"+f, filepath.Join(dir, f)).Run()
|
|
if err != nil && f == "jellyfin.db" {
|
|
return "", fmt.Errorf("copying Jellyfin's database: %w", err)
|
|
}
|
|
}
|
|
db, err := sql.Open("sqlite", "file:"+filepath.Join(dir, "jellyfin.db"))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer db.Close()
|
|
var key string
|
|
err = db.QueryRow(`SELECT AccessToken FROM ApiKeys WHERE Name = ? ORDER BY DateCreated DESC LIMIT 1`, name).Scan(&key)
|
|
if err == sql.ErrNoRows {
|
|
return "", nil
|
|
}
|
|
return key, err
|
|
}
|