diff --git a/go.mod b/go.mod index 40fc0b0..93d1d0f 100644 --- a/go.mod +++ b/go.mod @@ -4,15 +4,22 @@ go 1.21.0 require ( github.com/dgraph-io/badger/v4 v4.2.0 + github.com/knadh/koanf/parsers/toml v0.1.0 + github.com/knadh/koanf/providers/env v0.1.0 + github.com/knadh/koanf/providers/file v0.1.0 + github.com/knadh/koanf/providers/rawbytes v0.1.0 + github.com/knadh/koanf/v2 v2.0.1 github.com/labstack/echo/v4 v4.11.1 - github.com/martinlindhe/base36 v1.1.1 github.com/matoous/go-nanoid/v2 v2.0.0 + github.com/mr-tron/base58 v1.2.0 + github.com/rs/zerolog v1.30.0 ) require ( github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/dustin/go-humanize v1.0.0 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/golang/glog v1.0.0 // indirect @@ -21,10 +28,14 @@ require ( github.com/golang/snappy v0.0.3 // indirect github.com/google/flatbuffers v1.12.1 // indirect github.com/klauspost/compress v1.12.3 // indirect + github.com/knadh/koanf/maps v0.1.1 // indirect github.com/labstack/gommon v0.4.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect - github.com/mr-tron/base58 v1.2.0 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/pelletier/go-toml v1.9.5 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect diff --git a/go.sum b/go.sum index 85a9fa2..b7522b4 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,7 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -15,6 +16,9 @@ github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczC github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= @@ -41,28 +45,50 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.12.3 h1:G5AfA94pHPysR56qqrkO2pxEexdDzrpFJ6yt/VqWxVU= github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/knadh/koanf/maps v0.1.1 h1:G5TjmUh2D7G2YWf5SQQqSiHRJEjaicvU0KpypqB3NIs= +github.com/knadh/koanf/maps v0.1.1/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI= +github.com/knadh/koanf/parsers/toml v0.1.0 h1:S2hLqS4TgWZYj4/7mI5m1CQQcWurxUz6ODgOub/6LCI= +github.com/knadh/koanf/parsers/toml v0.1.0/go.mod h1:yUprhq6eo3GbyVXFFMdbfZSo928ksS+uo0FFqNMnO18= +github.com/knadh/koanf/providers/env v0.1.0 h1:LqKteXqfOWyx5Ab9VfGHmjY9BvRXi+clwyZozgVRiKg= +github.com/knadh/koanf/providers/env v0.1.0/go.mod h1:RE8K9GbACJkeEnkl8L/Qcj8p4ZyPXZIQ191HJi44ZaQ= +github.com/knadh/koanf/providers/file v0.1.0 h1:fs6U7nrV58d3CFAFh8VTde8TM262ObYf3ODrc//Lp+c= +github.com/knadh/koanf/providers/file v0.1.0/go.mod h1:rjJ/nHQl64iYCtAW2QQnF0eSmDEX/YZ/eNFj5yR6BvA= +github.com/knadh/koanf/providers/rawbytes v0.1.0 h1:dpzgu2KO6uf6oCb4aP05KDmKmAmI51k5pe8RYKQ0qME= +github.com/knadh/koanf/providers/rawbytes v0.1.0/go.mod h1:mMTB1/IcJ/yE++A2iEZbY1MLygX7vttU+C+S/YmPu9c= +github.com/knadh/koanf/v2 v2.0.1 h1:1dYGITt1I23x8cfx8ZnldtezdyaZtfAuRtIFOiRzK7g= +github.com/knadh/koanf/v2 v2.0.1/go.mod h1:ZeiIlIDXTE7w1lMT6UVcNiRAS2/rCeLn/GdLNvY1Dus= github.com/labstack/echo/v4 v4.11.1 h1:dEpLU2FLg4UVmvCGPuk/APjlH6GDpbEPti61srUUUs4= github.com/labstack/echo/v4 v4.11.1/go.mod h1:YuYRTSM3CHs2ybfrL8Px48bO6BAnYIN4l8wSTMP6BDQ= github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= -github.com/martinlindhe/base36 v1.1.1 h1:1F1MZ5MGghBXDZ2KJ3QfxmiydlWOGB8HCEtkap5NkVg= -github.com/martinlindhe/base36 v1.1.1/go.mod h1:vMS8PaZ5e/jV9LwFKlm0YLnXl/hpOihiBxKkIoc3g08= github.com/matoous/go-nanoid v1.5.0/go.mod h1:zyD2a71IubI24efhpvkJz+ZwfwagzgSO6UNiFsZKN7U= github.com/matoous/go-nanoid/v2 v2.0.0 h1:d19kur2QuLeHmJBkvYkFdhFBzLoo1XVm2GgTpL+9Tj0= github.com/matoous/go-nanoid/v2 v2.0.0/go.mod h1:FtS4aGPVfEkxKxhdWPAspZpZSh1cOjtM7Ej/So3hR0g= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= 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.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 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/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= +github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 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.30.0 h1:SymVODrcRsaRaSInD9yQtKbtWqwsfoPcRff/oRXLj4c= +github.com/rs/zerolog v1.30.0/go.mod h1:/tk+P47gFdPXq4QYjvCmT5/Gsug2nagsFWBWhAiSi1w= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -115,6 +141,7 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= diff --git a/internal/app/app.go b/internal/app/app.go index 9daa800..2110a7a 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -3,6 +3,8 @@ package app import ( "html/template" + "log/slog" + "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" "github.com/merlinfuchs/vaultbin/internal/db" @@ -25,6 +27,23 @@ func New(db *db.DB) *echo.Echo { t := views.New() e.Renderer = t + e.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{ + LogStatus: true, + LogURI: true, + LogMethod: true, + LogError: true, + LogValuesFunc: func(c echo.Context, v middleware.RequestLoggerValues) error { + logCtx := slog.With("method", v.Method).With("path", v.URI).With("status", v.Status).With("error", v.Error) + + if v.Error != nil { + logCtx.Error("Request has failed") + } else { + logCtx.Info("Request has been processed") + } + return nil + }, + })) + pastes := pastes.New(db) e.GET("/", pastes.PasteNew).Name = "paste_new" diff --git a/internal/app/error.go b/internal/app/error.go new file mode 100644 index 0000000..b63d5ea --- /dev/null +++ b/internal/app/error.go @@ -0,0 +1,20 @@ +package app + +import ( + "fmt" + "net/http" + + "github.com/labstack/echo/v4" +) + +func logErrorsHandler(err error, c echo.Context) { + code := http.StatusInternalServerError + if he, ok := err.(*echo.HTTPError); ok { + code = he.Code + } + c.Logger().Error(err) + errorPage := fmt.Sprintf("%d.html", code) + if err := c.File(errorPage); err != nil { + c.Logger().Error(err) + } +} diff --git a/internal/config/config.go b/internal/config/config.go new file mode 100644 index 0000000..0c63f12 --- /dev/null +++ b/internal/config/config.go @@ -0,0 +1,49 @@ +package config + +import ( + "errors" + "fmt" + "os" + "strings" + + "log/slog" + + "github.com/knadh/koanf/parsers/toml" + "github.com/knadh/koanf/providers/env" + "github.com/knadh/koanf/providers/file" + "github.com/knadh/koanf/v2" +) + +var K = koanf.New(".") + +const DefaultConfigName = "vaultbin.toml" +const envVarPrefix = "VBIN__" + +var CfgFile string + +func fileExists(name string) (bool, error) { + _, err := os.Stat(name) + if os.IsNotExist(err) { + return false, nil + } + return err == nil, err +} + +func InitConfig() { + setupDefaults() + + if err := K.Load(file.Provider(DefaultConfigName), toml.Parser()); err != nil { + if !errors.Is(err, os.ErrNotExist) { + slog.Error(fmt.Sprintf("Failed to load config file %s", DefaultConfigName)) + panic(nil) + } + } + + if err := K.Load(env.Provider(envVarPrefix, ".", func(s string) string { + return strings.Replace(strings.ToLower( + strings.TrimPrefix(s, envVarPrefix)), "_", ".", -1) + }), nil); err != nil { + slog.Error("Failed to load env vars") + panic(nil) + } +} diff --git a/internal/config/default.config.toml b/internal/config/default.config.toml new file mode 100644 index 0000000..c7994f1 --- /dev/null +++ b/internal/config/default.config.toml @@ -0,0 +1,6 @@ +port = "8080" +host = "localhost" + +db_path = "vaultbin.badger" + +paste_ttl = 2592000 # 30 seconds \ No newline at end of file diff --git a/internal/config/defaults.go b/internal/config/defaults.go new file mode 100644 index 0000000..96735f3 --- /dev/null +++ b/internal/config/defaults.go @@ -0,0 +1,18 @@ +package config + +import ( + _ "embed" + + "github.com/knadh/koanf/parsers/toml" + "github.com/knadh/koanf/providers/rawbytes" + "github.com/rs/zerolog/log" +) + +//go:embed default.config.toml +var defaultConfigTomlBytes []byte + +func setupDefaults() { + if err := K.Load(rawbytes.Provider(defaultConfigTomlBytes), toml.Parser()); err != nil { + log.Panic().Err(err).Msgf("Failed to load default config") + } +} diff --git a/internal/db/db.go b/internal/db/db.go index 3c60d32..355f513 100644 --- a/internal/db/db.go +++ b/internal/db/db.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/dgraph-io/badger/v4" + "github.com/merlinfuchs/vaultbin/internal/config" ) type DB struct { @@ -12,7 +13,7 @@ type DB struct { } func New() (*DB, error) { - db, err := badger.Open(badger.DefaultOptions("vaultbin.badger")) + db, err := badger.Open(badger.DefaultOptions(config.K.String("db_path"))) if err != nil { return nil, fmt.Errorf("error opening badger db: %w", err) } diff --git a/internal/db/store_pastes.go b/internal/db/store_pastes.go index 971504b..2901a8c 100644 --- a/internal/db/store_pastes.go +++ b/internal/db/store_pastes.go @@ -7,6 +7,7 @@ import ( "github.com/dgraph-io/badger/v4" "github.com/merlinfuchs/vaultbin/internal/common" + "github.com/merlinfuchs/vaultbin/internal/config" "github.com/merlinfuchs/vaultbin/internal/store" ) @@ -41,7 +42,8 @@ func (db *DB) CreatePaste(content, language string) (*store.Paste, error) { key := fmt.Sprintf("pastes.%s", keyHash) err = db.bg.Update(func(txn *badger.Txn) error { - return txn.Set([]byte(key), val) + e := badger.NewEntry([]byte(key), val).WithTTL(time.Duration(config.K.Int("paste_ttl")) * time.Second) + return txn.SetEntry(e) }) if err != nil { return nil, fmt.Errorf("db transaction failed: %w", err) diff --git a/internal/handler/pastes/api_create.go b/internal/handler/pastes/api_create.go index cae1a16..a7ee6d8 100644 --- a/internal/handler/pastes/api_create.go +++ b/internal/handler/pastes/api_create.go @@ -4,6 +4,7 @@ import ( "fmt" "log/slog" "net/http" + "strings" "github.com/labstack/echo/v4" "github.com/merlinfuchs/vaultbin/internal/public/views" @@ -21,6 +22,18 @@ func (h *PastesHandler) PasteAPICreate(c echo.Context) error { return err } + if strings.TrimSpace(req.Content) == "" { + err = c.Render(http.StatusOK, "paste.content", views.PasteViewData{ + New: true, + Content: req.Content, + }) + if err != nil { + slog.With("error", err).Error("failed to render paste_new template") + return err + } + return nil + } + paste, err := h.store.CreatePaste(req.Content, req.Language) if err != nil { slog.With("error", err).Error("Failed to create paste") diff --git a/internal/handler/pastes/paste_raw.go b/internal/handler/pastes/paste_raw.go index a5755a8..62b5697 100644 --- a/internal/handler/pastes/paste_raw.go +++ b/internal/handler/pastes/paste_raw.go @@ -1,7 +1,6 @@ package pastes import ( - "fmt" "net/http" "github.com/labstack/echo/v4" @@ -14,8 +13,9 @@ func (h *PastesHandler) PasteRaw(c echo.Context) error { if err != nil { return err } + if paste == nil { - return fmt.Errorf("Paste not found") + return c.String(http.StatusOK, "Paste doesn't exist or has expired") } return c.String(http.StatusOK, paste.Content) diff --git a/internal/handler/pastes/paste_view.go b/internal/handler/pastes/paste_view.go index 94bc3ee..fc3e988 100644 --- a/internal/handler/pastes/paste_view.go +++ b/internal/handler/pastes/paste_view.go @@ -1,7 +1,6 @@ package pastes import ( - "fmt" "log/slog" "net/http" @@ -16,8 +15,17 @@ func (h *PastesHandler) PasteView(c echo.Context) error { if err != nil { return err } + if paste == nil { - return fmt.Errorf("Paste not found") + err = c.Render(http.StatusOK, "paste", views.PasteViewData{ + New: true, + Content: "Paste doesn't exist or has expired", + }) + if err != nil { + slog.With("error", err).Error("failed to render paste_new template") + return err + } + return nil } viewCount, err := h.store.CountPasteView(paste.ID) diff --git a/internal/public/views/paste.html b/internal/public/views/paste.html index a29c9b3..b43a42d 100644 --- a/internal/public/views/paste.html +++ b/internal/public/views/paste.html @@ -4,8 +4,16 @@ vaultbin + + @@ -32,8 +40,17 @@