Adding upstream version 0.8.9.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
3b2c48b5e4
commit
c0c4addb85
285 changed files with 25880 additions and 0 deletions
240
shoutrrr/cmd/generate/generate.go
Normal file
240
shoutrrr/cmd/generate/generate.go
Normal file
|
@ -0,0 +1,240 @@
|
|||
package generate
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/nicholas-fedor/shoutrrr/pkg/generators"
|
||||
"github.com/nicholas-fedor/shoutrrr/pkg/router"
|
||||
"github.com/nicholas-fedor/shoutrrr/pkg/types"
|
||||
)
|
||||
|
||||
// MaximumNArgs defines the maximum number of positional arguments allowed.
|
||||
const MaximumNArgs = 2
|
||||
|
||||
// ErrNoServiceSpecified indicates that no service was provided for URL generation.
|
||||
var (
|
||||
ErrNoServiceSpecified = errors.New("no service specified")
|
||||
)
|
||||
|
||||
// serviceRouter manages the creation of notification services.
|
||||
var serviceRouter router.ServiceRouter
|
||||
|
||||
// Cmd generates a notification service URL from user input.
|
||||
var Cmd = &cobra.Command{
|
||||
Use: "generate",
|
||||
Short: "Generates a notification service URL from user input",
|
||||
Run: Run,
|
||||
PreRun: loadArgsFromAltSources,
|
||||
Args: cobra.MaximumNArgs(MaximumNArgs),
|
||||
}
|
||||
|
||||
// loadArgsFromAltSources populates command flags from positional arguments if provided.
|
||||
func loadArgsFromAltSources(cmd *cobra.Command, args []string) {
|
||||
if len(args) > 0 {
|
||||
_ = cmd.Flags().Set("service", args[0])
|
||||
}
|
||||
|
||||
if len(args) > 1 {
|
||||
_ = cmd.Flags().Set("generator", args[1])
|
||||
}
|
||||
}
|
||||
|
||||
// init initializes the command flags for the generate command.
|
||||
func init() {
|
||||
serviceRouter = router.ServiceRouter{}
|
||||
|
||||
Cmd.Flags().
|
||||
StringP("service", "s", "", "Notification service to generate a URL for (e.g., discord, smtp)")
|
||||
Cmd.Flags().
|
||||
StringP("generator", "g", "basic", "Generator to use (e.g., basic, or service-specific)")
|
||||
Cmd.Flags().
|
||||
StringArrayP("property", "p", []string{}, "Configuration property in key=value format (e.g., token=abc123)")
|
||||
Cmd.Flags().
|
||||
BoolP("show-sensitive", "x", false, "Show sensitive data in the generated URL (default: masked)")
|
||||
}
|
||||
|
||||
// maskSensitiveURL masks sensitive parts of a Shoutrrr URL based on the service schema.
|
||||
func maskSensitiveURL(serviceSchema, urlStr string) string {
|
||||
parsedURL, err := url.Parse(urlStr)
|
||||
if err != nil {
|
||||
return urlStr // Return original URL if parsing fails
|
||||
}
|
||||
|
||||
switch serviceSchema {
|
||||
case "discord", "slack", "teams":
|
||||
maskUser(parsedURL, "REDACTED")
|
||||
case "smtp":
|
||||
maskSMTPUser(parsedURL)
|
||||
case "pushover":
|
||||
maskPushoverQuery(parsedURL)
|
||||
case "gotify":
|
||||
maskGotifyQuery(parsedURL)
|
||||
default:
|
||||
maskGeneric(parsedURL)
|
||||
}
|
||||
|
||||
return parsedURL.String()
|
||||
}
|
||||
|
||||
// maskUser redacts the username in a URL with a placeholder.
|
||||
func maskUser(parsedURL *url.URL, placeholder string) {
|
||||
if parsedURL.User != nil {
|
||||
parsedURL.User = url.User(placeholder)
|
||||
}
|
||||
}
|
||||
|
||||
// maskSMTPUser redacts the password in an SMTP URL, preserving the username.
|
||||
func maskSMTPUser(parsedURL *url.URL) {
|
||||
if parsedURL.User != nil {
|
||||
parsedURL.User = url.UserPassword(parsedURL.User.Username(), "REDACTED")
|
||||
}
|
||||
}
|
||||
|
||||
// maskPushoverQuery redacts token and user query parameters in a Pushover URL.
|
||||
func maskPushoverQuery(parsedURL *url.URL) {
|
||||
queryParams := parsedURL.Query()
|
||||
if queryParams.Get("token") != "" {
|
||||
queryParams.Set("token", "REDACTED")
|
||||
}
|
||||
|
||||
if queryParams.Get("user") != "" {
|
||||
queryParams.Set("user", "REDACTED")
|
||||
}
|
||||
|
||||
parsedURL.RawQuery = queryParams.Encode()
|
||||
}
|
||||
|
||||
// maskGotifyQuery redacts the token query parameter in a Gotify URL.
|
||||
func maskGotifyQuery(parsedURL *url.URL) {
|
||||
queryParams := parsedURL.Query()
|
||||
if queryParams.Get("token") != "" {
|
||||
queryParams.Set("token", "REDACTED")
|
||||
}
|
||||
|
||||
parsedURL.RawQuery = queryParams.Encode()
|
||||
}
|
||||
|
||||
// maskGeneric redacts userinfo and all query parameters for unrecognized services.
|
||||
func maskGeneric(parsedURL *url.URL) {
|
||||
maskUser(parsedURL, "REDACTED")
|
||||
|
||||
queryParams := parsedURL.Query()
|
||||
for key := range queryParams {
|
||||
queryParams.Set(key, "REDACTED")
|
||||
}
|
||||
|
||||
parsedURL.RawQuery = queryParams.Encode()
|
||||
}
|
||||
|
||||
// Run executes the generate command, producing a notification service URL.
|
||||
func Run(cmd *cobra.Command, _ []string) {
|
||||
var service types.Service
|
||||
|
||||
var err error
|
||||
|
||||
serviceSchema, _ := cmd.Flags().GetString("service")
|
||||
generatorName, _ := cmd.Flags().GetString("generator")
|
||||
propertyFlags, _ := cmd.Flags().GetStringArray("property")
|
||||
showSensitive, _ := cmd.Flags().GetBool("show-sensitive")
|
||||
|
||||
// Parse properties into a key-value map.
|
||||
props := make(map[string]string, len(propertyFlags))
|
||||
|
||||
for _, prop := range propertyFlags {
|
||||
parts := strings.Split(prop, "=")
|
||||
if len(parts) != MaximumNArgs {
|
||||
fmt.Fprint(
|
||||
color.Output,
|
||||
"Invalid property key/value pair: ",
|
||||
color.HiYellowString(prop),
|
||||
"\n",
|
||||
)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
props[parts[0]] = parts[1]
|
||||
}
|
||||
|
||||
if len(propertyFlags) > 0 {
|
||||
fmt.Fprint(color.Output, "\n") // Add spacing after property warnings
|
||||
}
|
||||
|
||||
// Validate and create the service.
|
||||
if serviceSchema == "" {
|
||||
err = ErrNoServiceSpecified
|
||||
} else {
|
||||
service, err = serviceRouter.NewService(serviceSchema)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
fmt.Fprint(os.Stdout, "Error: ", err, "\n")
|
||||
}
|
||||
|
||||
if service == nil {
|
||||
services := serviceRouter.ListServices()
|
||||
serviceList := strings.Join(services, ", ")
|
||||
cmd.SetUsageTemplate(cmd.UsageTemplate() + "\nAvailable services:\n " + serviceList + "\n")
|
||||
_ = cmd.Usage()
|
||||
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Determine the generator to use.
|
||||
var generator types.Generator
|
||||
|
||||
generatorFlag := cmd.Flags().Lookup("generator")
|
||||
if !generatorFlag.Changed {
|
||||
// Use the service-specific default generator if available and no explicit generator is set.
|
||||
generator, _ = generators.NewGenerator(serviceSchema)
|
||||
}
|
||||
|
||||
if generator != nil {
|
||||
generatorName = serviceSchema
|
||||
} else {
|
||||
var genErr error
|
||||
|
||||
generator, genErr = generators.NewGenerator(generatorName)
|
||||
if genErr != nil {
|
||||
fmt.Fprint(os.Stdout, "Error: ", genErr, "\n")
|
||||
}
|
||||
}
|
||||
|
||||
if generator == nil {
|
||||
generatorList := strings.Join(generators.ListGenerators(), ", ")
|
||||
cmd.SetUsageTemplate(
|
||||
cmd.UsageTemplate() + "\nAvailable generators:\n " + generatorList + "\n",
|
||||
)
|
||||
|
||||
_ = cmd.Usage()
|
||||
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Generate and display the URL.
|
||||
fmt.Fprint(color.Output, "Generating URL for ", color.HiCyanString(serviceSchema))
|
||||
fmt.Fprint(color.Output, " using ", color.HiMagentaString(generatorName), " generator\n")
|
||||
|
||||
serviceConfig, err := generator.Generate(service, props, cmd.Flags().Args())
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprint(os.Stdout, "Error: ", err, "\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Fprint(color.Output, "\n")
|
||||
|
||||
maskedURL := maskSensitiveURL(serviceSchema, serviceConfig.GetURL().String())
|
||||
|
||||
if showSensitive {
|
||||
fmt.Fprint(os.Stdout, "URL: ", serviceConfig.GetURL().String(), "\n")
|
||||
} else {
|
||||
fmt.Fprint(os.Stdout, "URL: ", maskedURL, "\n")
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue