diff --git a/.github/workflows/deploy-dev.yaml b/.github/workflows/deploy-dev.yaml new file mode 100644 index 0000000..bbd9123 --- /dev/null +++ b/.github/workflows/deploy-dev.yaml @@ -0,0 +1,111 @@ +name: Build and Push Docker Image + +on: + push: + branches: + - develop +env: + ENV: dev + AWS_REGION: ap-northeast-1 + AWS_ACCOUNT_ID: 905418376731 + +jobs: + build-and-push: + outputs: + ecr_image_name: ${{ steps.set_outputs.outputs.ecr_image_name }} + runs-on: ubuntu-latest + permissions: + packages: write + id-token: write + contents: read + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: configure aws credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: arn:aws:iam::905418376731:role/magische-${{ env.ENV }}-api-deploy + aws-region: ${{ env.AWS_REGION }} + + - name: login to ecr + uses: aws-actions/amazon-ecr-login@v1 + id: login-ecr + + - id: set_env + name: set env + run: | + echo "image_tag=build-$(date +%Y%m%d)-${{ github.sha }}" >> $GITHUB_OUTPUT + echo "ecr_repository_name=${{ steps.login-ecr.outputs.registry }}/magische-${{ env.ENV }}-api" >> $GITHUB_OUTPUT + + - id: set_outputs + name: set outputs + run: | + echo "ecr_image_name=${{ steps.set_env.outputs.ecr_repository_name }}:${{ steps.set_env.outputs.image_tag }}" >> $GITHUB_OUTPUT + + - uses: docker/metadata-action@v5 + id: meta + with: + images: | + ghcr.io/${{ github.repository }}/server + ${{ steps.set_env.outputs.ecr_repository_name }} + tags: | + type=raw,value=${{ steps.set_env.outputs.image_tag }} + type=raw,value=latest + + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: . + cache-from: type=gha + cache-to: type=gha,mode=max + file: ./Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + target: deploy + platforms: linux/amd64,linux/arm64 + + deploy: + permissions: + id-token: write + contents: read + needs: [build-and-push] + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: configure aws credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + role-to-assume: arn:aws:iam::905418376731:role/magische-${{ env.ENV }}-api-deploy + aws-region: ${{ env.AWS_REGION }} + + - uses: kayac/ecspresso@v2 + with: + version-file: ./infra/ecs/.ecspresso-version + + - working-directory: ./infra/ecs + run: | + ecspresso deploy --config ecspresso.yml + env: + AWS_REGION: ${{ env.AWS_REGION }} + AWS_ACCOUNT_ID: ${{ env.AWS_ACCOUNT_ID }} + ENV: ${{ env.ENV }} + IMAGE_NAME: ${{ needs.build-and-push.outputs.ecr_image_name }} + CPU: 256 + MEMORY: 512 + CPU_ARCHITECTURE: ARM64 diff --git a/.github/workflows/deploy-prd.yaml b/.github/workflows/deploy-prd.yaml new file mode 100644 index 0000000..d7faef2 --- /dev/null +++ b/.github/workflows/deploy-prd.yaml @@ -0,0 +1,111 @@ +name: Build and Push Docker Image + +on: + push: + branches: + - main +env: + ENV: prd + AWS_REGION: ap-northeast-1 + AWS_ACCOUNT_ID: 905418376731 + +jobs: + build-and-push: + outputs: + ecr_image_name: ${{ steps.set_outputs.outputs.ecr_image_name }} + runs-on: ubuntu-latest + permissions: + packages: write + id-token: write + contents: read + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: configure aws credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: arn:aws:iam::905418376731:role/magische-${{ env.ENV }}-api-deploy + aws-region: ${{ env.AWS_REGION }} + + - name: login to ecr + uses: aws-actions/amazon-ecr-login@v1 + id: login-ecr + + - id: set_env + name: set env + run: | + echo "image_tag=build-$(date +%Y%m%d)-${{ github.sha }}" >> $GITHUB_OUTPUT + echo "ecr_repository_name=${{ steps.login-ecr.outputs.registry }}/magische-${{ env.ENV }}-api" >> $GITHUB_OUTPUT + + - id: set_outputs + name: set outputs + run: | + echo "ecr_image_name=${{ steps.set_env.outputs.ecr_repository_name }}:${{ steps.set_env.outputs.image_tag }}" >> $GITHUB_OUTPUT + + - uses: docker/metadata-action@v5 + id: meta + with: + images: | + ghcr.io/${{ github.repository }}/server + ${{ steps.set_env.outputs.ecr_repository_name }} + tags: | + type=raw,value=${{ steps.set_env.outputs.image_tag }} + type=raw,value=latest + + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: . + cache-from: type=gha + cache-to: type=gha,mode=max + file: ./Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + target: deploy + platforms: linux/amd64,linux/arm64 + + deploy: + permissions: + id-token: write + contents: read + needs: [build-and-push] + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: configure aws credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + role-to-assume: arn:aws:iam::905418376731:role/magische-${{ env.ENV }}-api-deploy + aws-region: ${{ env.AWS_REGION }} + + - uses: kayac/ecspresso@v2 + with: + version-file: ./infra/ecs/.ecspresso-version + + - working-directory: ./infra/ecs + run: | + ecspresso deploy --config ecspresso.yml + env: + AWS_REGION: ${{ env.AWS_REGION }} + AWS_ACCOUNT_ID: ${{ env.AWS_ACCOUNT_ID }} + ENV: ${{ env.ENV }} + IMAGE_NAME: ${{ needs.build-and-push.outputs.ecr_image_name }} + CPU: 256 + MEMORY: 512 + CPU_ARCHITECTURE: ARM64 diff --git a/Dockerfile b/Dockerfile index 27c8be6..bbedd91 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # build for deploy -FROM golang:1.21-bullseye as deploy-builder +FROM golang:1.22-bullseye as deploy-builder WORKDIR /app @@ -33,7 +33,7 @@ CMD ["/app/migrate"] # for local development with air -FROM golang:1.21-bullseye as dev +FROM golang:1.22-bullseye as dev WORKDIR /app RUN go install github.com/cosmtrek/air@latest && \ go install github.com/volatiletech/sqlboiler/v4@latest && \ diff --git a/config/config.go b/config/config.go index ba2b147..364806b 100644 --- a/config/config.go +++ b/config/config.go @@ -8,7 +8,7 @@ import ( ) type Config struct { - Port int `env:"PORT" envDefault:"80"` + Port int `env:"PORT" envDefault:"8080"` Env string `env:"ENV" envDefault:"dev"` BaseURL string `env:"BASE_URL" envDefault:"http://localhost:8080"` SecretKey string `env:"SECRET_KEY" envDefault:"secret"` diff --git a/db/db.go b/db/db.go index 7c96364..6766b00 100644 --- a/db/db.go +++ b/db/db.go @@ -1,10 +1,8 @@ package db import ( - "context" "database/sql" "fmt" - "time" "github.com/geekcamp-vol11-team30/backend/config" "go.uber.org/zap" @@ -42,11 +40,11 @@ func NewDB(cfg *config.Config, logger *zap.Logger) (*sql.DB, error) { return nil, err } - ctx, canncel := context.WithTimeout(context.Background(), 10*time.Second) - defer canncel() - if err := db.PingContext(ctx); err != nil { - logger.Error("failed to ping db", zap.Error(err)) - return nil, err - } + // ctx, canncel := context.WithTimeout(context.Background(), 10*time.Second) + // defer canncel() + // if err := db.PingContext(ctx); err != nil { + // logger.Error("failed to ping db", zap.Error(err)) + // return nil, err + // } return db, nil } diff --git a/infra/ecs/.ecspresso-version b/infra/ecs/.ecspresso-version new file mode 100644 index 0000000..f90b1af --- /dev/null +++ b/infra/ecs/.ecspresso-version @@ -0,0 +1 @@ +2.3.2 diff --git a/infra/ecs/ecs-task-def.jsonnet b/infra/ecs/ecs-task-def.jsonnet new file mode 100644 index 0000000..1f0ccf5 --- /dev/null +++ b/infra/ecs/ecs-task-def.jsonnet @@ -0,0 +1,55 @@ +{ + containerDefinitions: [ + { + cpu: 0, + essential: true, + image: '{{ must_env `IMAGE_NAME` }}', + logConfiguration: { + logDriver: 'awslogs', + options: { + 'awslogs-group': '/ecs/magische-{{ must_env `ENV` }}-api-server', + 'awslogs-region': '{{ must_env `AWS_REGION` }}', + 'awslogs-stream-prefix': 'magische-{{ must_env `ENV` }}-api-server', + }, + }, + name: 'magische-{{ must_env `ENV` }}-api', + portMappings: [ + { + appProtocol: '', + containerPort: 8080, + hostPort: 8080, + protocol: 'tcp', + }, + ], + }, + ], + cpu: '{{ must_env `CPU` }}', + executionRoleArn: 'arn:aws:iam::905418376731:role/magische-{{ must_env `ENV` }}-api-server-task-exec', + family: 'magische-{{ must_env `ENV` }}-api', + ipcMode: '', + memory: '{{ must_env `MEMORY` }}', + networkMode: 'awsvpc', + pidMode: '', + requiresCompatibilities: [ + 'FARGATE', + ], + runtimePlatform: { + cpuArchitecture: '{{ must_env `CPU_ARCHITECTURE` }}', + operatingSystemFamily: 'LINUX', + }, + tags: [ + { + key: 'Env', + value: '{{ must_env `ENV` }}', + }, + { + key: 'Service', + value: 'api', + }, + { + key: 'Name', + value: 'magische-{{ must_env `ENV` }}-api-server', + }, + ], + taskRoleArn: 'arn:aws:iam::905418376731:role/magische-{{ must_env `ENV` }}-api-server-task', +} diff --git a/infra/ecs/ecspresso.jsonnet b/infra/ecs/ecspresso.jsonnet new file mode 100644 index 0000000..206bc2d --- /dev/null +++ b/infra/ecs/ecspresso.jsonnet @@ -0,0 +1,16 @@ +{ + region: 'ap-northeast-1', + cluster: 'magische-{{ must_env `ENV` }}', + service: 'magische-{{ must_env `ENV` }}-api', + service_definition: '', + task_definition: 'ecs-task-def.jsonnet', + timeout: '10m0s', + // plugins: [ + // { + // name: 'tfstate', + // config: { + // url: 'remote://app.terraform.io/magische/magische_infra_dev', + // }, + // }, + // ], +} diff --git a/logger/logger.go b/logger/logger.go index 1125718..e9024fa 100644 --- a/logger/logger.go +++ b/logger/logger.go @@ -99,9 +99,9 @@ func SetRequestLoggerToEcho(e *echo.Echo, logger *zap.Logger) { } e.Use(func(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { - if c.Path() == "/health" { - return next(c) - } + // if c.Path() == "/health" { + // return next(c) + // } return middleware.RequestLoggerWithConfig(cfg)(next)(c) } }) diff --git a/main.go b/main.go index c54e40e..096b553 100644 --- a/main.go +++ b/main.go @@ -6,15 +6,11 @@ import ( "net" "github.com/geekcamp-vol11-team30/backend/config" - "github.com/geekcamp-vol11-team30/backend/controller" "github.com/geekcamp-vol11-team30/backend/db" - "github.com/geekcamp-vol11-team30/backend/middleware" - "github.com/geekcamp-vol11-team30/backend/repository" - "github.com/geekcamp-vol11-team30/backend/router" - "github.com/geekcamp-vol11-team30/backend/service" - "github.com/geekcamp-vol11-team30/backend/usecase" - "github.com/geekcamp-vol11-team30/backend/validator" + applogger "github.com/geekcamp-vol11-team30/backend/logger" + "github.com/geekcamp-vol11-team30/backend/util" _ "github.com/go-sql-driver/mysql" + "github.com/labstack/echo/v4" "github.com/volatiletech/sqlboiler/v4/boil" "go.uber.org/zap" ) @@ -43,35 +39,42 @@ func run(ctx context.Context, logger *zap.Logger) error { boil.SetDB(db) boil.DebugMode = cfg.SqlLog - ur := repository.NewUserRepository(db) - ar := repository.NewAuthRepository(db) - er := repository.NewEventRepository(db) - oar := repository.NewOauthRepository(db) - gs := service.NewGoogleService(cfg, oar, ur) - ms := service.NewMicrosoftService(cfg, oar, ur) - uv := validator.NewUserValidator() - uu := usecase.NewUserUsecase(ur, oar, er, uv, gs, ms) - au := usecase.NewAuthUsecase(cfg, logger, ar) - eu := usecase.NewEventUsecase(cfg, er) - oau := usecase.NewOauthUsecase(cfg, oar, ur, gs, ms, uu) + // ur := repository.NewUserRepository(db) + // ar := repository.NewAuthRepository(db) + // er := repository.NewEventRepository(db) + // oar := repository.NewOauthRepository(db) + // gs := service.NewGoogleService(cfg, oar, ur) + // ms := service.NewMicrosoftService(cfg, oar, ur) + // uv := validator.NewUserValidator() + // uu := usecase.NewUserUsecase(ur, oar, er, uv, gs, ms) + // au := usecase.NewAuthUsecase(cfg, logger, ar) + // eu := usecase.NewEventUsecase(cfg, er) + // oau := usecase.NewOauthUsecase(cfg, oar, ur, gs, ms, uu) - em := middleware.NewErrorMiddleware(logger, uu) - atm := middleware.NewAccessTimeMiddleware() - am := middleware.NewAuthMiddleware(cfg, logger, au, uu) + // em := middleware.NewErrorMiddleware(logger, uu) + // atm := middleware.NewAccessTimeMiddleware() + // am := middleware.NewAuthMiddleware(cfg, logger, au, uu) - uc := controller.NewUserController(uu) - ac := controller.NewAuthController(cfg, uu, au) - ec := controller.NewEventController(eu) - oc := controller.NewOauthController(cfg, oau, uu, au) + // uc := controller.NewUserController(uu) + // ac := controller.NewAuthController(cfg, uu, au) + // ec := controller.NewEventController(eu) + // oc := controller.NewOauthController(cfg, oau, uu, au) l, err := net.Listen("tcp", fmt.Sprintf(":%d", cfg.Port)) if err != nil { logger.Fatal("failed to listen port", zap.Error(err)) } + // err = util.SendMail(*cfg, "tak848.0428771@gmail.com", "konnitiha", "hello") // fmt.Println(err) - e := router.NewRouter(cfg, logger, em, atm, am, uc, ac, ec, oc) + // e := router.NewRouter(cfg, logger, em, atm, am, uc, ac, ec, oc) + e := echo.New() + // enable log + e.GET("/health", func(c echo.Context) error { + return util.JSONResponse(c, 200, "OK") + }) + applogger.SetRequestLoggerToEcho(e, logger) s := NewServer(e, l, logger) return s.Run(ctx) } diff --git a/usecase/event.go b/usecase/event.go index 06d2ac7..b8e3771 100644 --- a/usecase/event.go +++ b/usecase/event.go @@ -166,31 +166,31 @@ func (eu *eventUsecase) CreateUserAnswer(ctx context.Context, eventId ulid.ULID, return entity.UserEventAnswer{}, err } - go func() { - log.Println("start goroutine", event) - // メールの通知を希望するなら - fmt.Println("aaaaaaaaaaaaaa", event.NotifyByEmail, event.ConfirmationEmail) - if event.NotifyByEmail && event.ConfirmationEmail != "" { - // ユーザーの回答数を数える - userAnswerCount, err := eu.er.FetchUserAnswerCount(ctx, nil, eventId) - fmt.Println("aaaaaaaaaaaaaa", userAnswerCount, event.NumberOfParticipants) - if userAnswerCount == event.NumberOfParticipants { - title := `[マジスケ]「` + event.Name + `」イベント参加者が集まりました!` - idstr := util.ULIDToString(eventId) - body := `マジスケをご利用頂き誠にありがとうございます。 + // go func() { + fmt.Println("start goroutine", event) + // メールの通知を希望するなら + fmt.Println("aaaaaaaaaaaaaa", event.NotifyByEmail, event.ConfirmationEmail) + if event.NotifyByEmail && event.ConfirmationEmail != "" { + // ユーザーの回答数を数える + userAnswerCount, err := eu.er.FetchUserAnswerCount(ctx, nil, eventId) + fmt.Println("aaaaaaaaaaaaaa", userAnswerCount, event.NumberOfParticipants) + if userAnswerCount == event.NumberOfParticipants { + title := `[マジスケ]「` + event.Name + `」イベント参加者が集まりました!` + idstr := util.ULIDToString(eventId) + body := `マジスケをご利用頂き誠にありがとうございます。 回答者数が,予定人数の` + fmt.Sprintf("%d", event.NumberOfParticipants) + - `人に到達しました。 + `人に到達しました。 https://magi-sche.net/detail/` + idstr + `から確認できます。 今後ともマジスケをよろしくお願いいたします。` - util.SendMail(*eu.cfg, event.ConfirmationEmail, title, body) - if err != nil { - log.Printf("failed to send confirmation email: %v", err) - // return entity.UserEventAnswer{}, apperror.NewUnknownError(fmt.Errorf("failed to send confirmation email: %w", err), nil) - } + util.SendMail(*eu.cfg, event.ConfirmationEmail, title, body) + if err != nil { + log.Printf("failed to send confirmation email: %v", err) + // return entity.UserEventAnswer{}, apperror.NewUnknownError(fmt.Errorf("failed to send confirmation email: %w", err), nil) } } - }() + } + // }() return newAnswer, nil } diff --git a/util/util.go b/util/util.go index c9511f9..4688096 100644 --- a/util/util.go +++ b/util/util.go @@ -123,7 +123,8 @@ func SendMail(cfg config.Config, targetAddrs string, title string, body string) hostname := cfg.SMTP.Host // SMTPサーバーのホスト名 port := cfg.SMTP.Port // SMTPサーバーのポート番号 password := cfg.SMTP.Password - from := cfg.SMTP.Email // 送信者のメールアドレス + // from := cfg.SMTP.Email // 送信者のメールアドレス + // from = "マジスケ" username := cfg.SMTP.User recipients := []string{targetAddrs} // 送信先のメールアドレス @@ -131,10 +132,10 @@ func SendMail(cfg config.Config, targetAddrs string, title string, body string) auth := smtp.PlainAuth("", username, password, hostname) msg := []byte(strings.ReplaceAll(fmt.Sprintf( - "To: %s\nSubject: %s\n\n%s", strings.Join(recipients, ","), title, body), + "From: マジスケ\nTo: %s\nSubject: %s\n\n%s", strings.Join(recipients, ","), title, body), "\n", "\r\n")) // メール送信 - err := smtp.SendMail(fmt.Sprintf("%s:%d", hostname, port), auth, from, recipients, msg) + err := smtp.SendMail(fmt.Sprintf("%s:%d", hostname, port), auth, "noreply@magi-sche.net", recipients, msg) if err != nil { return fmt.Errorf("failed to send email: %w", err)