add more commands and stuff
This commit is contained in:
parent
970990123e
commit
1931e85213
|
@ -8,8 +8,8 @@ linters:
|
||||||
- contextcheck
|
- contextcheck
|
||||||
- cyclop
|
- cyclop
|
||||||
- decorder
|
- decorder
|
||||||
- dogsled
|
# - dogsled
|
||||||
- dupl
|
# - dupl
|
||||||
- durationcheck
|
- durationcheck
|
||||||
- errcheck
|
- errcheck
|
||||||
- errchkjson
|
- errchkjson
|
||||||
|
|
4
Makefile
4
Makefile
|
@ -11,8 +11,10 @@ install: install-frontend install-backend
|
||||||
frontend:
|
frontend:
|
||||||
VITE_API_URL=/api npm run --prefix frontend build
|
VITE_API_URL=/api npm run --prefix frontend build
|
||||||
|
|
||||||
|
VERSION=$(shell git describe --tags --abbrev=0)
|
||||||
|
|
||||||
backend:
|
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
|
all: frontend backend
|
||||||
|
|
||||||
|
|
129
cmd/main.go
129
cmd/main.go
|
@ -3,6 +3,7 @@ package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
@ -12,12 +13,28 @@ import (
|
||||||
devcmd "git.maronato.dev/maronato/goshort/cmd/dev"
|
devcmd "git.maronato.dev/maronato/goshort/cmd/dev"
|
||||||
healthcheckcmd "git.maronato.dev/maronato/goshort/cmd/healthcheck"
|
healthcheckcmd "git.maronato.dev/maronato/goshort/cmd/healthcheck"
|
||||||
servecmd "git.maronato.dev/maronato/goshort/cmd/serve"
|
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/config"
|
||||||
"git.maronato.dev/maronato/goshort/internal/util/logging"
|
"git.maronato.dev/maronato/goshort/internal/util/logging"
|
||||||
"github.com/peterbourgon/ff/v3/ffcli"
|
"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
|
// Create the application-wide context, and
|
||||||
// implement graceful shutdown.
|
// implement graceful shutdown.
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
@ -26,11 +43,12 @@ func Run() {
|
||||||
trapSignalsCrossPlatform(cancel)
|
trapSignalsCrossPlatform(cancel)
|
||||||
|
|
||||||
// Create the root command and register subcommands.
|
// Create the root command and register subcommands.
|
||||||
rootCmd, cfg := newRootCmd()
|
rootCmd, cfg := newRootCmd(version)
|
||||||
rootCmd.Subcommands = []*ffcli.Command{
|
rootCmd.Subcommands = append(
|
||||||
|
rootCmd.Subcommands,
|
||||||
servecmd.New(cfg),
|
servecmd.New(cfg),
|
||||||
healthcheckcmd.New(cfg),
|
healthcheckcmd.New(cfg),
|
||||||
}
|
)
|
||||||
|
|
||||||
// Look for the env ENV_DOCKER=true to disable the dev command
|
// Look for the env ENV_DOCKER=true to disable the dev command
|
||||||
// since the docker image won't have node installed.
|
// since the docker image won't have node installed.
|
||||||
|
@ -40,14 +58,20 @@ func Run() {
|
||||||
|
|
||||||
// Parse the command-line arguments.
|
// Parse the command-line arguments.
|
||||||
if err := rootCmd.Parse(os.Args[1:]); err != nil {
|
if err := rootCmd.Parse(os.Args[1:]); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
if errors.Is(err, flag.ErrHelp) {
|
||||||
os.Exit(1)
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("%w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate config
|
// Validate config
|
||||||
if err := config.Validate(cfg); err != nil {
|
if err := config.Validate(cfg); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
if errors.Is(err, flag.ErrHelp) {
|
||||||
os.Exit(1)
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("%w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create system logger
|
// Create system logger
|
||||||
|
@ -56,26 +80,80 @@ func Run() {
|
||||||
|
|
||||||
// Run the command.
|
// Run the command.
|
||||||
if err := rootCmd.Run(ctx); err != nil {
|
if err := rootCmd.Run(ctx); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
if errors.Is(err, flag.ErrHelp) {
|
||||||
os.Exit(1)
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("%w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// newRootCmd constructs a root command from the provided options.
|
// 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
|
var cfg config.Config
|
||||||
|
|
||||||
fs := flag.NewFlagSet("goshort", flag.ContinueOnError)
|
fs := flag.NewFlagSet("goshort", flag.ContinueOnError)
|
||||||
|
|
||||||
return &ffcli.Command{
|
cmd := &ffcli.Command{
|
||||||
Name: "goshort",
|
Name: "goshort",
|
||||||
ShortUsage: "goshort <subcommand> [flags]",
|
ShortUsage: "goshort <subcommand> [flags]",
|
||||||
ShortHelp: "goshort is a tiny URL shortener",
|
ShortHelp: "goshort is a tiny URL shortener",
|
||||||
FlagSet: fs,
|
FlagSet: fs,
|
||||||
Exec: func(ctx context.Context, args []string) error {
|
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
|
// 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
|
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() {
|
func main() {
|
||||||
// Run root command
|
// 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
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -127,3 +128,16 @@ func Validate(cfg *Config) error {
|
||||||
|
|
||||||
return nil
|
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,
|
*apiserver.APIHandler,
|
||||||
*shortservice.ShortService,
|
*shortservice.ShortService,
|
||||||
*userservice.UserService,
|
*userservice.UserService,
|
||||||
*tokenservice.TokenService,
|
*tokenservice.TokenService, //nolint:unparam // We need to return this for testing purposes
|
||||||
*shortlogservice.ShortLogService,
|
*shortlogservice.ShortLogService, //nolint:unparam // We need to return this for testing purposes
|
||||||
) {
|
) {
|
||||||
str := newStorage(ctx, cfg)
|
str := newStorage(ctx, cfg)
|
||||||
|
|
||||||
|
@ -164,7 +164,7 @@ func TestEndpointCreateShort(t *testing.T) {
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
ctx context.Context
|
ctx context.Context //nolint:containedctx // We need to pass the context to test user presence
|
||||||
body io.Reader
|
body io.Reader
|
||||||
responseCode int
|
responseCode int
|
||||||
shortName string
|
shortName string
|
||||||
|
@ -228,7 +228,10 @@ func TestEndpointCreateShort(t *testing.T) {
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
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)
|
server.ServeHTTP(res, req)
|
||||||
|
|
||||||
assert.Equal(t, tt.responseCode, res.Code)
|
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
|
var newID string
|
||||||
|
|
||||||
maxIters := 10
|
maxIters := 10
|
||||||
|
|
||||||
for {
|
for {
|
||||||
// Make sure we don't get stuck in an infinite loop
|
// Make sure we don't get stuck in an infinite loop
|
||||||
maxIters--
|
maxIters--
|
||||||
|
|
Loading…
Reference in New Issue
Block a user