add more commands and stuff
This commit is contained in:
parent
970990123e
commit
1931e85213
|
@ -8,8 +8,8 @@ linters:
|
|||
- contextcheck
|
||||
- cyclop
|
||||
- decorder
|
||||
- dogsled
|
||||
- dupl
|
||||
# - dogsled
|
||||
# - dupl
|
||||
- durationcheck
|
||||
- errcheck
|
||||
- errchkjson
|
||||
|
|
4
Makefile
4
Makefile
|
@ -11,8 +11,10 @@ install: install-frontend install-backend
|
|||
frontend:
|
||||
VITE_API_URL=/api npm run --prefix frontend build
|
||||
|
||||
VERSION=$(shell git describe --tags --abbrev=0)
|
||||
|
||||
backend:
|
||||
CGO_ENABLED=0 go build -o goshort goshort.go
|
||||
CGO_ENABLED=0 go build -v -ldflags="-X 'main.version=${VERSION}'" -o goshort goshort.go
|
||||
|
||||
all: frontend backend
|
||||
|
||||
|
|
129
cmd/main.go
129
cmd/main.go
|
@ -3,6 +3,7 @@ package cmd
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
@ -12,12 +13,28 @@ import (
|
|||
devcmd "git.maronato.dev/maronato/goshort/cmd/dev"
|
||||
healthcheckcmd "git.maronato.dev/maronato/goshort/cmd/healthcheck"
|
||||
servecmd "git.maronato.dev/maronato/goshort/cmd/serve"
|
||||
"git.maronato.dev/maronato/goshort/cmd/shared"
|
||||
"git.maronato.dev/maronato/goshort/internal/config"
|
||||
"git.maronato.dev/maronato/goshort/internal/util/logging"
|
||||
"github.com/peterbourgon/ff/v3/ffcli"
|
||||
)
|
||||
|
||||
func Run() {
|
||||
var (
|
||||
// ErrUnknownCommand is returned when the user tries to run an unknown command.
|
||||
ErrUnknownCommand = errors.New("unknown command")
|
||||
// ErrUnknownHelpTopic is returned when the user tries to run an unknown help topic.
|
||||
ErrUnknownHelpTopic = errors.New("unknown help topic")
|
||||
)
|
||||
|
||||
func NewUnknownCommand(cmd string) error {
|
||||
return fmt.Errorf("%q: %w\nRun 'goshort help' for usage", cmd, ErrUnknownCommand)
|
||||
}
|
||||
|
||||
func NewUnknownHelpTopic(topic string) error {
|
||||
return fmt.Errorf("%q: %w. Run 'goshort help'", topic, ErrUnknownHelpTopic)
|
||||
}
|
||||
|
||||
func Run(version string) error {
|
||||
// Create the application-wide context, and
|
||||
// implement graceful shutdown.
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
@ -26,11 +43,12 @@ func Run() {
|
|||
trapSignalsCrossPlatform(cancel)
|
||||
|
||||
// Create the root command and register subcommands.
|
||||
rootCmd, cfg := newRootCmd()
|
||||
rootCmd.Subcommands = []*ffcli.Command{
|
||||
rootCmd, cfg := newRootCmd(version)
|
||||
rootCmd.Subcommands = append(
|
||||
rootCmd.Subcommands,
|
||||
servecmd.New(cfg),
|
||||
healthcheckcmd.New(cfg),
|
||||
}
|
||||
)
|
||||
|
||||
// Look for the env ENV_DOCKER=true to disable the dev command
|
||||
// since the docker image won't have node installed.
|
||||
|
@ -40,14 +58,20 @@ func Run() {
|
|||
|
||||
// Parse the command-line arguments.
|
||||
if err := rootCmd.Parse(os.Args[1:]); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
os.Exit(1)
|
||||
if errors.Is(err, flag.ErrHelp) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("%w", err)
|
||||
}
|
||||
|
||||
// Validate config
|
||||
if err := config.Validate(cfg); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
os.Exit(1)
|
||||
if errors.Is(err, flag.ErrHelp) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("%w", err)
|
||||
}
|
||||
|
||||
// Create system logger
|
||||
|
@ -56,26 +80,80 @@ func Run() {
|
|||
|
||||
// Run the command.
|
||||
if err := rootCmd.Run(ctx); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
os.Exit(1)
|
||||
if errors.Is(err, flag.ErrHelp) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("%w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// newRootCmd constructs a root command from the provided options.
|
||||
func newRootCmd() (*ffcli.Command, *config.Config) {
|
||||
func newRootCmd(version string) (*ffcli.Command, *config.Config) {
|
||||
var cfg config.Config
|
||||
|
||||
fs := flag.NewFlagSet("goshort", flag.ContinueOnError)
|
||||
|
||||
return &ffcli.Command{
|
||||
cmd := &ffcli.Command{
|
||||
Name: "goshort",
|
||||
ShortUsage: "goshort <subcommand> [flags]",
|
||||
ShortHelp: "goshort is a tiny URL shortener",
|
||||
FlagSet: fs,
|
||||
Exec: func(ctx context.Context, args []string) error {
|
||||
return flag.ErrHelp
|
||||
// If the user didn't provide any subcommand, then
|
||||
// we'll just show the help message.
|
||||
if len(args) == 0 {
|
||||
return flag.ErrHelp
|
||||
}
|
||||
|
||||
return NewUnknownCommand(args[0])
|
||||
},
|
||||
}, &cfg
|
||||
}
|
||||
|
||||
cmd.Subcommands = []*ffcli.Command{
|
||||
// Register help command
|
||||
{
|
||||
Name: "help",
|
||||
ShortUsage: "help [command]",
|
||||
ShortHelp: "Show help for a command",
|
||||
Exec: func(ctx context.Context, args []string) error {
|
||||
if len(args) == 0 || args[0] == "help" {
|
||||
cmd.FlagSet.Usage()
|
||||
|
||||
fmt.Printf("\nUse 'goshort help <command>' for more information about that command.\n\n")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, c := range cmd.Subcommands {
|
||||
if c.Name == args[0] {
|
||||
return c.ParseAndRun(ctx, []string{"-h"}) //nolint:wrapcheck // We don't want to wrap the error here
|
||||
}
|
||||
}
|
||||
|
||||
return NewUnknownHelpTopic(args[0])
|
||||
},
|
||||
},
|
||||
|
||||
// Register version command
|
||||
{
|
||||
Name: "version",
|
||||
ShortUsage: "version",
|
||||
ShortHelp: "Show version information",
|
||||
Exec: func(ctx context.Context, args []string) error {
|
||||
fmt.Printf("goshort version %s\n", version)
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
|
||||
// Register config command
|
||||
NewConfigCmd(&cfg),
|
||||
}
|
||||
|
||||
return cmd, &cfg
|
||||
}
|
||||
|
||||
// https://github.com/caddyserver/caddy/blob/fbb0ecfa322aa7710a3448453fd3ae40f037b8d1/sigtrap.go#L37
|
||||
|
@ -100,3 +178,26 @@ func trapSignalsCrossPlatform(cancel context.CancelFunc) {
|
|||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func NewConfigCmd(cfg *config.Config) *ffcli.Command {
|
||||
// Create the flagset and register the flags.
|
||||
fs := flag.NewFlagSet("goshort serve", flag.ContinueOnError)
|
||||
|
||||
shared.RegisterBaseFlags(fs, cfg)
|
||||
shared.RegisterServerFlags(fs, cfg)
|
||||
fs.BoolVar(&cfg.Prod, "prod", config.DefaultProd, "run in production mode")
|
||||
|
||||
// Return the ffcli command.
|
||||
return &ffcli.Command{
|
||||
Name: "config",
|
||||
ShortUsage: "goshort config [flags]",
|
||||
ShortHelp: "Prints the current config",
|
||||
FlagSet: fs,
|
||||
Exec: func(ctx context.Context, args []string) error {
|
||||
cfg.PrettyPrint()
|
||||
|
||||
return nil
|
||||
},
|
||||
Options: shared.NewSharedCmdOptions(),
|
||||
}
|
||||
}
|
||||
|
|
16
goshort.go
16
goshort.go
|
@ -1,8 +1,20 @@
|
|||
package main
|
||||
|
||||
import "git.maronato.dev/maronato/goshort/cmd"
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"git.maronato.dev/maronato/goshort/cmd"
|
||||
)
|
||||
|
||||
// Version of the application
|
||||
var version = "dev"
|
||||
|
||||
func main() {
|
||||
// Run root command
|
||||
cmd.Run()
|
||||
if err := cmd.Run(version); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
//nolint:forbidigo // Allow printing for prettyprint
|
||||
package config
|
||||
|
||||
import (
|
||||
|
@ -127,3 +128,16 @@ func Validate(cfg *Config) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Config) PrettyPrint() {
|
||||
fmt.Println("Configuration:")
|
||||
fmt.Println(" - Production mode:", c.Prod)
|
||||
fmt.Println(" - Debug mode:", c.Debug)
|
||||
fmt.Println(" - Host:", c.Host)
|
||||
fmt.Println(" - Port:", c.Port)
|
||||
fmt.Println(" - UI Port:", c.UIPort)
|
||||
fmt.Println(" - Database type:", c.DBType)
|
||||
fmt.Println(" - Database URL:", c.DBURL)
|
||||
fmt.Println(" - Session duration:", c.SessionDuration)
|
||||
fmt.Println(" - Disable registration:", c.DisableRegistration)
|
||||
}
|
||||
|
|
|
@ -53,8 +53,8 @@ func setup(ctx context.Context, cfg *config.Config) (
|
|||
*apiserver.APIHandler,
|
||||
*shortservice.ShortService,
|
||||
*userservice.UserService,
|
||||
*tokenservice.TokenService,
|
||||
*shortlogservice.ShortLogService,
|
||||
*tokenservice.TokenService, //nolint:unparam // We need to return this for testing purposes
|
||||
*shortlogservice.ShortLogService, //nolint:unparam // We need to return this for testing purposes
|
||||
) {
|
||||
str := newStorage(ctx, cfg)
|
||||
|
||||
|
@ -164,7 +164,7 @@ func TestEndpointCreateShort(t *testing.T) {
|
|||
|
||||
tests := []struct {
|
||||
name string
|
||||
ctx context.Context
|
||||
ctx context.Context //nolint:containedctx // We need to pass the context to test user presence
|
||||
body io.Reader
|
||||
responseCode int
|
||||
shortName string
|
||||
|
@ -228,7 +228,10 @@ func TestEndpointCreateShort(t *testing.T) {
|
|||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
res, req := makeRequestResponse(tt.ctx, "POST", "/api/shorts", tt.body)
|
||||
ctx, cancel := context.WithTimeout(tt.ctx, config.RequestTimeout)
|
||||
defer cancel()
|
||||
|
||||
res, req := makeRequestResponse(ctx, "POST", "/api/shorts", tt.body)
|
||||
server.ServeHTTP(res, req)
|
||||
|
||||
assert.Equal(t, tt.responseCode, res.Code)
|
||||
|
|
|
@ -600,6 +600,7 @@ func createNewID(ctx context.Context, db bun.IDB, model interface{}) (string, er
|
|||
var newID string
|
||||
|
||||
maxIters := 10
|
||||
|
||||
for {
|
||||
// Make sure we don't get stuck in an infinite loop
|
||||
maxIters--
|
||||
|
|
Loading…
Reference in New Issue
Block a user