├── .github
├── img
│ └── header.png
└── workflows
│ ├── commitlint.yml
│ ├── golangci-lint.yml
│ └── goreleaser.yml
├── .gitignore
├── .goreleaser.yaml
├── LICENSE
├── Makefile
├── README.md
├── cmd
├── configure.go
├── default.go
├── root.go
├── shared
│ └── shared.go
└── update.go
├── go.mod
├── go.sum
├── install.sh
├── main.go
└── models
├── config
├── config.go
└── manager
│ └── manager.go
├── global
└── global.go
└── service
├── service.go
├── servicemap
└── servicemap.go
└── services
├── apprise.go
├── autobrr.go
├── changedetectionio.go
├── cloudflared.go
├── gotify.go
├── homepage.go
├── huginn.go
├── jellyfin.go
├── kavita.go
├── nginx.go
├── overseerr.go
├── playwright.go
├── plex.go
├── prowlarr.go
├── qbittorrent.go
├── radarr.go
├── sabnzbd.go
├── sonarr.go
├── tautulli.go
└── thelounge.go
/.github/img/header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TechSquidTV/uhs-cli/7269d301c765d1e68f9420f7975b8ff76521d1ea/.github/img/header.png
--------------------------------------------------------------------------------
/.github/workflows/commitlint.yml:
--------------------------------------------------------------------------------
1 | name: Lint Commit Messages
2 | on: [pull_request]
3 |
4 | jobs:
5 | commitlint:
6 | runs-on: ubuntu-latest
7 | steps:
8 | - uses: actions/checkout@v3
9 | with:
10 | fetch-depth: 0
11 | - uses: wagoid/commitlint-github-action@v5
--------------------------------------------------------------------------------
/.github/workflows/golangci-lint.yml:
--------------------------------------------------------------------------------
1 | name: golangci-lint
2 | on:
3 | push:
4 | tags:
5 | - v*
6 | branches:
7 | - main
8 | pull_request:
9 | permissions:
10 | contents: read
11 | jobs:
12 | golangci:
13 | name: lint
14 | runs-on: ubuntu-latest
15 | steps:
16 | - uses: actions/setup-go@v4
17 | with:
18 | go-version: '1.19'
19 | cache: false
20 | - uses: actions/checkout@v3
21 | - name: golangci-lint
22 | uses: golangci/golangci-lint-action@v3
23 | with:
24 | # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
25 | version: latest
26 | - name: gofmt
27 | uses: Jerome1337/gofmt-action@v1.0.5
28 | with:
29 | gofmt-flags: '-l -d'
--------------------------------------------------------------------------------
/.github/workflows/goreleaser.yml:
--------------------------------------------------------------------------------
1 | name: goreleaser
2 |
3 | on:
4 | push:
5 | tags:
6 | - '*'
7 |
8 | permissions:
9 | contents: write
10 |
11 | jobs:
12 | goreleaser:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - name: Checkout
16 | uses: actions/checkout@v3
17 | with:
18 | fetch-depth: 0
19 |
20 | - name: Set up Go
21 | uses: actions/setup-go@v4
22 |
23 | - name: Run GoReleaser
24 | uses: goreleaser/goreleaser-action@v4
25 | with:
26 | # either 'goreleaser' (default) or 'goreleaser-pro'
27 | distribution: goreleaser
28 | version: latest
29 | args: release --clean
30 | env:
31 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode
2 | secrets.y*
3 | values.y*
4 | dist/
5 | .DS_Store
--------------------------------------------------------------------------------
/.goreleaser.yaml:
--------------------------------------------------------------------------------
1 | # This is an example .goreleaser.yml file with some sensible defaults.
2 | # Make sure to check the documentation at https://goreleaser.com
3 | before:
4 | hooks:
5 | # You may remove this if you don't use go modules.
6 | - go mod tidy
7 | # you may remove this if you don't need go generate
8 | - go generate ./...
9 | builds:
10 | - env:
11 | - CGO_ENABLED=0
12 | goos:
13 | - linux
14 | # - windows
15 | - darwin
16 |
17 | archives:
18 | - format: tar.gz
19 | # this name template makes the OS and Arch compatible with the results of uname.
20 | name_template: >-
21 | {{ .ProjectName }}_
22 | {{- title .Os }}_
23 | {{- if eq .Arch "amd64" }}x86_64
24 | {{- else if eq .Arch "386" }}i386
25 | {{- else }}{{ .Arch }}{{ end }}
26 | {{- if .Arm }}v{{ .Arm }}{{ end }}
27 | # use zip for windows archives
28 | format_overrides:
29 | - goos: windows
30 | format: zip
31 | checksum:
32 | name_template: 'checksums.txt'
33 | snapshot:
34 | name_template: "{{ incpatch .Version }}-next"
35 | changelog:
36 | sort: asc
37 | filters:
38 | exclude:
39 | - '^docs:'
40 | - '^test:'
41 |
42 | # The lines beneath this are called `modelines`. See `:help modeline`
43 | # Feel free to remove those if you don't want/use them.
44 | # yaml-language-server: $schema=https://goreleaser.com/static/schema.json
45 | # vim: set ts=2 sw=2 tw=0 fo=cnqoj
46 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 TechSquidTV
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: lint install-linter test format setup prep
2 |
3 | lint: install-linter
4 | golangci-lint run -v ./...
5 |
6 | install-linter:
7 | which golangci-lint || go get -u github.com/golangci/golangci-lint/cmd/golangci-lint
8 |
9 | test:
10 | go test ./...
11 |
12 | format:
13 | go fmt ./...
14 |
15 | setup: install-linter
16 | go mod download
17 |
18 | prep: format lint test
19 | rm -rf ./bin
20 | go mod tidy
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | UltimateHomeServer CLI
21 |
22 |
23 |
24 | An interactive CLI to assist in configuring services for the UHS stack.
25 |
26 |
27 |
28 | **Beta:** This CLI is currently in active development and is subject to change.
29 |
30 |
31 | # Getting Started
32 |
33 | ## Installation
34 |
35 |
36 | ### Binary
37 |
38 | Utilize the install script to download the latest release for your platform.
39 |
40 | ```bash
41 | wget https://raw.githubusercontent.com/TechSquidTV/uhs-cli/main/install.sh
42 | ```
43 |
44 | ```bash
45 | chmod +x install.sh
46 | ```
47 |
48 | ```bash
49 | ./install.sh
50 | ```
51 |
52 | ### Go
53 |
54 | ```bash
55 | go install github.com/techsquidtv/uhs-cli@latest
56 | ```
57 |
58 | ## Usage
59 |
60 | ```bash
61 | Usage:
62 | uhs [command]
63 |
64 | Available Commands:
65 | configure Configure your UHS instance
66 | default Get the default configuration for UHS
67 | help Help about any command
68 |
69 | Flags:
70 | -h, --help help for uhs-cli
71 | -t, --toggle Help message for toggle
72 | ```
73 |
74 | Generate configurations for individual services:
75 |
76 | ```bash
77 | uhs-cli configure -o values.yaml
78 | ```
79 |
80 | Generate a default configuration for all services:
81 |
82 | ```bash
83 | uhs-cli default -o values.yaml
84 | ```
--------------------------------------------------------------------------------
/cmd/configure.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright © 2023 TechSquidTV
3 | */
4 | package cmd
5 |
6 | import (
7 | "fmt"
8 | "os"
9 | "sort"
10 |
11 | "github.com/AlecAivazis/survey/v2"
12 | "github.com/spf13/cobra"
13 | "github.com/techsquidtv/uhs-cli/cmd/shared"
14 | "github.com/techsquidtv/uhs-cli/models/config"
15 | "github.com/techsquidtv/uhs-cli/models/global"
16 | "github.com/techsquidtv/uhs-cli/models/service/servicemap"
17 | )
18 |
19 | // Return each key from the default config map
20 | var serviceNames = servicemap.Keys(servicemap.Registered)
21 |
22 | // configureCmd represents the configure command
23 | var configureCmd = &cobra.Command{
24 | Use: "configure",
25 | Short: "Configure your UHS instance",
26 | Long: `Customize and configure your desired services for your UHS instance.`,
27 | ValidArgs: serviceNames,
28 | Run: func(cmd *cobra.Command, args []string) {
29 | // Create a new instance of the UHSConfig struct
30 | var selectedServices []string
31 | if len(args) > 0 {
32 | selectedServices = args
33 | }
34 | uhsConfig := config.Config{
35 | Global: global.NewGlobal(),
36 | Services: make(config.ServicesConfig),
37 | }
38 | sort.Strings(serviceNames)
39 |
40 | if len(selectedServices) == 0 {
41 | // Prompt user to select services to enable
42 | serviceSelectPrompt := &survey.MultiSelect{
43 | Message: "Select services to enable:",
44 | Options: serviceNames,
45 | }
46 | err := survey.AskOne(serviceSelectPrompt, &selectedServices)
47 | if err != nil {
48 | fmt.Println(err.Error())
49 | return
50 | }
51 | }
52 | // Validate selected services
53 | if len(selectedServices) == 0 {
54 | fmt.Println("No services selected. Exiting...")
55 | os.Exit(0)
56 | }
57 | serviceListString := ""
58 | for _, service := range selectedServices {
59 | serviceListString += fmt.Sprintf(" - %v\n", service)
60 | }
61 |
62 | validateSelectionPrompt := &survey.Confirm{
63 | Message: fmt.Sprintf("You have selected the following services:\n%v\n Is this correct?", serviceListString),
64 | }
65 | var validateSelection bool
66 | err := survey.AskOne(validateSelectionPrompt, &validateSelection)
67 | if err != nil {
68 | fmt.Println(err.Error())
69 | return
70 | }
71 | // If selection is not valid, exit
72 | if !validateSelection {
73 | fmt.Println("Exiting...")
74 | os.Exit(0)
75 |
76 | }
77 | // Execute configuration for each selected service
78 | for _, serviceName := range selectedServices {
79 | fmt.Println("Configuring " + serviceName + "...")
80 | service := servicemap.Registered[serviceName]()
81 | service.Configure()
82 | uhsConfig.Services[serviceName] = service
83 | }
84 | // Output
85 | outputFile, err := cmd.Flags().GetString("output")
86 | if err != nil {
87 | fmt.Println(err.Error())
88 | return
89 | }
90 | err = shared.Output(outputFile, &uhsConfig)
91 | if err != nil {
92 | fmt.Println(err.Error())
93 | return
94 | }
95 | },
96 | }
97 |
98 | func init() {
99 | rootCmd.AddCommand(configureCmd)
100 | configureCmd.PersistentFlags().StringP("output", "o", "", "Output file path")
101 |
102 | // Here you will define your flags and configuration settings.
103 |
104 | // Cobra supports Persistent Flags which will work for this command
105 | // and all subcommands, e.g.:
106 | // configureCmd.PersistentFlags().String("foo", "", "A help for foo")
107 |
108 | // Cobra supports local flags which will only run when this command
109 | // is called directly, e.g.:
110 | // configureCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
111 | }
112 |
--------------------------------------------------------------------------------
/cmd/default.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright © 2023 TechSquidTV
3 | */
4 | package cmd
5 |
6 | import (
7 | "fmt"
8 |
9 | "github.com/spf13/cobra"
10 | "github.com/techsquidtv/uhs-cli/cmd/shared"
11 | "github.com/techsquidtv/uhs-cli/models/config"
12 | "github.com/techsquidtv/uhs-cli/models/global"
13 | "github.com/techsquidtv/uhs-cli/models/service/servicemap"
14 | )
15 |
16 | // defaultCmd represents the default command
17 | var defaultCmd = &cobra.Command{
18 | Use: "default",
19 | Short: "Get the default configuration for UHS",
20 | Long: `Get the default configuration for UHS.
21 | This will output the default configuration file, meant to be overwritten manually of via the configure command.`,
22 | Run: func(cmd *cobra.Command, args []string) {
23 | uhsConfig := config.Config{
24 | Global: global.NewGlobal(),
25 | Services: make(config.ServicesConfig),
26 | }
27 |
28 | // Set services to default
29 | for k, v := range servicemap.Registered {
30 | uhsConfig.Services[k] = v()
31 | }
32 | // Output
33 | outputFile, err := cmd.Flags().GetString("output")
34 | if err != nil {
35 | fmt.Println(err.Error())
36 | return
37 | }
38 | err = shared.Output(outputFile, &uhsConfig)
39 | if err != nil {
40 | fmt.Println(err.Error())
41 | return
42 | }
43 | },
44 | }
45 |
46 | func init() {
47 | rootCmd.AddCommand(defaultCmd)
48 | defaultCmd.PersistentFlags().StringP("output", "o", "", "Output file path")
49 | // Here you will define your flags and configuration settings.
50 |
51 | // Cobra supports Persistent Flags which will work for this command
52 | // and all subcommands, e.g.:
53 | // defaultCmd.PersistentFlags().String("foo", "", "A help for foo")
54 |
55 | // Cobra supports local flags which will only run when this command
56 | // is called directly, e.g.:
57 | // defaultCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
58 | }
59 |
--------------------------------------------------------------------------------
/cmd/root.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright © 2023 NAME HERE
3 | */
4 | package cmd
5 |
6 | import (
7 | "os"
8 |
9 | "github.com/spf13/cobra"
10 | )
11 |
12 | // rootCmd represents the base command when called without any subcommands
13 | var rootCmd = &cobra.Command{
14 | Use: "uhs",
15 | Short: "Create and customize configuration files for UltimateHomeServer",
16 | Long: `Utilize the UHS CLI to customize your deployments of UltimateHomeServer.
17 | Interactively select the services you want to deploy, and the CLI will generate the necessary configuration files for you.`,
18 | // Uncomment the following line if your bare application
19 | // has an action associated with it:
20 | // Run: func(cmd *cobra.Command, args []string) { },
21 | }
22 |
23 | // Execute adds all child commands to the root command and sets flags appropriately.
24 | // This is called by main.main(). It only needs to happen once to the rootCmd.
25 | func Execute() {
26 | err := rootCmd.Execute()
27 | if err != nil {
28 | os.Exit(1)
29 | }
30 | }
31 |
32 | func init() {
33 | // Here you will define your flags and configuration settings.
34 | // Cobra supports persistent flags, which, if defined here,
35 | // will be global for your application.
36 |
37 | // rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.uhs-cli.yaml)")
38 |
39 | // Cobra also supports local flags, which will only run
40 | // when this action is called directly.
41 | rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
42 | }
43 |
--------------------------------------------------------------------------------
/cmd/shared/shared.go:
--------------------------------------------------------------------------------
1 | package shared
2 |
3 | import (
4 | "fmt"
5 | "os"
6 |
7 | "github.com/techsquidtv/uhs-cli/models/config"
8 | "gopkg.in/yaml.v3"
9 | )
10 |
11 | func Output(filePath string, config *config.Config) error {
12 | yamlConfig, err := yaml.Marshal(config)
13 | if err != nil {
14 | return err
15 | }
16 | if filePath == "" {
17 | fmt.Println(string(yamlConfig))
18 | return nil
19 | } else {
20 | err = os.WriteFile(filePath, yamlConfig, 0644)
21 | if err != nil {
22 | return err
23 | }
24 | fmt.Println("Configuration complete! Your configuration file has been saved to " + filePath + ".")
25 | }
26 | return nil
27 | }
28 |
29 | func Input(filePath string, config *config.Config) error {
30 | inputFile, err := os.ReadFile(filePath)
31 | if err != nil {
32 | fmt.Printf("Unable to read file: %v", err)
33 | return err
34 | }
35 | err = yaml.Unmarshal(inputFile, config)
36 | if err != nil {
37 | fmt.Printf("Unable to unmarshal data: %v", err)
38 | return err
39 | }
40 | return nil
41 | }
42 |
--------------------------------------------------------------------------------
/cmd/update.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright © 2023 TechSquidTV
3 | */
4 | package cmd
5 |
6 | import (
7 | "fmt"
8 | "os"
9 | "sort"
10 |
11 | "github.com/AlecAivazis/survey/v2"
12 | "github.com/spf13/cobra"
13 | "github.com/techsquidtv/uhs-cli/cmd/shared"
14 | "github.com/techsquidtv/uhs-cli/models/config"
15 | "github.com/techsquidtv/uhs-cli/models/service/servicemap"
16 | )
17 |
18 | // updateCmd represents the update command
19 | var updateCmd = &cobra.Command{
20 | Use: "update",
21 | Short: "Update your UHS instance configuration",
22 | Long: `Customize and update your desired services for your UHS instance.`,
23 | ValidArgs: serviceNames,
24 | Run: func(cmd *cobra.Command, args []string) {
25 | // Validate files were provided
26 | inputFile, err := cmd.Flags().GetString("input")
27 | if err != nil {
28 | fmt.Println(err.Error())
29 | return
30 | }
31 | outputFile, err := cmd.Flags().GetString("output")
32 | if err != nil {
33 | fmt.Println(err.Error())
34 | return
35 | }
36 | var cfg config.Config
37 | cfg.Services = make(config.ServicesConfig)
38 | err = shared.Input(inputFile, &cfg)
39 | if err != nil {
40 | fmt.Println(err.Error())
41 | return
42 | }
43 | var selectedServices []string
44 | if len(args) > 0 {
45 | selectedServices = args
46 | }
47 | sort.Strings(serviceNames)
48 |
49 | if len(selectedServices) == 0 {
50 | // Prompt user to select services to update
51 | serviceSelectPrompt := &survey.MultiSelect{
52 | Message: "Select services to modify. If a selected service wasn't present in the input config, it will be added:",
53 | Options: serviceNames,
54 | }
55 | err := survey.AskOne(serviceSelectPrompt, &selectedServices)
56 | if err != nil {
57 | fmt.Println(err.Error())
58 | return
59 | }
60 | }
61 | // Validate selected services
62 | if len(selectedServices) == 0 {
63 | fmt.Println("No services selected to update. Exiting...")
64 | os.Exit(0)
65 | }
66 |
67 | validateSelectionPrompt := &survey.Confirm{
68 | Message: fmt.Sprintf("You have selected the following services:\n%v\n Is this correct?", selectedServices),
69 | }
70 | var validateSelection bool
71 | err = survey.AskOne(validateSelectionPrompt, &validateSelection)
72 | if err != nil {
73 | fmt.Println(err.Error())
74 | return
75 | }
76 | // If selection is not valid, exit
77 | // TODO: Make this flow better. Have user go through selection process again.
78 | if !validateSelection {
79 | fmt.Println("Exiting...")
80 | os.Exit(0)
81 |
82 | }
83 | // Execute configuration for each selected service
84 | for _, serviceName := range selectedServices {
85 | fmt.Println("Updating " + serviceName + "...")
86 | service := servicemap.Registered[serviceName]()
87 | service.Configure()
88 | cfg.Services[serviceName] = service
89 | }
90 |
91 | err = shared.Output(outputFile, &cfg)
92 | if err != nil {
93 | fmt.Println(err.Error())
94 | return
95 | }
96 | },
97 | }
98 |
99 | func init() {
100 | rootCmd.AddCommand(updateCmd)
101 | updateCmd.PersistentFlags().StringP("output", "o", "", "Output file path")
102 | updateCmd.PersistentFlags().StringP("input", "i", "", "Input file path")
103 |
104 | // Here you will define your flags and configuration settings.
105 |
106 | // Cobra supports Persistent Flags which will work for this command
107 | // and all subcommands, e.g.:
108 | // updateCmd.PersistentFlags().String("foo", "", "A help for foo")
109 |
110 | // Cobra supports local flags which will only run when this command
111 | // is called directly, e.g.:
112 | // updateCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
113 | }
114 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/techsquidtv/uhs-cli
2 |
3 | go 1.19
4 |
5 | require github.com/AlecAivazis/survey/v2 v2.3.6
6 |
7 | require (
8 | github.com/inconshreveable/mousetrap v1.1.0 // indirect
9 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
10 | github.com/mattn/go-colorable v0.1.2 // indirect
11 | github.com/mattn/go-isatty v0.0.8 // indirect
12 | github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
13 | github.com/spf13/cobra v1.7.0
14 | github.com/spf13/pflag v1.0.5 // indirect
15 | golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 // indirect
16 | golang.org/x/term v0.0.0-20210503060354-a79de5458b56 // indirect
17 | golang.org/x/text v0.3.3 // indirect
18 | gopkg.in/yaml.v3 v3.0.1
19 | )
20 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/AlecAivazis/survey/v2 v2.3.6 h1:NvTuVHISgTHEHeBFqt6BHOe4Ny/NwGZr7w+F8S9ziyw=
2 | github.com/AlecAivazis/survey/v2 v2.3.6/go.mod h1:4AuI9b7RjAR+G7v9+C4YSlX/YL3K3cWNXgWXOhllqvI=
3 | github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
4 | github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
5 | github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
6 | github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
7 | github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
8 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
9 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
10 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
11 | github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
12 | github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
13 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
14 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
15 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
16 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
17 | github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
18 | github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
19 | github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
20 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
21 | github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
22 | github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
23 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
24 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
25 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
26 | github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
27 | github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
28 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
29 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
30 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
31 | github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
32 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
33 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
34 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
35 | golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 h1:xHms4gcpe1YE7A3yIllJXP16CMAGuqwO2lX1mTyyRRc=
36 | golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
37 | golang.org/x/term v0.0.0-20210503060354-a79de5458b56 h1:b8jxX3zqjpqb2LklXPzKSGJhzyxCOZSz8ncv8Nv+y7w=
38 | golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
39 | golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
40 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
41 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
42 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
43 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
44 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
45 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
46 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
47 |
--------------------------------------------------------------------------------
/install.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Get the latest version
4 | VERSION=$(curl -s https://api.github.com/repos/TechSquidTV/uhs-cli/releases/latest | grep tag_name | cut -d '"' -f 4)
5 | RED='\033[0;31m'
6 | NC='\033[0m' # No Color
7 |
8 | # The operating system and architecture
9 | OS=$(uname)
10 | ARCH=$(uname -m)
11 |
12 | # The URL of the release
13 | URL="https://github.com/TechSquidTV/uhs-cli/releases/download/${VERSION}/uhs-cli_${OS}_${ARCH}.tar.gz"
14 |
15 | # Download and extract the binary
16 | wget $URL
17 | mkdir uhs-cli_${OS}_${ARCH}
18 | tar -xvzf uhs-cli_${OS}_${ARCH}.tar.gz -C uhs-cli_${OS}_${ARCH}
19 |
20 | # Move the binary to a location in your PATH
21 | if [ ! -d ~/.local/bin ]; then
22 | mkdir -p ~/.local/bin
23 | fi
24 | mv uhs-cli_${OS}_${ARCH}/uhs-cli ~/.local/bin/uhs
25 |
26 | # Ensure the binary is executable
27 | chmod +x ~/.local/bin/uhs
28 |
29 | # Clean up the downloaded file and the extracted directory
30 | rm uhs-cli_${OS}_${ARCH}.tar.gz
31 | rm -rf uhs-cli_${OS}_${ARCH}
32 |
33 | echo "Installed uhs-cli version ${VERSION}"
34 | echo "Run 'uhs --help' to get started"
35 | echo
36 | # Check if the binary is in your PATH
37 | if ! command -v uhs &> /dev/null; then
38 | echo "${RED}[WARNING]${NC} uhs could not be found in your PATH. Please add '\${HOME}/.local/bin/' to your PATH."
39 | fi
40 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright © 2023 NAME HERE
3 | */
4 | package main
5 |
6 | import "github.com/techsquidtv/uhs-cli/cmd"
7 |
8 | func main() {
9 | cmd.Execute()
10 | }
11 |
--------------------------------------------------------------------------------
/models/config/config.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/techsquidtv/uhs-cli/models/config/manager"
7 | "github.com/techsquidtv/uhs-cli/models/service/servicemap"
8 | "gopkg.in/yaml.v3"
9 | )
10 |
11 | type Config struct {
12 | Global manager.Configurer `yaml:"global,omitempty"`
13 | Services ServicesConfig `yaml:"services"`
14 | }
15 |
16 | type ServicesConfig map[string]manager.Configurer
17 |
18 | func (sc ServicesConfig) UnmarshalYAML(unmarshal func(any) error) error {
19 | services := make(map[string]any)
20 | if err := unmarshal(&services); err != nil {
21 | fmt.Println("could not unmarshal:", err)
22 | return err
23 | }
24 | for k, v := range services {
25 | b, err := yaml.Marshal(v) // remarshal this specific service so we can unmarshal it properly.
26 | if err != nil {
27 | fmt.Println("Error marshalling:", err)
28 | continue
29 | }
30 | service, err := unmarshalService(k, b)
31 | if err != nil {
32 | fmt.Println("Failed to unmarshal service", err)
33 | continue
34 | }
35 | sc[k] = service
36 | }
37 | return nil
38 | }
39 |
40 | func unmarshalService(key string, data []byte) (manager.Configurer, error) {
41 | v, ok := servicemap.Registered[key]
42 | if !ok {
43 | return nil, fmt.Errorf("unregistered service name: %q", key)
44 | }
45 | sc := v()
46 | if err := sc.Fill(data); err != nil {
47 | return nil, err
48 | }
49 | return sc, nil
50 | }
51 |
--------------------------------------------------------------------------------
/models/config/manager/manager.go:
--------------------------------------------------------------------------------
1 | package manager
2 |
3 | // Configurer represents the config manager interface.
4 | type Configurer interface {
5 | // Configure the service config based on user specification
6 | Configure()
7 | // Fill populates the service config
8 | Fill([]byte) error
9 | }
10 |
--------------------------------------------------------------------------------
/models/global/global.go:
--------------------------------------------------------------------------------
1 | package global
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/AlecAivazis/survey/v2"
7 | "github.com/techsquidtv/uhs-cli/models/config/manager"
8 | )
9 |
10 | type Global struct {
11 | TZ string `yaml:"tz"`
12 | Network Network `yaml:"network"`
13 | Domain string `yaml:"domain"`
14 | Certs Certs `yaml:"certs"`
15 | }
16 |
17 | type Network struct {
18 | Gateway string `yaml:"gateway"`
19 | }
20 |
21 | type Certs struct {
22 | SSLCertificateKey string `yaml:"ssl_certificate_key"`
23 | SSLCertificate string `yaml:"ssl_certificate"`
24 | SSLDHParam string `yaml:"ssl_dhparam"`
25 | }
26 |
27 | func NewGlobal() manager.Configurer {
28 | return &Global{
29 | TZ: "America/New_York",
30 | Domain: "UltimateHomeServer.com",
31 | Network: Network{
32 | Gateway: "192.168.1.1",
33 | },
34 | Certs: Certs{
35 | SSLCertificateKey: "/etc/letsencrypt/live/${DOMAIN}/privkey.pem",
36 | SSLCertificate: "/etc/letsencrypt/live/${DOMAIN}/fullchain.pem",
37 | SSLDHParam: "/etc/letsencrypt/certs/dhparam.pem",
38 | },
39 | }
40 | }
41 |
42 | func (c *Global) Configure() {
43 | inputDomain := &survey.Input{
44 | Message: "Enter your domain:",
45 | Default: c.Domain,
46 | }
47 | err := survey.AskOne(inputDomain, &c.Domain)
48 | if err != nil {
49 | fmt.Println(err.Error())
50 | }
51 |
52 | inputTz := &survey.Input{
53 | Message: "Enter your timezone:",
54 | Default: c.TZ,
55 | }
56 | err = survey.AskOne(inputTz, &c.TZ)
57 | if err != nil {
58 | fmt.Println(err.Error())
59 | }
60 |
61 | inputGateway := &survey.Input{
62 | Message: "Enter your network gateway:",
63 | Default: c.Network.Gateway,
64 | }
65 | err = survey.AskOne(inputGateway, &c.Network.Gateway)
66 | if err != nil {
67 | fmt.Println(err.Error())
68 | }
69 |
70 | inputSSLCertificateKey := &survey.Input{
71 | Message: "Enter your SSL certificate key path:",
72 | Default: c.Certs.SSLCertificateKey,
73 | }
74 | err = survey.AskOne(inputSSLCertificateKey, &c.Certs.SSLCertificateKey)
75 | if err != nil {
76 | fmt.Println(err.Error())
77 | }
78 |
79 | inputSSLCertificate := &survey.Input{
80 | Message: "Enter your SSL certificate path:",
81 | Default: c.Certs.SSLCertificate,
82 | }
83 | err = survey.AskOne(inputSSLCertificate, &c.Certs.SSLCertificate)
84 | if err != nil {
85 | fmt.Println(err.Error())
86 | }
87 |
88 | inputSSLDHParam := &survey.Input{
89 | Message: "Enter your SSL DHParam path:",
90 | Default: c.Certs.SSLDHParam,
91 | }
92 | err = survey.AskOne(inputSSLDHParam, &c.Certs.SSLDHParam)
93 | if err != nil {
94 | fmt.Println(err.Error())
95 | }
96 | }
97 |
98 | func (c *Global) Fill(data []byte) error { return nil }
99 |
--------------------------------------------------------------------------------
/models/service/service.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | type Ports struct {
4 | Http *int `yaml:"http,omitempty"`
5 | Https *int `yaml:"https,omitempty"`
6 | Udp *int `yaml:"udp,omitempty"`
7 | P2p *int `yaml:"p2p,omitempty"`
8 | P2pudp *int `yaml:"p2pudp,omitempty"`
9 | }
10 |
11 | type Image struct {
12 | Repository string `yaml:"repository"`
13 | Tag string `yaml:"tag"`
14 | PullPolicy string `yaml:"pullPolicy"`
15 | }
16 |
17 | type Service struct {
18 | Enabled bool `yaml:"enabled"`
19 | ReplicaCount int `yaml:"replicaCount"`
20 | Image Image `yaml:"image"`
21 | Ports Ports `yaml:"ports,omitempty"`
22 | }
23 |
--------------------------------------------------------------------------------
/models/service/servicemap/servicemap.go:
--------------------------------------------------------------------------------
1 | package servicemap
2 |
3 | import (
4 | "github.com/techsquidtv/uhs-cli/models/config/manager"
5 | "github.com/techsquidtv/uhs-cli/models/service/services"
6 | )
7 |
8 | var Registered = map[string]func() manager.Configurer{
9 | "qbittorrent": services.NewQbittorrent,
10 | "plex": services.NewPlex,
11 | "sonarr": services.NewSonarr,
12 | "radarr": services.NewRadarr,
13 | "sabnzbd": services.NewSabnzbd,
14 | "cloudflared": services.NewCloudflared,
15 | "overseerr": services.NewOverseerr,
16 | "autobrr": services.NewAutobrr,
17 | "prowlarr": services.NewProwlarr,
18 | "kavita": services.NewKavita,
19 | "gotify": services.NewGotify,
20 | "tautulli": services.NewTautulli,
21 | "playwright": services.NewPlaywright,
22 | "thelounge": services.NewThelounge,
23 | "apprise": services.NewApprise,
24 | "changedetectionio": services.NewChangedetectionio,
25 | "huginn": services.NewHuginn,
26 | "nginx": services.NewNginx,
27 | "homepage": services.NewHomepage,
28 | "jellyfin": services.NewJellyfin,
29 | }
30 |
31 | // Keys is a utility function which returns the (string) keys of a map.
32 | func Keys(m map[string]func() manager.Configurer) []string {
33 | var r []string
34 | for k := range m {
35 | r = append(r, k)
36 | }
37 | return r
38 | }
39 |
--------------------------------------------------------------------------------
/models/service/services/apprise.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/AlecAivazis/survey/v2"
7 | "github.com/techsquidtv/uhs-cli/models/config/manager"
8 | "github.com/techsquidtv/uhs-cli/models/service"
9 | "gopkg.in/yaml.v3"
10 | )
11 |
12 | type apprise struct {
13 | service.Service `yaml:",inline"`
14 | appriseOptions `yaml:",inline"`
15 | }
16 |
17 | type appriseOptions struct {
18 | Config string `yaml:"config"`
19 | }
20 |
21 | // Return default values for service
22 | func NewApprise() manager.Configurer {
23 | http := 8000
24 |
25 | return &apprise{
26 | Service: service.Service{
27 | Enabled: false,
28 | ReplicaCount: 1,
29 | Image: service.Image{
30 | Repository: "lscr.io/linuxserver/apprise-api",
31 | Tag: "latest",
32 | PullPolicy: "Always",
33 | },
34 | Ports: service.Ports{
35 | Http: &http,
36 | },
37 | },
38 | appriseOptions: appriseOptions{
39 | Config: "/opt/apprise/config",
40 | },
41 | }
42 | }
43 |
44 | func (s *apprise) Configure() {
45 | s.Enabled = true
46 | inputConfigPath := &survey.Input{
47 | Message: "Enter the path to your Apprise config folder:",
48 | Default: s.Config,
49 | }
50 | err := survey.AskOne(inputConfigPath, &s.Config)
51 | if err != nil {
52 | fmt.Println(err.Error())
53 | }
54 | }
55 |
56 | func (s *apprise) Fill(data []byte) error { return yaml.Unmarshal(data, s) }
57 |
--------------------------------------------------------------------------------
/models/service/services/autobrr.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/AlecAivazis/survey/v2"
7 | "github.com/techsquidtv/uhs-cli/models/config/manager"
8 | "github.com/techsquidtv/uhs-cli/models/service"
9 | "gopkg.in/yaml.v3"
10 | )
11 |
12 | type autobrr struct {
13 | service.Service `yaml:",inline"`
14 | autobrrOptions `yaml:",inline"`
15 | }
16 |
17 | type autobrrOptions struct {
18 | Config string `yaml:"config"`
19 | }
20 |
21 | // Return default values for service
22 | func NewAutobrr() manager.Configurer {
23 | http := 7474
24 |
25 | return &autobrr{
26 | Service: service.Service{
27 | Enabled: false,
28 | ReplicaCount: 1,
29 | Image: service.Image{
30 | Repository: "ghcr.io/autobrr/autobrr",
31 | Tag: "latest",
32 | PullPolicy: "Always",
33 | },
34 | Ports: service.Ports{
35 | Http: &http,
36 | },
37 | },
38 | autobrrOptions: autobrrOptions{
39 | Config: "/opt/autobrr/config",
40 | },
41 | }
42 | }
43 |
44 | func (s *autobrr) Configure() {
45 | s.Enabled = true
46 | inputConfigPath := &survey.Input{
47 | Message: "Enter the path to your Autobrr config folder:",
48 | Default: s.Config,
49 | }
50 | err := survey.AskOne(inputConfigPath, &s.Config)
51 | if err != nil {
52 | fmt.Println(err.Error())
53 | }
54 | }
55 |
56 | func (s *autobrr) Fill(data []byte) error { return yaml.Unmarshal(data, s) }
57 |
--------------------------------------------------------------------------------
/models/service/services/changedetectionio.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/AlecAivazis/survey/v2"
7 | "github.com/techsquidtv/uhs-cli/models/config/manager"
8 | "github.com/techsquidtv/uhs-cli/models/service"
9 | "gopkg.in/yaml.v3"
10 | )
11 |
12 | type changedetectionio struct {
13 | service.Service `yaml:",inline"`
14 | changedetectionioOptions `yaml:",inline"`
15 | }
16 |
17 | type changedetectionioOptions struct {
18 | Config string `yaml:"config"`
19 | }
20 |
21 | // Return default values for service
22 | func NewChangedetectionio() manager.Configurer {
23 | http := 5000
24 |
25 | return &changedetectionio{
26 | Service: service.Service{
27 | Enabled: false,
28 | ReplicaCount: 1,
29 | Image: service.Image{
30 | Repository: "ghcr.io/dgtlmoon/changedetection.io",
31 | Tag: "latest",
32 | PullPolicy: "Always",
33 | },
34 | Ports: service.Ports{
35 | Http: &http,
36 | },
37 | },
38 | changedetectionioOptions: changedetectionioOptions{
39 | Config: "/opt/changedetectionio/config",
40 | },
41 | }
42 | }
43 |
44 | func (s *changedetectionio) Configure() {
45 | s.Enabled = true
46 | inputConfigPath := &survey.Input{
47 | Message: "Enter the path to your Changedetectionio config folder:",
48 | Default: s.Config,
49 | }
50 | err := survey.AskOne(inputConfigPath, &s.Config)
51 | if err != nil {
52 | fmt.Println(err.Error())
53 | }
54 | }
55 |
56 | func (s *changedetectionio) Fill(data []byte) error { return yaml.Unmarshal(data, s) }
57 |
--------------------------------------------------------------------------------
/models/service/services/cloudflared.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/AlecAivazis/survey/v2"
7 | "github.com/techsquidtv/uhs-cli/models/config/manager"
8 | "github.com/techsquidtv/uhs-cli/models/service"
9 | "gopkg.in/yaml.v3"
10 | )
11 |
12 | type cloudflared struct {
13 | service.Service `yaml:",inline"`
14 | cloudflaredOptions `yaml:",inline"`
15 | }
16 |
17 | type cloudflaredOptions struct {
18 | Tunnel string `yaml:"tunnel"`
19 | Domain string `yaml:"domain"`
20 | URL string `yaml:"url"`
21 | Target string `yaml:"target"`
22 | }
23 |
24 | // Return default values for service
25 | func NewCloudflared() manager.Configurer {
26 |
27 | return &cloudflared{
28 | Service: service.Service{
29 | Enabled: false,
30 | ReplicaCount: 1,
31 | Image: service.Image{
32 | Repository: "cloudflare/cloudflared",
33 | Tag: "latest",
34 | PullPolicy: "IfNotPresent", // For stability
35 | },
36 | Ports: service.Ports{},
37 | },
38 | cloudflaredOptions: cloudflaredOptions{
39 | Tunnel: "example-tunnel",
40 | Domain: "example.com",
41 | URL: "http://foo.example.com",
42 | Target: "localhost:8080",
43 | },
44 | }
45 | }
46 |
47 | func (s *cloudflared) Configure() {
48 | s.Enabled = true
49 | inputTunnel := &survey.Input{
50 | Message: "Enter the name of your tunnel:",
51 | Default: s.Tunnel,
52 | }
53 | err := survey.AskOne(inputTunnel, &s.Tunnel)
54 | if err != nil {
55 | fmt.Println(err.Error())
56 | }
57 |
58 | inputDomain := &survey.Input{
59 | Message: "Enter the domain you want to use:",
60 | Default: s.Domain,
61 | }
62 | err = survey.AskOne(inputDomain, &s.Domain)
63 | if err != nil {
64 | fmt.Println(err.Error())
65 | }
66 |
67 | inputURL := &survey.Input{
68 | Message: "Enter the URL you want to use:",
69 | Default: s.URL,
70 | }
71 | err = survey.AskOne(inputURL, &s.URL)
72 | if err != nil {
73 | fmt.Println(err.Error())
74 | }
75 |
76 | inputTarget := &survey.Input{
77 | Message: "Enter the target you want to use:",
78 | Default: s.Target,
79 | }
80 | err = survey.AskOne(inputTarget, &s.Target)
81 | if err != nil {
82 | fmt.Println(err.Error())
83 | }
84 | }
85 |
86 | func (s *cloudflared) Fill(data []byte) error { return yaml.Unmarshal(data, s) }
87 |
--------------------------------------------------------------------------------
/models/service/services/gotify.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/AlecAivazis/survey/v2"
7 | "github.com/techsquidtv/uhs-cli/models/config/manager"
8 | "github.com/techsquidtv/uhs-cli/models/service"
9 | "gopkg.in/yaml.v3"
10 | )
11 |
12 | type gotify struct {
13 | service.Service `yaml:",inline"`
14 | gotifyOptions `yaml:",inline"`
15 | }
16 |
17 | type gotifyOptions struct {
18 | Data string `yaml:"data"`
19 | }
20 |
21 | // Return default values for service
22 | func NewGotify() manager.Configurer {
23 | http := 80
24 |
25 | return &gotify{
26 | Service: service.Service{
27 | Enabled: false,
28 | ReplicaCount: 1,
29 | Image: service.Image{
30 | Repository: "ghcr.io/gotify/server",
31 | Tag: "latest",
32 | PullPolicy: "Always",
33 | },
34 | Ports: service.Ports{
35 | Http: &http,
36 | },
37 | },
38 | gotifyOptions: gotifyOptions{
39 | Data: "/opt/gotify/data",
40 | },
41 | }
42 | }
43 |
44 | func (s *gotify) Configure() {
45 | s.Enabled = true
46 | inputConfigPath := &survey.Input{
47 | Message: "Enter the path to your Gotify data folder:",
48 | Default: s.Data,
49 | }
50 | err := survey.AskOne(inputConfigPath, &s.Data)
51 | if err != nil {
52 | fmt.Println(err.Error())
53 | }
54 | }
55 |
56 | func (s *gotify) Fill(data []byte) error { return yaml.Unmarshal(data, s) }
57 |
--------------------------------------------------------------------------------
/models/service/services/homepage.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import (
4 | "github.com/techsquidtv/uhs-cli/models/config/manager"
5 | "github.com/techsquidtv/uhs-cli/models/service"
6 | "gopkg.in/yaml.v3"
7 | )
8 |
9 | type homepage struct {
10 | service.Service `yaml:",inline"`
11 | homepageOptions `yaml:",inline"`
12 | }
13 |
14 | type homepageOptions struct {
15 | Bookmarks []homepageBookmarkGroup `yaml:"bookmarks"`
16 | Services []homepageServiceGroup `yaml:"services"`
17 | Widgets []homepageWidget `yaml:"widgets"`
18 | Settings map[any]any `yaml:"settings"`
19 | }
20 |
21 | // Return default values for service
22 | func NewHomepage() manager.Configurer {
23 | http := 3000
24 |
25 | bookmarksConfig := []homepageBookmarkGroup{
26 | {
27 | "Development": []homepageBookmark{
28 | createHomepageBookmark("github", "mdi-github", "gh", "https://github.com"),
29 | },
30 | },
31 | {
32 | "Media": []homepageBookmark{
33 | createHomepageBookmark("youtube", "mdi-youtube", "yt", "https://youtube.com"),
34 | createHomepageBookmark("plex", "mdi-plex", "plex", "https://app.plex.tv/desktop"),
35 | },
36 | },
37 | }
38 | servicesConfig := []homepageServiceGroup{
39 | {
40 | "Media": []homepageService{
41 | createHomepageService("plex", "mdi-plex", "https://app.plex.tv/desktop", "Plex"),
42 | },
43 | },
44 | }
45 | widgetConfig := []homepageWidget{
46 | createHomepageWidget("search", map[string]any{
47 | "provider": "[duckduckgo, google]",
48 | "focus": true,
49 | "target": "_blank",
50 | }),
51 | }
52 | settingsConfig := map[any]any{
53 | "title": "Homepage",
54 | "theme": "dark",
55 | }
56 |
57 | return &homepage{
58 | Service: service.Service{
59 | Enabled: false,
60 | ReplicaCount: 1,
61 | Image: service.Image{
62 | Repository: "ghcr.io/benphelps/homepage",
63 | Tag: "latest",
64 | PullPolicy: "Always",
65 | },
66 | Ports: service.Ports{
67 | Http: &http,
68 | },
69 | },
70 | homepageOptions: homepageOptions{
71 | Bookmarks: bookmarksConfig,
72 | Services: servicesConfig,
73 | Widgets: widgetConfig,
74 | Settings: settingsConfig,
75 | },
76 | }
77 | }
78 |
79 | func (s *homepage) Configure() {
80 | s.Enabled = true
81 | }
82 |
83 | func (s *homepage) Fill(data []byte) error { return yaml.Unmarshal(data, s) }
84 |
85 | //////////////
86 | // Bookmark //
87 | //////////////
88 |
89 | type homepageBookmarkGroup map[string][]homepageBookmark
90 | type homepageBookmark map[string][]homepageBookmarkOptions
91 |
92 | type homepageBookmarkOptions struct {
93 | Icon *string `yaml:"icon,omitempty"`
94 | Abbr *string `yaml:"abbr,omitempty"`
95 | Href string `yaml:"href"`
96 | }
97 |
98 | func createHomepageBookmark(name, icon, abbr, href string) homepageBookmark {
99 | return homepageBookmark{
100 | name: []homepageBookmarkOptions{
101 | {
102 | Icon: &icon,
103 | Abbr: &abbr,
104 | Href: href,
105 | },
106 | },
107 | }
108 | }
109 |
110 | /////////////
111 | // Service //
112 | /////////////
113 |
114 | type homepageServiceGroup map[string][]homepageService
115 | type homepageService map[string][]homepageServiceOptions
116 |
117 | type homepageServiceOptions struct {
118 | Icon *string `yaml:"icon,omitempty"`
119 | Href string `yaml:"href"`
120 | Description *string `yaml:"description,omitempty"`
121 | }
122 |
123 | func createHomepageService(name, icon, href, description string) homepageService {
124 | return homepageService{
125 | name: []homepageServiceOptions{
126 | {
127 | Icon: &icon,
128 | Href: href,
129 | Description: &description,
130 | },
131 | },
132 | }
133 | }
134 |
135 | ////////////
136 | // Widget //
137 | ////////////
138 |
139 | type homepageWidget map[string]map[string]any
140 |
141 | func createHomepageWidget(name string, resources map[string]any) homepageWidget {
142 | return homepageWidget{
143 | name: resources,
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/models/service/services/huginn.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/AlecAivazis/survey/v2"
7 | "github.com/techsquidtv/uhs-cli/models/config/manager"
8 | "github.com/techsquidtv/uhs-cli/models/service"
9 | "gopkg.in/yaml.v3"
10 | )
11 |
12 | type huginn struct {
13 | service.Service `yaml:",inline"`
14 | huginnOptions `yaml:",inline"`
15 | }
16 |
17 | type huginnOptions struct {
18 | Data string `yaml:"data"`
19 | Invitation_code string `yaml:"invitation_code"`
20 | App_secret_token string `yaml:"app_secret_token"`
21 | }
22 |
23 | // Return default values for service
24 | func NewHuginn() manager.Configurer {
25 | http := 3000
26 |
27 | return &huginn{
28 | Service: service.Service{
29 | Enabled: false,
30 | ReplicaCount: 1,
31 | Image: service.Image{
32 | Repository: "ghcr.io/huginn/huginn",
33 | Tag: "latest",
34 | PullPolicy: "Always",
35 | },
36 | Ports: service.Ports{
37 | Http: &http,
38 | },
39 | },
40 | huginnOptions: huginnOptions{
41 | Data: "/opt/huginn/data",
42 | Invitation_code: "invite-me",
43 | App_secret_token: "",
44 | },
45 | }
46 | }
47 |
48 | func (s *huginn) Configure() {
49 | s.Enabled = true
50 | inputDataPath := &survey.Input{
51 | Message: "Enter the path to your Huginn data folder:",
52 | Default: s.Data,
53 | }
54 | err := survey.AskOne(inputDataPath, &s.Data)
55 | if err != nil {
56 | fmt.Println(err.Error())
57 | }
58 | inputInvitationCode := &survey.Input{
59 | Message: "Enter the invitation code for your Huginn instance:",
60 | Default: s.Invitation_code,
61 | }
62 | err = survey.AskOne(inputInvitationCode, &s.Invitation_code)
63 | if err != nil {
64 | fmt.Println(err.Error())
65 | }
66 | inputAppSecretToken := &survey.Input{
67 | Message: "Enter the app secret token for your Huginn instance:",
68 | Default: s.App_secret_token,
69 | }
70 | err = survey.AskOne(inputAppSecretToken, &s.App_secret_token)
71 | if err != nil {
72 | fmt.Println(err.Error())
73 | }
74 | }
75 |
76 | func (s *huginn) Fill(data []byte) error { return yaml.Unmarshal(data, s) }
77 |
--------------------------------------------------------------------------------
/models/service/services/jellyfin.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/AlecAivazis/survey/v2"
7 | "github.com/techsquidtv/uhs-cli/models/config/manager"
8 | "github.com/techsquidtv/uhs-cli/models/service"
9 | "gopkg.in/yaml.v3"
10 | )
11 |
12 | type jellyfin struct {
13 | service.Service `yaml:",inline"`
14 | jellyfinOptions `yaml:",inline"`
15 | }
16 |
17 | type jellyfinOptions struct {
18 | Config string `yaml:"config"`
19 | Library string `yaml:"library"`
20 | }
21 |
22 | // Return default values for service
23 | func NewJellyfin() manager.Configurer {
24 | http := 8096
25 | udp := 7359
26 |
27 | return &jellyfin{
28 | Service: service.Service{
29 | Enabled: false,
30 | ReplicaCount: 1,
31 | Image: service.Image{
32 | Repository: "ghcr.io/onedr0p/jellyfin",
33 | Tag: "rolling",
34 | PullPolicy: "Always",
35 | },
36 | Ports: service.Ports{
37 | Http: &http,
38 | Udp: &udp,
39 | },
40 | },
41 | jellyfinOptions: jellyfinOptions{
42 | Config: "/opt/jellyfin/config",
43 | Library: "/data/library",
44 | },
45 | }
46 | }
47 |
48 | func (s *jellyfin) Configure() {
49 | s.Enabled = true
50 | inputConfigPath := &survey.Input{
51 | Message: "Enter the path to your jellyfin config folder:",
52 | Default: s.Config,
53 | }
54 | err := survey.AskOne(inputConfigPath, &s.Config)
55 | if err != nil {
56 | fmt.Println(err.Error())
57 | }
58 |
59 | inputLibraryPath := &survey.Input{
60 | Message: "Enter the path to your jellyfin library folder:",
61 | Default: s.Library,
62 | }
63 |
64 | err = survey.AskOne(inputLibraryPath, &s.Library)
65 | if err != nil {
66 | fmt.Println(err.Error())
67 | }
68 | }
69 |
70 | func (s *jellyfin) Fill(data []byte) error { return yaml.Unmarshal(data, s) }
71 |
--------------------------------------------------------------------------------
/models/service/services/kavita.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/AlecAivazis/survey/v2"
7 | "github.com/techsquidtv/uhs-cli/models/config/manager"
8 | "github.com/techsquidtv/uhs-cli/models/service"
9 | "gopkg.in/yaml.v3"
10 | )
11 |
12 | type kavita struct {
13 | service.Service `yaml:",inline"`
14 | kavitaOptions `yaml:",inline"`
15 | }
16 |
17 | type kavitaOptions struct {
18 | Config string `yaml:"config"`
19 | Library string `yaml:"library"`
20 | }
21 |
22 | // Return default values for service
23 | func NewKavita() manager.Configurer {
24 | http := 5000
25 |
26 | return &kavita{
27 | Service: service.Service{
28 | Enabled: false,
29 | ReplicaCount: 1,
30 | Image: service.Image{
31 | Repository: "kizaing/kavita",
32 | Tag: "latest",
33 | PullPolicy: "Always",
34 | },
35 | Ports: service.Ports{
36 | Http: &http,
37 | },
38 | },
39 | kavitaOptions: kavitaOptions{
40 | Config: "/opt/kavita/config",
41 | Library: "/data/library/books",
42 | },
43 | }
44 | }
45 |
46 | func (s *kavita) Configure() {
47 | s.Enabled = true
48 | inputConfigPath := &survey.Input{
49 | Message: "Enter the path to your Kavita config folder:",
50 | Default: s.Config,
51 | }
52 | err := survey.AskOne(inputConfigPath, &s.Config)
53 | if err != nil {
54 | fmt.Println(err.Error())
55 | }
56 | inputLibraryPath := &survey.Input{
57 | Message: "Enter the path to your Kavita library folder:",
58 | Default: s.Library,
59 | }
60 | err = survey.AskOne(inputLibraryPath, &s.Library)
61 | if err != nil {
62 | fmt.Println(err.Error())
63 | }
64 | }
65 |
66 | func (s *kavita) Fill(data []byte) error { return yaml.Unmarshal(data, s) }
67 |
--------------------------------------------------------------------------------
/models/service/services/nginx.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/AlecAivazis/survey/v2"
7 | "github.com/techsquidtv/uhs-cli/models/config/manager"
8 | "github.com/techsquidtv/uhs-cli/models/service"
9 | "gopkg.in/yaml.v3"
10 | )
11 |
12 | type nginx struct {
13 | service.Service `yaml:",inline"`
14 | nginxOptions `yaml:",inline"`
15 | }
16 |
17 | type nginxOptions struct {
18 | PublicPath string `yaml:"public_path"`
19 | Domain string `yaml:"domain"`
20 | }
21 |
22 | // Return default values for service
23 | func NewNginx() manager.Configurer {
24 | http := 80
25 | https := 443
26 |
27 | return &nginx{
28 | Service: service.Service{
29 | Enabled: false,
30 | ReplicaCount: 1,
31 | Image: service.Image{
32 | Repository: "nginx",
33 | Tag: "latest",
34 | PullPolicy: "Always",
35 | },
36 | Ports: service.Ports{
37 | Http: &http,
38 | Https: &https,
39 | },
40 | },
41 | nginxOptions: nginxOptions{
42 | PublicPath: "/opt/nginx/public",
43 | Domain: "example.com",
44 | },
45 | }
46 | }
47 |
48 | func (s *nginx) Configure() {
49 | s.Enabled = true
50 | inputPublicPath := &survey.Input{
51 | Message: "Enter the path to your Nginx public folder:",
52 | Default: s.PublicPath,
53 | }
54 | err := survey.AskOne(inputPublicPath, &s.PublicPath)
55 | if err != nil {
56 | fmt.Println(err.Error())
57 | }
58 |
59 | inputDomain := &survey.Input{
60 | Message: "Enter the domain you want to use:",
61 | Default: s.Domain,
62 | }
63 |
64 | err = survey.AskOne(inputDomain, &s.Domain)
65 | if err != nil {
66 | fmt.Println(err.Error())
67 | }
68 | }
69 |
70 | func (s *nginx) Fill(data []byte) error { return yaml.Unmarshal(data, s) }
71 |
--------------------------------------------------------------------------------
/models/service/services/overseerr.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/AlecAivazis/survey/v2"
7 | "github.com/techsquidtv/uhs-cli/models/config/manager"
8 | "github.com/techsquidtv/uhs-cli/models/service"
9 | "gopkg.in/yaml.v3"
10 | )
11 |
12 | type overseerr struct {
13 | service.Service `yaml:",inline"`
14 | overseerrOptions `yaml:",inline"`
15 | }
16 |
17 | type overseerrOptions struct {
18 | Config string `yaml:"config"`
19 | }
20 |
21 | // Return default values for service
22 | func NewOverseerr() manager.Configurer {
23 | http := 5055
24 |
25 | return &overseerr{
26 | Service: service.Service{
27 | Enabled: false,
28 | ReplicaCount: 1,
29 | Image: service.Image{
30 | Repository: "lscr.io/linuxserver/overseerr",
31 | Tag: "latest",
32 | PullPolicy: "Always",
33 | },
34 | Ports: service.Ports{
35 | Http: &http,
36 | },
37 | },
38 | overseerrOptions: overseerrOptions{
39 | Config: "/opt/overseerr/config",
40 | },
41 | }
42 | }
43 |
44 | func (s *overseerr) Configure() {
45 | s.Enabled = true
46 | inputConfigPath := &survey.Input{
47 | Message: "Enter the path to your overseerr config folder:",
48 | Default: s.Config,
49 | }
50 | err := survey.AskOne(inputConfigPath, &s.Config)
51 | if err != nil {
52 | fmt.Println(err.Error())
53 | }
54 | }
55 |
56 | func (s *overseerr) Fill(data []byte) error { return yaml.Unmarshal(data, s) }
57 |
--------------------------------------------------------------------------------
/models/service/services/playwright.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/AlecAivazis/survey/v2"
7 | "github.com/techsquidtv/uhs-cli/models/config/manager"
8 | "github.com/techsquidtv/uhs-cli/models/service"
9 | "gopkg.in/yaml.v3"
10 | )
11 |
12 | type playwright struct {
13 | service.Service `yaml:",inline"`
14 | playwrightOptions `yaml:",inline"`
15 | }
16 |
17 | type playwrightOptions struct {
18 | Config string `yaml:"config"`
19 | }
20 |
21 | // Return default values for service
22 | func NewPlaywright() manager.Configurer {
23 | http := 3000
24 |
25 | return &playwright{
26 | Service: service.Service{
27 | Enabled: false,
28 | ReplicaCount: 1,
29 | Image: service.Image{
30 | Repository: "browserless/chrome",
31 | Tag: "latest",
32 | PullPolicy: "Always",
33 | },
34 | Ports: service.Ports{
35 | Http: &http,
36 | },
37 | },
38 | playwrightOptions: playwrightOptions{
39 | Config: "/opt/playwright/config",
40 | },
41 | }
42 | }
43 |
44 | func (s *playwright) Configure() {
45 | s.Enabled = true
46 | inputConfigPath := &survey.Input{
47 | Message: "Enter the path to your Playwright config folder:",
48 | Default: s.Config,
49 | }
50 | err := survey.AskOne(inputConfigPath, &s.Config)
51 | if err != nil {
52 | fmt.Println(err.Error())
53 | }
54 | }
55 |
56 | func (s *playwright) Fill(data []byte) error { return yaml.Unmarshal(data, s) }
57 |
--------------------------------------------------------------------------------
/models/service/services/plex.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/AlecAivazis/survey/v2"
7 | "github.com/techsquidtv/uhs-cli/models/config/manager"
8 | "github.com/techsquidtv/uhs-cli/models/service"
9 | "gopkg.in/yaml.v3"
10 | )
11 |
12 | type plex struct {
13 | service.Service `yaml:",inline"`
14 | plexOptions `yaml:",inline"`
15 | }
16 |
17 | type plexOptions struct {
18 | Config string `yaml:"config"`
19 | Library string `yaml:"library"`
20 | }
21 |
22 | // Return default values for service
23 | func NewPlex() manager.Configurer {
24 | http := 32400
25 |
26 | return &plex{
27 | Service: service.Service{
28 | Enabled: false,
29 | ReplicaCount: 1,
30 | Image: service.Image{
31 | Repository: "lscr.io/linuxserver/plex",
32 | Tag: "latest",
33 | PullPolicy: "Always",
34 | },
35 | Ports: service.Ports{
36 | Http: &http,
37 | },
38 | },
39 | plexOptions: plexOptions{
40 | Config: "/opt/plex/config",
41 | Library: "/data/library",
42 | },
43 | }
44 | }
45 |
46 | func (s *plex) Configure() {
47 | s.Enabled = true
48 | inputConfigPath := &survey.Input{
49 | Message: "Enter the path to your plex config folder:",
50 | Default: s.Config,
51 | }
52 | err := survey.AskOne(inputConfigPath, &s.Config)
53 | if err != nil {
54 | fmt.Println(err.Error())
55 | }
56 |
57 | inputLibraryPath := &survey.Input{
58 | Message: "Enter the path to your plex library folder:",
59 | Default: s.Library,
60 | }
61 | err = survey.AskOne(inputLibraryPath, &s.Library)
62 | if err != nil {
63 | fmt.Println(err.Error())
64 | }
65 | }
66 |
67 | func (s *plex) Fill(data []byte) error { return yaml.Unmarshal(data, s) }
68 |
--------------------------------------------------------------------------------
/models/service/services/prowlarr.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/AlecAivazis/survey/v2"
7 | "github.com/techsquidtv/uhs-cli/models/config/manager"
8 | "github.com/techsquidtv/uhs-cli/models/service"
9 | "gopkg.in/yaml.v3"
10 | )
11 |
12 | type prowlarr struct {
13 | service.Service `yaml:",inline"`
14 | prowlarrOptions `yaml:",inline"`
15 | }
16 |
17 | type prowlarrOptions struct {
18 | Config string `yaml:"config"`
19 | }
20 |
21 | // Return default values for service
22 | func NewProwlarr() manager.Configurer {
23 | http := 9696
24 |
25 | return &prowlarr{
26 | Service: service.Service{
27 | Enabled: false,
28 | ReplicaCount: 1,
29 | Image: service.Image{
30 | Repository: "lscr.io/linuxserver/prowlarr",
31 | Tag: "latest",
32 | PullPolicy: "Always",
33 | },
34 | Ports: service.Ports{
35 | Http: &http,
36 | },
37 | },
38 | prowlarrOptions: prowlarrOptions{
39 | Config: "/opt/prowlarr/config",
40 | },
41 | }
42 | }
43 |
44 | func (s *prowlarr) Configure() {
45 | s.Enabled = true
46 | inputConfigPath := &survey.Input{
47 | Message: "Enter the path to your prowlarr config folder:",
48 | Default: s.Config,
49 | }
50 | err := survey.AskOne(inputConfigPath, &s.Config)
51 | if err != nil {
52 | fmt.Println(err.Error())
53 | }
54 | }
55 |
56 | func (s *prowlarr) Fill(data []byte) error { return yaml.Unmarshal(data, s) }
57 |
--------------------------------------------------------------------------------
/models/service/services/qbittorrent.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/AlecAivazis/survey/v2"
7 | "github.com/techsquidtv/uhs-cli/models/config/manager"
8 | "github.com/techsquidtv/uhs-cli/models/service"
9 | "gopkg.in/yaml.v3"
10 | )
11 |
12 | type qbittorrent struct {
13 | service.Service `yaml:",inline"`
14 | qbittorrentOptions `yaml:",inline"`
15 | }
16 |
17 | type qbittorrentOptions struct {
18 | Config string `yaml:"config"`
19 | Data string `yaml:"data"`
20 | }
21 |
22 | // Return default values for service
23 | func NewQbittorrent() manager.Configurer {
24 | http := 8080
25 | p2p := 6881
26 | p2pudp := 6881
27 |
28 | // Create a new Qbittorrent instance
29 | return &qbittorrent{
30 | Service: service.Service{
31 | Enabled: false,
32 | ReplicaCount: 1,
33 | Image: service.Image{
34 | Repository: "lscr.io/linuxserver/qbittorrent",
35 | Tag: "latest",
36 | PullPolicy: "Always",
37 | },
38 | Ports: service.Ports{
39 | Http: &http,
40 | P2p: &p2p,
41 | P2pudp: &p2pudp,
42 | },
43 | },
44 | qbittorrentOptions: qbittorrentOptions{
45 | Config: "/opt/qbittorrent/config",
46 | Data: "/data/torrents",
47 | },
48 | }
49 | }
50 |
51 | func (s *qbittorrent) Configure() {
52 | s.Enabled = true
53 | inputConfigPath := &survey.Input{
54 | Message: "Enter the path to your qbittorrent config folder:",
55 | Default: s.Config,
56 | }
57 | err := survey.AskOne(inputConfigPath, &s.Config)
58 | if err != nil {
59 | fmt.Println(err.Error())
60 | }
61 |
62 | inputDataPath := &survey.Input{
63 | Message: "Enter the path to your torrent data folder:",
64 | Default: s.Data,
65 | }
66 | err = survey.AskOne(inputDataPath, &s.Data)
67 | if err != nil {
68 | fmt.Println(err.Error())
69 | }
70 | }
71 |
72 | func (s *qbittorrent) Fill(data []byte) error { return yaml.Unmarshal(data, s) }
73 |
--------------------------------------------------------------------------------
/models/service/services/radarr.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/AlecAivazis/survey/v2"
7 | "github.com/techsquidtv/uhs-cli/models/config/manager"
8 | "github.com/techsquidtv/uhs-cli/models/service"
9 | "gopkg.in/yaml.v3"
10 | )
11 |
12 | type radarr struct {
13 | service.Service `yaml:",inline"`
14 | radarrOptions `yaml:",inline"`
15 | }
16 |
17 | type radarrOptions struct {
18 | Config string `yaml:"config"`
19 | Data string `yaml:"data"`
20 | }
21 |
22 | // Return default values for service
23 | func NewRadarr() manager.Configurer {
24 | http := 7878
25 |
26 | return &radarr{
27 | Service: service.Service{
28 | Enabled: false,
29 | ReplicaCount: 1,
30 | Image: service.Image{
31 | Repository: "lscr.io/linuxserver/radarr",
32 | Tag: "latest",
33 | PullPolicy: "Always",
34 | },
35 | Ports: service.Ports{
36 | Http: &http,
37 | },
38 | },
39 | radarrOptions: radarrOptions{
40 | Config: "/opt/radarr/config",
41 | Data: "/data",
42 | },
43 | }
44 | }
45 |
46 | func (s *radarr) Configure() {
47 | s.Enabled = true
48 | inputConfigPath := &survey.Input{
49 | Message: "Enter the path to your radarr config folder:",
50 | Default: s.Config,
51 | }
52 | err := survey.AskOne(inputConfigPath, &s.Config)
53 | if err != nil {
54 | fmt.Println(err.Error())
55 | }
56 |
57 | inputDataPath := &survey.Input{
58 | Message: "Enter the path to your top level media folder:",
59 | Default: s.Data,
60 | }
61 | err = survey.AskOne(inputDataPath, &s.Data)
62 | if err != nil {
63 | fmt.Println(err.Error())
64 | }
65 | }
66 |
67 | func (s *radarr) Fill(data []byte) error { return yaml.Unmarshal(data, s) }
68 |
--------------------------------------------------------------------------------
/models/service/services/sabnzbd.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/AlecAivazis/survey/v2"
7 | "github.com/techsquidtv/uhs-cli/models/config/manager"
8 | "github.com/techsquidtv/uhs-cli/models/service"
9 | "gopkg.in/yaml.v3"
10 | )
11 |
12 | type sabnzbd struct {
13 | service.Service `yaml:",inline"`
14 | sabnzbdOptions `yaml:",inline"`
15 | }
16 |
17 | type sabnzbdOptions struct {
18 | Config string `yaml:"config"`
19 | Data string `yaml:"data"`
20 | }
21 |
22 | // Return default values for service
23 | func NewSabnzbd() manager.Configurer {
24 | http := 8080
25 |
26 | // Create a new Sabnzbd instance
27 | return &sabnzbd{
28 | Service: service.Service{
29 | Enabled: false,
30 | ReplicaCount: 1,
31 | Image: service.Image{
32 | Repository: "lscr.io/linuxserver/sabnzbd",
33 | Tag: "latest",
34 | PullPolicy: "Always",
35 | },
36 | Ports: service.Ports{
37 | Http: &http,
38 | },
39 | },
40 | sabnzbdOptions: sabnzbdOptions{
41 | Config: "/opt/sabnzbd/config",
42 | Data: "/data/usenet",
43 | },
44 | }
45 | }
46 |
47 | func (s *sabnzbd) Configure() {
48 | s.Enabled = true
49 | inputConfigPath := &survey.Input{
50 | Message: "Enter the path to your sabnzbd config folder:",
51 | Default: s.Config,
52 | }
53 | err := survey.AskOne(inputConfigPath, &s.Config)
54 | if err != nil {
55 | fmt.Println(err.Error())
56 | }
57 |
58 | inputDataPath := &survey.Input{
59 | Message: "Enter the path to your usenet data folder:",
60 | Default: s.Data,
61 | }
62 | err = survey.AskOne(inputDataPath, &s.Data)
63 | if err != nil {
64 | fmt.Println(err.Error())
65 | }
66 | }
67 |
68 | func (s *sabnzbd) Fill(data []byte) error { return yaml.Unmarshal(data, s) }
69 |
--------------------------------------------------------------------------------
/models/service/services/sonarr.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/AlecAivazis/survey/v2"
7 | "github.com/techsquidtv/uhs-cli/models/config/manager"
8 | "github.com/techsquidtv/uhs-cli/models/service"
9 | "gopkg.in/yaml.v3"
10 | )
11 |
12 | type sonarr struct {
13 | service.Service `yaml:",inline"`
14 | sonarrOptions `yaml:",inline"`
15 | }
16 |
17 | type sonarrOptions struct {
18 | Config string `yaml:"config"`
19 | Data string `yaml:"data"`
20 | }
21 |
22 | // Return default values for service
23 | func NewSonarr() manager.Configurer {
24 | http := 8989
25 |
26 | return &sonarr{
27 | Service: service.Service{
28 | Enabled: false,
29 | ReplicaCount: 1,
30 | Image: service.Image{
31 | Repository: "lscr.io/linuxserver/sonarr",
32 | Tag: "latest",
33 | PullPolicy: "Always",
34 | },
35 | Ports: service.Ports{
36 | Http: &http,
37 | },
38 | },
39 | sonarrOptions: sonarrOptions{
40 | Config: "/opt/sonarr/config",
41 | Data: "/data",
42 | },
43 | }
44 | }
45 |
46 | func (s *sonarr) Configure() {
47 | s.Enabled = true
48 | inputConfigPath := &survey.Input{
49 | Message: "Enter the path to your Sonarr config folder:",
50 | Default: s.Config,
51 | }
52 | err := survey.AskOne(inputConfigPath, &s.Config)
53 | if err != nil {
54 | fmt.Println(err.Error())
55 | }
56 |
57 | inputDataPath := &survey.Input{
58 | Message: "Enter the path to your top level media folder:",
59 | Default: s.Data,
60 | }
61 | err = survey.AskOne(inputDataPath, &s.Data)
62 | if err != nil {
63 | fmt.Println(err.Error())
64 | }
65 | }
66 |
67 | func (s *sonarr) Fill(data []byte) error { return yaml.Unmarshal(data, s) }
68 |
--------------------------------------------------------------------------------
/models/service/services/tautulli.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/AlecAivazis/survey/v2"
7 | "github.com/techsquidtv/uhs-cli/models/config/manager"
8 | "github.com/techsquidtv/uhs-cli/models/service"
9 | "gopkg.in/yaml.v3"
10 | )
11 |
12 | type tautulli struct {
13 | service.Service `yaml:",inline"`
14 | tautulliOptions `yaml:",inline"`
15 | }
16 |
17 | type tautulliOptions struct {
18 | Config string `yaml:"config"`
19 | }
20 |
21 | // Return default values for service
22 | func NewTautulli() manager.Configurer {
23 | http := 8181
24 |
25 | return &tautulli{
26 | Service: service.Service{
27 | Enabled: false,
28 | ReplicaCount: 1,
29 | Image: service.Image{
30 | Repository: "lscr.io/linuxserver/tautulli",
31 | Tag: "latest",
32 | PullPolicy: "Always",
33 | },
34 | Ports: service.Ports{
35 | Http: &http,
36 | },
37 | },
38 | tautulliOptions: tautulliOptions{
39 | Config: "/opt/tautulli/config",
40 | },
41 | }
42 | }
43 |
44 | func (s *tautulli) Configure() {
45 | s.Enabled = true
46 | inputConfigPath := &survey.Input{
47 | Message: "Enter the path to your Tautulli config folder:",
48 | Default: s.Config,
49 | }
50 | err := survey.AskOne(inputConfigPath, &s.Config)
51 | if err != nil {
52 | fmt.Println(err.Error())
53 | }
54 | }
55 |
56 | func (s *tautulli) Fill(data []byte) error { return yaml.Unmarshal(data, s) }
57 |
--------------------------------------------------------------------------------
/models/service/services/thelounge.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/AlecAivazis/survey/v2"
7 | "github.com/techsquidtv/uhs-cli/models/config/manager"
8 | "github.com/techsquidtv/uhs-cli/models/service"
9 | "gopkg.in/yaml.v3"
10 | )
11 |
12 | type thelounge struct {
13 | service.Service `yaml:",inline"`
14 | theloungeOptions `yaml:",inline"`
15 | }
16 |
17 | type theloungeOptions struct {
18 | Config string `yaml:"config"`
19 | }
20 |
21 | // Return default values for service
22 | func NewThelounge() manager.Configurer {
23 | http := 9000
24 |
25 | return &thelounge{
26 | Service: service.Service{
27 | Enabled: false,
28 | ReplicaCount: 1,
29 | Image: service.Image{
30 | Repository: "lscr.io/linuxserver/thelounge",
31 | Tag: "latest",
32 | PullPolicy: "Always",
33 | },
34 | Ports: service.Ports{
35 | Http: &http,
36 | },
37 | },
38 | theloungeOptions: theloungeOptions{
39 | Config: "/opt/thelounge/config",
40 | },
41 | }
42 | }
43 |
44 | func (s *thelounge) Configure() {
45 | s.Enabled = true
46 | inputConfigPath := &survey.Input{
47 | Message: "Enter the path to your Thelounge config folder:",
48 | Default: s.Config,
49 | }
50 | err := survey.AskOne(inputConfigPath, &s.Config)
51 | if err != nil {
52 | fmt.Println(err.Error())
53 | }
54 | }
55 |
56 | func (s *thelounge) Fill(data []byte) error { return yaml.Unmarshal(data, s) }
57 |
--------------------------------------------------------------------------------