cmd

package
v0.1.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 18, 2026 License: Apache-2.0 Imports: 21 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var CreateAliasTargetsCmd = &cobra.Command{
	Use:     "alias-targets [flags] <alias-email> <target-email> [<target-email>...]",
	Aliases: []string{"alias-target", "targets", "target"},
	Short:   "Creates new alias targets for an alias",
	Long:    "Creates new alias targets for an alias.",
	Args:    cobra.MinimumNArgs(2),
	RunE: func(cmd *cobra.Command, args []string) error {
		flagForward, _ := cmd.Flags().GetBool("forward")
		flagSend, _ := cmd.Flags().GetBool("send")

		argEmails := ParseEmailArgs(args)
		if len(argEmails) != len(args) {
			return fmt.Errorf("invalid email arguments")
		}

		argAliasEmail := argEmails[0]
		argTargetEmails := argEmails[1:]

		options := db.AliasesTargetsCreateOptions{
			ForwardEnabled: flagForward,
			SendEnabled:    flagSend,
		}

		runner := db.TxForEachRunner[utils.EmailAddress]{
			Items: argTargetEmails,
			Exec: func(tx *sql.Tx, item utils.EmailAddress) error {
				return db.AliasesTargets(tx).Create(argAliasEmail, item, options)
			},
			ItemString:     func(item utils.EmailAddress) string { return argAliasEmail.String() + " -> " + item.String() },
			FailureMessage: "failed to create alias target",
			SuccessMessage: "Successfully created alias target",
		}

		runner.Run()
		return nil
	},
}
View Source
var CreateAliasesCmd = &cobra.Command{
	Use:     "aliases [flags] <email> [<email>...]",
	Aliases: []string{"alias"},
	Short:   "Creates new aliases",
	Long:    "Creates new aliases.",
	Args:    cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		flagDisabled, _ := cmd.Flags().GetBool("disabled")

		argEmails := ParseEmailArgs(args)
		if len(argEmails) != len(args) {
			return fmt.Errorf("invalid email arguments")
		}

		options := db.AliasesCreateOptions{
			Disabled: flagDisabled,
		}

		runner := db.TxForEachRunner[utils.EmailAddress]{
			Items: argEmails,
			Exec: func(tx *sql.Tx, item utils.EmailAddress) error {
				return db.Aliases(tx).Create(item, options)
			},
			ItemString:     func(item utils.EmailAddress) string { return item.String() },
			FailureMessage: "failed to create alias",
			SuccessMessage: "Successfully created alias",
		}

		runner.Run()
		return nil
	},
}
View Source
var CreateCmd = &cobra.Command{
	Use:   "create",
	Short: "Create mail system objects",
}
View Source
var CreateDomainCatchallTargetsCmd = &cobra.Command{
	Use:     "catchall-targets [flags] <domain> <target-email> [<target-email>...]",
	Aliases: []string{"catchall-target", "catchalls", "catchall"},
	Short:   "Creates new catch-all targets for a domain",
	Long:    "Creates new catch-all targets for a domain.",
	Args:    cobra.MinimumNArgs(2),
	RunE: func(cmd *cobra.Command, args []string) error {
		flagForward, _ := cmd.Flags().GetBool("forward")
		flagFallbackOnly, _ := cmd.Flags().GetBool("fallback-only")

		argDomain := args[0]
		argEmails := ParseEmailArgs(args[1:])
		if len(argEmails) != len(args)-1 {
			return fmt.Errorf("invalid email arguments")
		}

		options := db.DomainsCatchallTargetsCreateOptions{
			ForwardEnabled: flagForward,
			FallbackOnly:   flagFallbackOnly,
		}

		runner := db.TxForEachRunner[utils.EmailAddress]{
			Items: argEmails,
			Exec: func(tx *sql.Tx, item utils.EmailAddress) error {
				return db.DomainsCatchallTargets(tx).Create(argDomain, item, options)
			},
			ItemString:     func(item utils.EmailAddress) string { return "@" + argDomain + " -> " + item.String() },
			FailureMessage: "failed to create catchall target",
			SuccessMessage: "Successfully created catchall target",
		}

		runner.Run()
		return nil
	},
}
View Source
var CreateDomainsCmd = &cobra.Command{
	Use:     "domains [flags] <fqdn> [<fqdn>...]",
	Aliases: []string{"domain"},
	Short:   "Creates new domains",
	Long:    "Creates new domains. Supported domain types are 'managed', 'relayed', 'alias' and 'canonical'.",
	Args:    cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		flagDisabled, _ := cmd.Flags().GetBool("disabled")
		flagType, _ := cmd.Flags().GetString("type")
		flagTransport, _ := cmd.Flags().GetString("transport")
		flagTargetDomain, _ := cmd.Flags().GetString("target-domain")

		domainType := strings.ToLower(flagType)
		switch domainType {
		case "managed", "relayed":
			if flagTransport == "" {
				return fmt.Errorf("transport name is required for %s domains", domainType)
			}
		case "canonical":
			if flagTargetDomain == "" {
				return fmt.Errorf("target domain FQDN is required for canonical domains")
			}
		case "alias":

		default:
			return fmt.Errorf("invalid domain type: %s (must be 'managed', 'relayed', 'alias' or 'canonical')", flagType)
		}

		argDomains := ParseDomainFQDNArgs(args)
		if len(argDomains) != len(args) {
			return fmt.Errorf("invalid domain arguments")
		}

		options := db.DomainsCreateOptions{
			DomainType:       domainType,
			TransportName:    flagTransport,
			TargetDomainFQDN: flagTargetDomain,
			Enabled:          !flagDisabled,
		}

		runner := db.TxForEachRunner[string]{
			Items: argDomains,
			Exec: func(tx *sql.Tx, item string) error {
				return db.Domains(tx).Create(item, options)
			},
			ItemString:     func(item string) string { return item },
			FailureMessage: "failed to create domain",
			SuccessMessage: "Successfully created domain",
		}

		runner.Run()
		return nil
	},
}
View Source
var CreateMailboxesCmd = &cobra.Command{
	Use:     "mailboxes <email> [<email>...]",
	Aliases: []string{"mailbox"},
	Short:   "Creates new mailboxes",
	Long:    "Creates new mailboxes.",
	Args:    cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		flagPassword, _ := cmd.Flags().GetBool("password")
		flagPasswordStdin, _ := cmd.Flags().GetBool("password-stdin")
		flagPasswordMethod, _ := cmd.Flags().GetString("password-method")
		flagPasswordHashOptions, _ := cmd.Flags().GetString("password-hash-options")
		flagQuota, _ := cmd.Flags().GetInt32("quota")
		flagTransportName, _ := cmd.Flags().GetString("transport")
		flagLoginDisabled, _ := cmd.Flags().GetBool("login-disabled")
		flagReceivingDisabled, _ := cmd.Flags().GetBool("receiving-disabled")
		flagSendingDisabled, _ := cmd.Flags().GetBool("sending-disabled")

		if flagPassword && flagPasswordStdin {
			return fmt.Errorf("cannot use both --password and --password-stdin")
		}

		if len(args) > 1 && (flagPassword || flagPasswordStdin) {
			return fmt.Errorf("cannot set password while creating multiple mailboxes")
		}

		argEmails := ParseEmailArgs(args)
		if len(argEmails) != len(args) {
			return fmt.Errorf("invalid email arguments")
		}

		options := db.MailboxesCreateOptions{
			LoginEnabled:     !flagLoginDisabled,
			ReceivingEnabled: !flagReceivingDisabled,
			SendingEnabled:   !flagSendingDisabled,
		}

		if flagPassword || flagPasswordStdin {
			passwordHash, err := ReadPasswordHashed(flagPasswordMethod, flagPasswordHashOptions, flagPasswordStdin)
			if err != nil {
				utils.PrintErrorWithMessage("failed to read password", err)
				return nil
			}
			options.PasswordHash.Valid = true
			options.PasswordHash.String = passwordHash
		}

		if flagQuota > 0 {
			options.Quota.Valid = true
			options.Quota.Int32 = flagQuota
		}

		if flagTransportName != "" {
			options.TransportName.Valid = true
			options.TransportName.String = flagTransportName
		}

		runner := db.TxForEachRunner[utils.EmailAddress]{
			Items: argEmails,
			Exec: func(tx *sql.Tx, item utils.EmailAddress) error {
				return db.Mailboxes(tx).Create(item, options)
			},
			ItemString:     func(item utils.EmailAddress) string { return item.String() },
			FailureMessage: "failed to create mailbox",
			SuccessMessage: "Successfully created mailbox",
		}

		runner.Run()
		return nil
	},
}
View Source
var CreateRecipientsRelayedCmd = &cobra.Command{
	Use:     "recipients-relayed [flags] <email> [<email>...]",
	Aliases: []string{"recipient-relayed", "relayed-recipients", "relayed-recipient", "relayed"},
	Short:   "Creates new relayed recipients",
	Long:    "Creates new relayed recipients.",
	Args:    cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		flagDisabled, _ := cmd.Flags().GetBool("disabled")

		argEmails := ParseEmailArgs(args)
		if len(argEmails) != len(args) {
			return fmt.Errorf("invalid email arguments")
		}

		options := db.RecipientsRelayedCreateOptions{
			Enabled: !flagDisabled,
		}

		runner := db.TxForEachRunner[utils.EmailAddress]{
			Items: argEmails,
			Exec: func(tx *sql.Tx, item utils.EmailAddress) error {
				return db.RecipientsRelayed(tx).Create(item, options)
			},
			ItemString:     func(item utils.EmailAddress) string { return item.String() },
			FailureMessage: "failed to create relayed recipient",
			SuccessMessage: "Successfully created relayed recipient",
		}

		runner.Run()
		return nil
	},
}
View Source
var CreateRemoteSendGrantsCmd = &cobra.Command{
	Use:     "send-grants <remote-name> <email> [<email>...]",
	Aliases: []string{"send-grant"},
	Short:   "Creates new send grants for a remote",
	Long:    "Creates new send grants for a remote.",
	Args:    cobra.MinimumNArgs(2),
	RunE: func(cmd *cobra.Command, args []string) error {
		argRemoteName := args[0]
		argEmails := ParseEmailOrWildcardArgs(args[1:])
		if len(argEmails) != len(args[1:]) {
			return fmt.Errorf("invalid email argument")
		}

		options := db.RemotesSendGrantsCreateOptions{}

		runner := db.TxForEachRunner[utils.EmailAddressOrWildcard]{
			Items: argEmails,
			Exec: func(tx *sql.Tx, item utils.EmailAddressOrWildcard) error {
				return db.RemotesSendGrants(tx).Create(argRemoteName, item, options)
			},
			ItemString:     func(item utils.EmailAddressOrWildcard) string { return argRemoteName + " -> " + item.String() },
			FailureMessage: "failed to create send grant",
			SuccessMessage: "Successfully created send grant",
		}

		runner.Run()
		return nil
	},
}
View Source
var CreateRemotesCmd = &cobra.Command{
	Use:     "remotes <hostname> [<hostname>...]",
	Aliases: []string{"remote"},
	Short:   "Creates new remotes",
	Long:    "Creates new remotes.",
	Args:    cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		flagPassword, _ := cmd.Flags().GetBool("password")
		flagPasswordStdin, _ := cmd.Flags().GetBool("password-stdin")
		flagPasswordMethod, _ := cmd.Flags().GetString("password-method")
		flagPasswordHashOptions, _ := cmd.Flags().GetString("password-hash-options")
		flagDisabled, _ := cmd.Flags().GetBool("disabled")

		if flagPassword && flagPasswordStdin {
			return fmt.Errorf("cannot use both --password and --password-stdin")
		}

		if len(args) > 1 && (flagPassword || flagPasswordStdin) {
			return fmt.Errorf("cannot set password while creating multiple remotes")
		}

		options := db.RemotesCreateOptions{
			Enabled: !flagDisabled,
		}

		if flagPassword || flagPasswordStdin {
			passwordHash, err := ReadPasswordHashed(flagPasswordMethod, flagPasswordHashOptions, flagPasswordStdin)
			if err != nil {
				utils.PrintErrorWithMessage("failed to read password", err)
				return nil
			}
			options.PasswordHash = sql.NullString{
				String: passwordHash,
				Valid:  true,
			}
		}

		runner := db.TxForEachRunner[string]{
			Items: args,
			Exec: func(tx *sql.Tx, item string) error {
				return db.Remotes(tx).Create(item, options)
			},
			ItemString:     func(item string) string { return item },
			FailureMessage: "failed to create remote",
			SuccessMessage: "Successfully created remote",
		}

		runner.Run()
		return nil
	},
}
View Source
var CreateTransportsCmd = &cobra.Command{
	Use:   "transport <name>",
	Short: "Creates a new transport",
	Long:  "Creates a new transport.",
	Args:  cobra.ExactArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		name := args[0]
		flagMethod, _ := cmd.Flags().GetString("method")
		flagHost, _ := cmd.Flags().GetString("host")
		flagPort, _ := cmd.Flags().GetUint16("port")
		flagMxLookup, _ := cmd.Flags().GetBool("mx-lookup")

		options := db.TransportsCreateOptions{
			Method:   flagMethod,
			Host:     flagHost,
			MxLookup: flagMxLookup,
		}
		if flagPort > 0 {
			options.Port = sql.NullInt32{Int32: int32(flagPort), Valid: true}
		}

		runner := db.TxRunner{
			Exec: func(tx *sql.Tx) error {
				return db.Transports(tx).Create(name, options)
			},
			ItemString:     name,
			FailureMessage: "failed to create transport",
			SuccessMessage: "Successfully created transport",
		}

		runner.Run()
		return nil
	},
}
View Source
var DeleteAliasTargetsCmd = &cobra.Command{
	Use:     "alias-targets [flags] <alias-email> <target-email> [<target-email>...]",
	Aliases: []string{"alias-target", "targets", "target"},
	Short:   "Deletes alias targets from an alias",
	Long:    `Deletes alias targets from an alias. By default performs a soft delete. Use --permanent for hard delete.`,
	Args:    cobra.MinimumNArgs(2),
	RunE: func(cmd *cobra.Command, args []string) error {
		flagPermanent, _ := cmd.Flags().GetBool("permanent")
		flagForce, _ := cmd.Flags().GetBool("force")

		if flagPermanent && flagForce {
			return fmt.Errorf("cannot use --permanent and --force flags together")
		}

		argEmails := ParseEmailArgs(args)
		if len(argEmails) != len(args) {
			return fmt.Errorf("invalid email arguments")
		}

		argAliasEmail := argEmails[0]
		argTargetEmails := argEmails[1:]

		options := db.DeleteOptions{
			Permanent: flagPermanent,
			Force:     flagForce,
		}

		runner := db.TxForEachRunner[utils.EmailAddress]{
			Items: argTargetEmails,
			Exec: func(tx *sql.Tx, item utils.EmailAddress) error {
				return db.AliasesTargets(tx).Delete(argAliasEmail, item, options)
			},
			ItemString:     func(item utils.EmailAddress) string { return argAliasEmail.String() + " -> " + item.String() },
			FailureMessage: "failed to delete alias target",
			SuccessMessage: "Successfully deleted alias target",
		}

		if flagPermanent {
			runner.SuccessMessage += " permanently"
		}

		runner.Run()
		return nil
	},
}
View Source
var DeleteAliasesCmd = &cobra.Command{
	Use:     "aliases [flags] <email> [<email>...]",
	Aliases: []string{"alias"},
	Short:   "Deletes aliases",
	Long:    "Deletes aliases. By default performs a soft delete. Use --permanent for hard delete.",
	Args:    cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		flagPermanent, _ := cmd.Flags().GetBool("permanent")
		flagForce, _ := cmd.Flags().GetBool("force")

		if flagPermanent && flagForce {
			return fmt.Errorf("cannot use --permanent and --force flags together")
		}

		argEmails := ParseEmailArgs(args)
		if len(argEmails) != len(args) {
			return fmt.Errorf("invalid email arguments")
		}

		options := db.DeleteOptions{
			Permanent: flagPermanent,
			Force:     flagForce,
		}

		runner := db.TxForEachRunner[utils.EmailAddress]{
			Items: argEmails,
			Exec: func(tx *sql.Tx, item utils.EmailAddress) error {
				return db.Aliases(tx).Delete(item, options)
			},
			ItemString:     func(item utils.EmailAddress) string { return item.String() },
			FailureMessage: "failed to delete alias",
			SuccessMessage: "Successfully deleted alias",
		}

		if flagPermanent {
			runner.SuccessMessage += " permanently"
		}

		runner.Run()
		return nil
	},
}
View Source
var DeleteCmd = &cobra.Command{
	Use:   "delete",
	Short: "Delete mail system objects",
}
View Source
var DeleteDomainCatchallTargetsCmd = &cobra.Command{
	Use:     "catchall-targets [flags] <domain> <target-email> [<target-email>...]",
	Aliases: []string{"catchall-target", "catchalls", "catchall"},
	Short:   "Deletes catch-all targets from a domain",
	Long:    "Deletes catch-all targets from a domain. By default performs a soft delete. Use --permanent for hard delete.",
	Args:    cobra.MinimumNArgs(2),
	RunE: func(cmd *cobra.Command, args []string) error {
		flagPermanent, _ := cmd.Flags().GetBool("permanent")
		flagForce, _ := cmd.Flags().GetBool("force")

		if flagPermanent && flagForce {
			return fmt.Errorf("cannot use --permanent and --force flags together")
		}

		argDomain := args[0]
		argEmails := ParseEmailArgs(args[1:])
		if len(argEmails) != len(args)-1 {
			return fmt.Errorf("invalid email arguments")
		}

		options := db.DeleteOptions{
			Permanent: flagPermanent,
			Force:     flagForce,
		}

		runner := db.TxForEachRunner[utils.EmailAddress]{
			Items: argEmails,
			Exec: func(tx *sql.Tx, item utils.EmailAddress) error {
				return db.DomainsCatchallTargets(tx).Delete(argDomain, item, options)
			},
			ItemString:     func(item utils.EmailAddress) string { return "@" + argDomain + " -> " + item.String() },
			FailureMessage: "failed to delete catchall target",
			SuccessMessage: "Successfully deleted catchall target",
		}

		if flagPermanent {
			runner.SuccessMessage += " permanently"
		}

		runner.Run()
		return nil
	},
}
View Source
var DeleteDomainsCmd = &cobra.Command{
	Use:     "domains [flags] <fqdn> [<fqdn>...]",
	Aliases: []string{"domain"},
	Short:   "Deletes domains",
	Long:    "Deletes domains. By default performs a soft delete. Use --permanent for hard delete.",
	Args:    cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		flagPermanent, _ := cmd.Flags().GetBool("permanent")
		flagForce, _ := cmd.Flags().GetBool("force")

		if flagPermanent && flagForce {
			return fmt.Errorf("cannot use --permanent and --force flags together")
		}

		argDomains := ParseDomainFQDNArgs(args)
		if len(argDomains) != len(args) {
			return fmt.Errorf("invalid domain arguments")
		}

		options := db.DeleteOptions{
			Permanent: flagPermanent,
			Force:     flagForce,
		}

		runner := db.TxForEachRunner[string]{
			Items: argDomains,
			Exec: func(tx *sql.Tx, item string) error {
				return db.Domains(tx).Delete(item, options)
			},
			ItemString:     func(item string) string { return item },
			FailureMessage: "failed to delete domain",
			SuccessMessage: "Successfully deleted domain",
		}

		if flagPermanent {
			runner.SuccessMessage += " permanently"
		}

		runner.Run()
		return nil
	},
}
View Source
var DeleteMailboxesCmd = &cobra.Command{
	Use:     "mailboxes [flags] <email> [<email>...]",
	Aliases: []string{"mailbox"},
	Short:   "Deletes mailboxes",
	Long:    "Deletes mailboxes. By default performs a soft delete. Use --permanent for hard delete.",
	Args:    cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		flagPermanent, _ := cmd.Flags().GetBool("permanent")
		flagForce, _ := cmd.Flags().GetBool("force")

		if flagPermanent && flagForce {
			return fmt.Errorf("cannot use --permanent and --force flags together")
		}

		argEmails := ParseEmailArgs(args)
		if len(argEmails) != len(args) {
			return fmt.Errorf("invalid email arguments")
		}

		options := db.DeleteOptions{
			Permanent: flagPermanent,
			Force:     flagForce,
		}

		runner := db.TxForEachRunner[utils.EmailAddress]{
			Items: argEmails,
			Exec: func(tx *sql.Tx, item utils.EmailAddress) error {
				return db.Mailboxes(tx).Delete(item, options)
			},
			ItemString:     func(item utils.EmailAddress) string { return item.String() },
			FailureMessage: "failed to delete mailbox",
			SuccessMessage: "Successfully deleted mailbox",
		}

		if flagPermanent {
			runner.SuccessMessage += " permanently"
		}

		runner.Run()
		return nil
	},
}
View Source
var DeleteRecipientsRelayedCmd = &cobra.Command{
	Use:     "recipients-relayed [flags] <email> [<email>...]",
	Aliases: []string{"recipient-relayed", "relayed-recipients", "relayed-recipient", "relayed"},
	Short:   "Deletes relayed recipients",
	Long:    "Deletes relayed recipients. By default performs a soft delete. Use --permanent for hard delete.",
	Args:    cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		flagPermanent, _ := cmd.Flags().GetBool("permanent")
		flagForce, _ := cmd.Flags().GetBool("force")

		if flagPermanent && flagForce {
			return fmt.Errorf("cannot use --permanent and --force flags together")
		}

		argEmails := ParseEmailArgs(args)
		if len(argEmails) != len(args) {
			return fmt.Errorf("invalid email arguments")
		}

		options := db.DeleteOptions{
			Permanent: flagPermanent,
			Force:     flagForce,
		}

		runner := db.TxForEachRunner[utils.EmailAddress]{
			Items: argEmails,
			Exec: func(tx *sql.Tx, item utils.EmailAddress) error {
				return db.RecipientsRelayed(tx).Delete(item, options)
			},
			ItemString:     func(item utils.EmailAddress) string { return item.String() },
			FailureMessage: "failed to delete relayed recipient",
			SuccessMessage: "Successfully deleted relayed recipient",
		}

		if flagPermanent {
			runner.SuccessMessage += " permanently"
		}

		runner.Run()
		return nil
	},
}
View Source
var DeleteRemoteSendGrantsCmd = &cobra.Command{
	Use:     "send-grants <remote-name> <email> [<email>...]",
	Aliases: []string{"send-grant"},
	Short:   "Deletes send grants from a remote",
	Long:    "Deletes send grants from a remote. By default performs a soft delete. Use --permanent --force for hard delete.",
	Args:    cobra.MinimumNArgs(2),
	RunE: func(cmd *cobra.Command, args []string) error {
		flagPermanent, _ := cmd.Flags().GetBool("permanent")
		flagForce, _ := cmd.Flags().GetBool("force")

		if flagPermanent && flagForce {
			return fmt.Errorf("cannot use --permanent and --force flags together")
		}

		argRemoteName := args[0]
		argEmails := ParseEmailOrWildcardArgs(args[1:])
		if len(argEmails) != len(args[1:]) {
			return fmt.Errorf("invalid email argument")
		}

		options := db.DeleteOptions{
			Permanent: flagPermanent,
			Force:     flagForce,
		}

		runner := db.TxForEachRunner[utils.EmailAddressOrWildcard]{
			Items: argEmails,
			Exec: func(tx *sql.Tx, item utils.EmailAddressOrWildcard) error {
				return db.RemotesSendGrants(tx).Delete(argRemoteName, item, options)
			},
			ItemString:     func(item utils.EmailAddressOrWildcard) string { return argRemoteName + " -> " + item.String() },
			FailureMessage: "failed to delete send grant",
			SuccessMessage: "Successfully deleted send grant",
		}

		runner.Run()
		return nil
	},
}
View Source
var DeleteRemotesCmd = &cobra.Command{
	Use:     "remotes [flags] <name> [<name>...]",
	Aliases: []string{"remote"},
	Short:   "Deletes remotes",
	Long:    "Deletes remotes. By default performs a soft delete. Use --permanent for hard delete.",
	Args:    cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		flagPermanent, _ := cmd.Flags().GetBool("permanent")
		flagForce, _ := cmd.Flags().GetBool("force")

		options := db.DeleteOptions{
			Permanent: flagPermanent,
			Force:     flagForce,
		}

		runner := db.TxForEachRunner[string]{
			Items: args,
			Exec: func(tx *sql.Tx, item string) error {
				return db.Remotes(tx).Delete(item, options)
			},
			ItemString:     func(item string) string { return item },
			FailureMessage: "failed to delete remote",
			SuccessMessage: "Successfully deleted remote",
		}

		runner.Run()
		return nil
	},
}
View Source
var DeleteTransportsCmd = &cobra.Command{
	Use:     "transports [flags] <name> [<name>...]",
	Aliases: []string{"transport"},
	Short:   "Deletes transports",
	Long:    "Deletes transports. By default performs a soft delete. Use --permanent for hard delete.",
	Args:    cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		flagPermanent, _ := cmd.Flags().GetBool("permanent")
		flagForce, _ := cmd.Flags().GetBool("force")

		options := db.DeleteOptions{
			Permanent: flagPermanent,
			Force:     flagForce,
		}

		runner := db.TxForEachRunner[string]{
			Items: args,
			Exec: func(tx *sql.Tx, item string) error {
				return db.Transports(tx).Delete(item, options)
			},
			ItemString:     func(item string) string { return item },
			FailureMessage: "failed to delete transport",
			SuccessMessage: "Successfully deleted transport",
		}

		if flagPermanent {
			runner.SuccessMessage += " permanently"
		}

		runner.Run()
		return nil
	},
}
View Source
var DescribeCmd = &cobra.Command{
	Use:   "describe",
	Short: "Describe mail system objects",
	Args:  cobra.ExactArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		dbConn, err := db.Connect()
		if err != nil {
			utils.PrintErrorWithMessage("failed to connect to database", err)
			return nil
		}
		defer func() {
			if err := dbConn.Close(); err != nil {
				utils.PrintErrorWithMessage("failed to close database connection", err)
			}
		}()

		if strings.Contains(args[0], "@") {

			emailOrWildcard, err := utils.ParseEmailAddressOrWildcard(args[0])
			if err != nil {
				utils.PrintError(fmt.Errorf("failed to parse email: %w", err))
				return nil
			}

			if emailOrWildcard.IsWildcard() {

				if described, err := describeDomainscatchalltargets(dbConn, emailOrWildcard.DomainFQDN); described || err != nil {
					if err != nil {
						utils.PrintError(err)
					}
					return nil
				}
			} else {

				email := utils.EmailAddress{
					DomainFQDN: emailOrWildcard.DomainFQDN,
					LocalPart:  *emailOrWildcard.LocalPart,
				}

				if described, err := describeAliases(dbConn, email); described || err != nil {
					if err != nil {
						utils.PrintError(err)
					}
					return nil
				}

				if described, err := DescribeCanonicalAddress(dbConn, email); described || err != nil {
					if err != nil {
						utils.PrintError(err)
					}
					return nil
				}

				if described, err := describeMailboxes(dbConn, email); described || err != nil {
					if err != nil {
						utils.PrintError(err)
					}
					return nil
				}

				if described, err := describeRecipientsrelayed(dbConn, email); described || err != nil {
					if err != nil {
						utils.PrintError(err)
					}
					return nil
				}
			}

			describeUnknownEmail(dbConn, emailOrWildcard)
			return nil
		} else {

			if described, err := describeDomains(dbConn, args[0]); described || err != nil {
				if err != nil {
					utils.PrintError(err)
				}
				return nil
			}

			if described, err := describeTransports(dbConn, args[0]); described || err != nil {
				if err != nil {
					utils.PrintError(err)
				}
				return nil
			}

			if described, err := describeRemotes(dbConn, args[0]); described || err != nil {
				if err != nil {
					utils.PrintError(err)
				}
				return nil
			}

			describeUnknown(dbConn, args[0])
			return nil
		}
	},
}
View Source
var DisableAliasTargetsCmd = &cobra.Command{
	Use:     "alias-targets [flags] <alias-email> <target-email> [<target-email>...]",
	Aliases: []string{"alias-target", "targets", "target"},
	Short:   "Disables features on alias targets of an alias",
	Long:    `Disable forwarding and/or sending on an alias target. Use flags to select which property to disable.`,
	Args:    cobra.MinimumNArgs(2),
	RunE: func(cmd *cobra.Command, args []string) error {
		flagForward, _ := cmd.Flags().GetBool("forward")
		flagSend, _ := cmd.Flags().GetBool("send")

		if !flagForward && !flagSend {
			return fmt.Errorf("at least one of --forward or --send flags must be specified")
		}

		argEmails := ParseEmailArgs(args)
		if len(argEmails) != len(args) {
			return fmt.Errorf("invalid email arguments")
		}

		argAliasEmail := argEmails[0]
		argTargetEmails := argEmails[1:]

		disabled := false
		options := db.AliasesTargetsPatchOptions{}
		if flagForward {
			options.ForwardingToTargetEnabled = &disabled
		}
		if flagSend {
			options.SendingFromTargetEnabled = &disabled
		}

		runner := db.TxForEachRunner[utils.EmailAddress]{
			Items: argTargetEmails,
			Exec: func(tx *sql.Tx, item utils.EmailAddress) error {
				return db.AliasesTargets(tx).Patch(argAliasEmail, item, options)
			},
			ItemString:     func(item utils.EmailAddress) string { return argAliasEmail.String() + " -> " + item.String() },
			FailureMessage: "failed to disable alias target",
			SuccessMessage: "Successfully disable alias target",
		}

		runner.Run()
		return nil
	},
}
View Source
var DisableAliasesCmd = &cobra.Command{
	Use:     "aliases <email> [<email>...]",
	Aliases: []string{"alias"},
	Short:   "Disables aliases",
	Long:    "Disables aliases.",
	Args:    cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		argEmails := ParseEmailArgs(args)
		if len(argEmails) != len(args) {
			return fmt.Errorf("invalid email arguments")
		}

		enabled := false
		options := db.AliasesPatchOptions{
			Enabled: &enabled,
		}

		runner := db.TxForEachRunner[utils.EmailAddress]{
			Items: argEmails,
			Exec: func(tx *sql.Tx, item utils.EmailAddress) error {
				return db.Aliases(tx).Patch(item, options)
			},
			ItemString:     func(item utils.EmailAddress) string { return item.String() },
			FailureMessage: "failed to disable alias",
			SuccessMessage: "Successfully disabled alias",
		}

		runner.Run()
		return nil
	},
}
View Source
var DisableCmd = &cobra.Command{
	Use:   "disable",
	Short: "Disable mail system objects",
}
View Source
var DisableDomainCatchallTargetsCmd = &cobra.Command{
	Use:     "catchall-targets <domain> <target-email> [<target-email>...]",
	Aliases: []string{"catchall-target", "catchalls", "catchall"},
	Short:   "Disables forwarding on catch-all targets of a domain",
	Long:    "Disables forwarding on catch-all targets of a domain.",
	Args:    cobra.MinimumNArgs(2),
	RunE: func(cmd *cobra.Command, args []string) error {
		argDomain := args[0]
		argEmails := ParseEmailArgs(args[1:])
		if len(argEmails) != len(args)-1 {
			return fmt.Errorf("invalid email arguments")
		}

		enabled := false
		options := db.DomainsCatchallTargetsPatchOptions{
			ForwardingToTargetEnabled: &enabled,
		}

		runner := db.TxForEachRunner[utils.EmailAddress]{
			Items: argEmails,
			Exec: func(tx *sql.Tx, item utils.EmailAddress) error {
				return db.DomainsCatchallTargets(tx).Patch(argDomain, item, options)
			},
			ItemString:     func(item utils.EmailAddress) string { return "@" + argDomain + " -> " + item.String() },
			FailureMessage: "failed to disable catchall target",
			SuccessMessage: "Successfully disable catchall target",
		}

		runner.Run()
		return nil
	},
}
View Source
var DisableDomainsCmd = &cobra.Command{
	Use:     "domains <fqdn> [<fqdn>...]",
	Aliases: []string{"domain"},
	Short:   "Disables domains",
	Long:    "Disables domains.",
	Args:    cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		argDomains := ParseDomainFQDNArgs(args)
		if len(argDomains) != len(args) {
			return fmt.Errorf("invalid domain arguments")
		}

		enabled := false
		options := db.DomainsPatchOptions{
			Enabled: &enabled,
		}

		runner := db.TxForEachRunner[string]{
			Items: argDomains,
			Exec: func(tx *sql.Tx, item string) error {
				return db.Domains(tx).Patch(item, options)
			},
			ItemString:     func(item string) string { return item },
			FailureMessage: "failed to disable domain",
			SuccessMessage: "Successfully disabled domain",
		}

		runner.Run()
		return nil
	},
}
View Source
var DisableMailboxesCmd = &cobra.Command{
	Use:     "mailboxes [flags] <email> [<email>...]",
	Aliases: []string{"mailbox"},
	Short:   "Disables features on mailboxes",
	Long:    "Disables login, receiving, and/or sending for mailboxes. Use flags to select which property to disable.",
	Args:    cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		flagLogin, _ := cmd.Flags().GetBool("login")
		flagReceiving, _ := cmd.Flags().GetBool("receiving")
		flagSending, _ := cmd.Flags().GetBool("sending")

		if !flagLogin && !flagReceiving && !flagSending {
			return fmt.Errorf("at least one of --login, --receiving, or --sending must be true when specified")
		}

		argEmails := ParseEmailArgs(args)
		if len(argEmails) != len(args) {
			return fmt.Errorf("invalid email arguments")
		}

		disabled := false
		options := db.MailboxesPatchOptions{}
		if flagLogin {
			options.Login = &disabled
		}
		if flagReceiving {
			options.Receiving = &disabled
		}
		if flagSending {
			options.Sending = &disabled
		}

		runner := db.TxForEachRunner[utils.EmailAddress]{
			Items: argEmails,
			Exec: func(tx *sql.Tx, item utils.EmailAddress) error {
				return db.Mailboxes(tx).Patch(item, options)
			},
			ItemString:     func(item utils.EmailAddress) string { return item.String() },
			FailureMessage: "failed to patch mailbox",
			SuccessMessage: "Successfully patched mailbox",
		}

		runner.Run()
		return nil
	},
}
View Source
var DisableRecipientsRelayedCmd = &cobra.Command{
	Use:     "recipients-relayed <email> [<email>...]",
	Aliases: []string{"recipient-relayed", "relayed-recipients", "relayed-recipient", "relayed"},
	Short:   "Disables relayed recipients",
	Long:    "Disables relayed recipients.",
	Args:    cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		argEmails := ParseEmailArgs(args)
		if len(argEmails) != len(args) {
			return fmt.Errorf("invalid email arguments")
		}

		enabled := false
		options := db.RecipientsRelayedPatchOptions{
			Enabled: &enabled,
		}

		runner := db.TxForEachRunner[utils.EmailAddress]{
			Items: argEmails,
			Exec: func(tx *sql.Tx, item utils.EmailAddress) error {
				return db.RecipientsRelayed(tx).Patch(item, options)
			},
			ItemString:     func(item utils.EmailAddress) string { return item.String() },
			FailureMessage: "failed to disable relayed recipient",
			SuccessMessage: "Successfully disabled relayed recipient",
		}

		runner.Run()
		return nil
	},
}
View Source
var DisableRemotesCmd = &cobra.Command{
	Use:   "remotes <name> [<name>...]",
	Short: "Disables remotes",
	Long:  "Disables remotes.",
	Args:  cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		enabled := false
		options := db.RemotesPatchOptions{
			Enabled: &enabled,
		}

		runner := db.TxForEachRunner[string]{
			Items: args,
			Exec: func(tx *sql.Tx, item string) error {
				return db.Remotes(tx).Patch(item, options)
			},
			ItemString:     func(item string) string { return item },
			FailureMessage: "failed to disable remote",
			SuccessMessage: "Successfully disabled remote",
		}

		runner.Run()
		return nil
	},
}
View Source
var EnableAliasTargetsCmd = &cobra.Command{
	Use:     "alias-targets [flags] <alias-email> <target-email> [<target-email>...]",
	Aliases: []string{"alias-target", "targets", "target"},
	Short:   "Enables features on alias targets of an alias",
	Long:    `Enables forwarding and/or sending on an alias target. Use flags to select which property to enable.`,
	Args:    cobra.MinimumNArgs(2),
	RunE: func(cmd *cobra.Command, args []string) error {
		flagForward, _ := cmd.Flags().GetBool("forward")
		flagSend, _ := cmd.Flags().GetBool("send")

		if !flagForward && !flagSend {
			return fmt.Errorf("at least one of --forward or --send flags must be specified")
		}

		argEmails := ParseEmailArgs(args)
		if len(argEmails) != len(args) {
			return fmt.Errorf("invalid email arguments")
		}

		argAliasEmail := argEmails[0]
		argTargetEmails := argEmails[1:]

		enabled := true
		options := db.AliasesTargetsPatchOptions{}
		if flagForward {
			options.ForwardingToTargetEnabled = &enabled
		}
		if flagSend {
			options.SendingFromTargetEnabled = &enabled
		}

		runner := db.TxForEachRunner[utils.EmailAddress]{
			Items: argTargetEmails,
			Exec: func(tx *sql.Tx, item utils.EmailAddress) error {
				return db.AliasesTargets(tx).Patch(argAliasEmail, item, options)
			},
			ItemString:     func(item utils.EmailAddress) string { return argAliasEmail.String() + " -> " + item.String() },
			FailureMessage: "failed to enable alias target",
			SuccessMessage: "Successfully enable alias target",
		}

		runner.Run()
		return nil
	},
}
View Source
var EnableAliasesCmd = &cobra.Command{
	Use:     "aliases <email> [<email>...]",
	Aliases: []string{"alias"},
	Short:   "Enables aliases",
	Long:    "Enables aliases.",
	Args:    cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		argEmails := ParseEmailArgs(args)
		if len(argEmails) != len(args) {
			return fmt.Errorf("invalid email arguments")
		}

		enabled := true
		options := db.AliasesPatchOptions{
			Enabled: &enabled,
		}

		runner := db.TxForEachRunner[utils.EmailAddress]{
			Items: argEmails,
			Exec: func(tx *sql.Tx, item utils.EmailAddress) error {
				return db.Aliases(tx).Patch(item, options)
			},
			ItemString:     func(item utils.EmailAddress) string { return item.String() },
			FailureMessage: "failed to enable alias",
			SuccessMessage: "Successfully enabled alias",
		}

		runner.Run()
		return nil
	},
}
View Source
var EnableCmd = &cobra.Command{
	Use:   "enable",
	Short: "Enable mail system objects",
}
View Source
var EnableDomainCatchallTargetsCmd = &cobra.Command{
	Use:     "catchall-targets <domain> <target-email> [<target-email>...]",
	Aliases: []string{"catchall-target", "catchalls", "catchall"},
	Short:   "Enables forwarding on catch-all targets of a domain",
	Long:    "Enables forwarding on catch-all targets of a domain.",
	Args:    cobra.MinimumNArgs(2),
	RunE: func(cmd *cobra.Command, args []string) error {
		argDomain := args[0]
		argEmails := ParseEmailArgs(args[1:])
		if len(argEmails) != len(args)-1 {
			return fmt.Errorf("invalid email arguments")
		}

		enabled := true
		options := db.DomainsCatchallTargetsPatchOptions{
			ForwardingToTargetEnabled: &enabled,
		}

		runner := db.TxForEachRunner[utils.EmailAddress]{
			Items: argEmails,
			Exec: func(tx *sql.Tx, item utils.EmailAddress) error {
				return db.DomainsCatchallTargets(tx).Patch(argDomain, item, options)
			},
			ItemString:     func(item utils.EmailAddress) string { return "@" + argDomain + " -> " + item.String() },
			FailureMessage: "failed to enable catchall target",
			SuccessMessage: "Successfully enabled catchall target",
		}

		runner.Run()
		return nil
	},
}
View Source
var EnableDomainsCmd = &cobra.Command{
	Use:     "domains <fqdn> [<fqdn>...]",
	Aliases: []string{"domain"},
	Short:   "Enables domains",
	Long:    "Enables domains.",
	Args:    cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		argDomains := ParseDomainFQDNArgs(args)
		if len(argDomains) != len(args) {
			return fmt.Errorf("invalid domain arguments")
		}

		enabled := true
		options := db.DomainsPatchOptions{
			Enabled: &enabled,
		}

		runner := db.TxForEachRunner[string]{
			Items: argDomains,
			Exec: func(tx *sql.Tx, item string) error {
				return db.Domains(tx).Patch(item, options)
			},
			ItemString:     func(item string) string { return item },
			FailureMessage: "failed to enable domain",
			SuccessMessage: "Successfully enabled domain",
		}

		runner.Run()
		return nil
	},
}
View Source
var EnableMailboxesCmd = &cobra.Command{
	Use:     "mailboxes [flags] <email> [<email>...]",
	Aliases: []string{"mailbox"},
	Short:   "Enables features on mailboxes",
	Long:    "Enables login, receiving, and/or sending for mailboxes. Use flags to select which property to disable.",
	Args:    cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		flagLogin, _ := cmd.Flags().GetBool("login")
		flagReceiving, _ := cmd.Flags().GetBool("receiving")
		flagSending, _ := cmd.Flags().GetBool("sending")

		if !flagLogin && !flagReceiving && !flagSending {
			return fmt.Errorf("at least one of --login, --receiving, or --sending must be true when specified")
		}

		argEmails := ParseEmailArgs(args)
		if len(argEmails) != len(args) {
			return fmt.Errorf("invalid email arguments")
		}

		enabled := true
		options := db.MailboxesPatchOptions{}
		if flagLogin {
			options.Login = &enabled
		}
		if flagReceiving {
			options.Receiving = &enabled
		}
		if flagSending {
			options.Sending = &enabled
		}

		runner := db.TxForEachRunner[utils.EmailAddress]{
			Items: argEmails,
			Exec: func(tx *sql.Tx, item utils.EmailAddress) error {
				return db.Mailboxes(tx).Patch(item, options)
			},
			ItemString:     func(item utils.EmailAddress) string { return item.String() },
			FailureMessage: "failed to enable mailbox",
			SuccessMessage: "Successfully enabled mailbox",
		}

		runner.Run()
		return nil
	},
}
View Source
var EnableRecipientsRelayedCmd = &cobra.Command{
	Use:     "recipients-relayed <email> [<email>...]",
	Aliases: []string{"recipient-relayed", "relayed-recipients", "relayed-recipient", "relayed"},
	Short:   "Enables relayed recipients",
	Long:    "Enables relayed recipients.",
	Args:    cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		argEmails := ParseEmailArgs(args)
		if len(argEmails) != len(args) {
			return fmt.Errorf("invalid email arguments")
		}

		enabled := true
		options := db.RecipientsRelayedPatchOptions{
			Enabled: &enabled,
		}

		runner := db.TxForEachRunner[utils.EmailAddress]{
			Items: argEmails,
			Exec: func(tx *sql.Tx, item utils.EmailAddress) error {
				return db.RecipientsRelayed(tx).Patch(item, options)
			},
			ItemString:     func(item utils.EmailAddress) string { return item.String() },
			FailureMessage: "failed to enable relayed recipient",
			SuccessMessage: "Successfully enabled relayed recipient",
		}

		runner.Run()
		return nil
	},
}
View Source
var EnableRemotesCmd = &cobra.Command{
	Use:     "remotes <name> [<name>...]",
	Aliases: []string{"remote"},
	Short:   "Enables remotes",
	Long:    "Enables remotes.",
	Args:    cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		enabled := true
		options := db.RemotesPatchOptions{
			Enabled: &enabled,
		}

		runner := db.TxForEachRunner[string]{
			Items: args,
			Exec: func(tx *sql.Tx, item string) error {
				return db.Remotes(tx).Patch(item, options)
			},
			ItemString:     func(item string) string { return item },
			FailureMessage: "failed to enable remote",
			SuccessMessage: "Successfully enabled remote",
		}

		runner.Run()
		return nil
	},
}
View Source
var (
	ErrNoValueProvided = errors.New("no value provided")
)
View Source
var ListAliasTargetsCmd = &cobra.Command{
	Use:     "alias-targets [flags] [<alias-email>...]",
	Aliases: []string{"alias-target", "targets", "target"},
	Short:   "List alias targets",
	Long:    "List alias targets. If aliases are provided, only targets for these aliases are listed.",
	Args:    cobra.ArbitraryArgs,
	RunE: func(cmd *cobra.Command, args []string) error {
		flagDeleted, _ := cmd.Flags().GetBool("deleted")
		flagAll, _ := cmd.Flags().GetBool("all")
		flagJSON, _ := cmd.Flags().GetBool("json")
		flagVerbose, _ := cmd.Flags().GetBool("verbose")

		var filterEmails []utils.EmailAddress
		if len(args) > 0 {
			filterEmails = ParseEmailArgs(args)
			if len(filterEmails) != len(args) {
				return fmt.Errorf("invalid email arguments")
			}
		}

		targets, err := listAliasTargets(db.AliasesTargetsListOptions{
			FilterAliasEmails: filterEmails,
			IncludeDeleted:    flagDeleted,
			IncludeAll:        flagAll,
		})
		if err != nil {
			return nil
		}

		if len(targets) == 0 {
			fmt.Println("No targets found")
			return nil
		}

		if flagJSON {
			output, err := json.MarshalIndent(targets, "", "  ")
			if err != nil {
				utils.PrintErrorWithMessage("Failed to marshal targets", err)
				return nil
			}
			fmt.Println(string(output))
			return nil
		}

		headers := []string{"Alias", "Target", "Foreign", "Forward", "Send"}
		if flagVerbose {
			headers = append(headers, "Created", "Updated")
		}
		if flagAll || flagDeleted {
			headers = append(headers, "Deleted")
		}

		t := table.New().
			Border(lipgloss.RoundedBorder()).
			BorderStyle(utils.BlackStyle).
			StyleFunc(func(row, col int) lipgloss.Style {
				if row == table.HeaderRow {
					return utils.TableHeaderStyle
				}
				cellStyle := utils.TableRowStyle
				switch col {
				case 2, 3, 4:
					cellStyle = cellStyle.Align(lipgloss.Center)
				}
				return cellStyle
			}).
			Headers(headers...)

		for _, target := range targets {
			row := []string{
				target.AliasEmail,
				target.TargetEmail,
				utils.MaybeEnabledTableStyle.Render(target.IsForeign),
				utils.MaybeEnabledTableStyle.Render(target.ForwardingToTargetEnabled, target.AliasEnabled, target.DomainEnabled),
				utils.MaybeEnabledTableStyle.Render(target.SendingFromTargetEnabled, target.AliasEnabled, target.DomainEnabled),
			}
			if flagVerbose {
				row = append(row,
					utils.MaybeTimeStyle.Render(target.CreatedAt),
					utils.MaybeTimeStyle.Render(target.UpdatedAt),
				)
			}
			if flagAll || flagDeleted {
				row = append(row,
					utils.MaybeTimeStyle.Render(target.DeletedAt),
				)
			}

			t.Row(row...)
		}

		fmt.Println(t)
		return nil
	},
}
View Source
var ListAliasesCmd = &cobra.Command{
	Use:     "aliases [flags] [<domain>...]",
	Aliases: []string{"alias"},
	Short:   "List aliases",
	Long:    "List aliases. If domains are provided, only aliases for these domains are listed.",
	Args:    cobra.ArbitraryArgs,
	RunE: func(cmd *cobra.Command, args []string) error {
		flagDeleted, _ := cmd.Flags().GetBool("deleted")
		flagAll, _ := cmd.Flags().GetBool("all")
		flagJSON, _ := cmd.Flags().GetBool("json")
		flagVerbose, _ := cmd.Flags().GetBool("verbose")

		if flagDeleted && flagAll {
			return fmt.Errorf("cannot use --deleted and --all flags together")
		}

		filterDomains := ParseDomainFQDNArgs(args)
		if len(filterDomains) != len(args) {
			return fmt.Errorf("invalid domain arguments")
		}

		aliases, err := listAliases(db.AliasesListOptions{
			FilterDomains:  filterDomains,
			IncludeDeleted: flagDeleted,
			IncludeAll:     flagAll,
		})
		if err != nil {
			return nil
		}

		if flagJSON {
			json, err := json.Marshal(aliases)
			if err != nil {
				utils.PrintErrorWithMessage("Failed to marshal aliases to JSON", err)
			}
			fmt.Println(string(json))
			return nil
		}

		headers := []string{"Domain", "Name", "Enabled", "Targets"}
		if flagVerbose {
			headers = append(headers, "Created", "Last Updated")
		}
		if flagDeleted || flagAll {
			headers = append(headers, "Deleted")
		}

		t := table.New().
			Border(lipgloss.RoundedBorder()).
			BorderStyle(utils.BlackStyle).
			StyleFunc(func(row, col int) lipgloss.Style {
				cellStyle := utils.TableRowStyle
				if row == table.HeaderRow {
					cellStyle = utils.TableHeaderStyle
				}
				switch col {
				case 2:
					return cellStyle.Align(lipgloss.Center)
				case 3:
					return cellStyle.Align(lipgloss.Right)
				default:
					return cellStyle.Align(lipgloss.Left)
				}
			}).
			Headers(headers...)

		for _, a := range aliases {
			row := []string{
				a.DomainFQDN,
				utils.MaybeWildcardNameStyle.Render(a.Name),
				utils.MaybeEnabledTableStyle.Render(a.Enabled, a.DomainEnabled),
				utils.MaybeZeroStyle.Render(a.TargetCount),
			}
			if flagVerbose {
				row = append(row,
					utils.MaybeTimeStyle.Render(a.CreatedAt),
					utils.MaybeTimeStyle.Render(a.UpdatedAt),
				)
			}
			if flagDeleted || flagAll {
				row = append(row,
					utils.MaybeTimeStyle.Render(a.DeletedAt),
				)
			}

			t.Row(row...)
		}

		fmt.Println(t.Render())
		return nil
	},
}
View Source
var ListCmd = &cobra.Command{
	Use:   "list",
	Short: "List mail system objects",
}
View Source
var ListDomainCatchallTargetsCmd = &cobra.Command{
	Use:     "catchall-targets [flags] [<domain>...]",
	Aliases: []string{"catchall-target", "catchalls", "catchall"},
	Short:   "List catch-all targets.",
	Long:    "List catch-all targets. If domains are provided, only catch-all targets for these domains are listed.",
	Args:    cobra.ArbitraryArgs,
	RunE: func(cmd *cobra.Command, args []string) error {
		flagDeleted, _ := cmd.Flags().GetBool("deleted")
		flagAll, _ := cmd.Flags().GetBool("all")
		flagJSON, _ := cmd.Flags().GetBool("json")
		flagVerbose, _ := cmd.Flags().GetBool("verbose")

		argDomains := args

		options := db.DomainsCatchallTargetsListOptions{
			FilterDomains:  argDomains,
			IncludeDeleted: flagDeleted,
			IncludeAll:     flagAll,
		}

		targets, err := listCatchallTargets(options)
		if err != nil {
			return nil
		}

		if len(targets) == 0 {
			fmt.Println("No catchall targets found for this domain")
			return nil
		}

		if flagJSON {
			out, err := json.MarshalIndent(targets, "", "  ")
			if err != nil {
				utils.PrintErrorWithMessage("Failed to marshal targets", err)
				return nil
			}
			fmt.Println(string(out))
			return nil
		}

		headers := []string{"Domain", "Target", "Forward", "Fallback Only"}
		if flagVerbose {
			headers = append(headers, "Created", "Updated")
		}
		if flagAll || flagDeleted {
			headers = append(headers, "Deleted")
		}

		t := table.New().
			Border(lipgloss.RoundedBorder()).
			BorderStyle(utils.BlackStyle).
			StyleFunc(func(row, col int) lipgloss.Style {
				if row == table.HeaderRow {
					return utils.TableHeaderStyle
				}
				cellStyle := utils.TableRowStyle
				if col == 2 || col == 3 {
					cellStyle = cellStyle.Align(lipgloss.Center)
				}
				return cellStyle
			}).
			Headers(headers...)

		for _, tgt := range targets {
			row := []string{
				tgt.DomainFQDN,
				tgt.TargetEmail,
				utils.MaybeEnabledTableStyle.Render(tgt.ForwardingToTargetEnabled),
				utils.MaybeEnabledTableStyle.Render(tgt.FallbackOnly),
			}
			if flagVerbose {
				row = append(row, utils.MaybeTimeStyle.Render(tgt.CreatedAt), utils.MaybeTimeStyle.Render(tgt.UpdatedAt))
			}
			if flagAll || flagDeleted {
				row = append(row, utils.MaybeTimeStyle.Render(tgt.DeletedAt))
			}
			t.Row(row...)
		}

		fmt.Println(t)
		return nil
	},
}
View Source
var ListDomainsCmd = &cobra.Command{
	Use:     "domains [flags]",
	Aliases: []string{"domain"},
	Short:   "List domains",
	Long:    "List domains.",
	Args:    cobra.MinimumNArgs(0),
	RunE: func(cmd *cobra.Command, args []string) error {
		flagDeleted, _ := cmd.Flags().GetBool("deleted")
		flagAll, _ := cmd.Flags().GetBool("all")
		flagJSON, _ := cmd.Flags().GetBool("json")
		flagVerbose, _ := cmd.Flags().GetBool("verbose")

		if flagDeleted && flagAll {
			return fmt.Errorf("cannot use --deleted and --all flags together")
		}

		options := db.DomainsListOptions{
			IncludeDeleted: flagDeleted,
			IncludeAll:     flagAll,
		}

		domains, err := listDomains(options)
		if err != nil {
			return nil
		}

		if flagJSON {
			jsonData, err := json.Marshal(domains)
			if err != nil {
				utils.PrintErrorWithMessage("failed to marshal domains to JSON", err)
				return nil
			}
			fmt.Println(string(jsonData))
			return nil
		}

		headers := []string{"FQDN", "Type", "Enabled", "Transport / Target Domain"}
		if flagVerbose {
			headers = append(headers, "Created", "Last Updated")
		}
		if flagDeleted || flagAll {
			headers = append(headers, "Deleted")
		}

		t := table.New().
			Border(lipgloss.RoundedBorder()).
			BorderStyle(utils.BlackStyle).
			StyleFunc(func(row, col int) lipgloss.Style {
				cellStyle := utils.TableRowStyle
				if row == table.HeaderRow {
					cellStyle = utils.TableHeaderStyle
				}
				switch col {
				case 2:
					return cellStyle.Align(lipgloss.Center)
				default:
					return cellStyle.Align(lipgloss.Left)
				}
			}).
			Headers(headers...)

		for _, domain := range domains {
			row := []string{
				domain.FQDN,
				domain.Type,
			}
			switch domain.Type {
			case "canonical":
				row = append(row,
					utils.MaybeEnabledTableStyle.Render(domain.Enabled, domain.TargetDomainEnabled),
					utils.MaybeEmptyStyle.Render(domain.TargetDomainFQDN),
				)
			case "alias":
				row = append(row,
					utils.MaybeEnabledTableStyle.Render(domain.Enabled),
					utils.BlackStyle.Render("-"),
				)
			default:
				row = append(row,
					utils.MaybeEnabledTableStyle.Render(domain.Enabled),
					utils.MaybeIDSuffixStyle.Render(domain.Transport, domain.TransportName),
				)
			}
			if flagVerbose {
				row = append(row,
					utils.MaybeTimeStyle.Render(domain.CreatedAt),
					utils.MaybeTimeStyle.Render(domain.UpdatedAt),
				)
			}
			if flagDeleted || flagAll {
				row = append(row,
					utils.MaybeTimeStyle.Render(domain.DeletedAt),
				)
			}

			t.Row(row...)
		}

		fmt.Println(t.Render())
		return nil
	},
}
View Source
var ListMailboxesCmd = &cobra.Command{
	Use:     "mailboxes [flags] [<domain>...]",
	Aliases: []string{"mailbox"},
	Short:   "List mailboxes",
	Long:    "List mailboxes.\nIf domains are provided, only mailboxes for these domains are listed.",
	Args:    cobra.ArbitraryArgs,
	RunE: func(cmd *cobra.Command, args []string) error {
		flagDeleted, _ := cmd.Flags().GetBool("deleted")
		flagAll, _ := cmd.Flags().GetBool("all")
		flagJSON, _ := cmd.Flags().GetBool("json")
		flagVerbose, _ := cmd.Flags().GetBool("verbose")

		if flagDeleted && flagAll {
			return fmt.Errorf("cannot use --deleted and --all flags together")
		}

		filterDomains := ParseDomainFQDNArgs(args)
		if len(filterDomains) != len(args) {
			return fmt.Errorf("invalid domain arguments")
		}

		options := db.MailboxesListOptions{
			FilterDomains:  filterDomains,
			IncludeDeleted: flagDeleted,
			IncludeAll:     flagAll,
		}

		mailboxes, err := listMailboxes(options)
		if err != nil {
			return nil
		}

		if flagJSON {
			out, err := json.Marshal(mailboxes)
			if err != nil {
				utils.PrintErrorWithMessage("Failed to marshal mailboxes to JSON", err)
				return nil
			}
			fmt.Println(string(out))
			return nil
		}

		headers := []string{"Domain", "Name", "Login", "Receive", "Send", "Pwd", "Quota", "Transport"}
		if flagVerbose {
			headers = append(headers, "Created", "Last Updated")
		}
		if flagDeleted || flagAll {
			headers = append(headers, "Deleted")
		}

		t := table.New().
			Border(lipgloss.RoundedBorder()).
			BorderStyle(utils.BlackStyle).
			StyleFunc(func(row, col int) lipgloss.Style {
				cellStyle := utils.TableRowStyle
				if row == table.HeaderRow {
					cellStyle = utils.TableHeaderStyle
				}
				switch col {
				case 2, 3, 4, 5:
					return cellStyle.Align(lipgloss.Center)
				case 6:
					return cellStyle.Align(lipgloss.Right)
				default:
					return cellStyle.Align(lipgloss.Left)
				}
			}).
			Headers(headers...)

		for _, m := range mailboxes {
			row := []string{
				m.DomainFQDN,
				m.Name,
				utils.MaybeEnabledTableStyle.Render(m.LoginEnabled, m.DomainEnabled),
				utils.MaybeEnabledTableStyle.Render(m.ReceivingEnabled, m.DomainEnabled),
				utils.MaybeEnabledTableStyle.Render(m.SendingEnabled, m.DomainEnabled),
				utils.MaybePasswordStyle.Render(m.PasswordSet),
				utils.MaybeQuotaStyle.Render(m.StorageQuota, 1024*1024),
				utils.MaybeIDSuffixStyle.Render(m.Transport, m.TransportName),
			}

			if flagVerbose {
				row = append(row,
					utils.MaybeTimeStyle.Render(m.CreatedAt),
					utils.MaybeTimeStyle.Render(m.UpdatedAt),
				)
			}
			if flagDeleted || flagAll {
				row = append(row,
					utils.MaybeTimeStyle.Render(m.DeletedAt),
				)
			}

			t.Row(row...)
		}

		fmt.Println(t.Render())
		return nil
	},
}
View Source
var ListRecipientsRelayedCmd = &cobra.Command{
	Use:     "relayed-recipients [flags] [<domain>...]",
	Aliases: []string{"recipient-relayed", "relayed-recipients", "relayed-recipient", "relayed"},
	Short:   "List relayed recipients",
	Long:    "List relayed recipients.\nIf domains are provided, only recipients for these domains are listed.",
	Args:    cobra.ArbitraryArgs,
	RunE: func(cmd *cobra.Command, args []string) error {
		flagDeleted, _ := cmd.Flags().GetBool("deleted")
		flagAll, _ := cmd.Flags().GetBool("all")
		flagJSON, _ := cmd.Flags().GetBool("json")
		flagVerbose, _ := cmd.Flags().GetBool("verbose")

		if flagDeleted && flagAll {
			return fmt.Errorf("cannot use --deleted and --all flags together")
		}

		filterDomains := ParseDomainFQDNArgs(args)
		if len(filterDomains) != len(args) {
			return fmt.Errorf("invalid domain arguments")
		}

		options := db.RecipientsRelayedListOptions{
			FilterDomains:  filterDomains,
			IncludeDeleted: flagDeleted,
			IncludeAll:     flagAll,
		}

		recipients, err := listRelayedRecipients(options)
		if err != nil {
			return nil
		}

		if flagJSON {
			out, err := json.Marshal(recipients)
			if err != nil {
				utils.PrintErrorWithMessage("Failed to marshal recipients to JSON", err)
				return nil
			}
			fmt.Println(string(out))
			return nil
		}

		if len(recipients) == 0 {
			fmt.Println("No relayed recipients found")
			return nil
		}

		headers := []string{"Domain", "Name", "Enabled"}
		if flagVerbose {
			headers = append(headers, "Created", "Last Updated")
		}
		if flagDeleted || flagAll {
			headers = append(headers, "Deleted")
		}

		t := table.New().
			Border(lipgloss.RoundedBorder()).
			BorderStyle(utils.BlackStyle).
			StyleFunc(func(row, col int) lipgloss.Style {
				cellStyle := utils.TableRowStyle
				if row == table.HeaderRow {
					cellStyle = utils.TableHeaderStyle
				}
				switch col {
				case 2:
					return cellStyle.Align(lipgloss.Center)
				default:
					return cellStyle.Align(lipgloss.Left)
				}
			}).
			Headers(headers...)

		for _, r := range recipients {
			row := []string{
				r.DomainFQDN,
				r.Name,
				utils.MaybeEnabledTableStyle.Render(r.Enabled, r.DomainEnabled),
			}
			if flagVerbose {
				row = append(row,
					utils.MaybeTimeStyle.Render(r.CreatedAt),
					utils.MaybeTimeStyle.Render(r.UpdatedAt),
				)
			}
			if flagDeleted || flagAll {
				row = append(row,
					utils.MaybeTimeStyle.Render(r.DeletedAt),
				)
			}

			t.Row(row...)
		}

		fmt.Println(t.Render())
		return nil
	},
}
View Source
var ListRemoteSendGrantsCmd = &cobra.Command{
	Use:     "send-grants [flags] [<remote-name>...]",
	Aliases: []string{"send-grant"},
	Short:   "List send grants for remotes",
	Long:    "List send grants for remotes.\nIf remote names are provided, only send grants for these remotes are listed.",
	Args:    cobra.ArbitraryArgs,
	RunE: func(cmd *cobra.Command, args []string) error {
		flagDeleted, _ := cmd.Flags().GetBool("deleted")
		flagAll, _ := cmd.Flags().GetBool("all")
		flagJSON, _ := cmd.Flags().GetBool("json")
		flagVerbose, _ := cmd.Flags().GetBool("verbose")

		if flagDeleted && flagAll {
			return fmt.Errorf("cannot use --deleted and --all flags together")
		}

		var filterNames []string
		if len(args) > 0 {
			filterNames = args
		}

		grants, err := listGrants(db.RemotesSendGrantsListOptions{
			FilterRemoteNames: filterNames,
			IncludeDeleted:    flagDeleted,
			IncludeAll:        flagAll,
		})
		if err != nil {
			return nil
		}

		if flagJSON {
			out, err := json.Marshal(grants)
			if err != nil {
				utils.PrintErrorWithMessage("Failed to marshal send grants to JSON", err)
				return nil
			}
			fmt.Println(string(out))
			return nil
		}

		if len(grants) == 0 {
			fmt.Println("No send grants found")
			return nil
		}

		headers := []string{"Remote", "Domain", "Name"}
		if flagVerbose {
			headers = append(headers, "Created", "Last Updated")
		}
		if flagDeleted || flagAll {
			headers = append(headers, "Deleted")
		}

		t := table.New().
			Border(lipgloss.RoundedBorder()).
			BorderStyle(utils.BlackStyle).
			StyleFunc(func(row, col int) lipgloss.Style {
				cellStyle := utils.TableRowStyle
				if row == table.HeaderRow {
					cellStyle = utils.TableHeaderStyle
				}
				switch col {
				default:
					return cellStyle.Align(lipgloss.Left)
				}
			}).
			Headers(headers...)

		for _, g := range grants {
			row := []string{
				g.RemoteName,
				g.DomainFQDN,
				utils.SQLLikeStyle.Render(g.Name),
			}

			if flagVerbose {
				row = append(row,
					utils.MaybeTimeStyle.Render(g.CreatedAt),
					utils.MaybeTimeStyle.Render(g.UpdatedAt),
				)
			}
			if flagDeleted || flagAll {
				row = append(row,
					utils.MaybeTimeStyle.Render(g.DeletedAt),
				)
			}

			t.Row(row...)
		}

		fmt.Println(t.Render())
		return nil
	},
}
View Source
var ListRemotesCmd = &cobra.Command{
	Use:   "remotes [flags]",
	Short: "List remotes",
	Long:  "List remotes.",
	Args:  cobra.ArbitraryArgs,
	RunE: func(cmd *cobra.Command, args []string) error {
		flagDeleted, _ := cmd.Flags().GetBool("deleted")
		flagAll, _ := cmd.Flags().GetBool("all")
		flagJSON, _ := cmd.Flags().GetBool("json")
		flagVerbose, _ := cmd.Flags().GetBool("verbose")

		if flagDeleted && flagAll {
			return fmt.Errorf("cannot use --deleted and --all flags together")
		}

		options := db.RemotesListOptions{
			IncludeDeleted: flagDeleted,
			IncludeAll:     flagAll,
		}

		remotes, err := listRemotes(options)
		if err != nil {
			return nil
		}

		if flagJSON {
			out, err := json.Marshal(remotes)
			if err != nil {
				utils.PrintErrorWithMessage("Failed to marshal remotes to JSON", err)
				return nil
			}
			fmt.Println(string(out))
			return nil
		}

		if len(remotes) == 0 {
			fmt.Println("No remotes found")
			return nil
		}

		headers := []string{"Name", "Enabled", "Pwd"}
		if flagVerbose {
			headers = append(headers, "Created", "Last Updated")
		}
		if flagDeleted || flagAll {
			headers = append(headers, "Deleted")
		}

		t := table.New().
			Border(lipgloss.RoundedBorder()).
			BorderStyle(utils.BlackStyle).
			StyleFunc(func(row, col int) lipgloss.Style {
				cellStyle := utils.TableRowStyle
				if row == table.HeaderRow {
					cellStyle = utils.TableHeaderStyle
				}
				switch col {
				case 1, 2:
					return cellStyle.Align(lipgloss.Center)
				default:
					return cellStyle.Align(lipgloss.Left)
				}
			}).
			Headers(headers...)

		for _, r := range remotes {
			row := []string{
				r.Name,
				utils.MaybeEnabledTableStyle.Render(r.Enabled),
				utils.MaybePasswordStyle.Render(r.PasswordSet),
			}

			if flagVerbose {
				row = append(row,
					utils.MaybeTimeStyle.Render(r.CreatedAt),
					utils.MaybeTimeStyle.Render(r.UpdatedAt),
				)
			}
			if flagDeleted || flagAll {
				row = append(row,
					utils.MaybeTimeStyle.Render(r.DeletedAt),
				)
			}

			t.Row(row...)
		}

		fmt.Println(t.Render())
		return nil
	},
}
View Source
var ListTransportsCmd = &cobra.Command{
	Use:   "transports [flags]",
	Short: "List transports",
	Long:  "List all transports. Use --deleted to show only deleted transports, --all to show both active and deleted.",
	RunE: func(cmd *cobra.Command, args []string) error {
		flagDeleted, _ := cmd.Flags().GetBool("deleted")
		flagAll, _ := cmd.Flags().GetBool("all")
		flagJSON, _ := cmd.Flags().GetBool("json")
		flagVerbose, _ := cmd.Flags().GetBool("verbose")

		transports, err := listTransports(db.TransportsListOptions{IncludeDeleted: flagDeleted, IncludeAll: flagAll})
		if err != nil {
			return nil
		}

		if flagJSON {
			encoder := json.NewEncoder(os.Stdout)
			if err := encoder.Encode(transports); err != nil {
				utils.PrintErrorWithMessage("Failed to encode JSON", err)
				return nil
			}
			return nil
		}

		headers := []string{"Name", "Method", "Host", "Port", "MX Lookup"}
		if flagVerbose {
			headers = append(headers, "Created", "Last Updated")
		}
		if flagDeleted || flagAll {
			headers = append(headers, "Deleted")
		}

		t := table.New().
			Border(lipgloss.RoundedBorder()).
			BorderStyle(utils.BlackStyle).
			Headers(headers...).
			StyleFunc(func(row, col int) lipgloss.Style {
				if row == table.HeaderRow {
					return utils.TableHeaderStyle
				}
				cellStyle := utils.TableRowStyle
				switch col {
				case 3:
					return cellStyle.Align(lipgloss.Right)
				case 4:
					return cellStyle.Align(lipgloss.Center)
				}
				return cellStyle
			})

		mxLookupStyle := utils.MaybeEnabledTableStyle
		mxLookupStyle.TrueStyle = utils.BlueStyle.Bold(true)
		mxLookupStyle.FalseStyle = utils.BlackStyle.Bold(true)

		for _, transport := range transports {
			row := []string{
				transport.Name,
				transport.Method,
				transport.Host,
				utils.MaybeEmptyStyle.Render(transport.Port),
				mxLookupStyle.Render(transport.MXLookup),
			}

			if flagVerbose {
				row = append(row,
					utils.MaybeTimeStyle.Render(transport.CreatedAt),
					utils.MaybeTimeStyle.Render(transport.UpdatedAt),
				)
			}

			if flagAll || flagDeleted {
				row = append(row,
					utils.MaybeTimeStyle.Render(transport.DeletedAt),
				)
			}

			t.Row(row...)
		}

		fmt.Println(t.Render())
		return nil
	},
}
View Source
var PatchAliasTargetsCmd = &cobra.Command{
	Use:     "alias-targets [flags] <email> [<email>...]",
	Aliases: []string{"alias-target", "targets", "target"},
	Short:   "Updates existing alias targets",
	Long:    "Updates specified properties for existing alias targets.",
	Args:    cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		if !cmd.Flags().Changed("forward") && !cmd.Flags().Changed("send") {
			return fmt.Errorf("at least one of --forward or --send flags must be specified")
		}

		argEmails := ParseEmailArgs(args)
		if len(argEmails) != len(args) {
			return fmt.Errorf("invalid email arguments")
		}

		argAliasEmail := argEmails[0]
		argTargetEmails := argEmails[1:]

		options := db.AliasesTargetsPatchOptions{}
		if cmd.Flags().Changed("forward") {
			flagForward, _ := cmd.Flags().GetBool("forward")
			options.ForwardingToTargetEnabled = &flagForward
		}
		if cmd.Flags().Changed("send") {
			flagSend, _ := cmd.Flags().GetBool("send")
			options.SendingFromTargetEnabled = &flagSend
		}

		runner := db.TxForEachRunner[utils.EmailAddress]{
			Items: argTargetEmails,
			Exec: func(tx *sql.Tx, item utils.EmailAddress) error {
				return db.AliasesTargets(tx).Patch(argAliasEmail, item, options)
			},
			ItemString:     func(item utils.EmailAddress) string { return argAliasEmail.String() + " -> " + item.String() },
			FailureMessage: "failed to patch alias target",
			SuccessMessage: "Successfully patch alias target",
		}

		runner.Run()
		return nil
	},
}
View Source
var PatchAliasesCmd = &cobra.Command{
	Use:     "aliases [flags] <email> [<email>...]",
	Aliases: []string{"alias"},
	Short:   "Updates existing aliases",
	Long:    "Updates specified properties of existing aliases.",
	Args:    cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		flagEnabled, _ := cmd.Flags().GetBool("enabled")

		if !cmd.Flags().Changed("enabled") {
			return fmt.Errorf("no changes specified")
		}

		argEmails := ParseEmailArgs(args)
		if len(argEmails) != len(args) {
			return fmt.Errorf("invalid email arguments")
		}

		options := db.AliasesPatchOptions{}

		if cmd.Flags().Changed("enabled") {
			options.Enabled = &flagEnabled
		}

		runner := db.TxForEachRunner[utils.EmailAddress]{
			Items: argEmails,
			Exec: func(tx *sql.Tx, item utils.EmailAddress) error {
				return db.Aliases(tx).Patch(item, options)
			},
			ItemString:     func(item utils.EmailAddress) string { return item.String() },
			FailureMessage: "failed to patch alias",
			SuccessMessage: "Successfully patched alias",
		}

		runner.Run()
		return nil
	},
}
View Source
var PatchCmd = &cobra.Command{
	Use:   "patch",
	Short: "Patch mail system objects",
}
View Source
var PatchDomainCatchallTargetsCmd = &cobra.Command{
	Use:     "catchall-targets [flags] <domain> <target> [<target>...]",
	Aliases: []string{"catchall-target", "catchalls", "catchall"},
	Short:   "Updates existing catchall targets",
	Long:    "Updates specified properties for existing catchall targets.",
	Args:    cobra.MinimumNArgs(2),
	RunE: func(cmd *cobra.Command, args []string) error {
		if !cmd.Flags().Changed("forward") && !cmd.Flags().Changed("fallback-only") {
			return fmt.Errorf("at least one of --forward or --fallback-only flags must be specified")
		}

		argDomain := args[0]
		argEmails := ParseEmailArgs(args[1:])
		if len(argEmails) != len(args)-1 {
			return fmt.Errorf("invalid email arguments")
		}

		options := db.DomainsCatchallTargetsPatchOptions{}
		if cmd.Flags().Changed("forward") {
			flagForward, _ := cmd.Flags().GetBool("forward")
			options.ForwardingToTargetEnabled = &flagForward
		}
		if cmd.Flags().Changed("fallback-only") {
			flagOnly, _ := cmd.Flags().GetBool("fallback-only")
			options.FallbackOnly = &flagOnly
		}

		runner := db.TxForEachRunner[utils.EmailAddress]{
			Items: argEmails,
			Exec: func(tx *sql.Tx, item utils.EmailAddress) error {
				return db.DomainsCatchallTargets(tx).Patch(argDomain, item, options)
			},
			ItemString:     func(item utils.EmailAddress) string { return "@" + argDomain + " -> " + item.String() },
			FailureMessage: "failed to patch catchall target",
			SuccessMessage: "Successfully patched catchall target",
		}

		runner.Run()
		return nil
	},
}
View Source
var PatchDomainsCmd = &cobra.Command{
	Use:     "domains [flags] <fqdn> [<fqdn>...]",
	Aliases: []string{"domain"},
	Short:   "Updates existing domains",
	Long:    "Updates specified properties for existing domains.",
	Args:    cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		flagEnabled, _ := cmd.Flags().GetBool("enabled")
		flagTransport, _ := cmd.Flags().GetString("transport")
		flagTargetDomain, _ := cmd.Flags().GetString("target-domain")

		if !cmd.Flags().Changed("enabled") && !cmd.Flags().Changed("transport") && !cmd.Flags().Changed("target-domain") {
			return fmt.Errorf("no changes specified")
		}

		argDomains := ParseDomainFQDNArgs(args)
		if len(argDomains) != len(args) {
			return fmt.Errorf("invalid domain arguments")
		}

		var options db.DomainsPatchOptions
		if cmd.Flags().Changed("enabled") {
			options.Enabled = &flagEnabled
		}
		if cmd.Flags().Changed("transport") {
			options.TransportName = &flagTransport
		}
		if cmd.Flags().Changed("target-domain") {
			options.TargetDomainFQDN = &flagTargetDomain
		}

		runner := db.TxForEachRunner[string]{
			Items: argDomains,
			Exec: func(tx *sql.Tx, item string) error {
				return db.Domains(tx).Patch(item, options)
			},
			ItemString:     func(item string) string { return item },
			FailureMessage: "failed to patch domain",
			SuccessMessage: "Successfully patched domain",
		}

		runner.Run()
		return nil
	},
}
View Source
var PatchMailboxesCmd = &cobra.Command{
	Use:     "mailboxes [flags] <email> [<email>...]",
	Aliases: []string{"mailbox"},
	Short:   "Updates existing mailboxes",
	Long:    "Updates specified properties for existing mailboxes.",
	Args:    cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		flagPassword, _ := cmd.Flags().GetBool("password")
		flagPasswordStdin, _ := cmd.Flags().GetBool("password-stdin")
		flagPasswordMethod, _ := cmd.Flags().GetString("password-method")
		flagPasswordHashOptions, _ := cmd.Flags().GetString("password-hash-options")
		flagPasswordNo, _ := cmd.Flags().GetBool("no-password")

		if (flagPassword || flagPasswordStdin) && flagPasswordNo {
			return fmt.Errorf("cannot use --no-password with --password or --password-stdin")
		}

		if flagPassword && flagPasswordStdin {
			return fmt.Errorf("cannot use both --password and --password-stdin")
		}

		if len(args) > 1 && (flagPassword || flagPasswordStdin) {
			return fmt.Errorf("cannot set password while updating multiple mailboxes")
		}

		argEmails := ParseEmailArgs(args)
		if len(argEmails) != len(args) {
			return fmt.Errorf("invalid email arguments")
		}

		options := db.MailboxesPatchOptions{}
		if flagPassword || flagPasswordStdin {
			passwordHash, err := ReadPasswordHashed(flagPasswordMethod, flagPasswordHashOptions, flagPasswordStdin)
			if err != nil {
				utils.PrintErrorWithMessage("failed to read password", err)
				return nil
			}
			options.PasswordHash = &sql.NullString{Valid: true, String: passwordHash}
		}
		if flagPasswordNo {
			options.PasswordHash = &sql.NullString{Valid: false}
		}
		if cmd.Flags().Changed("quota") {
			q, _ := cmd.Flags().GetInt32("quota")
			if q <= 0 {
				options.Quota = &sql.NullInt32{Valid: false}
			} else {
				options.Quota = &sql.NullInt32{Valid: true, Int32: q}
			}
		}
		if cmd.Flags().Changed("transport") {
			transportName, _ := cmd.Flags().GetString("transport")
			if transportName == "-" {
				options.TransportName = &sql.NullString{Valid: false}
			} else {
				options.TransportName = &sql.NullString{Valid: true, String: transportName}
			}
		}
		if cmd.Flags().Changed("login") {
			v, _ := cmd.Flags().GetBool("login")
			options.Login = &v
		}
		if cmd.Flags().Changed("receiving") {
			v, _ := cmd.Flags().GetBool("receiving")
			options.Receiving = &v
		}
		if cmd.Flags().Changed("sending") {
			v, _ := cmd.Flags().GetBool("sending")
			options.Sending = &v
		}

		runner := db.TxForEachRunner[utils.EmailAddress]{
			Items: argEmails,
			Exec: func(tx *sql.Tx, item utils.EmailAddress) error {
				return db.Mailboxes(tx).Patch(item, options)
			},
			ItemString:     func(item utils.EmailAddress) string { return item.String() },
			FailureMessage: "failed to patch mailbox",
			SuccessMessage: "Successfully patched mailbox",
		}

		runner.Run()
		return nil
	},
}
View Source
var PatchRecipientsRelayedCmd = &cobra.Command{
	Use:     "recipients-relayed [flags] <email> [<email>...]",
	Aliases: []string{"recipient-relayed", "relayed-recipients", "relayed-recipient", "relayed"},
	Short:   "Updates existing relayed recipients",
	Long:    "Updates specified properties for existing relayed recipients. Emails must be in the format \"name@example.com\".",
	Args:    cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		flagEnabled, _ := cmd.Flags().GetBool("enabled")

		if !cmd.Flags().Changed("enabled") {
			return fmt.Errorf("no changes specified")
		}

		argEmails := ParseEmailArgs(args)
		if len(argEmails) != len(args) {
			return fmt.Errorf("invalid email arguments")
		}

		options := db.RecipientsRelayedPatchOptions{}
		if cmd.Flags().Changed("enabled") {
			options.Enabled = &flagEnabled
		}

		runner := db.TxForEachRunner[utils.EmailAddress]{
			Items: argEmails,
			Exec: func(tx *sql.Tx, item utils.EmailAddress) error {
				return db.RecipientsRelayed(tx).Patch(item, options)
			},
			ItemString:     func(item utils.EmailAddress) string { return item.String() },
			FailureMessage: "failed to patch relayed recipient",
			SuccessMessage: "Successfully patched relayed recipient",
		}

		runner.Run()
		return nil
	},
}
View Source
var PatchRemotesCmd = &cobra.Command{
	Use:   "remotes [flags] <hostname> [<hostname>...]",
	Short: "Updates existing remotes",
	Long:  "Updates specified properties for existing remotes.",
	Args:  cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		flagEnabled, _ := cmd.Flags().GetBool("enabled")
		flagPassword, _ := cmd.Flags().GetBool("password")
		flagPasswordStdin, _ := cmd.Flags().GetBool("password-stdin")
		flagPasswordMethod, _ := cmd.Flags().GetString("password-method")
		flagPasswordHashOptions, _ := cmd.Flags().GetString("password-hash-options")
		flagPasswordNo, _ := cmd.Flags().GetBool("no-password")

		if (flagPassword || flagPasswordStdin) && flagPasswordNo {
			return fmt.Errorf("cannot use --no-password with --password or --password-stdin")
		}

		if flagPassword && flagPasswordStdin {
			return fmt.Errorf("cannot use both --password and --password-stdin")
		}

		if len(args) > 1 && (flagPassword || flagPasswordStdin) {
			return fmt.Errorf("cannot set password while updating multiple remotes")
		}

		if !flagPassword && !flagPasswordStdin && !flagPasswordNo && !cmd.Flags().Changed("enabled") {
			return fmt.Errorf("no changes specified. Use --password, --no-password, or --enabled flags")
		}

		options := db.RemotesPatchOptions{}

		if cmd.Flags().Changed("enabled") {
			options.Enabled = &flagEnabled
		}

		if flagPassword || flagPasswordStdin {
			passwordHash, err := ReadPasswordHashed(flagPasswordMethod, flagPasswordHashOptions, flagPasswordStdin)
			if err != nil {
				utils.PrintErrorWithMessage("failed to read password", err)
				return nil
			}
			options.PasswordHash = &sql.NullString{
				String: passwordHash,
				Valid:  true,
			}
		}
		if flagPasswordNo {
			options.PasswordHash = &sql.NullString{
				Valid: false,
			}
		}

		runner := db.TxForEachRunner[string]{
			Items: args,
			Exec: func(tx *sql.Tx, item string) error {
				return db.Remotes(tx).Patch(item, options)
			},
			ItemString:     func(item string) string { return item },
			FailureMessage: "failed to patch remote",
			SuccessMessage: "Successfully patched remote",
		}

		runner.Run()
		return nil
	},
}
View Source
var PatchTransportsCmd = &cobra.Command{
	Use:     "transports [flags] <name> [<name>...]",
	Aliases: []string{"transport"},
	Short:   "Updates existing transports",
	Long:    "Updates specified properties of an existing transport.",
	Args:    cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		flagMethod, _ := cmd.Flags().GetString("method")
		flagHost, _ := cmd.Flags().GetString("host")
		flagPort, _ := cmd.Flags().GetUint16("port")
		flagMxLookup, _ := cmd.Flags().GetBool("mx-lookup")

		if !cmd.Flags().Changed("method") && !cmd.Flags().Changed("host") && !cmd.Flags().Changed("port") && !cmd.Flags().Changed("mx-lookup") {
			return fmt.Errorf("no changes specified")
		}

		options := db.TransportsPatchOptions{}
		if cmd.Flags().Changed("method") {
			options.Method = &flagMethod
		}
		if cmd.Flags().Changed("host") {
			options.Host = &flagHost
		}
		if cmd.Flags().Changed("port") {
			if flagPort > 0 {
				options.Port = &sql.NullInt32{Int32: int32(flagPort), Valid: true}
			} else {
				options.Port = &sql.NullInt32{Valid: false}
			}
		}
		if cmd.Flags().Changed("mx-lookup") {
			options.MxLookup = &flagMxLookup
		}

		runner := db.TxForEachRunner[string]{
			Items: args,
			Exec: func(tx *sql.Tx, item string) error {
				return db.Transports(tx).Patch(item, options)
			},
			ItemString:     func(item string) string { return item },
			FailureMessage: "failed to patch transport",
			SuccessMessage: "Successfully patched transport",
		}

		runner.Run()
		return nil
	},
}
View Source
var RenameAliasesCmd = &cobra.Command{
	Use:     "alias <email> <new-email>",
	Aliases: []string{"aliases"},
	Short:   "Renames an alias",
	Long:    "Renames an alias by changing its email address. This also supports changing the domain without loosing any of the targets or other relations.",
	Args:    cobra.ExactArgs(2),
	RunE: func(cmd *cobra.Command, args []string) error {
		argEmails := ParseEmailArgs(args)
		if len(argEmails) != len(args) {
			return fmt.Errorf("invalid email arguments")
		}
		argOldEmail := argEmails[0]
		argNewEmail := argEmails[1]

		runner := db.TxRunner{
			Exec: func(tx *sql.Tx) error {
				return db.Aliases(tx).Rename(argOldEmail, argNewEmail)
			},
			ItemString:     fmt.Sprintf("%s -> %s", argOldEmail.String(), argNewEmail.String()),
			FailureMessage: "failed to rename alias",
			SuccessMessage: "Successfully renamed alias",
		}

		runner.Run()
		return nil
	},
}
View Source
var RenameCmd = &cobra.Command{
	Use:   "rename",
	Short: "Rename mail system objects",
}
View Source
var RenameDomainsCmd = &cobra.Command{
	Use:     "domain <old-fqdn> <new-fqdn>",
	Aliases: []string{"domains"},
	Short:   "Renames a domain",
	Long:    "Renames a domain by changing its FQDN. All contained mailboxes, aliases, recipients, and other relations will be renamed too.",
	Args:    cobra.ExactArgs(2),
	RunE: func(cmd *cobra.Command, args []string) error {
		argDomains := ParseDomainFQDNArgs(args)
		if len(argDomains) != len(args) {
			return fmt.Errorf("invalid domain arguments")
		}

		oldDomain := argDomains[0]
		newDomain := argDomains[1]

		runner := db.TxRunner{
			Exec: func(tx *sql.Tx) error {
				return db.Domains(tx).Rename(oldDomain, newDomain)
			},
			ItemString:     fmt.Sprintf("%s -> %s", oldDomain, newDomain),
			FailureMessage: "failed to rename domain",
			SuccessMessage: "Successfully renamed domain",
		}

		runner.Run()
		return nil
	},
}
View Source
var RenameMailboxesCmd = &cobra.Command{
	Use:     "mailbox <email> <new-email>",
	Aliases: []string{"mailboxes"},
	Short:   "Renames a mailbox",
	Long:    "Renames a mailbox. This also supports changing the domain without loosing any of the aliases, or other relations.",
	Args:    cobra.ExactArgs(2),
	RunE: func(cmd *cobra.Command, args []string) error {
		argEmails := ParseEmailArgs(args)
		if len(argEmails) != len(args) {
			return fmt.Errorf("invalid email arguments")
		}

		oldEmail := argEmails[0]
		newEmail := argEmails[1]

		runner := db.TxRunner{
			Exec: func(tx *sql.Tx) error {
				return db.Mailboxes(tx).Rename(oldEmail, newEmail)
			},
			ItemString:     fmt.Sprintf("%s -> %s", oldEmail, newEmail),
			FailureMessage: "failed to rename mailbox",
			SuccessMessage: "Successfully renamed mailbox",
		}

		runner.Run()
		return nil
	},
}
View Source
var RenameRecipientsRelayedCmd = &cobra.Command{
	Use:     "recipient-relayed <email> <new-email>",
	Aliases: []string{"recipients-relayed", "relayed-recipients", "relayed-recipient", "relayed"},
	Short:   "Renames a relayed recipient",
	Long:    "Renames a relayed recipient. This also supports changing the domain without loosing any of the relations.",
	Args:    cobra.ExactArgs(2),
	RunE: func(cmd *cobra.Command, args []string) error {
		argEmails := ParseEmailArgs(args)
		if len(argEmails) != len(args) {
			return fmt.Errorf("invalid email arguments")
		}
		oldEmail := argEmails[0]
		newEmail := argEmails[1]

		runner := db.TxRunner{
			Exec: func(tx *sql.Tx) error {
				return db.RecipientsRelayed(tx).Rename(oldEmail, newEmail)
			},
			ItemString:     fmt.Sprintf("%s -> %s", oldEmail.String(), newEmail.String()),
			FailureMessage: "failed to rename relayed recipient",
			SuccessMessage: "Successfully renamed relayed recipient",
		}

		runner.Run()
		return nil
	},
}
View Source
var RenameRemotesCmd = &cobra.Command{
	Use:   "remote <old-name> <new-name>",
	Short: "Renames a remote",
	Long:  "Renames a remote by changing its name.",
	Args:  cobra.ExactArgs(2),
	RunE: func(cmd *cobra.Command, args []string) error {
		oldName := args[0]
		newName := args[1]

		runner := db.TxRunner{
			Exec: func(tx *sql.Tx) error {
				return db.Remotes(tx).Rename(oldName, newName)
			},
			ItemString:     fmt.Sprintf("%s -> %s", oldName, newName),
			FailureMessage: "failed to rename remote",
			SuccessMessage: "Successfully renamed remote",
		}

		runner.Run()
		return nil
	},
}
View Source
var RenameTransportsCmd = &cobra.Command{
	Use:     "transport <old-name> <new-name>",
	Aliases: []string{"transports"},
	Short:   "Renames a transport",
	Long:    "Renames a transport by changing its name.",
	Args:    cobra.ExactArgs(2),
	RunE: func(cmd *cobra.Command, args []string) error {
		oldName := args[0]
		newName := args[1]

		runner := db.TxRunner{
			Exec: func(tx *sql.Tx) error {
				return db.Transports(tx).Rename(oldName, newName)
			},
			ItemString:     fmt.Sprintf("%s -> %s", oldName, newName),
			FailureMessage: "failed to rename transport",
			SuccessMessage: "Successfully renamed transport",
		}

		runner.Run()
		return nil
	},
}
View Source
var RestoreAliasTargetsCmd = &cobra.Command{
	Use:     "alias-targets <alias-email> <target-email> [<target-email>...]",
	Aliases: []string{"alias-target", "targets", "target"},
	Short:   "Restores soft-deleted alias targets",
	Long:    "Restore soft-deleted targets to an alias.",
	Args:    cobra.MinimumNArgs(2),
	RunE: func(cmd *cobra.Command, args []string) error {
		argEmails := ParseEmailArgs(args)
		if len(argEmails) != len(args) {
			return fmt.Errorf("invalid email arguments")
		}

		argAliasEmail := argEmails[0]
		argTargetEmails := argEmails[1:]

		runner := db.TxForEachRunner[utils.EmailAddress]{
			Items: argTargetEmails,
			Exec: func(tx *sql.Tx, item utils.EmailAddress) error {
				return db.AliasesTargets(tx).Restore(argAliasEmail, item)
			},
			ItemString:     func(item utils.EmailAddress) string { return argAliasEmail.String() + " -> " + item.String() },
			FailureMessage: "failed to restore alias target",
			SuccessMessage: "Successfully restore alias target",
		}

		runner.Run()
		return nil
	},
}
View Source
var RestoreAliasesCmd = &cobra.Command{
	Use:     "aliases <email> [<email>...]",
	Aliases: []string{"alias"},
	Short:   "Restores soft-deleted aliases",
	Long:    "Restores soft-deleted aliases.",
	Args:    cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		argEmails := ParseEmailArgs(args)
		if len(argEmails) != len(args) {
			return fmt.Errorf("invalid email arguments")
		}

		runner := db.TxForEachRunner[utils.EmailAddress]{
			Items: argEmails,
			Exec: func(tx *sql.Tx, item utils.EmailAddress) error {
				return db.Aliases(tx).Restore(item)
			},
			ItemString:     func(item utils.EmailAddress) string { return item.String() },
			FailureMessage: "failed to restore alias",
			SuccessMessage: "Successfully restored alias",
		}

		runner.Run()
		return nil
	},
}
View Source
var RestoreCmd = &cobra.Command{
	Use:   "restore",
	Short: "Restore soft-deleted mail system objects",
}
View Source
var RestoreDomainCatchallTargetsCmd = &cobra.Command{
	Use:     "catchall-targets <domain> <target-email> [<target-email>...]",
	Aliases: []string{"catchall-target", "catchalls", "catchall"},
	Short:   "Restores soft-deleted domain catch-all targets",
	Long:    "Restores soft-deleted domain catch-all targets.",
	Args:    cobra.MinimumNArgs(2),
	RunE: func(cmd *cobra.Command, args []string) error {
		argDomain := args[0]
		argEmails := ParseEmailArgs(args[1:])
		if len(argEmails) != len(args)-1 {
			return fmt.Errorf("invalid email arguments")
		}

		runner := db.TxForEachRunner[utils.EmailAddress]{
			Items: argEmails,
			Exec: func(tx *sql.Tx, item utils.EmailAddress) error {
				return db.DomainsCatchallTargets(tx).Restore(argDomain, item)
			},
			ItemString:     func(item utils.EmailAddress) string { return "@" + argDomain + " -> " + item.String() },
			FailureMessage: "failed to restore catchall target",
			SuccessMessage: "Successfully restored catchall target",
		}

		runner.Run()
		return nil
	},
}
View Source
var RestoreDomainsCmd = &cobra.Command{
	Use:     "domains <fqdn> [<fqdn>...]",
	Aliases: []string{"domain"},
	Short:   "Restores soft-deleted domains",
	Long:    "Restores soft-deleted domains.",
	Args:    cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		argDomains := ParseDomainFQDNArgs(args)
		if len(argDomains) != len(args) {
			return fmt.Errorf("invalid domain arguments")
		}

		runner := db.TxForEachRunner[string]{
			Items: argDomains,
			Exec: func(tx *sql.Tx, item string) error {
				return db.Domains(tx).Restore(item)
			},
			ItemString:     func(item string) string { return item },
			FailureMessage: "failed to restore domain",
			SuccessMessage: "Successfully restored domain",
		}

		runner.Run()
		return nil
	},
}
View Source
var RestoreMailboxesCmd = &cobra.Command{
	Use:     "mailboxes <email> [<email>...]",
	Aliases: []string{"mailbox"},
	Short:   "Restores soft-deleted mailboxes",
	Long:    "Restores soft-deleted mailboxes.",
	Args:    cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		argEmails := ParseEmailArgs(args)
		if len(argEmails) != len(args) {
			return fmt.Errorf("invalid email arguments")
		}

		runner := db.TxForEachRunner[utils.EmailAddress]{
			Items: argEmails,
			Exec: func(tx *sql.Tx, item utils.EmailAddress) error {
				return db.Mailboxes(tx).Restore(item)
			},
			ItemString:     func(item utils.EmailAddress) string { return item.String() },
			FailureMessage: "failed to restore mailbox",
			SuccessMessage: "Successfully restored mailbox",
		}

		runner.Run()
		return nil
	},
}
View Source
var RestoreRecipientsRelayedCmd = &cobra.Command{
	Use:     "recipients-relayed <email> [<email>...]",
	Aliases: []string{"recipient-relayed", "relayed-recipients", "relayed-recipient", "relayed"},
	Short:   "Restores soft-deleted relayed recipients",
	Long:    "Restores soft-deleted relayed recipients.",
	Args:    cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		argEmails := ParseEmailArgs(args)
		if len(argEmails) != len(args) {
			return fmt.Errorf("invalid email arguments")
		}

		runner := db.TxForEachRunner[utils.EmailAddress]{
			Items: argEmails,
			Exec: func(tx *sql.Tx, item utils.EmailAddress) error {
				return db.RecipientsRelayed(tx).Restore(item)
			},
			ItemString:     func(item utils.EmailAddress) string { return item.String() },
			FailureMessage: "failed to restore relayed recipient",
			SuccessMessage: "Successfully restored relayed recipient",
		}

		runner.Run()
		return nil
	},
}
View Source
var RestoreRemoteSendGrantsCmd = &cobra.Command{
	Use:   "send-grant <remote-name> <email> [<email>...]",
	Short: "Restores soft-deleted send grants for a remote",
	Long:  "Restores soft-deleted send grants for a remote.",
	Args:  cobra.MinimumNArgs(2),
	RunE: func(cmd *cobra.Command, args []string) error {
		argRemoteName := args[0]
		argEmails := ParseEmailOrWildcardArgs(args[1:])
		if len(argEmails) != len(args[1:]) {
			return fmt.Errorf("invalid email or domain argument")
		}

		runner := db.TxForEachRunner[utils.EmailAddressOrWildcard]{
			Items: argEmails,
			Exec: func(tx *sql.Tx, item utils.EmailAddressOrWildcard) error {
				return db.RemotesSendGrants(tx).Restore(argRemoteName, item)
			},
			ItemString:     func(item utils.EmailAddressOrWildcard) string { return argRemoteName + " -> " + item.String() },
			FailureMessage: "failed to restore send grant",
			SuccessMessage: "Successfully restored send grant",
		}

		runner.Run()
		return nil
	},
}
View Source
var RestoreRemotesCmd = &cobra.Command{
	Use:     "remotes <name> [<name>...]",
	Aliases: []string{"remote"},
	Short:   "Restores soft-deleted remotes",
	Long:    "Restores soft-deleted remotes.",
	Args:    cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		runner := db.TxForEachRunner[string]{
			Items: args,
			Exec: func(tx *sql.Tx, item string) error {
				return db.Remotes(tx).Restore(item)
			},
			ItemString:     func(item string) string { return item },
			FailureMessage: "failed to restore remote",
			SuccessMessage: "Successfully restored remote",
		}

		runner.Run()
		return nil
	},
}
View Source
var RestoreTransportsCmd = &cobra.Command{
	Use:     "transports <name> [<name>...]",
	Aliases: []string{"transport"},
	Short:   "Restores soft-deleted transports",
	Long:    "Restores soft-deleted transports.",
	Args:    cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		runner := db.TxForEachRunner[string]{
			Items: args,
			Exec: func(tx *sql.Tx, item string) error {
				return db.Transports(tx).Restore(item)
			},
			ItemString:     func(item string) string { return item },
			FailureMessage: "failed to restore transport",
			SuccessMessage: "Successfully restored transport",
		}

		runner.Run()
		return nil
	},
}
View Source
var SchemaCmd = &cobra.Command{
	Use:   "schema",
	Short: "Manage database schema",
	Long:  `Manage database schema setup and upgrades`,
}
View Source
var SchemaDropUserCmd = &cobra.Command{
	Use:   "drop-user [username]",
	Short: "Drop an application-specific database user",
	Args:  cobra.MaximumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		flagNameFile, _ := cmd.Flags().GetString("name-file")
		flagEnvPrefix, _ := cmd.Flags().GetString("env-prefix")

		argUsername := ""
		if len(args) > 0 {
			argUsername = args[0]
		}

		userName, err := ResolveDynamicSourceArg(argUsername, flagNameFile, fmt.Sprintf("%s_NAME", flagEnvPrefix))
		if err != nil {
			utils.PrintError(fmt.Errorf("failed to resolve username: %w", err))
			return nil
		}

		dbConn, err := db.Connect()
		if err != nil {
			utils.PrintErrorWithMessage("Failed to connect to database", err)
			return nil
		}
		defer dbConn.Close()

		dbConfig := db.GetConfig()

		err = schema.DropUser(dbConn, dbConfig.DBName, dbConfig.User, userName)
		if err != nil {
			utils.PrintErrorWithMessage("Failed to drop database user", err)
			return nil
		}

		utils.PrintSuccess("Database user dropped successfully")
		return nil
	},
}
View Source
var SchemaEnsureUserCmd = &cobra.Command{
	Use:   "ensure-user [username]",
	Short: "Create/sync application-specific database user with limited permissions",
	Args:  cobra.MaximumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		flagType, _ := cmd.Flags().GetString("type")
		flagPasswordPrompt, _ := cmd.Flags().GetBool("password")
		flagPasswordStdin, _ := cmd.Flags().GetBool("password-stdin")
		flagTypeFile, _ := cmd.Flags().GetString("type-file")
		flagNameFile, _ := cmd.Flags().GetString("name-file")
		flagPasswordFile, _ := cmd.Flags().GetString("password-file")
		flagEnvPrefix, _ := cmd.Flags().GetString("env-prefix")

		argUsername := ""
		if len(args) > 0 {
			argUsername = args[0]
		}

		userType, err := ResolveDynamicSourceArg(flagType, flagTypeFile, fmt.Sprintf("%s_TYPE", flagEnvPrefix))
		if err != nil {
			utils.PrintError(fmt.Errorf("failed to retreive user type: %w", err))
			return nil
		}

		userName, err := ResolveDynamicSourceArg(argUsername, flagNameFile, fmt.Sprintf("%s_NAME", flagEnvPrefix))
		if err != nil {
			utils.PrintError(fmt.Errorf("failed to retreive username: %w", err))
			return nil
		}

		var password string
		if flagPasswordPrompt || flagPasswordStdin {
			password, err = ReadPassword(flagPasswordStdin)
			if err != nil {
				utils.PrintErrorWithMessage("failed to read password", err)
				return nil
			}
		} else {
			password, err = ResolveDynamicSourceArg("", flagPasswordFile, fmt.Sprintf("%s_PASSWORD", flagEnvPrefix))
			if err != nil {
				utils.PrintError(fmt.Errorf("failed to retreive password: %w", err))
				return nil
			}
		}

		dbConn, err := db.Connect()
		if err != nil {
			utils.PrintErrorWithMessage("Failed to connect to database", err)
			return nil
		}
		defer dbConn.Close()

		dbConfig := db.GetConfig()

		err = schema.EnsureUser(dbConn, dbConfig.DBName, userType, schema.User{
			Name:     userName,
			Password: password,
		})
		if err != nil {
			utils.PrintErrorWithMessage("Failed to sync database user", err)
			return nil
		}

		utils.PrintSuccess("Database user synced successfully")
		return nil
	},
}
View Source
var SchemaPurgeCmd = &cobra.Command{
	Use:   "purge",
	Short: "Purge all schema and data from the database",
	Long:  "Purge the entire database by dropping all tables, sequences, functions, and types.",
	RunE: func(cmd *cobra.Command, args []string) error {

		confirm, err := cmd.Flags().GetBool("confirm")
		if err != nil {
			utils.PrintErrorWithMessage("Failed to read confirmation flag", err)
			return nil
		}
		if !confirm {
			fmt.Printf("This will %s:\n", utils.RedStyle.Render("PERMANENTLY DELETE"))
			fmt.Println("  • All tables and their data")
			fmt.Println("  • All sequences")
			fmt.Println("  • All functions")
			fmt.Println("  • All custom types")
			fmt.Println("To proceed, use '--confirm'")
			os.Exit(1)
		}

		dbConn, err := db.Connect()
		if err != nil {
			utils.PrintErrorWithMessage("Failed to connect to database", err)
			return nil
		}
		defer dbConn.Close()

		fmt.Println("Purging database...")

		err = schema.Purge(dbConn)
		if err != nil {
			utils.PrintErrorWithMessage("Failed to purge database", err)
			return nil
		}

		utils.PrintSuccess("Schema purge completed successfully")
		return nil
	},
}
View Source
var SchemaStatusCmd = &cobra.Command{
	Use:   "status",
	Short: "Show current schema version",
	RunE: func(cmd *cobra.Command, args []string) error {
		dbConn, err := db.Connect()
		if err != nil {
			utils.PrintErrorWithMessage("failed to connect to database", err)
			return nil
		}
		defer dbConn.Close()

		currentVersion, err := schema.GetCurrentVersion(dbConn)
		if err != nil {
			utils.PrintErrorWithMessage("Failed to get current schema version", err)
			return nil
		}

		latestVersion, err := schema.GetLatestAvailableVersion()
		if err != nil {
			utils.PrintErrorWithMessage("Failed to get latest available schema version", err)
			return nil
		}

		if currentVersion > 0 && currentVersion < latestVersion {
			fmt.Println(utils.YellowStyle.Bold(true).Render("Schema upgrade available"))
			fmt.Printf("Installed schema: v%d\n", currentVersion)
			fmt.Printf("Available schema: v%d\n", latestVersion)
		} else if currentVersion > 0 && currentVersion == latestVersion {
			utils.PrintSuccess("Schema is up to date")
			fmt.Printf("Installed schema: v%d\n", currentVersion)
		} else {
			fmt.Println(utils.YellowStyle.Bold(true).Render("Schema is not installed"))
			fmt.Printf("Available schema: v%d\n", latestVersion)
		}
		return nil
	},
}
View Source
var SchemaUpgradeCmd = &cobra.Command{
	Use:   "upgrade",
	Short: "Upgrade database schema to latest version",
	Long:  `Upgrade the database schema to the latest available version by applying pending migrations.`,
	RunE: func(cmd *cobra.Command, args []string) error {
		latestVersion, err := schema.GetLatestAvailableVersion()
		if err != nil {
			utils.PrintErrorWithMessage("Failed to get latest available schema version", err)
			return nil
		}

		dbConn, err := db.Connect()
		if err != nil {
			utils.PrintErrorWithMessage("Failed to connect to database", err)
			return nil
		}
		defer dbConn.Close()

		currentVersion, err := schema.GetCurrentVersion(dbConn)
		if err != nil {
			utils.PrintErrorWithMessage("Failed to get current schema version", err)
			return nil
		}

		if currentVersion >= latestVersion {
			utils.PrintSuccess("Schema is already up to date")
			return nil
		}

		if currentVersion == 0 {
			fmt.Printf("Installing schema v%d...\n", latestVersion)
		} else {
			fmt.Printf("Upgrading schema: v%d -> v%d...\n", currentVersion, latestVersion)
		}

		err = schema.Upgrade(dbConn, latestVersion)
		if err != nil {
			utils.PrintErrorWithMessage("Failed to upgrade schema", err)
			return nil
		}

		utils.PrintSuccess("Schema upgrade completed successfully")
		return nil
	},
}

Functions

func DescribeCanonicalAddress

func DescribeCanonicalAddress(r sq.BaseRunner, email utils.EmailAddress) (bool, error)

func Execute

func Execute()

func ParseDomainFQDNArgs

func ParseDomainFQDNArgs(args []string) []string

func ParseEmailArgs

func ParseEmailArgs(args []string) []utils.EmailAddress

func ParseEmailOrWildcardArgs

func ParseEmailOrWildcardArgs(args []string) []utils.EmailAddressOrWildcard

func PasswordHash added in v0.1.3

func PasswordHash(password, method string, options string) (string, error)

func ReadFileValue

func ReadFileValue(path string) (string, error)

func ReadPassword

func ReadPassword(fromStdin bool) (string, error)

func ReadPasswordHashed

func ReadPasswordHashed(method string, options string, fromStdin bool) (string, error)

func RenderMetaSection

func RenderMetaSection(createdAt time.Time, updatedAt time.Time, deletedAt *time.Time) string

RenderMetaSection returns a standardized table showing created/updated/deleted timestamps.

func ResolveDynamicSourceArg

func ResolveDynamicSourceArg(directValue, filePath, envVarName string) (string, error)

Types

This section is empty.

Source Files

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL