Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions .github/workflows/dev-build-deploy-api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@ permissions:
contents: read
packages: write

concurrency:
group: dev-deploy
cancel-in-progress: true

jobs:
build-and-push:
name: Build and Push Docker Image to GHCR
Expand Down
90 changes: 90 additions & 0 deletions .github/workflows/dev-build-deploy-email-worker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
name: Deploy Email Worker to Development

on:
push:
branches:
- dev
paths:
- 'apps/api/**'
- 'infra/docker-compose.dev.yml'
workflow_dispatch:

permissions:
contents: read
packages: write

jobs:
build-and-push:
name: Build and Push Docker Image to GHCR
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up QEMU for cross-platform builds
uses: docker/setup-qemu-action@v2

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2

- name: Log in to GitHub Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and push multi-arch image
run: |
docker buildx build \
--platform linux/amd64,linux/arm64 \
--target prod \
--push \
-f ./apps/api/cmd/email_worker/Dockerfile \
-t ghcr.io/${{ github.repository_owner }}/core-email-worker:dev \
./apps/api

run-migrations:
name: Run Goose Migrations
runs-on: ubuntu-latest
needs: build-and-push

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Install Goose
run: |
curl -fsSL https://raw.githubusercontent.com/pressly/goose/master/install.sh | sh

- name: Run migrations
run: |
goose -dir ./apps/api/internal/db/migrations postgres "${{ secrets.DEV_DB_URL }}" up

deploy:
name: Deploy to Development Server
runs-on: ubuntu-latest
needs: [build-and-push, run-migrations]

steps:
- name: SSH proxy commmand
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SSH_HOST_SWAMPHACKS }}
username: ${{ secrets.SSH_USERNAME_SWAMPHACKS }}
key: ${{ secrets.SSH_KEY_SWAMPHACKS }}
port: ${{ secrets.SSH_PORT_SWAMPHACKS }}
proxy_host: ${{ secrets.SSH_HOST_JUMP }}
proxy_username: ${{ secrets.SSH_USERNAME_JUMP }}
proxy_key: ${{ secrets.SSH_KEY_JUMP }}
proxy_port: ${{ secrets.SSH_PORT_JUMP }}
script: |
cd /home/admin/core/infra
git fetch
git checkout dev
git reset --hard origin/dev
git pull
infisical export --env=dev --format=dotenv --path="/api" --projectId=${{ secrets.INFISICAL_PROJECT_ID }} > ./secrets/.env.dev.api
docker compose -f docker-compose.dev.yml pull dev-email-worker
docker compose -f docker-compose.dev.yml up -d --no-deps --force-recreate dev-email-worker
4 changes: 0 additions & 4 deletions .github/workflows/prod-build-deploy-api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@ permissions:
contents: read
packages: write

concurrency:
group: production-deploy
cancel-in-progress: true

jobs:
build-and-push:
name: Build and Push Docker Image to GHCR
Expand Down
90 changes: 90 additions & 0 deletions .github/workflows/prod-build-deploy-email-worker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
name: Deploy Email Worker to Production

on:
push:
branches:
- main
paths:
- 'apps/api/**'
- 'infra/docker-compose.yml'
workflow_dispatch:

permissions:
contents: read
packages: write

jobs:
build-and-push:
name: Build and Push Docker Image to GHCR
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up QEMU for cross-platform builds
uses: docker/setup-qemu-action@v2

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2

- name: Log in to GitHub Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and push multi-arch image
run: |
docker buildx build \
--platform linux/amd64,linux/arm64 \
--target prod \
--push \
-f ./apps/api/cmd/email_worker/Dockerfile \
-t ghcr.io/${{ github.repository_owner }}/core-email-worker:latest \
./apps/api

run-migrations:
name: Run Goose Migrations
runs-on: ubuntu-latest
needs: build-and-push

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Install Goose
run: |
curl -fsSL https://raw.githubusercontent.com/pressly/goose/master/install.sh | sh

- name: Run migrations
run: |
goose -dir ./apps/api/internal/db/migrations postgres "${{ secrets.DEV_DB_URL }}" up

deploy:
name: Deploy to Production Server
runs-on: ubuntu-latest
needs: [build-and-push, run-migrations]

steps:
- name: SSH proxy commmand
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SSH_HOST_SWAMPHACKS }}
username: ${{ secrets.SSH_USERNAME_SWAMPHACKS }}
key: ${{ secrets.SSH_KEY_SWAMPHACKS }}
port: ${{ secrets.SSH_PORT_SWAMPHACKS }}
proxy_host: ${{ secrets.SSH_HOST_JUMP }}
proxy_username: ${{ secrets.SSH_USERNAME_JUMP }}
proxy_key: ${{ secrets.SSH_KEY_JUMP }}
proxy_port: ${{ secrets.SSH_PORT_JUMP }}
script: |
cd /home/admin/core/infra
git fetch
git checkout main
git reset --hard origin/main
git pull
infisical export --env=prod --format=dotenv --path="/api" --projectId=${{ secrets.INFISICAL_PROJECT_ID }} > ./secrets/.env.api
docker compose pull email-worker
docker compose up -d --no-deps --force-recreate email-worker
1 change: 1 addition & 0 deletions apps/api/.env.dev.example
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
DATABASE_URL="postgres://postgres:postgres@postgres:5432/coredb"
DATABASE_URL_MIGRATION="postgres://postgres:postgres@localhost:5432/coredb"
REDIS_URL="redis:6379"

# For OAuth
AUTH_DISCORD_CLIENT_ID=
Expand Down
11 changes: 10 additions & 1 deletion apps/api/cmd/api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"net/http"
"time"

"github.com/hibiken/asynq"
"github.com/rs/zerolog/log"
"github.com/swamphacks/core/apps/api/internal/api"
"github.com/swamphacks/core/apps/api/internal/api/handlers"
Expand Down Expand Up @@ -38,6 +39,13 @@ func main() {
},
}

// Create asynq client
redisOpt, err := asynq.ParseRedisURI(cfg.RedisURL)
if err != nil {
logger.Fatal().Msg("Failed to parse REDIS_URL")
}
taskQueueClient := asynq.NewClient(redisOpt)

// Create new middleware injectable
mw := middleware.NewMiddleware(database, logger, cfg)

Expand All @@ -50,9 +58,10 @@ func main() {
// Injections into services
authService := services.NewAuthService(userRepo, accountRepo, sessionRepo, txm, client, logger, &cfg.Auth)
eventInterestService := services.NewEventInterestService(eventInterestRepo, logger)
emailService := services.NewEmailService(taskQueueClient, logger)

// Injections into handlers
apiHandlers := handlers.NewHandlers(authService, eventInterestService, cfg, logger)
apiHandlers := handlers.NewHandlers(authService, eventInterestService, emailService, cfg, logger)

api := api.NewAPI(&logger, apiHandlers, mw)

Expand Down
27 changes: 27 additions & 0 deletions apps/api/cmd/email_worker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
FROM golang:1.24-alpine AS base

WORKDIR /app

COPY go.mod go.sum ./

RUN go mod download

COPY . .

# Dev
FROM base AS dev

RUN go install github.com/air-verse/air@latest

CMD ["air"]

# Production
FROM base as prod

RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o email_worker ./cmd/email_worker

RUN apk --no-cache add ca-certificates

CMD ["./email_worker"]


51 changes: 51 additions & 0 deletions apps/api/cmd/email_worker/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package main

import (
"fmt"
"log"
"time"

"github.com/hibiken/asynq"
"github.com/swamphacks/core/apps/api/internal/config"
"github.com/swamphacks/core/apps/api/internal/logger"
"github.com/swamphacks/core/apps/api/internal/services"
"github.com/swamphacks/core/apps/api/internal/tasks"
"github.com/swamphacks/core/apps/api/internal/workers"
)

func main() {
logger := logger.New()
cfg := config.Load()

redisOpt, err := asynq.ParseRedisURI(cfg.RedisURL)
if err != nil {
logger.Fatal().Msg("Failed to parse REDIS_URL")
}

srv := asynq.NewServer(
redisOpt,
asynq.Config{
Concurrency: 1,
Queues: map[string]int{
"email": 1,
},
TaskCheckInterval: 60 * time.Second,
DelayedTaskCheckInterval: 2 * time.Minute,
HealthCheckInterval: 2 * time.Minute,
JanitorInterval: time.Hour,
JanitorBatchSize: 100,
},
)

emailService := services.NewEmailService(nil, logger)
emailWorker := workers.NewEmailWorker(emailService, logger)

mux := asynq.NewServeMux()

mux.HandleFunc(tasks.TypeSendEmail, emailWorker.HandleSendEmailTask)
fmt.Println("Starting email worker")

if err := srv.Run(mux); err != nil {
log.Fatalf("Failed to run email worker")
}
}
11 changes: 9 additions & 2 deletions apps/api/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,28 @@ require (
github.com/go-chi/chi/v5 v5.2.2
github.com/go-chi/cors v1.2.1
github.com/google/uuid v1.6.0
github.com/hibiken/asynq v0.25.1
github.com/jackc/pgx/v5 v5.7.4
github.com/joho/godotenv v1.5.1
github.com/lib/pq v1.10.9
github.com/rs/zerolog v1.34.0
)

require (
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/redis/go-redis/v9 v9.7.0 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/spf13/cast v1.7.0 // indirect
github.com/stretchr/testify v1.10.0 // indirect
golang.org/x/crypto v0.38.0 // indirect
golang.org/x/sync v0.14.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/sys v0.34.0 // indirect
golang.org/x/text v0.25.0 // indirect
golang.org/x/time v0.8.0 // indirect
google.golang.org/protobuf v1.35.2 // indirect
)
Loading
Loading