diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..357bec1 --- /dev/null +++ b/Makefile @@ -0,0 +1,3 @@ +build: gordio +gordio: + go build -o gordio ./cmd/gordio/ diff --git a/cmd/gordio/main.go b/cmd/gordio/main.go new file mode 100644 index 0000000..b741743 --- /dev/null +++ b/cmd/gordio/main.go @@ -0,0 +1,34 @@ +package main + +import ( + "os" + + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" + + "dynatron.me/x/stillbox/pkg/gordio" + "dynatron.me/x/stillbox/pkg/gordio/config" + + "github.com/spf13/cobra" +) + +func main() { + log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) + + rootCmd := cobra.Command{ + Use: gordio.AppName, + } + + cfg, err := config.ReadConfig() + if err != nil { + log.Fatal().Err(err).Msg("Config read failed") + } + + rootCmd.AddCommand(gordio.Command(cfg)) + + err = rootCmd.Execute() + if err != nil { + log.Fatal().Err(err).Msg("Dying") + } + +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..98e5d1d --- /dev/null +++ b/go.mod @@ -0,0 +1,28 @@ +module dynatron.me/x/stillbox + +go 1.21.12 + +require ( + github.com/rs/zerolog v1.33.0 + github.com/spf13/cobra v1.8.1 +) + +require ( + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect + github.com/go-chi/chi/v5 v5.1.0 // indirect + github.com/go-chi/jwtauth/v5 v5.3.1 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/lestrrat-go/blackmagic v1.0.2 // indirect + github.com/lestrrat-go/httpcc v1.0.1 // indirect + github.com/lestrrat-go/httprc v1.0.4 // indirect + github.com/lestrrat-go/iter v1.0.2 // indirect + github.com/lestrrat-go/jwx/v2 v2.0.20 // indirect + github.com/lestrrat-go/option v1.0.1 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/segmentio/asm v1.2.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + golang.org/x/crypto v0.21.0 // indirect + golang.org/x/sys v0.18.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..f5b2e19 --- /dev/null +++ b/go.sum @@ -0,0 +1,57 @@ +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= +github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-chi/jwtauth/v5 v5.3.1 h1:1ePWrjVctvp1tyBq5b/2ER8Th/+RbYc7x4qNsc5rh5A= +github.com/go-chi/jwtauth/v5 v5.3.1/go.mod h1:6Fl2RRmWXs3tJYE1IQGX81FsPoGqDwq9c15j52R5q80= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k= +github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= +github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= +github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= +github.com/lestrrat-go/httprc v1.0.4 h1:bAZymwoZQb+Oq8MEbyipag7iSq6YIga8Wj6GOiJGdI8= +github.com/lestrrat-go/httprc v1.0.4/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= +github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI= +github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= +github.com/lestrrat-go/jwx/v2 v2.0.20 h1:sAgXuWS/t8ykxS9Bi2Qtn5Qhpakw1wrcjxChudjolCc= +github.com/lestrrat-go/jwx/v2 v2.0.20/go.mod h1:UlCSmKqw+agm5BsOBfEAbTvKsEApaGNqHAEUTv5PJC4= +github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= +github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= +github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= +github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/common/common.go b/internal/common/common.go new file mode 100644 index 0000000..abd926d --- /dev/null +++ b/internal/common/common.go @@ -0,0 +1,27 @@ +package common + +import ( + "github.com/spf13/cobra" +) + +type cmdOptions interface { + Options(*cobra.Command, []string) error + Execute() error +} + +func RunE(c cmdOptions) func(cmd *cobra.Command, args []string) error { + return func(cmd *cobra.Command, args []string) error { + err := c.Options(cmd, args) + if err != nil { + cmd.SilenceUsage = true + return err + } + + err = c.Execute() + if err != nil { + cmd.SilenceUsage = true + } + + return err + } +} diff --git a/pkg/gordio/config/config.go b/pkg/gordio/config/config.go new file mode 100644 index 0000000..ce17925 --- /dev/null +++ b/pkg/gordio/config/config.go @@ -0,0 +1,15 @@ +package config + +type Config struct { + DB string `yaml:"db"` + JWTSecret string `yaml:"jwtsecret"` + Listen string `yaml:"listen"` + Public bool `yaml:"public"` +} + +func ReadConfig() (*Config, error) { + return &Config{ + JWTSecret: "s3c4r", + Listen: ":3050", + }, nil +} diff --git a/pkg/gordio/gordio.go b/pkg/gordio/gordio.go new file mode 100644 index 0000000..693c395 --- /dev/null +++ b/pkg/gordio/gordio.go @@ -0,0 +1,50 @@ +package gordio + +import ( + "dynatron.me/x/stillbox/internal/common" + "dynatron.me/x/stillbox/pkg/gordio/config" + "dynatron.me/x/stillbox/pkg/gordio/server" + + "github.com/spf13/cobra" +) + +const AppName = "gordio" + +type ServeOptions struct { + cfg *config.Config +} + +func Command(cfg *config.Config) *cobra.Command { + opts := makeOptions(cfg) + serveCmd := &cobra.Command{ + Use: "serve", + Short: "starts the" + AppName + " server", + RunE: common.RunE(opts), + } + + return serveCmd +} + +func makeOptions(cfg *config.Config) *ServeOptions { + return &ServeOptions{ + cfg: cfg, + } +} + +func (o *ServeOptions) Options(_ *cobra.Command, args []string) error { + return nil +} + +func (o *ServeOptions) Execute() error { + srv, err := server.New(o.cfg) + if err != nil { + return err + } + + err = srv.Go() + if err != nil { + return err + } + + return nil +} diff --git a/pkg/gordio/server/routes.go b/pkg/gordio/server/routes.go new file mode 100644 index 0000000..70e35c8 --- /dev/null +++ b/pkg/gordio/server/routes.go @@ -0,0 +1,44 @@ +package server + +import ( + "fmt" + "net/http" + + "github.com/go-chi/chi/v5" + "github.com/go-chi/jwtauth/v5" +) + +func (s *Server) setupRoutes() { + r := s.r + + r.Group(func(r chi.Router) { + r.Use(jwtauth.Verifier(s.jwt)) + r.Use(jwtauth.Authenticator(s.jwt)) + + }) + + r.Group(func (r chi.Router) { + // public routes + }) + + r.Group(func(r chi.Router) { + r.Use(jwtauth.Verifier(s.jwt)) + + // optional auth routes + + r.Get("/", s.routeIndex) + }) +} + +func (s *Server) routeIndex(w http.ResponseWriter, r *http.Request) { + if s.Authenticated(r) { + w.Write([]byte(fmt.Sprint("Welcome\n"))) + // error + } +} + +func (s *Server) Authenticated(r *http.Request) bool { + // TODO: check IP against ACL, or conf.Public, and against map of routes + tok, _, _ := jwtauth.FromContext(r.Context()) + return tok != nil +} diff --git a/pkg/gordio/server/server.go b/pkg/gordio/server/server.go new file mode 100644 index 0000000..d3023c7 --- /dev/null +++ b/pkg/gordio/server/server.go @@ -0,0 +1,44 @@ +package server + +import ( + "log" + "net/http" + + "dynatron.me/x/stillbox/pkg/gordio/config" + "github.com/go-chi/chi/v5" + "github.com/go-chi/chi/v5/middleware" + "github.com/go-chi/jwtauth/v5" +) + +type Server struct { + conf *config.Config + r *chi.Mux + jwt *jwtauth.JWTAuth +} + +func New(cfg *config.Config) (*Server, error) { + r := chi.NewRouter() + srv := &Server{ + conf: cfg, + r: r, + jwt: jwtauth.New("HS256", []byte(cfg.JWTSecret), nil), + } + _, tokenString, err := srv.jwt.Encode(map[string]interface{}{"user_id": 123}) + if err != nil { + panic(err) + } + log.Printf("DEBUG token is %s", tokenString) + r.Use(middleware.RequestID) + r.Use(middleware.RealIP) + r.Use(middleware.Logger) + r.Use(middleware.Recoverer) + srv.setupRoutes() + + return srv, nil +} + +func (s *Server) Go() error { + http.ListenAndServe(s.conf.Listen, s.r) + return nil +} +