package main import ( "context" "fmt" "net/http" "os" "time" "go.jolheiser.com/cabinet/internal/gc" "go.jolheiser.com/cabinet/internal/router" "go.jolheiser.com/cabinet/internal/workspace" "github.com/AlecAivazis/survey/v2" "github.com/rs/zerolog" "github.com/rs/zerolog/log" ) type serveOpts struct { jsonMode bool debugMode bool maxFileSize int maxDiskSize int workspacePath string gcInterval time.Duration requestPerMinute int sizePerMinute int burstSize int memPerRequest int port int domain string } var serveCmd = func(opts *serveOpts) func(context.Context, []string) error { return func(_ context.Context, _ []string) error { if opts.jsonMode { log.Logger = zerolog.New(os.Stderr).With().Timestamp().Logger() } if opts.debugMode { zerolog.SetGlobalLevel(zerolog.DebugLevel) } if opts.domain == "" { opts.domain = fmt.Sprintf("http://localhost:%d", opts.port) } ws, err := workspace.New(opts.workspacePath) if err != nil { log.Fatal().Err(err).Msg("could not open workspace") } go gc.Start(ws, opts.maxDiskSize, opts.gcInterval) r := router.New(opts.domain, ws, router.NewLimit(opts.requestPerMinute, opts.sizePerMinute, opts.burstSize, opts.memPerRequest)) portStr := fmt.Sprintf(":%d", opts.port) log.Info().Msgf("Listening at %s", opts.domain) if err := http.ListenAndServe(portStr, r); err != nil { log.Err(err).Msg("could not start HTTP server") } return nil } } type tokenOpts struct { token string perm workspace.TokenPermission desc string workspacePath string delete bool } var tokenCmd = func(opts *tokenOpts) func(context.Context, []string) error { return func(_ context.Context, _ []string) error { ws, err := workspace.New(opts.workspacePath) if err != nil { log.Fatal().Err(err).Msg("could not open workspace") } if opts.delete { return deleteTokens(ws, opts) } return addToken(ws, opts) } } func addToken(ws *workspace.Workspace, opts *tokenOpts) error { token := opts.token if token == "" { r, err := randToken() if err != nil { return err } q := &survey.Input{ Message: "Token", Default: r, Help: "Input token or use randomly generated", } if err := survey.AskOne(q, &token, survey.WithValidator(survey.Required)); err != nil { return err } } perm := opts.perm if perm == 0 { q := &survey.Select{ Message: "Permission", Default: "all", Help: "Token permission", Options: []string{ "all", "redirect", "file", }, } var p string err := survey.AskOne(q, &p, survey.WithValidator(survey.Required)) if err != nil { return err } perm, err = workspace.ParseTokenPermission(p) if err != nil { return err } } desc := opts.desc if desc == "" { q := &survey.Input{ Message: "Description", Default: "", Help: "Description of the token usage/user", } if err := survey.AskOne(q, &desc, survey.WithValidator(survey.Required)); err != nil { return err } } if err := ws.AddToken(workspace.Token{ Key: token, Permission: perm, Description: desc, }); err != nil { return err } log.Info().Msg(token) return nil } func deleteTokens(ws *workspace.Workspace, opts *tokenOpts) error { if opts.token != "" { if err := ws.DeleteTokens(opts.token); err != nil { return err } log.Info().Msgf("Successfully deleted %q", opts.token) return nil } tokens, err := ws.Tokens() if err != nil { return err } if len(tokens) == 0 { log.Info().Msg("This instance has no tokens") return nil } var tokenSlice []string tokenMap := make(map[string]string) for _, token := range tokens { k := fmt.Sprintf("%s (%s) -- %s", token.Key, token.Permission, token.Description) tokenSlice = append(tokenSlice, k) tokenMap[k] = token.Key } q := &survey.MultiSelect{ Message: "Token(s) to delete", Options: tokenSlice, Help: "Select any tokens you wish to remove", } var selections []string if err := survey.AskOne(q, &selections); err != nil { return err } var keys []string for _, selection := range selections { keys = append(keys, tokenMap[selection]) } if err := ws.DeleteTokens(keys...); err != nil { return err } log.Info().Msgf("Successfully deleted %d token(s)", len(keys)) return nil }