From c8a944f9e1acede99285b168b00074cd56559caa Mon Sep 17 00:00:00 2001 From: Garrett Ladley <92384606+garrettladley@users.noreply.github.com> Date: Sat, 18 May 2024 14:44:18 -0400 Subject: [PATCH 1/6] feat: backend telemetry (#843) --- backend/go.mod | 9 ++++ backend/go.sum | 19 +++++++ backend/main.go | 96 ++++++++++++++++++++-------------- backend/server/server.go | 6 +++ backend/telemetry/README.md | 3 ++ backend/telemetry/telemetry.go | 48 +++++++++++++++++ go.work.sum | 8 ++- 7 files changed, 144 insertions(+), 45 deletions(-) create mode 100644 backend/telemetry/README.md create mode 100644 backend/telemetry/telemetry.go diff --git a/backend/go.mod b/backend/go.mod index cff7af345..19e721d77 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -40,11 +40,17 @@ require ( ) require ( + github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/lib/pq v1.10.9 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect + go.opentelemetry.io/contrib v1.17.0 // indirect + go.opentelemetry.io/otel/metric v1.26.0 // indirect + go.opentelemetry.io/otel/sdk v1.26.0 // indirect + go.opentelemetry.io/otel/trace v1.26.0 // indirect go.uber.org/atomic v1.11.0 // indirect ) @@ -61,6 +67,7 @@ require ( github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 + github.com/gofiber/contrib/otelfiber v1.0.10 github.com/golang-migrate/migrate/v4 v4.17.1 github.com/hashicorp/hcl v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect @@ -91,6 +98,8 @@ require ( github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.52.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect + go.opentelemetry.io/otel v1.26.0 + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.26.0 go.uber.org/multierr v1.11.0 // indirect golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 // indirect golang.org/x/net v0.24.0 // indirect diff --git a/backend/go.sum b/backend/go.sum index dfa72fd5b..10869128a 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -40,6 +40,11 @@ github.com/garrettladley/fiberpaginate v1.0.1 h1:Ren6wx8L8KLcPvCKUkgRuaZaxX7CjCC github.com/garrettladley/fiberpaginate v1.0.1/go.mod h1:MHVWGQkhtnt2kE8F0wkTF5iUQOyqZlRktfcGgBLRBv0= github.com/garrettladley/mattress v0.4.0 h1:ZB3iqyc5q6bqIryNfsh2FMcbMdnV1XEryvqivouceQE= github.com/garrettladley/mattress v0.4.0/go.mod h1:OWKIRc9wC3gtD3Ng/nUuNEiR1TJvRYLmn/KZYw9nl5Q= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= @@ -60,6 +65,8 @@ github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 h1:TQcrn6Wq+sKGkpyPvppOz99zsM github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= 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/gofiber/contrib/otelfiber v1.0.10 h1:Bu28Pi4pfYmGfIc/9+sNaBbFwTHGY/zpSIK5jBxuRtM= +github.com/gofiber/contrib/otelfiber v1.0.10/go.mod h1:jN6AvS1HolDHTQHFURsV+7jSX96FpXYeKH6nmkq8AIw= github.com/gofiber/fiber/v2 v2.52.4 h1:P+T+4iK7VaqUsq2PALYEfBBo6bJZ4q3FP8cZ84EggTM= github.com/gofiber/fiber/v2 v2.52.4/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ= github.com/gofiber/swagger v1.0.0 h1:BzUzDS9ZT6fDUa692kxmfOjc1DZiloLiPK/W5z1H1tc= @@ -209,6 +216,18 @@ github.com/valyala/fasthttp v1.52.0 h1:wqBQpxH71XW0e2g+Og4dzQM8pk34aFYlA1Ga8db7g github.com/valyala/fasthttp v1.52.0/go.mod h1:hf5C4QnVMkNXMspnsUlfM3WitlgYflyhHYoKol/szxQ= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +go.opentelemetry.io/contrib v1.17.0 h1:lJJdtuNsP++XHD7tXDYEFSpsqIc7DzShuXMR5PwkmzA= +go.opentelemetry.io/contrib v1.17.0/go.mod h1:gIzjwWFoGazJmtCaDgViqOSJPde2mCWzv60o0bWPcZs= +go.opentelemetry.io/otel v1.26.0 h1:LQwgL5s/1W7YiiRwxf03QGnWLb2HW4pLiAhaA5cZXBs= +go.opentelemetry.io/otel v1.26.0/go.mod h1:UmLkJHUAidDval2EICqBMbnAd0/m2vmpf/dAM+fvFs4= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.26.0 h1:0W5o9SzoR15ocYHEQfvfipzcNog1lBxOLfnex91Hk6s= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.26.0/go.mod h1:zVZ8nz+VSggWmnh6tTsJqXQ7rU4xLwRtna1M4x5jq58= +go.opentelemetry.io/otel/metric v1.26.0 h1:7S39CLuY5Jgg9CrnA9HHiEjGMF/X2VHvoXGgSllRz30= +go.opentelemetry.io/otel/metric v1.26.0/go.mod h1:SY+rHOI4cEawI9a7N1A4nIg/nTQXe1ccCNWYOJUrpX4= +go.opentelemetry.io/otel/sdk v1.26.0 h1:Y7bumHf5tAiDlRYFmGqetNcLaVUZmh4iYfmGxtmz7F8= +go.opentelemetry.io/otel/sdk v1.26.0/go.mod h1:0p8MXpqLeJ0pzcszQQN4F0S5FVjBLgypeGSngLsmirs= +go.opentelemetry.io/otel/trace v1.26.0 h1:1ieeAUb4y0TE26jUFrCIXKpTuVK7uJGN9/Z/2LP5sQA= +go.opentelemetry.io/otel/trace v1.26.0/go.mod h1:4iDxvGDQuUkHve82hJJ8UqrwswHYsZuWCBllGV2U2y0= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= diff --git a/backend/main.go b/backend/main.go index 27d524daf..87113b713 100644 --- a/backend/main.go +++ b/backend/main.go @@ -1,8 +1,10 @@ package main import ( + "context" "flag" "fmt" + "log/slog" "net" "os" "path/filepath" @@ -15,42 +17,12 @@ import ( "github.com/GenerateNU/sac/backend/integrations/file" "github.com/GenerateNU/sac/backend/integrations/search" "github.com/GenerateNU/sac/backend/server" + "github.com/GenerateNU/sac/backend/telemetry" "github.com/GenerateNU/sac/backend/tests/api/mocks" + "go.opentelemetry.io/otel" ) -func CheckServerRunning(host string, port uint16) error { - address := fmt.Sprintf("%s:%d", host, port) - conn, err := net.Dial("tcp", address) - if err != nil { - return err - } - defer conn.Close() - return nil -} - -func Exit(format string, a ...interface{}) { - fmt.Fprintf(os.Stderr, format, a...) - os.Exit(0) -} - -func configureIntegrations(config *config.Settings, connectToPinecone bool) integrations.Integrations { - openAi := search.NewOpenAIClient(config.OpenAI) - var pinecone search.SearchClientInterface - - if connectToPinecone { - pinecone = search.NewPineconeClient(openAi, config.Pinecone) - } else { - pinecone = mocks.NewPineconeMockClient() - } - - integrations := integrations.Integrations{ - File: file.NewAWSProvider(config.AWS), - AI: openAi, - Email: email.NewResendClient(config.Resend, true), - Search: pinecone, - } - return integrations -} +var tracer = otel.Tracer("sac") func main() { onlyMigrate := flag.Bool("only-migrate", false, "Specify if you want to only perform the database migration") @@ -63,17 +35,17 @@ func main() { config, err := config.GetConfiguration(*configPath, *useDevDotEnv) if err != nil { - Exit("Error getting configuration: %s", err.Error()) + exit("Error getting configuration: %s", err.Error()) } - err = CheckServerRunning(config.Application.Host, config.Application.Port) + err = checkServerRunning(config.Application.Host, config.Application.Port) if err == nil { - Exit("A server is already running on %s:%d.\n", config.Application.Host, config.Application.Port) + exit("A server is already running on %s:%d.\n", config.Application.Host, config.Application.Port) } db, err := database.ConfigureDB(*config) if err != nil { - Exit("Error migrating database: %s", err.Error()) + exit("Error migrating database: %s", err.Error()) } if *onlyMigrate { @@ -86,22 +58,66 @@ func main() { err := pinecone.Seed(db) if err != nil { - Exit("Error seeding PineconeDB: %s\n", err.Error()) + exit("Error seeding PineconeDB: %s\n", err.Error()) } return } err = database.ConnPooling(db) if err != nil { - Exit("Error with connection pooling: %s", err.Error()) + exit("Error with connection pooling: %s", err.Error()) } integrations := configureIntegrations(config, *connectToPinecone) + tp := telemetry.InitTracer() + + slog.Info("appease linter since we aren't tracing anything yet", "tracer", tracer) + + defer func() { + if err := tp.Shutdown(context.Background()); err != nil { + slog.Error("error shutting down tracer", "error", err) + } + }() + app := server.Init(db, integrations, *config) err = app.Listen(fmt.Sprintf("%s:%d", config.Application.Host, config.Application.Port)) if err != nil { - Exit("Error starting server: %s", err.Error()) + exit("Error starting server: %s", err.Error()) } } + +func checkServerRunning(host string, port uint16) error { + address := fmt.Sprintf("%s:%d", host, port) + conn, err := net.Dial("tcp", address) + if err != nil { + return err + } + defer conn.Close() + return nil +} + +func exit(format string, a ...interface{}) { + fmt.Fprintf(os.Stderr, format, a...) + os.Exit(0) +} + +func configureIntegrations(config *config.Settings, connectToPinecone bool) integrations.Integrations { + openAi := search.NewOpenAIClient(config.OpenAI) + var pinecone search.SearchClientInterface + + if connectToPinecone { + pinecone = search.NewPineconeClient(openAi, config.Pinecone) + } else { + pinecone = mocks.NewPineconeMockClient() + } + + integrations := integrations.Integrations{ + File: file.NewAWSProvider(config.AWS), + AI: openAi, + Email: email.NewResendClient(config.Resend, true), + Search: pinecone, + } + return integrations +} diff --git a/backend/server/server.go b/backend/server/server.go index 54bb31e75..2c20e81c2 100644 --- a/backend/server/server.go +++ b/backend/server/server.go @@ -21,6 +21,7 @@ import ( "github.com/GenerateNU/sac/backend/types" "github.com/GenerateNU/sac/backend/utilities" + "github.com/gofiber/contrib/otelfiber" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/compress" "github.com/gofiber/fiber/v2/middleware/cors" @@ -95,13 +96,18 @@ func newFiberApp(appSettings config.ApplicationSettings) *fiber.App { AllowOrigins: fmt.Sprintf("http://%s:%d", appSettings.Host, appSettings.Port), AllowCredentials: true, })) + app.Use(requestid.New()) + app.Use(logger.New(logger.Config{ Format: "[${time}] ${ip}:${port} ${pid} ${locals:requestid} ${status} - ${latency} ${method} ${path}\n", })) + app.Use(compress.New(compress.Config{ Level: compress.LevelBestSpeed, })) + app.Use(otelfiber.Middleware()) + return app } diff --git a/backend/telemetry/README.md b/backend/telemetry/README.md new file mode 100644 index 000000000..db0a2ff9a --- /dev/null +++ b/backend/telemetry/README.md @@ -0,0 +1,3 @@ +# tracing + +See this [example](https://github.com/gofiber/contrib/blob/main/otelfiber/README.md) for how to instrument an endpoint. diff --git a/backend/telemetry/telemetry.go b/backend/telemetry/telemetry.go new file mode 100644 index 000000000..842bd0376 --- /dev/null +++ b/backend/telemetry/telemetry.go @@ -0,0 +1,48 @@ +package telemetry + +import ( + "fmt" + "log" + + "github.com/gofiber/fiber/v2" + "go.opentelemetry.io/otel/sdk/resource" + + "go.opentelemetry.io/otel" + stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" + + // uncomment the following line to use Jaeger + // "go.opentelemetry.io/otel/exporters/jaeger" + "github.com/gofiber/contrib/otelfiber" + "go.opentelemetry.io/otel/propagation" + sdktrace "go.opentelemetry.io/otel/sdk/trace" + semconv "go.opentelemetry.io/otel/semconv/v1.4.0" +) + +func InitTracer() *sdktrace.TracerProvider { + exporter, err := stdout.New(stdout.WithPrettyPrint()) + // uncomment the following line to use Jaeger + // exporter, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint("http://localhost:14268/api/traces"))) + if err != nil { + log.Fatal(err) + } + tp := sdktrace.NewTracerProvider( + sdktrace.WithSampler(sdktrace.AlwaysSample()), + sdktrace.WithBatcher(exporter), + sdktrace.WithResource( + resource.NewWithAttributes( + semconv.SchemaURL, + semconv.ServiceNameKey.String("sac-service"), + )), + ) + otel.SetTracerProvider(tp) + otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{})) + return tp +} + +func Use(app *fiber.App) *fiber.App { + app.Use(otelfiber.Middleware(otelfiber.WithSpanNameFormatter(func(c *fiber.Ctx) string { + return fmt.Sprintf("%s - %s", c.Method(), c.Route().Path) + }))) + + return app +} diff --git a/go.work.sum b/go.work.sum index 989b274a0..11b05c648 100644 --- a/go.work.sum +++ b/go.work.sum @@ -198,8 +198,6 @@ github.com/form3tech-oss/jwt-go v3.2.5+incompatible/go.mod h1:pbq4aXjuKjdthFRnoD github.com/fsouza/fake-gcs-server v1.17.0/go.mod h1:D1rTE4YCyHFNa99oyJJ5HyclvN/0uQR+pM/VdlL83bw= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= -github.com/garrettladley/fiberpaginate v1.0.1 h1:Ren6wx8L8KLcPvCKUkgRuaZaxX7CjCCoPhCUytnrGyk= -github.com/garrettladley/fiberpaginate v1.0.1/go.mod h1:MHVWGQkhtnt2kE8F0wkTF5iUQOyqZlRktfcGgBLRBv0= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM= @@ -262,10 +260,7 @@ github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/ktrysmt/go-bitbucket v0.6.4/go.mod h1:9u0v3hsd2rqCHRIpbir1oP7F58uo5dq19sBYvuMoyQ4= github.com/lyft/protoc-gen-star/v2 v2.0.3/go.mod h1:amey7yeodaJhXSbf/TlLvWiqQfLOSpEk//mLlc+axEk= github.com/markbates/pkger v0.15.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI= @@ -339,6 +334,9 @@ go.lsp.dev/pkg v0.0.0-20210717090340-384b27a52fb2/go.mod h1:gtSHRuYfbCT0qnbLnovp go.lsp.dev/uri v0.3.0/go.mod h1:P5sbO1IQR+qySTWOCnhnK7phBx+W3zbLqSMDJNTw88I= go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/propagators/b3 v1.17.0/go.mod h1:IkfUfMpKWmynvvE0264trz0sf32NRTZL4nuAN9AbWRc= +go.opentelemetry.io/otel/oteltest v1.0.0-RC3/go.mod h1:xpzajI9JBRr7gX63nO6kAmImmYIAtuQblZ36Z+LfCjE= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= From 36e5eab0eded2bfc13e62073cf1e73734f3ad5d4 Mon Sep 17 00:00:00 2001 From: Garrett Ladley <92384606+garrettladley@users.noreply.github.com> Date: Sat, 18 May 2024 15:28:19 -0400 Subject: [PATCH 2/6] feat: move fiberparingate to service params & middleware cleanup (#846) --- backend/entities/categories/base/routes.go | 3 +- backend/entities/categories/tags/routes.go | 3 +- backend/entities/clubs/base/routes.go | 7 ++--- backend/entities/clubs/contacts/routes.go | 4 +-- backend/entities/clubs/events/routes.go | 3 +- backend/entities/clubs/followers/routes.go | 3 +- backend/entities/clubs/members/routes.go | 9 +++--- backend/entities/clubs/pocs/routes.go | 10 +++---- backend/entities/clubs/tags/routes.go | 6 ++-- backend/entities/contacts/base/routes.go | 3 +- backend/entities/events/base/routes.go | 7 ++--- backend/entities/events/series/routes.go | 8 +++--- backend/entities/events/tags/routes.go | 6 ++-- backend/entities/files/base/routes.go | 3 +- backend/entities/pocs/base/routes.go | 3 +- backend/entities/users/base/routes.go | 3 +- backend/middleware/{ => auth}/auth.go | 22 +------------- backend/middleware/{ => auth}/club.go | 2 +- backend/middleware/{ => auth}/event.go | 2 +- backend/middleware/{ => auth}/extractor.go | 2 +- backend/middleware/{ => auth}/middleware.go | 10 ++----- backend/middleware/{ => auth}/user.go | 2 +- backend/middleware/utility/limiter.go | 24 ++++++++++++++++ backend/middleware/utility/middleware.go | 22 ++++++++++++++ backend/middleware/utility/paginator.go | 9 ++++++ backend/server/server.go | 32 ++++++++++++--------- backend/types/params.go | 30 ++++++++++++++++--- 27 files changed, 142 insertions(+), 96 deletions(-) rename backend/middleware/{ => auth}/auth.go (78%) rename backend/middleware/{ => auth}/club.go (98%) rename backend/middleware/{ => auth}/event.go (98%) rename backend/middleware/{ => auth}/extractor.go (98%) rename backend/middleware/{ => auth}/middleware.go (63%) rename backend/middleware/{ => auth}/user.go (97%) create mode 100644 backend/middleware/utility/limiter.go create mode 100644 backend/middleware/utility/middleware.go create mode 100644 backend/middleware/utility/paginator.go diff --git a/backend/entities/categories/base/routes.go b/backend/entities/categories/base/routes.go index a171c0074..f41980ee0 100644 --- a/backend/entities/categories/base/routes.go +++ b/backend/entities/categories/base/routes.go @@ -4,7 +4,6 @@ import ( "github.com/GenerateNU/sac/backend/auth" "github.com/GenerateNU/sac/backend/entities/categories/tags" "github.com/GenerateNU/sac/backend/types" - "github.com/garrettladley/fiberpaginate" "github.com/gofiber/fiber/v2" ) @@ -24,7 +23,7 @@ func Category(categoryParams types.RouteParams) fiber.Router { categories := categoryParams.Router.Group("/categories") categories.Post("/", categoryParams.AuthMiddleware.Authorize(auth.CreateAll), categoryController.CreateCategory) - categories.Get("/", fiberpaginate.New(), categoryController.GetCategories) + categories.Get("/", categoryParams.UtilityMiddleware.Paginator, categoryController.GetCategories) // api/v1/categories/:categoryID/* categoryID := categories.Group("/:categoryID") diff --git a/backend/entities/categories/tags/routes.go b/backend/entities/categories/tags/routes.go index 816dcc9eb..f4a0d4cb4 100644 --- a/backend/entities/categories/tags/routes.go +++ b/backend/entities/categories/tags/routes.go @@ -2,7 +2,6 @@ package tags import ( "github.com/GenerateNU/sac/backend/types" - "github.com/garrettladley/fiberpaginate" ) func CategoryTag(categoryParams types.RouteParams) { @@ -11,6 +10,6 @@ func CategoryTag(categoryParams types.RouteParams) { // api/v1/categories/:categoryID/tags/* categoryTags := categoryParams.Router.Group("/tags") - categoryTags.Get("/", fiberpaginate.New(), categoryTagController.GetTagsByCategory) + categoryTags.Get("/", categoryParams.UtilityMiddleware.Paginator, categoryTagController.GetTagsByCategory) categoryTags.Get("/:tagID", categoryTagController.GetTagByCategory) } diff --git a/backend/entities/clubs/base/routes.go b/backend/entities/clubs/base/routes.go index 5d89fd9ee..7633698b2 100644 --- a/backend/entities/clubs/base/routes.go +++ b/backend/entities/clubs/base/routes.go @@ -8,8 +8,7 @@ import ( "github.com/GenerateNU/sac/backend/entities/clubs/members" "github.com/GenerateNU/sac/backend/entities/clubs/pocs" "github.com/GenerateNU/sac/backend/entities/clubs/tags" - "github.com/GenerateNU/sac/backend/middleware" - "github.com/garrettladley/fiberpaginate" + authMiddleware "github.com/GenerateNU/sac/backend/middleware/auth" "github.com/GenerateNU/sac/backend/types" "github.com/gofiber/fiber/v2" @@ -32,7 +31,7 @@ func ClubRouter(clubParams types.RouteParams) fiber.Router { // api/v1/clubs/* clubs := clubParams.Router.Group("/clubs") - clubs.Get("/", fiberpaginate.New(), clubController.GetClubs) + clubs.Get("/", clubParams.UtilityMiddleware.Paginator, clubController.GetClubs) clubs.Post("/", clubParams.AuthMiddleware.Authorize(p.CreateAll), clubController.CreateClub) // api/v1/clubs/:clubID/* @@ -41,7 +40,7 @@ func ClubRouter(clubParams types.RouteParams) fiber.Router { clubID.Get("/", clubController.GetClub) clubID.Patch( "/", - middleware.AttachExtractor(clubParams.AuthMiddleware.ClubAuthorizeById, middleware.ExtractFromParams("clubID")), + authMiddleware.AttachExtractor(clubParams.AuthMiddleware.ClubAuthorizeById, authMiddleware.ExtractFromParams("clubID")), clubController.UpdateClub, ) clubID.Delete("/", clubParams.AuthMiddleware.Authorize(p.DeleteAll), clubController.DeleteClub) diff --git a/backend/entities/clubs/contacts/routes.go b/backend/entities/clubs/contacts/routes.go index 358408bfb..252fcfe52 100644 --- a/backend/entities/clubs/contacts/routes.go +++ b/backend/entities/clubs/contacts/routes.go @@ -1,7 +1,7 @@ package contacts import ( - "github.com/GenerateNU/sac/backend/middleware" + authMiddleware "github.com/GenerateNU/sac/backend/middleware/auth" "github.com/GenerateNU/sac/backend/types" ) @@ -14,7 +14,7 @@ func ClubContact(clubParams types.RouteParams) { clubContacts.Get("/", clubContactController.GetClubContacts) clubContacts.Put( "/", - middleware.AttachExtractor(clubParams.AuthMiddleware.ClubAuthorizeById, middleware.ExtractFromParams("clubID")), + authMiddleware.AttachExtractor(clubParams.AuthMiddleware.ClubAuthorizeById, authMiddleware.ExtractFromParams("clubID")), clubContactController.PutContact, ) } diff --git a/backend/entities/clubs/events/routes.go b/backend/entities/clubs/events/routes.go index 4aefdf69a..1a0bd42dd 100644 --- a/backend/entities/clubs/events/routes.go +++ b/backend/entities/clubs/events/routes.go @@ -2,7 +2,6 @@ package events import ( "github.com/GenerateNU/sac/backend/types" - "github.com/garrettladley/fiberpaginate" ) func ClubEvent(clubParams types.RouteParams) { @@ -11,5 +10,5 @@ func ClubEvent(clubParams types.RouteParams) { // api/v1/clubs/:clubID/events/* events := clubParams.Router.Group("/events") - events.Get("/", fiberpaginate.New(), clubEventController.GetClubEvents) + events.Get("/", clubParams.UtilityMiddleware.Paginator, clubEventController.GetClubEvents) } diff --git a/backend/entities/clubs/followers/routes.go b/backend/entities/clubs/followers/routes.go index a67a4744e..81f9e3a61 100644 --- a/backend/entities/clubs/followers/routes.go +++ b/backend/entities/clubs/followers/routes.go @@ -2,7 +2,6 @@ package followers import ( "github.com/GenerateNU/sac/backend/types" - "github.com/garrettladley/fiberpaginate" ) func ClubFollower(clubParams types.RouteParams) { @@ -11,5 +10,5 @@ func ClubFollower(clubParams types.RouteParams) { clubFollowers := clubParams.Router.Group("/followers") // api/clubs/:clubID/followers/* - clubFollowers.Get("/", fiberpaginate.New(), clubFollowerController.GetClubFollowers) + clubFollowers.Get("/", clubParams.UtilityMiddleware.Paginator, clubFollowerController.GetClubFollowers) } diff --git a/backend/entities/clubs/members/routes.go b/backend/entities/clubs/members/routes.go index 891ec3a7d..660a83db8 100644 --- a/backend/entities/clubs/members/routes.go +++ b/backend/entities/clubs/members/routes.go @@ -1,9 +1,8 @@ package members import ( - "github.com/GenerateNU/sac/backend/middleware" + authMiddleware "github.com/GenerateNU/sac/backend/middleware/auth" "github.com/GenerateNU/sac/backend/types" - "github.com/garrettladley/fiberpaginate" ) func ClubMember(clubParams types.RouteParams) { @@ -12,15 +11,15 @@ func ClubMember(clubParams types.RouteParams) { clubMembers := clubParams.Router.Group("/members") // api/v1/clubs/:clubID/members/* - clubMembers.Get("/", fiberpaginate.New(), clubMemberController.GetClubMembers) + clubMembers.Get("/", clubParams.UtilityMiddleware.Paginator, clubMemberController.GetClubMembers) clubMembers.Post( "/:userID", - middleware.AttachExtractor(clubParams.AuthMiddleware.ClubAuthorizeById, middleware.ExtractFromParams("clubID")), + authMiddleware.AttachExtractor(clubParams.AuthMiddleware.ClubAuthorizeById, authMiddleware.ExtractFromParams("clubID")), clubMemberController.CreateClubMember, ) clubMembers.Delete( "/:userID", - middleware.AttachExtractor(clubParams.AuthMiddleware.ClubAuthorizeById, middleware.ExtractFromParams("clubID")), + authMiddleware.AttachExtractor(clubParams.AuthMiddleware.ClubAuthorizeById, authMiddleware.ExtractFromParams("clubID")), clubMemberController.DeleteClubMember, ) } diff --git a/backend/entities/clubs/pocs/routes.go b/backend/entities/clubs/pocs/routes.go index 55594beef..bd57f188c 100644 --- a/backend/entities/clubs/pocs/routes.go +++ b/backend/entities/clubs/pocs/routes.go @@ -1,7 +1,7 @@ package pocs import ( - "github.com/GenerateNU/sac/backend/middleware" + authMiddleware "github.com/GenerateNU/sac/backend/middleware/auth" "github.com/GenerateNU/sac/backend/types" ) @@ -15,22 +15,22 @@ func ClubPointOfContact(clubParams types.RouteParams) { clubPointOfContacts.Get("/:pocID", clubPointOfContactController.GetClubPointOfContact) clubPointOfContacts.Post( "/", - middleware.AttachExtractor(clubParams.AuthMiddleware.ClubAuthorizeById, middleware.ExtractFromParams("clubID")), + authMiddleware.AttachExtractor(clubParams.AuthMiddleware.ClubAuthorizeById, authMiddleware.ExtractFromParams("clubID")), clubPointOfContactController.CreateClubPointOfContact, ) clubPointOfContacts.Patch( "/:pocID", - middleware.AttachExtractor(clubParams.AuthMiddleware.ClubAuthorizeById, middleware.ExtractFromParams("clubID")), + authMiddleware.AttachExtractor(clubParams.AuthMiddleware.ClubAuthorizeById, authMiddleware.ExtractFromParams("clubID")), clubPointOfContactController.UpdateClubPointOfContact, ) clubPointOfContacts.Patch( "/:pocID/photo", - middleware.AttachExtractor(clubParams.AuthMiddleware.ClubAuthorizeById, middleware.ExtractFromParams("clubID")), + authMiddleware.AttachExtractor(clubParams.AuthMiddleware.ClubAuthorizeById, authMiddleware.ExtractFromParams("clubID")), clubPointOfContactController.UpdateClubPointOfContactPhoto, ) clubPointOfContacts.Delete( "/:pocID", - middleware.AttachExtractor(clubParams.AuthMiddleware.ClubAuthorizeById, middleware.ExtractFromParams("clubID")), + authMiddleware.AttachExtractor(clubParams.AuthMiddleware.ClubAuthorizeById, authMiddleware.ExtractFromParams("clubID")), clubPointOfContactController.DeleteClubPointOfContact, ) } diff --git a/backend/entities/clubs/tags/routes.go b/backend/entities/clubs/tags/routes.go index b3a5a140b..4384d1ee3 100644 --- a/backend/entities/clubs/tags/routes.go +++ b/backend/entities/clubs/tags/routes.go @@ -1,7 +1,7 @@ package tags import ( - "github.com/GenerateNU/sac/backend/middleware" + authMiddleware "github.com/GenerateNU/sac/backend/middleware/auth" "github.com/GenerateNU/sac/backend/types" ) @@ -14,12 +14,12 @@ func ClubTag(clubParams types.RouteParams) { clubTags.Get("/", clubTagController.GetClubTags) clubTags.Post( "/", - middleware.AttachExtractor(clubParams.AuthMiddleware.ClubAuthorizeById, middleware.ExtractFromParams("clubID")), + authMiddleware.AttachExtractor(clubParams.AuthMiddleware.ClubAuthorizeById, authMiddleware.ExtractFromParams("clubID")), clubTagController.CreateClubTags, ) clubTags.Delete( "/:tagID", - middleware.AttachExtractor(clubParams.AuthMiddleware.ClubAuthorizeById, middleware.ExtractFromParams("clubID")), + authMiddleware.AttachExtractor(clubParams.AuthMiddleware.ClubAuthorizeById, authMiddleware.ExtractFromParams("clubID")), clubTagController.DeleteClubTag, ) } diff --git a/backend/entities/contacts/base/routes.go b/backend/entities/contacts/base/routes.go index a68d7d15c..453705783 100644 --- a/backend/entities/contacts/base/routes.go +++ b/backend/entities/contacts/base/routes.go @@ -3,7 +3,6 @@ package base import ( "github.com/GenerateNU/sac/backend/auth" "github.com/GenerateNU/sac/backend/types" - "github.com/garrettladley/fiberpaginate" ) func Contact(contactParams types.RouteParams) { @@ -12,7 +11,7 @@ func Contact(contactParams types.RouteParams) { // api/v1/contacts/* contacts := contactParams.Router.Group("/contacts") - contacts.Get("/", fiberpaginate.New(), contactController.GetContacts) + contacts.Get("/", contactParams.UtilityMiddleware.Paginator, contactController.GetContacts) contacts.Get("/:contactID", contactController.GetContact) contacts.Delete("/:contactID", contactParams.AuthMiddleware.Authorize(auth.DeleteAll), contactController.DeleteContact) } diff --git a/backend/entities/events/base/routes.go b/backend/entities/events/base/routes.go index fa554a762..c0be2cb84 100644 --- a/backend/entities/events/base/routes.go +++ b/backend/entities/events/base/routes.go @@ -5,9 +5,8 @@ import ( "github.com/GenerateNU/sac/backend/entities/events/tags" "github.com/gofiber/fiber/v2" - "github.com/GenerateNU/sac/backend/middleware" + authMiddleware "github.com/GenerateNU/sac/backend/middleware/auth" "github.com/GenerateNU/sac/backend/types" - "github.com/garrettladley/fiberpaginate" ) func EventRoutes(eventParams types.RouteParams) { @@ -23,10 +22,10 @@ func EventRouter(eventParams types.RouteParams) fiber.Router { // api/v1/events/* events := eventParams.Router.Group("/events") - events.Get("/", fiberpaginate.New(), eventController.GetAllEvents) + events.Get("/", eventParams.UtilityMiddleware.Paginator, eventController.GetAllEvents) events.Post( "/", - middleware.AttachExtractor(eventParams.AuthMiddleware.ClubAuthorizeById, middleware.ExtractFromParams("clubID")), + authMiddleware.AttachExtractor(eventParams.AuthMiddleware.ClubAuthorizeById, authMiddleware.ExtractFromParams("clubID")), eventController.CreateEvent, ) diff --git a/backend/entities/events/series/routes.go b/backend/entities/events/series/routes.go index aaa97677d..b4310c2da 100644 --- a/backend/entities/events/series/routes.go +++ b/backend/entities/events/series/routes.go @@ -2,7 +2,7 @@ package series import ( "github.com/GenerateNU/sac/backend/extractors" - "github.com/GenerateNU/sac/backend/middleware" + authMiddleware "github.com/GenerateNU/sac/backend/middleware/auth" "github.com/GenerateNU/sac/backend/types" ) @@ -14,7 +14,7 @@ func EventSeries(eventParams types.RouteParams) { eventSeries.Post( "/", - middleware.AttachExtractor(eventParams.AuthMiddleware.ClubAuthorizeById, middleware.ExtractFromBody("host")), + authMiddleware.AttachExtractor(eventParams.AuthMiddleware.ClubAuthorizeById, authMiddleware.ExtractFromBody("host")), eventSeriesController.CreateEventSeries, ) @@ -23,9 +23,9 @@ func EventSeries(eventParams types.RouteParams) { eventSeriedID.Get("/", eventSeriesController.GetEventSeries) eventSeriedID.Delete( "/", - middleware.AttachExtractor( + authMiddleware.AttachExtractor( eventParams.AuthMiddleware.ClubAuthorizeById, - middleware.FromExtractFromParamIntoQuery("seriesID", eventParams.ServiceParams.DB, extractors.GetSeriesHost), + authMiddleware.FromExtractFromParamIntoQuery("seriesID", eventParams.ServiceParams.DB, extractors.GetSeriesHost), ), eventSeriesController.DeleteEventSeries, ) diff --git a/backend/entities/events/tags/routes.go b/backend/entities/events/tags/routes.go index 823647869..7ba3bb9c4 100644 --- a/backend/entities/events/tags/routes.go +++ b/backend/entities/events/tags/routes.go @@ -1,7 +1,7 @@ package tags import ( - "github.com/GenerateNU/sac/backend/middleware" + authMiddleware "github.com/GenerateNU/sac/backend/middleware/auth" "github.com/GenerateNU/sac/backend/types" ) @@ -13,12 +13,12 @@ func EventTags(eventParams types.RouteParams) { eventTags.Get("/", eventTagController.GetEventTags) eventTags.Post( "/", - middleware.AttachExtractor(eventParams.AuthMiddleware.EventAuthorizeById, middleware.ExtractFromParams("eventID")), + authMiddleware.AttachExtractor(eventParams.AuthMiddleware.EventAuthorizeById, authMiddleware.ExtractFromParams("eventID")), eventTagController.CreateEventTags, ) eventTags.Delete( "/:tagID", - middleware.AttachExtractor(eventParams.AuthMiddleware.EventAuthorizeById, middleware.ExtractFromParams("eventID")), + authMiddleware.AttachExtractor(eventParams.AuthMiddleware.EventAuthorizeById, authMiddleware.ExtractFromParams("eventID")), eventTagController.DeleteEventTag, ) } diff --git a/backend/entities/files/base/routes.go b/backend/entities/files/base/routes.go index 23d8baac4..c52205db5 100644 --- a/backend/entities/files/base/routes.go +++ b/backend/entities/files/base/routes.go @@ -2,7 +2,6 @@ package base import ( "github.com/GenerateNU/sac/backend/types" - "github.com/garrettladley/fiberpaginate" ) func File(fileParams types.RouteParams) { @@ -10,7 +9,7 @@ func File(fileParams types.RouteParams) { file := fileParams.Router.Group("/files") - file.Get("/", fiberpaginate.New(), fileController.GetFiles) + file.Get("/", fileParams.UtilityMiddleware.Paginator, fileController.GetFiles) file.Get("/:fileID", fileController.GetFile) file.Post("/", fileController.CreateFile) file.Delete("/:fileID", fileController.DeleteFile) diff --git a/backend/entities/pocs/base/routes.go b/backend/entities/pocs/base/routes.go index 9ec008cab..907459002 100644 --- a/backend/entities/pocs/base/routes.go +++ b/backend/entities/pocs/base/routes.go @@ -2,7 +2,6 @@ package base import ( "github.com/GenerateNU/sac/backend/types" - "github.com/garrettladley/fiberpaginate" ) func PointOfContact(pointOfContactParams types.RouteParams) { @@ -11,7 +10,7 @@ func PointOfContact(pointOfContactParams types.RouteParams) { // api/v1/pocs/* pointofContact := pointOfContactParams.Router.Group("/pocs") - pointofContact.Get("/", fiberpaginate.New(), pointOfContactController.GetPointOfContacts) + pointofContact.Get("/", pointOfContactParams.UtilityMiddleware.Paginator, pointOfContactController.GetPointOfContacts) pointofContact.Get("/:pocID", pointOfContactController.GetPointOfContact) // pointOfContact.Get("/:pocID/file", pointOfContactController.GetPointOfContacFileInfo)) } diff --git a/backend/entities/users/base/routes.go b/backend/entities/users/base/routes.go index 8c4753cf0..898a981ba 100644 --- a/backend/entities/users/base/routes.go +++ b/backend/entities/users/base/routes.go @@ -6,7 +6,6 @@ import ( "github.com/GenerateNU/sac/backend/entities/users/members" "github.com/GenerateNU/sac/backend/entities/users/tags" "github.com/GenerateNU/sac/backend/types" - "github.com/garrettladley/fiberpaginate" "github.com/gofiber/fiber/v2" ) @@ -27,7 +26,7 @@ func UsersRouter(userParams types.RouteParams) fiber.Router { // api/v1/users/* users := userParams.Router.Group("/users") - users.Get("/", userParams.AuthMiddleware.Authorize(p.ReadAll), fiberpaginate.New(), userController.GetUsers) + users.Get("/", userParams.AuthMiddleware.Authorize(p.ReadAll), userParams.UtilityMiddleware.Paginator, userController.GetUsers) users.Get("/me", userParams.AuthMiddleware.Authorize(p.UserRead), userController.GetMe) // api/v1/users/:userID/* diff --git a/backend/middleware/auth.go b/backend/middleware/auth/auth.go similarity index 78% rename from backend/middleware/auth.go rename to backend/middleware/auth/auth.go index 7538efdd3..1a0962766 100644 --- a/backend/middleware/auth.go +++ b/backend/middleware/auth/auth.go @@ -1,11 +1,8 @@ -package middleware +package auth import ( - "fmt" - "net/http" "slices" "strings" - "time" "github.com/GenerateNU/sac/backend/auth" "github.com/GenerateNU/sac/backend/utilities" @@ -15,7 +12,6 @@ import ( "github.com/golang-jwt/jwt" "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/limiter" ) func (m *AuthMiddlewareService) IsSuper(c *fiber.Ctx) bool { @@ -102,19 +98,3 @@ func (m *AuthMiddlewareService) Authorize(requiredPermissions ...auth.Permission return c.Next() } } - -// TODO: implement rate limiting with redis -func (m *AuthMiddlewareService) Limiter(rate int, expiration time.Duration) func(c *fiber.Ctx) error { - return limiter.New(limiter.Config{ - Max: rate, - Expiration: expiration, - KeyGenerator: func(c *fiber.Ctx) string { - return fmt.Sprintf("%s-%s", c.IP(), c.Path()) - }, - LimitReached: func(c *fiber.Ctx) error { - return c.Status(http.StatusTooManyRequests).JSON(fiber.Map{ - "error": "Too many requests", - }) - }, - }) -} diff --git a/backend/middleware/club.go b/backend/middleware/auth/club.go similarity index 98% rename from backend/middleware/club.go rename to backend/middleware/auth/club.go index 4c9560696..5a084fd7b 100644 --- a/backend/middleware/club.go +++ b/backend/middleware/auth/club.go @@ -1,4 +1,4 @@ -package middleware +package auth import ( "slices" diff --git a/backend/middleware/event.go b/backend/middleware/auth/event.go similarity index 98% rename from backend/middleware/event.go rename to backend/middleware/auth/event.go index b8a371fa5..542f6e7c2 100644 --- a/backend/middleware/event.go +++ b/backend/middleware/auth/event.go @@ -1,4 +1,4 @@ -package middleware +package auth import ( "slices" diff --git a/backend/middleware/extractor.go b/backend/middleware/auth/extractor.go similarity index 98% rename from backend/middleware/extractor.go rename to backend/middleware/auth/extractor.go index a9b4bcc37..8b10c29f5 100644 --- a/backend/middleware/extractor.go +++ b/backend/middleware/auth/extractor.go @@ -1,4 +1,4 @@ -package middleware +package auth import ( "errors" diff --git a/backend/middleware/middleware.go b/backend/middleware/auth/middleware.go similarity index 63% rename from backend/middleware/middleware.go rename to backend/middleware/auth/middleware.go index 5d3e6aabe..97b3cafa5 100644 --- a/backend/middleware/middleware.go +++ b/backend/middleware/auth/middleware.go @@ -1,8 +1,6 @@ -package middleware +package auth import ( - "time" - "github.com/GenerateNU/sac/backend/auth" "github.com/GenerateNU/sac/backend/config" "github.com/go-playground/validator/v10" @@ -14,10 +12,8 @@ type AuthMiddlewareInterface interface { ClubAuthorizeById(c *fiber.Ctx) error UserAuthorizeById(c *fiber.Ctx) error Authenticate(c *fiber.Ctx) error - Authorize(requiredPermissions ...auth.Permission) func(c *fiber.Ctx) error - Skip(h fiber.Handler) fiber.Handler + Authorize(requiredPermissions ...auth.Permission) fiber.Handler IsSuper(c *fiber.Ctx) bool - Limiter(rate int, duration time.Duration) func(c *fiber.Ctx) error } type AuthMiddlewareService struct { @@ -26,7 +22,7 @@ type AuthMiddlewareService struct { Auth config.AuthSettings } -func NewAuthMiddlewareService(db *gorm.DB, validate *validator.Validate, authSettings config.AuthSettings) *AuthMiddlewareService { +func New(db *gorm.DB, validate *validator.Validate, authSettings config.AuthSettings) *AuthMiddlewareService { return &AuthMiddlewareService{ DB: db, Validate: validate, diff --git a/backend/middleware/user.go b/backend/middleware/auth/user.go similarity index 97% rename from backend/middleware/user.go rename to backend/middleware/auth/user.go index 62b040bd9..bf489faa2 100644 --- a/backend/middleware/user.go +++ b/backend/middleware/auth/user.go @@ -1,4 +1,4 @@ -package middleware +package auth import ( "github.com/GenerateNU/sac/backend/auth" diff --git a/backend/middleware/utility/limiter.go b/backend/middleware/utility/limiter.go new file mode 100644 index 000000000..dc6ed2887 --- /dev/null +++ b/backend/middleware/utility/limiter.go @@ -0,0 +1,24 @@ +package utility + +import ( + "errors" + "fmt" + "time" + + "github.com/GenerateNU/sac/backend/utilities" + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/limiter" +) + +func (u *UtilityMiddlewareService) Limiter(rate int, expiration time.Duration) func(c *fiber.Ctx) error { + return limiter.New(limiter.Config{ + Max: rate, + Expiration: expiration, + KeyGenerator: func(c *fiber.Ctx) string { + return fmt.Sprintf("%s-%s", c.IP(), c.Path()) + }, + LimitReached: func(c *fiber.Ctx) error { + return utilities.NewAPIError(fiber.StatusTooManyRequests, errors.New("too many requests")) + }, + }) +} diff --git a/backend/middleware/utility/middleware.go b/backend/middleware/utility/middleware.go new file mode 100644 index 000000000..cd940ea93 --- /dev/null +++ b/backend/middleware/utility/middleware.go @@ -0,0 +1,22 @@ +package utility + +import ( + "time" + + "github.com/gofiber/fiber/v2" +) + +type UtilityMiddlewareInterface interface { + Paginator(c *fiber.Ctx) error + Limiter(rate int, duration time.Duration) fiber.Handler +} + +type UtilityMiddlewareService struct { + paginator fiber.Handler +} + +func New(paginator fiber.Handler) *UtilityMiddlewareService { + return &UtilityMiddlewareService{ + paginator: paginator, + } +} diff --git a/backend/middleware/utility/paginator.go b/backend/middleware/utility/paginator.go new file mode 100644 index 000000000..0e2827fda --- /dev/null +++ b/backend/middleware/utility/paginator.go @@ -0,0 +1,9 @@ +package utility + +import ( + "github.com/gofiber/fiber/v2" +) + +func (u *UtilityMiddlewareService) Paginator(c *fiber.Ctx) error { + return u.paginator(c) +} diff --git a/backend/server/server.go b/backend/server/server.go index 2c20e81c2..fcd95b3d6 100644 --- a/backend/server/server.go +++ b/backend/server/server.go @@ -3,6 +3,7 @@ package server import ( "fmt" + "github.com/garrettladley/fiberpaginate" go_json "github.com/goccy/go-json" authenticator "github.com/GenerateNU/sac/backend/auth" @@ -17,7 +18,8 @@ import ( tags "github.com/GenerateNU/sac/backend/entities/tags/base" users "github.com/GenerateNU/sac/backend/entities/users/base" "github.com/GenerateNU/sac/backend/integrations" - "github.com/GenerateNU/sac/backend/middleware" + authMiddleware "github.com/GenerateNU/sac/backend/middleware/auth" + utilityMiddleware "github.com/GenerateNU/sac/backend/middleware/utility" "github.com/GenerateNU/sac/backend/types" "github.com/GenerateNU/sac/backend/utilities" @@ -50,22 +52,24 @@ func Init(db *gorm.DB, integrations integrations.Integrations, settings config.S } jwt := authenticator.NewJWTClient(settings.Auth, jwt.SigningMethodHS256) - authMiddleware := middleware.NewAuthMiddlewareService(db, validate, settings.Auth) + authMiddleware := authMiddleware.New(db, validate, settings.Auth) + utilityMiddleware := utilityMiddleware.New(fiberpaginate.New()) apiv1 := app.Group("/api/v1") - routeParams := types.RouteParams{ - Router: apiv1, - AuthMiddleware: authMiddleware, - ServiceParams: types.ServiceParams{ - DB: db, - Validate: validate, - Auth: &settings.Auth, - JWT: jwt, - Integrations: integrations, - Calendar: &settings.Calendar, - }, - } + routeParams := types.NewRouteParams( + apiv1, + authMiddleware, + utilityMiddleware, + types.NewServiceParams( + db, + validate, + &settings.Auth, + jwt, + &settings.Calendar, + integrations, + ), + ) allRoutes(app, routeParams) diff --git a/backend/types/params.go b/backend/types/params.go index 6f4551524..47a65a348 100644 --- a/backend/types/params.go +++ b/backend/types/params.go @@ -4,16 +4,27 @@ import ( "github.com/GenerateNU/sac/backend/auth" "github.com/GenerateNU/sac/backend/config" "github.com/GenerateNU/sac/backend/integrations" - "github.com/GenerateNU/sac/backend/middleware" + authMiddleware "github.com/GenerateNU/sac/backend/middleware/auth" + utilityMiddleware "github.com/GenerateNU/sac/backend/middleware/utility" "github.com/go-playground/validator/v10" "github.com/gofiber/fiber/v2" "gorm.io/gorm" ) type RouteParams struct { - Router fiber.Router - AuthMiddleware *middleware.AuthMiddlewareService - ServiceParams ServiceParams + Router fiber.Router + AuthMiddleware *authMiddleware.AuthMiddlewareService + UtilityMiddleware *utilityMiddleware.UtilityMiddlewareService + ServiceParams ServiceParams +} + +func NewRouteParams(router fiber.Router, authMiddleware *authMiddleware.AuthMiddlewareService, utilityMiddleware *utilityMiddleware.UtilityMiddlewareService, serviceParams ServiceParams) RouteParams { + return RouteParams{ + Router: router, + AuthMiddleware: authMiddleware, + UtilityMiddleware: utilityMiddleware, + ServiceParams: serviceParams, + } } type ServiceParams struct { @@ -24,3 +35,14 @@ type ServiceParams struct { Calendar *config.CalendarSettings Integrations integrations.Integrations } + +func NewServiceParams(db *gorm.DB, validate *validator.Validate, auth *config.AuthSettings, jwt auth.JWTClientInterface, calendar *config.CalendarSettings, integrations integrations.Integrations) ServiceParams { + return ServiceParams{ + DB: db, + Validate: validate, + Auth: auth, + JWT: jwt, + Calendar: calendar, + Integrations: integrations, + } +} From 61f1d8aef2b767c4ada34558b6df1ecff980c423 Mon Sep 17 00:00:00 2001 From: David Oduneye <44040421+DOOduneye@users.noreply.github.com> Date: Sun, 19 May 2024 16:23:24 -0400 Subject: [PATCH 3/6] fix: allows Authorization Bearer to be accessed in a node env (#852) --- backend/go.mod | 2 +- backend/go.sum | 6 ++++++ backend/server/server.go | 3 +++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/backend/go.mod b/backend/go.mod index 19e721d77..f3312a02b 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -22,6 +22,7 @@ require ( github.com/sahilm/fuzzy v0.1.1 github.com/spf13/viper v1.18.2 github.com/swaggo/swag v1.16.3 + go.opentelemetry.io/otel/sdk v1.26.0 golang.org/x/crypto v0.23.0 golang.org/x/text v0.15.0 gorm.io/driver/postgres v1.5.7 @@ -49,7 +50,6 @@ require ( github.com/mitchellh/mapstructure v1.5.0 // indirect go.opentelemetry.io/contrib v1.17.0 // indirect go.opentelemetry.io/otel/metric v1.26.0 // indirect - go.opentelemetry.io/otel/sdk v1.26.0 // indirect go.opentelemetry.io/otel/trace v1.26.0 // indirect go.uber.org/atomic v1.11.0 // indirect ) diff --git a/backend/go.sum b/backend/go.sum index 10869128a..34077d18d 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -218,14 +218,20 @@ github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVS github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= go.opentelemetry.io/contrib v1.17.0 h1:lJJdtuNsP++XHD7tXDYEFSpsqIc7DzShuXMR5PwkmzA= go.opentelemetry.io/contrib v1.17.0/go.mod h1:gIzjwWFoGazJmtCaDgViqOSJPde2mCWzv60o0bWPcZs= +go.opentelemetry.io/contrib/propagators/b3 v1.17.0 h1:ImOVvHnku8jijXqkwCSyYKRDt2YrnGXD4BbhcpfbfJo= +go.opentelemetry.io/contrib/propagators/b3 v1.17.0/go.mod h1:IkfUfMpKWmynvvE0264trz0sf32NRTZL4nuAN9AbWRc= go.opentelemetry.io/otel v1.26.0 h1:LQwgL5s/1W7YiiRwxf03QGnWLb2HW4pLiAhaA5cZXBs= go.opentelemetry.io/otel v1.26.0/go.mod h1:UmLkJHUAidDval2EICqBMbnAd0/m2vmpf/dAM+fvFs4= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.26.0 h1:0W5o9SzoR15ocYHEQfvfipzcNog1lBxOLfnex91Hk6s= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.26.0/go.mod h1:zVZ8nz+VSggWmnh6tTsJqXQ7rU4xLwRtna1M4x5jq58= go.opentelemetry.io/otel/metric v1.26.0 h1:7S39CLuY5Jgg9CrnA9HHiEjGMF/X2VHvoXGgSllRz30= go.opentelemetry.io/otel/metric v1.26.0/go.mod h1:SY+rHOI4cEawI9a7N1A4nIg/nTQXe1ccCNWYOJUrpX4= +go.opentelemetry.io/otel/oteltest v1.0.0-RC3 h1:MjaeegZTaX0Bv9uB9CrdVjOFM/8slRjReoWoV9xDCpY= +go.opentelemetry.io/otel/oteltest v1.0.0-RC3/go.mod h1:xpzajI9JBRr7gX63nO6kAmImmYIAtuQblZ36Z+LfCjE= go.opentelemetry.io/otel/sdk v1.26.0 h1:Y7bumHf5tAiDlRYFmGqetNcLaVUZmh4iYfmGxtmz7F8= go.opentelemetry.io/otel/sdk v1.26.0/go.mod h1:0p8MXpqLeJ0pzcszQQN4F0S5FVjBLgypeGSngLsmirs= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= go.opentelemetry.io/otel/trace v1.26.0 h1:1ieeAUb4y0TE26jUFrCIXKpTuVK7uJGN9/Z/2LP5sQA= go.opentelemetry.io/otel/trace v1.26.0/go.mod h1:4iDxvGDQuUkHve82hJJ8UqrwswHYsZuWCBllGV2U2y0= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= diff --git a/backend/server/server.go b/backend/server/server.go index fcd95b3d6..ebb1e8ac7 100644 --- a/backend/server/server.go +++ b/backend/server/server.go @@ -99,6 +99,9 @@ func newFiberApp(appSettings config.ApplicationSettings) *fiber.App { app.Use(cors.New(cors.Config{ AllowOrigins: fmt.Sprintf("http://%s:%d", appSettings.Host, appSettings.Port), AllowCredentials: true, + AllowHeaders: "Origin, Content-Type, Accept, Authorization", + AllowMethods: "GET, POST, PUT, DELETE, OPTIONS", + ExposeHeaders: "Authorization, Content-Length, Content-Type", })) app.Use(requestid.New()) From d27d776785ecb0876a7d11a856a5abe56a9beaf9 Mon Sep 17 00:00:00 2001 From: David Oduneye <44040421+DOOduneye@users.noreply.github.com> Date: Sun, 19 May 2024 16:45:06 -0400 Subject: [PATCH 4/6] fix: refresh checks the cookies over request body (#853) --- backend/entities/auth/base/controller.go | 8 ++++---- backend/entities/auth/base/models.go | 4 ++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/backend/entities/auth/base/controller.go b/backend/entities/auth/base/controller.go index cb29fe93d..48396131e 100644 --- a/backend/entities/auth/base/controller.go +++ b/backend/entities/auth/base/controller.go @@ -88,8 +88,8 @@ func (a *AuthController) Register(c *fiber.Ctx) error { // Refresh godoc // -// @Summary Refreshes a user's access token -// @Description Refreshes a user's access token +// @Summary Refreshes a user's access token and returns a new pair of tokens +// @Description Refreshes a user's access token and returns a new pair of tokens // @ID refresh-user // @Tags auth // @Accept json @@ -100,9 +100,9 @@ func (a *AuthController) Register(c *fiber.Ctx) error { // @Failure 500 {object} error // @Router /auth/refresh [post] func (a *AuthController) Refresh(c *fiber.Ctx) error { - var refreshBody authEntities.RefreshTokenRequestBody + var refreshBody RefreshTokenCookieBody - if err := c.BodyParser(&refreshBody); err != nil { + if err := c.CookieParser(&refreshBody); err != nil { return utilities.InvalidJSON() } diff --git a/backend/entities/auth/base/models.go b/backend/entities/auth/base/models.go index 32f759f57..a15a48349 100644 --- a/backend/entities/auth/base/models.go +++ b/backend/entities/auth/base/models.go @@ -14,3 +14,7 @@ type VerifyPasswordResetTokenRequestBody struct { type EmailRequestBody struct { Email string `json:"email" validate:"required,email"` } + +type RefreshTokenCookieBody struct { + RefreshToken string `cookie:"refresh_token" validate:"required"` +} From 774f9e5ad77d1cc2374138c2d2b72cafb8686158 Mon Sep 17 00:00:00 2001 From: Garrett Ladley <92384606+garrettladley@users.noreply.github.com> Date: Sun, 19 May 2024 20:33:31 -0400 Subject: [PATCH 5/6] fix: update refresh error (#856) --- backend/entities/auth/base/controller.go | 2 +- backend/utilities/api_error.go | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/backend/entities/auth/base/controller.go b/backend/entities/auth/base/controller.go index 48396131e..0b2189416 100644 --- a/backend/entities/auth/base/controller.go +++ b/backend/entities/auth/base/controller.go @@ -103,7 +103,7 @@ func (a *AuthController) Refresh(c *fiber.Ctx) error { var refreshBody RefreshTokenCookieBody if err := c.CookieParser(&refreshBody); err != nil { - return utilities.InvalidJSON() + return utilities.InvalidCookies() } tokens, err := a.authService.Refresh(refreshBody.RefreshToken) diff --git a/backend/utilities/api_error.go b/backend/utilities/api_error.go index 7600ff9c3..f4ecc154e 100644 --- a/backend/utilities/api_error.go +++ b/backend/utilities/api_error.go @@ -58,6 +58,10 @@ func InvalidJSON() APIError { return NewAPIError(http.StatusBadRequest, fmt.Errorf("invalid JSON request data")) } +func InvalidCookies() APIError { + return NewAPIError(http.StatusBadRequest, fmt.Errorf("invalid cookies")) +} + func Unauthorized() APIError { return NewAPIError(http.StatusUnauthorized, fmt.Errorf("unauthorized")) } From 21ae8540c17328b56e0bcfa44cc5fb89775cb67e Mon Sep 17 00:00:00 2001 From: Garrett Ladley <92384606+garrettladley@users.noreply.github.com> Date: Sun, 19 May 2024 21:05:44 -0400 Subject: [PATCH 6/6] fix: update cli test commands (#857) --- cli/cmd/test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/cmd/test.go b/cli/cmd/test.go index ccf2d5b52..91a8c1f64 100644 --- a/cli/cmd/test.go +++ b/cli/cmd/test.go @@ -100,7 +100,7 @@ var testBackendCmd = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { clear, _ := cmd.Flags().GetBool("clean") - err := helpers.Execute(exec.Command("go", "test", "./..."), helpers.BACKEND_DIR) + err := helpers.Execute(exec.Command("go", "test", "-v", "-race", "./..."), helpers.BACKEND_DIR) if err != nil { fmt.Println(err) } @@ -120,7 +120,7 @@ var testCliCmd = &cobra.Command{ Short: "CLI testing commands", Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { - err := helpers.Execute(exec.Command("go", "test", "./..."), helpers.CLI_DIR) + err := helpers.Execute(exec.Command("go", "test", "-v", "-race", "./..."), helpers.CLI_DIR) if err != nil { fmt.Println(err) os.Exit(1)