this repo has no description smallweb.run
smallweb
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

switch back to http-01 challenge, drop acmedns

pomdtr 04968ef5 b4f08b0c

+116 -92
+1
cmd/root.go
··· 157 157 rootCmd.AddCommand(NewCmdLogs()) 158 158 rootCmd.AddCommand(NewCmdLink()) 159 159 rootCmd.AddCommand(NewCmdConfig()) 160 + rootCmd.AddCommand(NewCmdSecrets()) 160 161 161 162 if env, ok := os.LookupEnv("SMALLWEB_DISABLED_COMMANDS"); ok { 162 163 disabledCommands := strings.Split(env, ",")
+104
cmd/secrets.go
··· 1 + package cmd 2 + 3 + import ( 4 + "bytes" 5 + "encoding/json" 6 + "fmt" 7 + "os" 8 + "path/filepath" 9 + 10 + "github.com/cli/go-gh/v2/pkg/tableprinter" 11 + "github.com/getsops/sops/v3/decrypt" 12 + "github.com/joho/godotenv" 13 + "github.com/mattn/go-isatty" 14 + "github.com/spf13/cobra" 15 + "golang.org/x/term" 16 + ) 17 + 18 + func NewCmdSecrets() *cobra.Command { 19 + var flags struct { 20 + json bool 21 + dotenv bool 22 + } 23 + 24 + cmd := &cobra.Command{ 25 + Use: "secrets [app]", 26 + Short: "Print app secrets", 27 + ValidArgsFunction: completeApp, 28 + Args: cobra.MaximumNArgs(1), 29 + RunE: func(cmd *cobra.Command, args []string) error { 30 + if len(args) == 0 { 31 + return fmt.Errorf("app name is required") 32 + } 33 + 34 + secretPath := filepath.Join(k.String("dir"), args[0], "secrets.enc.env") 35 + 36 + rawBytes, err := os.ReadFile(secretPath) 37 + if err != nil { 38 + return fmt.Errorf("could not read %s: %v", secretPath, err) 39 + } 40 + 41 + dotenvBytes, err := decrypt.Data(rawBytes, "dotenv") 42 + if err != nil { 43 + return fmt.Errorf("could not decrypt %s: %v", secretPath, err) 44 + } 45 + 46 + dotenv, err := godotenv.Parse(bytes.NewReader(dotenvBytes)) 47 + if err != nil { 48 + return fmt.Errorf("could not parse %s: %v", secretPath, err) 49 + } 50 + 51 + if flags.json { 52 + enc := json.NewEncoder(cmd.OutOrStdout()) 53 + enc.SetIndent("", " ") 54 + if err := enc.Encode(dotenv); err != nil { 55 + return fmt.Errorf("could not encode %s: %v", secretPath, err) 56 + } 57 + 58 + return nil 59 + } 60 + 61 + if flags.dotenv { 62 + dotenvBytes, err := godotenv.Marshal(dotenv) 63 + if err != nil { 64 + return fmt.Errorf("could not marshal %s: %v", secretPath, err) 65 + } 66 + 67 + fmt.Fprintln(cmd.OutOrStdout(), string(dotenvBytes)) 68 + return nil 69 + } 70 + 71 + var printer tableprinter.TablePrinter 72 + if isatty.IsTerminal(os.Stdout.Fd()) { 73 + width, _, err := term.GetSize(int(os.Stdout.Fd())) 74 + if err != nil { 75 + return fmt.Errorf("failed to get terminal size: %w", err) 76 + } 77 + 78 + printer = tableprinter.New(cmd.OutOrStdout(), true, width) 79 + } else { 80 + printer = tableprinter.New(cmd.OutOrStdout(), false, 0) 81 + } 82 + 83 + printer.AddHeader([]string{"Env", "Value"}) 84 + 85 + for key, value := range dotenv { 86 + printer.AddField(key) 87 + printer.AddField(value) 88 + printer.EndRow() 89 + } 90 + 91 + if err := printer.Render(); err != nil { 92 + return fmt.Errorf("failed to render table: %w", err) 93 + } 94 + 95 + return nil 96 + }, 97 + } 98 + 99 + cmd.Flags().BoolVar(&flags.json, "json", false, "Output as JSON") 100 + cmd.Flags().BoolVar(&flags.dotenv, "dotenv", false, "Output as dotenv") 101 + 102 + return cmd 103 + 104 + }
+10 -88
cmd/up.go
··· 3 3 import ( 4 4 "context" 5 5 "crypto/tls" 6 - "encoding/json" 7 6 "errors" 8 7 "fmt" 9 8 "io" ··· 22 21 23 22 _ "embed" 24 23 25 - "github.com/adrg/xdg" 26 24 "github.com/caddyserver/certmagic" 27 25 "github.com/charmbracelet/keygen" 28 26 "github.com/charmbracelet/ssh" 29 27 "github.com/charmbracelet/wish" 30 28 "github.com/creack/pty" 31 - "github.com/libdns/acmedns" 32 29 "github.com/picosh/pobj" 33 30 "github.com/pomdtr/smallweb/storage" 34 31 ··· 53 50 sshHostKey string 54 51 tlsCert string 55 52 tlsKey string 56 - acmdnsCreds string 57 53 onDemandTLS bool 58 54 } 59 55 ··· 107 103 workers: make(map[string]*worker.Worker), 108 104 }) 109 105 110 - if flags.acmdnsCreds != "" { 111 - credentialPath := filepath.Join(xdg.DataHome, "smallweb", "acmedns", "credentials.json") 112 - if _, err := os.Stat(credentialPath); err != nil { 113 - return fmt.Errorf("acme-dns file not found: %s", credentialPath) 114 - } 115 - 116 - configBytes, err := os.ReadFile(credentialPath) 117 - if err != nil { 118 - return fmt.Errorf("failed to read acme-dns file: %v", err) 119 - } 120 - 121 - var configs map[string]acmedns.DomainConfig 122 - if err := json.Unmarshal(configBytes, &configs); err != nil { 123 - return fmt.Errorf("failed to unmarshal acme-dns file: %v", err) 124 - } 125 - 126 - certmagic.DefaultACME.DNS01Solver = &certmagic.DNS01Solver{ 127 - DNSManager: certmagic.DNSManager{ 128 - DNSProvider: &acmedns.Provider{ 129 - Configs: configs, 130 - }, 131 - }, 132 - } 133 - 134 - if k.String("email") != "" { 135 - certmagic.DefaultACME.Email = k.String("email") 136 - } else { 137 - certmagic.DefaultACME.Email = fmt.Sprintf("smallweb@%s", k.String("domain")) 138 - } 139 - 140 - domains := []string{k.String("domain"), fmt.Sprintf("*.%s", k.String("domain"))} 141 - for customDomain, target := range k.StringMap("customDomains") { 142 - domains = append(domains, customDomain) 143 - if target == "*" { 144 - domains = append(domains, fmt.Sprintf("*.%s", customDomain)) 145 - } 146 - } 147 - 148 - tlsConfig, err := certmagic.TLS(domains) 149 - if err != nil { 150 - return fmt.Errorf("failed to get tls config: %v", err) 151 - } 152 - tlsConfig.NextProtos = append([]string{"h2", "http/1.1"}, tlsConfig.NextProtos...) 153 - 154 - addr := flags.addr 155 - if addr == "" { 156 - addr = ":443" 157 - } 158 - 159 - ln, err := getListener(addr, tlsConfig) 160 - if err != nil { 161 - return fmt.Errorf("failed to get listener: %v", err) 162 - } 163 - 164 - fmt.Fprintf(cmd.ErrOrStderr(), "Serving *.%s from %s on %s...\n", k.String("domain"), utils.AddTilde(k.String("dir")), addr) 165 - go http.Serve(ln, handler) 166 - } else if flags.onDemandTLS { 106 + if flags.onDemandTLS { 167 107 certmagic.Default.OnDemand = &certmagic.OnDemandConfig{ 168 108 DecisionFunc: func(ctx context.Context, name string) error { 169 109 domain := k.String("domain") ··· 181 121 return fmt.Errorf("invalid domain: %s", name) 182 122 } 183 123 184 - if parts[1] == domain { 124 + base := parts[1] 125 + if base == domain { 185 126 return nil 186 127 } 187 128 188 - if target, ok := customDomains[parts[1]]; ok && target == "*" { 129 + if target, ok := customDomains[base]; ok && target == "*" { 189 130 return nil 190 131 } 191 132 192 133 return fmt.Errorf("domain not found: %s", name) 193 134 }, 194 135 } 195 - 196 - tlsConfig, err := certmagic.TLS(nil) 197 - if err != nil { 198 - return fmt.Errorf("failed to get tls config: %v", err) 199 - } 200 - tlsConfig.NextProtos = append([]string{"h2", "http/1.1"}, tlsConfig.NextProtos...) 201 - 202 - addr := flags.addr 203 - if addr == "" { 204 - addr = ":443" 205 - } 206 - 207 - ln, err := getListener(addr, tlsConfig) 208 - if err != nil { 209 - return fmt.Errorf("failed to get listener: %v", err) 210 - } 211 - 212 - fmt.Fprintf(cmd.ErrOrStderr(), "Serving *.%s from %s on %s...\n", k.String("domain"), utils.AddTilde(k.String("dir")), addr) 213 - go http.Serve(ln, handler) 136 + fmt.Fprintf(cmd.ErrOrStderr(), "Serving *.%s from %s on %s...\n", k.String("domain"), utils.AddTilde(k.String("dir")), ":443") 137 + go certmagic.HTTPS(nil, handler) 214 138 } else if flags.tlsCert != "" && flags.tlsKey != "" { 215 139 cert, err := tls.LoadX509KeyPair(flags.tlsCert, flags.tlsKey) 216 140 if err != nil { ··· 404 328 ) 405 329 406 330 if err != nil { 407 - return fmt.Errorf("failed to create wish server: %v", err) 331 + return fmt.Errorf("failed to create ssh server: %v", err) 408 332 } 409 333 334 + fmt.Fprintln(cmd.ErrOrStderr(), "Starting ssh server on", flags.sshAddr) 410 335 if err = srv.ListenAndServe(); err != nil && !errors.Is(err, ssh.ErrServerClosed) { 411 - fmt.Fprintf(cmd.ErrOrStderr(), "failed to start wish server: %v\n", err) 336 + fmt.Fprintf(cmd.ErrOrStderr(), "failed to start ssh server: %v\n", err) 412 337 } 413 338 } 414 339 ··· 427 352 cmd.Flags().StringVar(&flags.tlsCert, "tls-cert", "", "tls certificate file") 428 353 cmd.Flags().StringVar(&flags.tlsKey, "tls-key", "", "tls key file") 429 354 cmd.Flags().BoolVar(&flags.cron, "cron", false, "enable cron jobs") 430 - cmd.Flags().StringVar(&flags.acmdnsCreds, "acmedns-credentials", "", "acmedns credentials file") 431 355 cmd.Flags().BoolVar(&flags.onDemandTLS, "on-demand-tls", false, "enable on-demand tls") 432 356 433 357 cmd.MarkFlagsRequiredTogether("tls-cert", "tls-key") 434 - cmd.MarkFlagsMutuallyExclusive("acmedns-credentials", "tls-cert") 435 - cmd.MarkFlagsMutuallyExclusive("acmedns-credentials", "tls-key") 436 358 cmd.MarkFlagsMutuallyExclusive("on-demand-tls", "tls-cert") 437 359 cmd.MarkFlagsMutuallyExclusive("on-demand-tls", "tls-key") 438 - cmd.MarkFlagsMutuallyExclusive("on-demand-tls", "acmedns-credentials") 360 + cmd.MarkFlagsMutuallyExclusive("on-demand-tls", "addr") 439 361 440 362 return cmd 441 363 }
+1 -2
go.mod
··· 24 24 github.com/charmbracelet/keygen v0.5.1 25 25 github.com/charmbracelet/ssh v0.0.0-20250128164007-98fd5ae11894 26 26 github.com/charmbracelet/wish v1.4.6 27 + github.com/creack/pty v1.1.21 27 28 github.com/fsnotify/fsnotify v1.8.0 28 29 github.com/getsops/sops/v3 v3.9.1 29 30 github.com/knadh/koanf/providers/confmap v0.1.0 30 31 github.com/knadh/koanf/providers/posflag v0.1.0 31 - github.com/libdns/acmedns v0.2.0 32 32 github.com/picosh/pobj v0.0.0-20250115045405-73c816ed76c2 33 33 github.com/picosh/send v0.0.0-20250121195737-daab6db117d5 34 34 github.com/robfig/cron/v3 v3.0.1 ··· 90 90 github.com/charmbracelet/x/termios v0.1.0 // indirect 91 91 github.com/cloudflare/circl v1.4.0 // indirect 92 92 github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect 93 - github.com/creack/pty v1.1.21 // indirect 94 93 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 95 94 github.com/dustin/go-humanize v1.0.1 // indirect 96 95 github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
-2
go.sum
··· 295 295 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 296 296 github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= 297 297 github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= 298 - github.com/libdns/acmedns v0.2.0 h1:zTXdHZwe3r2issdVRyqt5/4X2yHpiBVmFnTrwBA29ik= 299 - github.com/libdns/acmedns v0.2.0/go.mod h1:XlKHilQQK/IGHYY//vCb903PdG4Wc/XnDQzcMp2hV3g= 300 298 github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s= 301 299 github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= 302 300 github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=