Skip to content
Open
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
9 changes: 9 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,20 @@ RABBITMQ_DEFAULT_PASS=
# redis
REDIS_PASSWORD=

# traefik
## ACME 使用 DNS challenge, 需要相关 API, 例如这里 provider 是 alidns
## https://doc.traefik.io/traefik/https/acme/#providers
DOMAIN=
ALICLOUD_ACCESS_KEY=
ALICLOUD_SECRET_KEY=
ALICLOUD_REGION_ID=cn-hangzhou

################################# backend 环境变量 #################################
# database
DATABASE_HOST=postgres
DATABASE_PORT=5432
DATABASE_DSN=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${DATABASE_HOST}:${DATABASE_PORT}/${POSTGRES_DB}?sslmode=disable
DATABASE_MIGRATIONS_DIR=/app/migrations # 绝对路径

# initial admin
INITIAL_ADMIN_PASSWORD=
Expand Down
75 changes: 75 additions & 0 deletions .github/workflows/backend.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: CI for backend

on:
push:
branches: [ "main", "ci" ]
tags: [ "v*" ]
paths: ["backend/**"]
workflow_dispatch:

env:
REGISTRY: ghcr.io
ORGANIZATION: sysu-ecnc-dev
IMAGE_NAME: shift-manager/backend

jobs:
build-and-push-image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

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

- name: Log in to the Container registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY }}/${{ env.ORGANIZATION }}/${{ env.IMAGE_NAME }}
tags: |
type=semver,pattern={{raw}}
type=sha,enable=${{ !startsWith(github.ref, 'refs/tags/v') }},prefix=,suffix=,format=long

- name: Build and push Docker image (untagged)
uses: docker/build-push-action@v4
if: ${{ !startsWith(github.ref, 'refs/tags/v') }}
with:
context: backend
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

- name: Build and push Docker image (tagged)
uses: docker/build-push-action@v4
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
with:
context: backend
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

- name: Prune old packages
uses: vlaurin/action-ghcr-prune@v0.6.0
with:
token: ${{ secrets.PACKAGE_TOKEN }}
organization: ${{ env.ORGANIZATION }}
container: ${{ env.IMAGE_NAME }}
dry-run: false
keep-last: 5
keep-tags-regexes: ^v(.)*
prune-tags-regexes: |
^[0-9a-f]{6,40}
sha-*
pr-*
keep-tags: |
latest
prune-untagged: true
75 changes: 75 additions & 0 deletions .github/workflows/frontend.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: CI for frontend

on:
push:
branches: [ "main", "ci" ]
tags: [ "v*" ]
paths: ["frontend/**"]
workflow_dispatch:

env:
REGISTRY: ghcr.io
ORGANIZATION: sysu-ecnc-dev
IMAGE_NAME: shift-manager/frontend

jobs:
build-and-push-image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

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

- name: Log in to the Container registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY }}/${{ env.ORGANIZATION }}/${{ env.IMAGE_NAME }}
tags: |
type=semver,pattern={{raw}}
type=sha,enable=${{ !startsWith(github.ref, 'refs/tags/v') }},prefix=,suffix=,format=long

- name: Build and push Docker image (untagged)
uses: docker/build-push-action@v4
if: ${{ !startsWith(github.ref, 'refs/tags/v') }}
with:
context: frontend
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

- name: Build and push Docker image (tagged)
uses: docker/build-push-action@v4
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
with:
context: frontend
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

- name: Prune old packages
uses: vlaurin/action-ghcr-prune@v0.6.0
with:
token: ${{ secrets.PACKAGE_TOKEN }}
organization: ${{ env.ORGANIZATION }}
container: ${{ env.IMAGE_NAME }}
dry-run: false
keep-last: 5
keep-tags-regexes: ^v(.)*
prune-tags-regexes: |
^[0-9a-f]{6,40}
sha-*
pr-*
keep-tags: |
latest
prune-untagged: true
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
.env
.env
acme
1 change: 1 addition & 0 deletions backend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_DBNAME=shift_manager_db
DATABASE_DSN=postgres://${DATABASE_USER}:${DATABASE_PASSWORD}@${DATABASE_HOST}:${DATABASE_PORT}/${DATABASE_DBNAME}?sslmode=disable
DATABASE_MIGRATIONS_DIR=/path/to/shift-manager/backend/migrations # 绝对路径

# initial admin
INITIAL_ADMIN_PASSWORD=
Expand Down
31 changes: 17 additions & 14 deletions backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
FROM golang:1.23-alpine

RUN go install github.com/pressly/goose/v3/cmd/goose@latest
RUN ln -s $(go env GOPATH)/bin/goose /usr/local/bin/goose

RUN ls -l /usr/local/bin/ && ls -l $(go env GOPATH)/bin/

FROM golang:1.23-alpine AS builder
WORKDIR /app

COPY go.mod go.sum ./
RUN go mod download

COPY . .
RUN GOPROXY=https://goproxy.cn,direct && \
CGO_ENABLED=0 && \
GOOS=linux && \
go mod download && \
go build -o /app/bin/api ./cmd/api/main.go && \
go build -o /app/bin/mail ./cmd/mail/main.go

RUN CGO_ENABLED=0 GOOS=linux go build -o /app/bin/api ./cmd/api/main.go
RUN CGO_ENABLED=0 GOOS=linux go build -o /app/bin/mail ./cmd/mail/main.go

FROM alpine:latest AS backend
WORKDIR /app
COPY --from=builder \
/app/migrations /app/ \
/app/templates /app/ \
/app/run.sh /app/ \
/app/bin/* /app/
RUN chmod +x run.sh
EXPOSE 3000
CMD ["/app/run.sh"]
13 changes: 12 additions & 1 deletion backend/cmd/api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (

"github.com/jackc/pgx/v5/pgconn"
_ "github.com/jackc/pgx/v5/stdlib"
"github.com/pressly/goose/v3"
)

func main() {
Expand Down Expand Up @@ -63,6 +64,14 @@ func main() {
return
}

/**********************************************
* migrate
**********************************************/
if err := goose.Up(dbpool, cfg.Database.Migrations_dir); err != nil {
logger.Error("执行数据库迁移失败", "error", err)
return
}

/**********************************************
* 创建 repository
**********************************************/
Expand All @@ -89,7 +98,7 @@ func main() {
case errors.As(err, &pgErr):
switch pgErr.ConstraintName {
case "users_username_key":
// 如果返回这个错误,说明数据库中已经存在初始管理员,不处理
// 数据库中已经存在初始管理员
default:
logger.Error("无法创建初始管理员", "error", err)
return
Expand All @@ -99,6 +108,8 @@ func main() {
return
}
}
logger.Info("users 初始管理员: " + cfg.InitialAdmin.Username)
logger.Info("数据库初始化完成")

/**********************************************
* 连接 rabbitmq
Expand Down
7 changes: 5 additions & 2 deletions backend/cmd/mail/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,13 +193,16 @@ func main() {
}

// 发送邮件
if err := client.DialAndSend(mail); err != nil {
sendCtx, sendCancel := context.WithTimeout(context.Background(), time.Duration(cfg.Email.SMTP.DialTimeout)*time.Second)
defer sendCancel()
if err := client.DialAndSendWithContext(sendCtx, mail); err != nil {
logger.Error("邮件发送失败", slog.String("error", err.Error()))
_ = msg.Nack(false, true) // 将消息重新入队
continue
}

// 确认消息
// 发送成功,确认消息
logger.Info(mailMessage.Type + "邮件已发送至" + mailMessage.To)
_ = msg.Ack(false)
}
}
Expand Down
71 changes: 64 additions & 7 deletions backend/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,79 @@ require (
github.com/rabbitmq/amqp091-go v1.10.0
github.com/redis/go-redis/v9 v9.7.0
github.com/wneessen/go-mail v0.6.1
golang.org/x/crypto v0.32.0
golang.org/x/crypto v0.34.0
)

require (
github.com/cespare/xxhash/v2 v2.2.0 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/ClickHouse/ch-go v0.65.1 // indirect
github.com/ClickHouse/clickhouse-go/v2 v2.32.2 // indirect
github.com/andybalholm/brotli v1.1.1 // indirect
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/coder/websocket v1.8.12 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/elastic/go-sysinfo v1.15.1 // indirect
github.com/elastic/go-windows v1.0.2 // indirect
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/go-faster/city v1.0.1 // indirect
github.com/go-faster/errors v0.7.1 // indirect
github.com/go-sql-driver/mysql v1.9.0 // indirect
github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
github.com/golang-sql/sqlexp v0.1.0 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // 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/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 // indirect
github.com/joho/godotenv v1.5.1 // indirect
github.com/jonboulle/clockwork v0.5.0 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mfridman/interpolate v0.0.2 // indirect
github.com/mfridman/xflag v0.1.0 // indirect
github.com/microsoft/go-mssqldb v1.8.0 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/paulmach/orb v0.11.1 // indirect
github.com/pierrec/lz4/v4 v4.1.22 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/stretchr/testify v1.9.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/text v0.21.0 // indirect
github.com/pressly/goose/v3 v3.24.1 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/segmentio/asm v1.2.0 // indirect
github.com/sethvargo/go-retry v0.3.0 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/stretchr/testify v1.10.0 // indirect
github.com/tursodatabase/libsql-client-go v0.0.0-20240902231107-85af5b9d094d // indirect
github.com/vertica/vertica-sql-go v1.3.3 // indirect
github.com/ydb-platform/ydb-go-genproto v0.0.0-20241112172322-ea1f63298f77 // indirect
github.com/ydb-platform/ydb-go-sdk/v3 v3.99.13 // indirect
github.com/ziutek/mymysql v1.5.4 // indirect
go.opentelemetry.io/otel v1.34.0 // indirect
go.opentelemetry.io/otel/trace v1.34.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa // indirect
golang.org/x/net v0.35.0 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/text v0.22.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250219182151-9fdb1cabc7b2 // indirect
google.golang.org/grpc v1.70.0 // indirect
google.golang.org/protobuf v1.36.5 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
howett.net/plist v1.0.1 // indirect
modernc.org/gc/v3 v3.0.0-20250121204235-2db1fde51ea4 // indirect
modernc.org/libc v1.61.13 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.8.2 // indirect
modernc.org/sqlite v1.35.0 // indirect
modernc.org/strutil v1.2.1 // indirect
modernc.org/token v1.1.0 // indirect
)
Loading