diff --git a/.env.example b/.env.example index aaaf5d4..9b56022 100644 --- a/.env.example +++ b/.env.example @@ -25,4 +25,10 @@ SMTP_HOST=smtp.yandex.ru SMTP_PORT=587 SMTP_URL=${SMTP_HOST}:${SMTP_PORT} SMTP_USERNAME=YOUR_USERNAME -SMTP_PASSWORD=YOUR_PASSWORD \ No newline at end of file +SMTP_PASSWORD=YOUR_PASSWORD + +S3_ACCESS_TOKEN=YOUR_KEY_OR_USERNAME +S3_SECRET_TOKEN=YOUR_KEY_OR_PASSWORD +S3_REGION=eu-north-1 +S3_HOST=http://minio.kissota.ru:9001 +S3_BUCKET=taskem \ No newline at end of file diff --git a/Makefile b/Makefile index dd234fe..207fa70 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,12 @@ coverage: test submodule: @git submodule update --remote --recursive +server-compose: + docker compose build server && docker compose up server + +run-server: + go run apps/server/cmd/server/main.go + gen: @./scripts/gen_proto.sh diff --git a/apps/notification/Dockerfile b/apps/notification/Dockerfile index 111b2b8..3c0a2fe 100644 --- a/apps/notification/Dockerfile +++ b/apps/notification/Dockerfile @@ -12,17 +12,19 @@ ARG GO_VERSION=1.23.0 FROM --platform=$BUILDPLATFORM golang:${GO_VERSION} AS build WORKDIR /src +ARG PROJECT_PATH=apps/notification + LABEL org.opencontainers.image.source="https://github.com/taskemapp/notification" # Download dependencies as a separate step to take advantage of Docker's caching. # Leverage a cache mount to /go/pkg/mod/ to speed up subsequent builds. # Leverage bind mounts to go.sum and go.mod to avoid having to copy them into # the container. -RUN --mount=type=cache,target=/go/pkg/mod/ \ - --mount=type=bind,source=go.sum,target=go.sum \ - --mount=type=bind,source=go.mod,target=go.mod \ +RUN --mount=type=bind,source=${PROJECT_PATH}/go.sum,target=go.sum \ + --mount=type=bind,source=${PROJECT_PATH}/go.mod,target=go.mod \ go mod download -x + # This is the architecture you're building for, which is passed in by the builder. # Placing it here allows the previous steps to be cached across architectures. ARG TARGETARCH @@ -31,9 +33,8 @@ ARG TARGETARCH # Leverage a cache mount to /go/pkg/mod/ to speed up subsequent builds. # Leverage a bind mount to the current directory to avoid having to copy the # source code into the container. -RUN --mount=type=cache,target=/go/pkg/mod/ \ - --mount=type=bind,target=. \ - CGO_ENABLED=0 GOARCH=$TARGETARCH go build -o /bin/server ./cmd/server +RUN --mount=type=bind,source=./${PROJECT_PATH}/,target=. \ + CGO_ENABLED=0 GOARCH=$TARGETARCH go build -o /bin/notification ./cmd/notification ################################################################################ # Create a new stage for running the application that contains the minimal @@ -71,9 +72,9 @@ RUN adduser \ USER appuser # Copy the executable from the "build" stage. -COPY --from=build /bin/server /bin/ +COPY --from=build /bin/notification /bin/ # Expose the port that the application listens on. -EXPOSE 50051 +EXPOSE 50052 # What the container should run when it is started. -ENTRYPOINT [ "/bin/server" ] \ No newline at end of file +ENTRYPOINT [ "/bin/notification" ] \ No newline at end of file diff --git a/apps/notification/cmd/main.go b/apps/notification/cmd/notification/main.go similarity index 100% rename from apps/notification/cmd/main.go rename to apps/notification/cmd/notification/main.go diff --git a/apps/server/Dockerfile b/apps/server/Dockerfile index a4063a1..8d669a2 100644 --- a/apps/server/Dockerfile +++ b/apps/server/Dockerfile @@ -12,17 +12,19 @@ ARG GO_VERSION=1.23.0 FROM --platform=$BUILDPLATFORM golang:${GO_VERSION} AS build WORKDIR /src +ARG PROJECT_PATH=apps/server + LABEL org.opencontainers.image.source="https://github.com/taskemapp/server" # Download dependencies as a separate step to take advantage of Docker's caching. # Leverage a cache mount to /go/pkg/mod/ to speed up subsequent builds. # Leverage bind mounts to go.sum and go.mod to avoid having to copy them into # the container. -RUN --mount=type=cache,target=/go/pkg/mod/ \ - --mount=type=bind,source=go.sum,target=go.sum \ - --mount=type=bind,source=go.mod,target=go.mod \ +RUN --mount=type=bind,source=${PROJECT_PATH}/go.sum,target=go.sum \ + --mount=type=bind,source=${PROJECT_PATH}/go.mod,target=go.mod \ go mod download -x + # This is the architecture you're building for, which is passed in by the builder. # Placing it here allows the previous steps to be cached across architectures. ARG TARGETARCH @@ -31,8 +33,7 @@ ARG TARGETARCH # Leverage a cache mount to /go/pkg/mod/ to speed up subsequent builds. # Leverage a bind mount to the current directory to avoid having to copy the # source code into the container. -RUN --mount=type=cache,target=/go/pkg/mod/ \ - --mount=type=bind,target=. \ +RUN --mount=type=bind,source=./${PROJECT_PATH}/,target=. \ CGO_ENABLED=0 GOARCH=$TARGETARCH go build -o /bin/server ./cmd/server ################################################################################ @@ -73,8 +74,9 @@ USER appuser # Copy the executable from the "build" stage. COPY --from=build /bin/server /bin/ -COPY ../ . -#COPY migrations migrations +# Copy the migrations directory +COPY migrations migrations + # Expose the port that the application listens on. EXPOSE 50051 diff --git a/apps/server/cmd/server/main.go b/apps/server/cmd/server/main.go index d71e66b..eb673f7 100644 --- a/apps/server/cmd/server/main.go +++ b/apps/server/cmd/server/main.go @@ -2,6 +2,7 @@ package main import ( "context" + "github.com/taskemapp/server/apps/server/internal/app" "go.uber.org/fx" ) diff --git a/apps/server/e2e_tests/auth_test.go b/apps/server/e2e_tests/auth_test.go index 9d17f68..8c09a6b 100644 --- a/apps/server/e2e_tests/auth_test.go +++ b/apps/server/e2e_tests/auth_test.go @@ -3,17 +3,22 @@ package e2e_tests import ( "context" "fmt" + "testing" + "github.com/brianvoe/gofakeit/v7" "github.com/stretchr/testify/require" "github.com/taskemapp/server/apps/server/internal/config" + "github.com/taskemapp/server/apps/server/internal/pkg/s3" v1 "github.com/taskemapp/server/apps/server/tools/gen/grpc/v1" + "github.com/taskemapp/server/libs/queue" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" - "testing" ) var ( - cfg, _ = config.New() + qCfg, _ = queue.NewConfig() + s3Cfg, _ = s3.NewConfig() + cfg, _ = config.New(qCfg, s3Cfg) testEmail = gofakeit.Email() testPassword = gofakeit.Password(true, true, true, true, false, 8) ) diff --git a/apps/server/go.mod b/apps/server/go.mod index 50cb4b4..f2006ee 100644 --- a/apps/server/go.mod +++ b/apps/server/go.mod @@ -20,8 +20,8 @@ require ( github.com/rabbitmq/amqp091-go v1.10.0 github.com/samber/lo v1.47.0 github.com/stretchr/testify v1.9.0 - github.com/taskemapp/server/apps/notification v0.0.0-20240823134806-8d5115da9e2c - github.com/taskemapp/server/libs/queue v0.0.0-20240831183951-3fedede642c3 + github.com/taskemapp/server/apps/notification v0.0.0-20240920224238-1123d7cd13f6 + github.com/taskemapp/server/libs/queue v0.0.0-20240906120508-8de35b503387 github.com/taskemapp/server/libs/template v0.0.0-20240912173021-e7f2908c811b go.uber.org/fx v1.22.2 go.uber.org/multierr v1.11.0 diff --git a/apps/server/go.sum b/apps/server/go.sum index e880903..6d5f419 100644 --- a/apps/server/go.sum +++ b/apps/server/go.sum @@ -101,10 +101,10 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/taskemapp/server/apps/notification v0.0.0-20240823134806-8d5115da9e2c h1:L4DI7XVHfb2xj4PgxT3d//Lc5g1o/nfsE7dthXRm1cQ= -github.com/taskemapp/server/apps/notification v0.0.0-20240823134806-8d5115da9e2c/go.mod h1:bWtPKaT7sO840/AcRDyMcCBhWXUGqlsOcBdh+7TLxqI= -github.com/taskemapp/server/libs/queue v0.0.0-20240831183951-3fedede642c3 h1:HN5SgxFwgMPb0nB/lSPnDPADRNkytR0S5MymoN0vWOU= -github.com/taskemapp/server/libs/queue v0.0.0-20240831183951-3fedede642c3/go.mod h1:nwYETy6ruFQx4XwfJr7K1cQ3hnf1oEgpP4ds7KJqaxE= +github.com/taskemapp/server/apps/notification v0.0.0-20240920224238-1123d7cd13f6 h1:1W8X3MvcpRiBV1Wj2gBqe5HtomjQHHvVsn4WQ+N09N0= +github.com/taskemapp/server/apps/notification v0.0.0-20240920224238-1123d7cd13f6/go.mod h1:ZbvCuDqNFM8pgNFYsJG5b7QfSNOrqg4SWd/IO814Ttc= +github.com/taskemapp/server/libs/queue v0.0.0-20240906120508-8de35b503387 h1:SMkXkBnYpmTujOTycchi4jpfsgqKDhBLuA0es8/ICRs= +github.com/taskemapp/server/libs/queue v0.0.0-20240906120508-8de35b503387/go.mod h1:nwYETy6ruFQx4XwfJr7K1cQ3hnf1oEgpP4ds7KJqaxE= github.com/taskemapp/server/libs/template v0.0.0-20240912173021-e7f2908c811b h1:vYfuwHvrq+OabVziP87hQTOc+ta5xasi6yOxFaIbT04= github.com/taskemapp/server/libs/template v0.0.0-20240912173021-e7f2908c811b/go.mod h1:c0QW0dsaeGAHACHELm5FruaoWCHZ5FYGvo67xx+J1+U= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= diff --git a/apps/server/internal/app/app.go b/apps/server/internal/app/app.go index 7ee6078..5318bbc 100644 --- a/apps/server/internal/app/app.go +++ b/apps/server/internal/app/app.go @@ -2,26 +2,26 @@ package app import ( "context" - "database/sql" "fmt" + "github.com/taskemapp/server/apps/server/internal/pkg/notifier" + "net/url" + "github.com/go-redis/redis/v8" "github.com/jackc/pgx/v5/pgxpool" _ "github.com/jackc/pgx/v5/stdlib" - "github.com/pressly/goose/v3" amqp "github.com/rabbitmq/amqp091-go" "github.com/taskemapp/server/apps/server/internal/app/auth" + "github.com/taskemapp/server/apps/server/internal/app/grpc" grpcsrv "github.com/taskemapp/server/apps/server/internal/app/grpc" "github.com/taskemapp/server/apps/server/internal/app/task" "github.com/taskemapp/server/apps/server/internal/app/team" "github.com/taskemapp/server/apps/server/internal/config" "github.com/taskemapp/server/apps/server/internal/grpc/interceptors" + "github.com/taskemapp/server/apps/server/internal/pkg/migrations" + "github.com/taskemapp/server/apps/server/internal/pkg/s3" "github.com/taskemapp/server/libs/queue" "go.uber.org/fx" "go.uber.org/zap" - "google.golang.org/grpc" - "google.golang.org/grpc/reflection" - "net" - "net/url" ) const ( @@ -35,11 +35,18 @@ var App = fx.Options( fx.Provide(setupPgPool), fx.Provide(setupRabbitMq), fx.Provide(setupRedisClient), + fx.Provide(fx.Annotate(func(cfg config.Config) *notifier.BasicGenerator { + return ¬ifier.BasicGenerator{HostDomain: cfg.HostDomain} + }, fx.As(new(notifier.LinkGenerator)))), //RabbitMq fx.Provide(queue.NewConfig), fx.Provide(fx.Annotate(queue.NewMQ, fx.As(new(queue.Queue)))), + //S3 + fx.Provide(s3.NewConfig), + fx.Provide(s3.New), + //General app auth.App, team.App, @@ -48,64 +55,24 @@ var App = fx.Options( fx.Provide(grpcsrv.New), fx.Invoke( - func(p *pgxpool.Pool, c config.Config, log *zap.Logger) error { - if err := goose.SetDialect("pgx"); err != nil { - log.Sugar().Error("Failed to set dialect: ", err) - return err - } - db, err := sql.Open("pgx", c.PostgresUrl) - if err != nil { - log.Sugar().Error("Failed to open db conn: ", err) - return err - } - defer db.Close() - - log.Sugar().Info("Run migrations") - err = goose.Up(db, "migrations") - if err != nil { - log.Sugar().Error("Migration failed: ", err) - return err - } - - return nil - }, - func(lc fx.Lifecycle, log *zap.Logger, c config.Config, srv *grpc.Server) { - lc.Append( - fx.Hook{ - OnStart: func(ctx context.Context) error { - log.Sugar().Infof("Server starting on port %d", c.GrpcPort) - - l, err := net.Listen("tcp", fmt.Sprintf(":%d", c.GrpcPort)) - if err != nil { - return err - } - - reflection.Register(srv) - - go func() { - err = srv.Serve(l) - if err != nil { - log.Error(err.Error()) - return - } - }() - - return nil - }, - OnStop: func(ctx context.Context) error { - log.Sugar().Info("Gracefully stopping grpc server") - srv.GracefulStop() - - return nil - }, - }, - ) - }, + migrations.Invoke, + s3.Invoke, + grpc.Invoke, ), ) func setupConfig() (config.Config, error) { - cfg, err := config.New() + var cfg config.Config + qCfg, err := queue.NewConfig() + if err != nil { + return cfg, err + } + s3Cfg, err := s3.NewConfig() + if err != nil { + return cfg, err + } + + cfg, err = config.New(qCfg, s3Cfg) if err != nil { return cfg, err } diff --git a/apps/server/internal/app/grpc/grpc.go b/apps/server/internal/app/grpc/grpc.go index 4f69f5f..32675b7 100644 --- a/apps/server/internal/app/grpc/grpc.go +++ b/apps/server/internal/app/grpc/grpc.go @@ -3,10 +3,13 @@ package grpc import ( "context" "fmt" + "net" + authMd "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/auth" "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging" "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/recovery" "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/selector" + "github.com/taskemapp/server/apps/server/internal/config" "github.com/taskemapp/server/apps/server/internal/grpc/auth" "github.com/taskemapp/server/apps/server/internal/grpc/interceptors" "github.com/taskemapp/server/apps/server/internal/grpc/team" @@ -15,6 +18,7 @@ import ( "go.uber.org/zap" "google.golang.org/grpc" "google.golang.org/grpc/codes" + "google.golang.org/grpc/reflection" "google.golang.org/grpc/status" ) @@ -65,6 +69,39 @@ func New(opts Opts) App { return App{Srv: srv} } +func Invoke(lc fx.Lifecycle, log *zap.Logger, c config.Config, srv *grpc.Server) { + lc.Append( + fx.Hook{ + OnStart: func(ctx context.Context) error { + log.Sugar().Infof("Server starting on port %d", c.GrpcPort) + + l, err := net.Listen("tcp", fmt.Sprintf(":%d", c.GrpcPort)) + if err != nil { + return err + } + + reflection.Register(srv) + + go func() { + err = srv.Serve(l) + if err != nil { + log.Error(err.Error()) + return + } + }() + + return nil + }, + OnStop: func(ctx context.Context) error { + log.Sugar().Info("Gracefully stopping grpc server") + srv.GracefulStop() + + return nil + }, + }, + ) +} + // interceptorLogger Retrieved from // https://github.com/grpc-ecosystem/go-grpc-middleware/blob/62b7de50cda5a5d633f1013bfbe50e0f38db34ef/interceptors/logging/examples/zap/example_test.go#L17 func interceptorLogger(l *zap.Logger) logging.Logger { diff --git a/apps/server/internal/config/config.go b/apps/server/internal/config/config.go index 0a77b89..bbf93db 100644 --- a/apps/server/internal/config/config.go +++ b/apps/server/internal/config/config.go @@ -3,6 +3,7 @@ package config import ( "github.com/joho/godotenv" "github.com/kelseyhightower/envconfig" + "github.com/taskemapp/server/apps/server/internal/pkg/s3" "github.com/taskemapp/server/libs/queue" "os" "path/filepath" @@ -28,14 +29,13 @@ type Config struct { HostDomain string `envconfig:"HOST_DOMAIN"` - S3Host string `envconfig:"S3_HOST"` - S3AccessToken string `envconfig:"S3_ACCESS_TOKEN"` - S3SecretToken string `envconfig:"S3_SECRET_TOKEN"` - S3Region string `envconfig:"S3_REGION"` - S3Bucket string `envconfig:"S3_BUCKET"` + S3 s3.Config } -func New() (Config, error) { +func New( + qCfg queue.Config, + s3Cfg s3.Config, +) (Config, error) { cfg := Config{} wd, err := os.Getwd() @@ -51,12 +51,8 @@ func New() (Config, error) { return cfg, err } - mqConfig, err := queue.NewConfig() - if err != nil { - panic(err) - } - - cfg.RabbitMq = mqConfig + cfg.RabbitMq = qCfg + cfg.S3 = s3Cfg return cfg, nil } diff --git a/apps/server/internal/grpc/interceptors/matcher.go b/apps/server/internal/grpc/interceptors/matcher.go index 3e929d8..2caaf5c 100644 --- a/apps/server/internal/grpc/interceptors/matcher.go +++ b/apps/server/internal/grpc/interceptors/matcher.go @@ -2,6 +2,7 @@ package interceptors import ( "context" + "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors" ) diff --git a/apps/server/internal/pkg/migrations/migrations.go b/apps/server/internal/pkg/migrations/migrations.go new file mode 100644 index 0000000..c903fc7 --- /dev/null +++ b/apps/server/internal/pkg/migrations/migrations.go @@ -0,0 +1,30 @@ +package migrations + +import ( + "database/sql" + "github.com/pressly/goose/v3" + "github.com/taskemapp/server/apps/server/internal/config" + "go.uber.org/zap" +) + +func Invoke(c config.Config, log *zap.Logger) error { + if err := goose.SetDialect("pgx"); err != nil { + log.Sugar().Error("Failed to set dialect: ", err) + return err + } + db, err := sql.Open("pgx", c.PostgresUrl) + if err != nil { + log.Sugar().Error("Failed to open db conn: ", err) + return err + } + defer db.Close() + + log.Sugar().Info("Run migrations") + err = goose.Up(db, "migrations") + if err != nil { + log.Sugar().Error("Migration failed: ", err) + return err + } + + return nil +} diff --git a/apps/server/internal/pkg/notifier/account.go b/apps/server/internal/pkg/notifier/account.go index 85473de..3ceb088 100644 --- a/apps/server/internal/pkg/notifier/account.go +++ b/apps/server/internal/pkg/notifier/account.go @@ -56,7 +56,7 @@ func (n *EmailAccountNotifier) VerifyEmail(username string, email string) error ConfirmationLink: confirmLink, UnsubscribeLink: unsubLink, }, - title: "", + title: "Verify your email", to: email, from: n.config.NoReplayEmail, }) diff --git a/apps/server/internal/pkg/notifier/account_test.go b/apps/server/internal/pkg/notifier/account_test.go index 9d9bb88..730fcd1 100644 --- a/apps/server/internal/pkg/notifier/account_test.go +++ b/apps/server/internal/pkg/notifier/account_test.go @@ -4,6 +4,8 @@ import ( "bytes" "encoding/json" "fmt" + "testing" + "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -11,7 +13,6 @@ import ( "github.com/taskemapp/server/apps/server/internal/config" "github.com/taskemapp/server/libs/queue" "github.com/taskemapp/server/libs/template" - "testing" ) type testGenerator struct { @@ -63,6 +64,7 @@ func setupMock(t *testing.T, tt args, generator LinkGenerator) *queue.MockQueue To: tt.to, From: tt.from, }) + assert.NoError(t, err) message := queue.Message{ ContentType: "application/json", @@ -79,7 +81,7 @@ func TestEmailAccountNotifier_VerifyEmail(t *testing.T) { tt := args{ name: "ripls", to: "ripls@taskem.test", - title: "", + title: "Verify your email", from: cfg.NoReplayEmail, } diff --git a/apps/server/internal/pkg/notifier/email.go b/apps/server/internal/pkg/notifier/email.go index 257d859..05db829 100644 --- a/apps/server/internal/pkg/notifier/email.go +++ b/apps/server/internal/pkg/notifier/email.go @@ -3,10 +3,11 @@ package notifier import ( "bytes" "encoding/json" + "html/template" + "github.com/go-faster/errors" "github.com/taskemapp/server/apps/notification/pkg/notifier" "github.com/taskemapp/server/libs/queue" - "html/template" ) type sendEmailOpts struct { diff --git a/apps/server/internal/pkg/s3/config.go b/apps/server/internal/pkg/s3/config.go new file mode 100644 index 0000000..4edb43a --- /dev/null +++ b/apps/server/internal/pkg/s3/config.go @@ -0,0 +1,37 @@ +package s3 + +import ( + "os" + "path/filepath" + + "github.com/joho/godotenv" + "github.com/kelseyhightower/envconfig" +) + +type Config struct { + Host string `envconfig:"S3_HOST"` + AccessToken string `envconfig:"S3_ACCESS_TOKEN"` + SecretToken string `envconfig:"S3_SECRET_TOKEN"` + Region string `envconfig:"S3_REGION"` + Bucket string `envconfig:"S3_BUCKET"` + Secure bool `envconfig:"S3_SECURE"` +} + +func NewConfig() (Config, error) { + cfg := Config{} + + wd, err := os.Getwd() + if err != nil { + return cfg, err + } + + envPath := filepath.Join(wd, ".env") + + _ = godotenv.Load(envPath) + + if err := envconfig.Process("", &cfg); err != nil { + return cfg, err + } + + return cfg, nil +} diff --git a/apps/server/internal/pkg/s3/s3.go b/apps/server/internal/pkg/s3/s3.go new file mode 100644 index 0000000..1179ae4 --- /dev/null +++ b/apps/server/internal/pkg/s3/s3.go @@ -0,0 +1,77 @@ +package s3 + +import ( + "context" + "fmt" + "time" + + "github.com/go-faster/errors" + "github.com/minio/minio-go/v7" + "github.com/minio/minio-go/v7/pkg/credentials" + "go.uber.org/fx" + "go.uber.org/zap" +) + +func New(cfg Config) (*minio.Client, error) { + c, err := minio.New(cfg.Host, &minio.Options{ + Creds: credentials.NewStaticV4("root", "password", ""), + Secure: cfg.Secure, + }) + if err != nil { + return nil, errors.Wrap(err, "s3 new") + } + + return c, nil +} + +func Invoke(lc fx.Lifecycle, log *zap.Logger, cfg Config, c *minio.Client) { + lc.Append( + fx.Hook{ + OnStart: func(ctx context.Context) error { + log.Info("Listing buckets") + ctx, cancel := context.WithTimeout(ctx, time.Second*2) + defer cancel() + + ok, err := c.BucketExists(ctx, cfg.Bucket) + if err != nil { + return fmt.Errorf("cannot list buckets: %w", err) + } + + log.Sugar().Debug("Bucket exist: ", ok) + + if !ok { + err = c.MakeBucket(ctx, cfg.Bucket, minio.MakeBucketOptions{}) + if err != nil { + return fmt.Errorf("cannot create bucket: %w", err) + } + } + + err = c.SetBucketPolicy( + ctx, + cfg.Bucket, + `{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": "*", + "Action": ["s3:GetObject"], + "Resource": [ + "arn:aws:s3:::`+cfg.Bucket+`/team/*/header.webp", + "arn:aws:s3:::`+cfg.Bucket+`/user/*/avatar.webp" + ] + } + ] + }`, + ) + + if err != nil { + return fmt.Errorf("cannot set bucket policy: %w", err) + } + + return nil + }, + OnStop: nil, + }, + ) +} diff --git a/apps/server/internal/repositories/user_files/file.go b/apps/server/internal/repositories/user_files/file.go new file mode 100644 index 0000000..a7dcb05 --- /dev/null +++ b/apps/server/internal/repositories/user_files/file.go @@ -0,0 +1,34 @@ +package user_files + +import ( + "context" + + "github.com/google/uuid" +) + +type Repository interface { + Create(ctx context.Context, opts CreateUserFileOpts) (*UserFile, error) + Update(ctx context.Context, opts UpdateUserFileOpts) (*UserFile, error) +} + +type CreateUserFileOpts struct { + UserID uuid.UUID + CdnPath string + FileName string + MimeType string +} + +type UpdateUserFileOpts struct { + UserID uuid.UUID + CdnPath string + FileName string + MimeType string +} + +type UserFile struct { + ID uuid.UUID + UserID uuid.UUID + CdnPath string + FileName string + MimeType string +} diff --git a/compose.yaml b/compose.yaml index fc4c684..91e41f2 100644 --- a/compose.yaml +++ b/compose.yaml @@ -32,29 +32,29 @@ services: interval: 10s timeout: 5s retries: 5 - smtp: - image: ghcr.io/docker-mailserver/docker-mailserver:latest - restart: unless-stopped + # smtp: + # image: ghcr.io/docker-mailserver/docker-mailserver:latest + # restart: unless-stopped - listmonk: - image: listmonk/listmonk:latest - restart: unless-stopped - depends_on: - - db - ports: - - "9000:9000" - command: [ sh, -c, "yes | ./listmonk --install --config=\"\" && ./listmonk" ] - environment: - LISTMONK_app__address: "0.0.0.0:9000" - LISTMONK_app__admin_username: taskem - LISTMONK_app__admin_password: taskem - LISTMONK_db__host: db - LISTMONK_db__port: 5432 - LISTMONK_db__user: taskem - LISTMONK_db__password: taskem - LISTMONK_db__database: taskem - LISTMONK_db__ssl_mode: disable + # listmonk: + # image: listmonk/listmonk:latest + # restart: unless-stopped + # depends_on: + # - db + # ports: + # - "9000:9000" + # command: [ sh, -c, "yes | ./listmonk --install --config=\"\" && ./listmonk" ] + # environment: + # LISTMONK_app__address: "0.0.0.0:9000" + # LISTMONK_app__admin_username: taskem + # LISTMONK_app__admin_password: taskem + # LISTMONK_db__host: db + # LISTMONK_db__port: 5432 + # LISTMONK_db__user: taskem + # LISTMONK_db__password: taskem + # LISTMONK_db__database: taskem + # LISTMONK_db__ssl_mode: disable # redis: # image: redis @@ -67,22 +67,22 @@ services: # timeout: 5s # retries: 5 - # minio: - # image: quay.io/minio/minio - # restart: always - # command: server /data --console-address ":9001" - # ports: - # - "12200:9000" - # - "9001:9001" - # environment: - # MINIO_ROOT_USER: root - # MINIO_ROOT_PASSWORD: YOUR_PASSWORD - # MINIO_DOMAIN: minio - # networks: - # default: - # aliases: - # - users.minio - # - teams.minio + minio: + image: quay.io/minio/minio + restart: always + command: server /data --console-address ":9001" + ports: + - "12200:9000" + - "9001:9001" + environment: + MINIO_ROOT_USER: root + MINIO_ROOT_PASSWORD: password + MINIO_DOMAIN: minio + networks: + default: + aliases: + - users.minio + - teams.minio rabbitmq: image: rabbitmq:3.13.6-management @@ -94,25 +94,30 @@ services: - "8080:15672" server: - env_file: .env.dev + env_file: .env build: - context: ./apps/server/ + context: . + dockerfile: apps/server/Dockerfile target: final depends_on: - db - redis + - minio + - rabbitmq ports: - "50051:50051" -# notification: -# env_file: ".env" -# build: -# context: ./apps/server/ -# target: final -# depends_on: -# - db -# ports: -# - 50051:50051 + notification: + env_file: .env + build: + context: . + dockerfile: apps/notification/Dockerfile + target: final + depends_on: + - db + - rabbitmq + ports: + - "50052:50052" volumes: db-data: diff --git a/libs/queue/config.go b/libs/queue/config.go index 4601da4..569dbb4 100644 --- a/libs/queue/config.go +++ b/libs/queue/config.go @@ -1,10 +1,11 @@ package queue import ( - "github.com/joho/godotenv" - "github.com/kelseyhightower/envconfig" "os" "path/filepath" + + "github.com/joho/godotenv" + "github.com/kelseyhightower/envconfig" ) type Config struct {