cabinet/cmd/cabinet/command.go

200 lines
4.3 KiB
Go

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
}