Skip to content

Commit f5da739

Browse files
committed
init
0 parents  commit f5da739

14 files changed

Lines changed: 786 additions & 0 deletions

File tree

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
name: Docker
2+
3+
on:
4+
push:
5+
tags: [ 'v*.*.*' ]
6+
branches:
7+
- main
8+
9+
env:
10+
REGISTRY: ghcr.io
11+
12+
jobs:
13+
build:
14+
strategy:
15+
fail-fast: false
16+
matrix:
17+
target:
18+
- api
19+
- crawl
20+
arch:
21+
- ubuntu-24.04
22+
- ubuntu-24.04-arm
23+
include:
24+
- target: api
25+
file: ./cmd/api/Dockerfile
26+
image: ghcr.io/anthrotech-dev/activity-api
27+
- target: crawl
28+
file: ./cmd/crawl/Dockerfile
29+
image: ghcr.io/anthrotech-dev/activity-crawl
30+
31+
runs-on: ${{ matrix.arch }}
32+
33+
permissions:
34+
contents: read
35+
packages: write
36+
id-token: write
37+
38+
steps:
39+
- name: Checkout repository
40+
uses: actions/checkout@v3
41+
with:
42+
fetch-depth: 0
43+
44+
- name: Get tag
45+
run: echo "TAG=${GITHUB_REF##*/}" >> $GITHUB_ENV
46+
47+
- name: Setup Docker buildx
48+
uses: docker/setup-buildx-action@v2
49+
50+
- name: Log into registry ${{ env.REGISTRY }}
51+
if: github.event_name != 'pull_request'
52+
uses: docker/login-action@v2
53+
with:
54+
registry: ${{ env.REGISTRY }}
55+
username: ${{ github.actor }}
56+
password: ${{ secrets.GITHUB_TOKEN }}
57+
58+
- name: Docker meta
59+
id: meta
60+
uses: docker/metadata-action@v5
61+
with:
62+
images: ${{ matrix.image }}
63+
64+
- name: Build and push Docker image
65+
id: build
66+
uses: docker/build-push-action@v4
67+
with:
68+
context: .
69+
file: ${{ matrix.file }}
70+
push: ${{ github.event_name != 'pull_request' }}
71+
labels: ${{ steps.meta.outputs.labels }}
72+
outputs: type=image,name=${{ matrix.image }},push-by-digest=true,name-canonical=true,push=true
73+
cache-from: type=gha
74+
cache-to: type=gha,mode=max
75+
build-args: VERSION=${{env.TAG}}
76+
77+
- name: Export digest
78+
run: |
79+
mkdir -p /tmp/digests
80+
digest="${{ steps.build.outputs.digest }}"
81+
touch "/tmp/digests/${digest#sha256:}"
82+
83+
- name: Upload digest
84+
uses: actions/upload-artifact@v4
85+
with:
86+
name: digests-${{ matrix.target }}-${{ matrix.arch }}
87+
path: /tmp/digests/*
88+
if-no-files-found: error
89+
retention-days: 1
90+
91+
92+
manifest:
93+
needs: build
94+
runs-on: ubuntu-latest
95+
96+
strategy:
97+
fail-fast: false
98+
matrix:
99+
include:
100+
- target: api
101+
image: ghcr.io/anthrotech-dev/activity-api
102+
- target: crawl
103+
image: ghcr.io/anthrotech-dev/activity-crawl
104+
105+
permissions:
106+
contents: read
107+
packages: write
108+
id-token: write
109+
110+
env:
111+
REGISTRY: ghcr.io
112+
113+
steps:
114+
- name: Download digests
115+
uses: actions/download-artifact@v4
116+
with:
117+
path: /tmp/digests
118+
pattern: digests-${{ matrix.target }}-*
119+
merge-multiple: true
120+
121+
- name: Get tag
122+
run: echo "TAG=${GITHUB_REF##*/}" >> $GITHUB_ENV
123+
124+
- name: Set up Docker Buildx
125+
uses: docker/setup-buildx-action@v3
126+
127+
- name: Log into registry
128+
uses: docker/login-action@v2
129+
with:
130+
registry: ${{ env.REGISTRY }}
131+
username: ${{ github.actor }}
132+
password: ${{ secrets.GITHUB_TOKEN }}
133+
134+
- name: Extract Docker metadata
135+
id: meta
136+
uses: docker/metadata-action@v5
137+
with:
138+
images: ${{ matrix.image }}
139+
tags: |
140+
type=raw,value=latest,enable=${{ !contains(env.TAG, 'beta') }}
141+
type=semver,pattern={{raw}}
142+
143+
- name: Create manifest list and push
144+
working-directory: /tmp/digests
145+
run: |
146+
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
147+
$(printf '${{ matrix.image }}@sha256:%s ' *)

cmd/api/Dockerfile

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
FROM golang:latest AS builder
2+
WORKDIR /work
3+
4+
ARG VERSION
5+
6+
RUN --mount=type=bind,source=./go.mod,target=/work/go.mod,ro \
7+
--mount=type=bind,source=./go.sum,target=/work/go.sum,ro \
8+
--mount=type=cache,target=/go/pkg/mod \
9+
--mount=type=cache,target=/root/.cache/go-build \
10+
go mod download && go mod verify
11+
12+
RUN --mount=type=bind,source=.,target=/work \
13+
--mount=type=cache,target=/go/pkg/mod \
14+
--mount=type=cache,target=/root/.cache/go-build \
15+
CGO_ENABLED=0 \
16+
VERSION=${VERSION:-$(git describe 2>/dev/null || echo "unset")} \
17+
BUILD_MACHINE="$(uname -srmo)" \
18+
BUILD_TIME="$(date)" \
19+
GO_VERSION="$(go version)" \
20+
go build -o /api ./cmd/api
21+
22+
FROM gcr.io/distroless/base:nonroot
23+
ENV HOME=/home/nonroot
24+
USER nonroot:nonroot
25+
26+
COPY --from=builder /api /api
27+
28+
CMD ["/api"]

cmd/api/main.go

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package main
2+
3+
import (
4+
"log"
5+
"os"
6+
"time"
7+
8+
"github.com/labstack/echo/v4"
9+
"gorm.io/driver/postgres"
10+
"gorm.io/gorm"
11+
"gorm.io/gorm/logger"
12+
13+
"github.com/anthrotech-dev/activity"
14+
)
15+
16+
type DailyActivity struct {
17+
Date time.Time `json:"date"`
18+
Count int `json:"count"`
19+
}
20+
21+
func main() {
22+
dsn := os.Getenv("DB_DSN")
23+
24+
gormLogger := logger.New(
25+
log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
26+
logger.Config{
27+
SlowThreshold: 300 * time.Millisecond, // Slow SQL threshold
28+
LogLevel: logger.Warn, // Log level
29+
IgnoreRecordNotFoundError: true, // Ignore ErrRecordNotFound error for logger
30+
Colorful: true, // Enable color
31+
},
32+
)
33+
34+
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
35+
Logger: gormLogger,
36+
TranslateError: true,
37+
})
38+
if err != nil {
39+
panic("failed to connect database")
40+
}
41+
42+
e := echo.New()
43+
44+
e.GET("/:id", func(c echo.Context) error {
45+
46+
id := c.Param("id")
47+
48+
since := time.Now().AddDate(-1, 0, 0)
49+
until := time.Now()
50+
51+
sinceQ := c.QueryParam("since")
52+
if sinceQ != "" {
53+
parsedSince, err := time.Parse("2006-01-02", sinceQ)
54+
if err == nil {
55+
since = parsedSince
56+
}
57+
}
58+
untilQ := c.QueryParam("until")
59+
if untilQ != "" {
60+
parsedUntil, err := time.Parse("2006-01-02", untilQ)
61+
if err == nil {
62+
until = parsedUntil
63+
}
64+
}
65+
66+
var results []DailyActivity
67+
db.
68+
Model(&activity.Activity{}).
69+
Select("DATE(timestamp) as date, COUNT(*) as count").
70+
Where("user_id = ? AND timestamp >= ? AND timestamp <= ?", id, since, until).
71+
Group("date").
72+
Order("date").
73+
Scan(&results)
74+
75+
resultsMap := make(map[string]int)
76+
for _, r := range results {
77+
resultsMap[r.Date.Format("2006-01-02")] = r.Count
78+
}
79+
80+
return c.JSON(200, resultsMap)
81+
})
82+
83+
e.GET("/:id/total", func(c echo.Context) error {
84+
id := c.Param("id")
85+
var count int64
86+
db.
87+
Model(&activity.Activity{}).
88+
Where("user_id = ?", id).
89+
Count(&count)
90+
91+
return c.JSON(200, map[string]int64{"total": count})
92+
})
93+
94+
e.GET("/:id/:date", func(c echo.Context) error {
95+
id := c.Param("id")
96+
dateStr := c.Param("date")
97+
date, err := time.Parse("2006-01-02", dateStr)
98+
if err != nil {
99+
return c.JSON(400, map[string]string{"error": "invalid date format"})
100+
}
101+
var activities []activity.Activity
102+
db.
103+
Where("user_id = ? AND DATE(timestamp) = ?", id, date).
104+
Find(&activities)
105+
return c.JSON(200, activities)
106+
})
107+
108+
e.Logger.Fatal(e.Start(":3000"))
109+
}

cmd/crawl/Dockerfile

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
FROM golang:latest AS builder
2+
WORKDIR /work
3+
4+
ARG VERSION
5+
6+
RUN --mount=type=bind,source=./go.mod,target=/work/go.mod,ro \
7+
--mount=type=bind,source=./go.sum,target=/work/go.sum,ro \
8+
--mount=type=cache,target=/go/pkg/mod \
9+
--mount=type=cache,target=/root/.cache/go-build \
10+
go mod download && go mod verify
11+
12+
RUN --mount=type=bind,source=.,target=/work \
13+
--mount=type=cache,target=/go/pkg/mod \
14+
--mount=type=cache,target=/root/.cache/go-build \
15+
CGO_ENABLED=0 \
16+
VERSION=${VERSION:-$(git describe 2>/dev/null || echo "unset")} \
17+
BUILD_MACHINE="$(uname -srmo)" \
18+
BUILD_TIME="$(date)" \
19+
GO_VERSION="$(go version)" \
20+
go build -o /crawl ./cmd/crawl
21+
22+
FROM gcr.io/distroless/base:nonroot
23+
ENV HOME=/home/nonroot
24+
USER nonroot:nonroot
25+
26+
COPY --from=builder /crawl /crawl
27+
28+
CMD ["/crawl"]

0 commit comments

Comments
 (0)