4 Commits

32 changed files with 400 additions and 36 deletions
+68
View File
@@ -0,0 +1,68 @@
name: Build and Deploy
on:
push:
branches:
- main
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Gitea Registry
uses: docker/login-action@v3
with:
registry: ${{ secrets.REGISTRY_URL }}
username: ${{ secrets.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_TOKEN }}
- name: Build and push postgres image
uses: docker/build-push-action@v5
if: |
contains(github.event.commits[0].modified, 'infra/docker/postgres-pg-cron') ||
contains(github.event.commits[0].added, 'infra/docker/postgres-pg-cron')
with:
context: .
file: infra/docker/postgres-pg-cron/Dockerfile
push: true
tags: |
${{ secrets.REGISTRY_URL }}/${{ secrets.REGISTRY_USER }}/familyhub-postgres:latest
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Build and push app image
uses: docker/build-push-action@v5
with:
context: .
file: infra/docker/application/Dockerfile
push: true
tags: |
${{ secrets.REGISTRY_URL }}/${{ secrets.REGISTRY_USER }}/familyhub:latest
${{ secrets.REGISTRY_URL }}/${{ secrets.REGISTRY_USER }}/familyhub:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Install kubectl
run: |
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl
sudo mv kubectl /usr/local/bin/
- name: Deploy to k3s
env:
KUBECONFIG_DATA: ${{ secrets.KUBECONFIG }}
run: |
mkdir -p ~/.kube
echo "$KUBECONFIG_DATA" > ~/.kube/config
chmod 600 ~/.kube/config
kubectl rollout restart deployment/application -n family-hub
kubectl rollout restart deployment/postgres -n family-hub
kubectl rollout status deployment/application -n family-hub --timeout=120s
kubectl rollout status deployment/postgres -n family-hub --timeout=120s
+2
View File
@@ -7,3 +7,5 @@ archive
volumes
*.dtmp
*.gocache
infra/k8s/secrets.yaml
infra/k8s/google-creds.yaml
-2
View File
@@ -1,2 +0,0 @@
Portainer
admin - 4c#;=H36$s^J
@@ -0,0 +1 @@
DROP EXTENSION pg_cron;
+2
View File
@@ -128,6 +128,8 @@ func NewServer(cfg config.Config) *Server {
authRouter := routers.NewAuthRouter(authService)
authRouter.RegisterRouter(apiV1)
// подключаем статику Vue — должно быть последним
registerStaticFiles(router)
return &Server{
httpServer: &http.Server{
Addr: cfg.APIHost + ":" + cfg.APIPort,
+27
View File
@@ -0,0 +1,27 @@
package api
import (
"embed"
"io/fs"
"net/http"
"github.com/gin-gonic/gin"
)
//go:embed dist
var staticFiles embed.FS
func registerStaticFiles(router *gin.Engine) {
// вырезаем префикс dist/ чтобы / отдавал index.html
distFS, err := fs.Sub(staticFiles, "dist")
if err != nil {
panic(err)
}
fileServer := http.FileServer(http.FS(distFS))
// все маршруты которые не /api и не /openapi — отдаём Vue
router.NoRoute(func(c *gin.Context) {
fileServer.ServeHTTP(c.Writer, c.Request)
})
}
+29 -4
View File
@@ -2,6 +2,7 @@ package config
import (
"errors"
"fmt"
"os"
"strings"
@@ -33,7 +34,6 @@ func Load() (Config, error) {
mode := os.Getenv("RUN_MODE")
debugMode := os.Getenv("DEBUG_MODE") == "true"
botToken := os.Getenv("BOT_TOKEN")
dbConnectionString := os.Getenv("DB_PATH")
ocrTokenPath := os.Getenv("GOOGLE_APPLICATION_CREDENTIALS")
apiPort := os.Getenv("API_PORT")
apiHost := os.Getenv("API_HOST")
@@ -42,6 +42,7 @@ func Load() (Config, error) {
openAPIEndpoint := os.Getenv("OPEN_API_ENDPOINT")
runMode, err := ParseRunMode(mode)
dbConnectionString := buildConnectionString()
if err != nil {
warnings = append(warnings, err.Error())
}
@@ -61,9 +62,6 @@ func Load() (Config, error) {
if apiSecret == "" {
warnings = append(warnings, "Missing required environment variable: API_SECRET")
}
if dbConnectionString == "" {
dbConnectionString = "sqlite://data/app.db"
}
if apiHost == "" {
apiHost = "localhost"
}
@@ -92,3 +90,30 @@ func Load() (Config, error) {
TelegramApi: "https://api.telegram.org",
}, nil
}
func buildConnectionString() string {
// если задана готовая строка — используем её (удобно для локальной разработки через .env)
if dsn := os.Getenv("DB_PATH"); dsn != "" {
return dsn
}
// собираем из отдельных переменных (для Kubernetes)
host := os.Getenv("DB_HOST")
port := os.Getenv("DB_PORT")
user := os.Getenv("DB_USER")
password := os.Getenv("DB_PASSWORD")
dbName := os.Getenv("DB_NAME")
if host == "" || user == "" || password == "" || dbName == "" {
return ""
}
if port == "" {
port = "5432"
}
return fmt.Sprintf(
"postgres://%s:%s@%s:%s/%s?sslmode=disable",
user, password, host, port, dbName,
)
}
+45
View File
@@ -0,0 +1,45 @@
# ================================
# Stage 1: сборка Vue
# ================================
FROM node:20-alpine AS frontend
WORKDIR /app
COPY frontend/package*.json ./
RUN npm ci
COPY frontend/ ./
RUN npm run build
# ================================
# Stage 2: сборка Go
# ================================
FROM golang:1.26-bookworm AS backend
WORKDIR /app
# зависимости отдельно — используем кэш слоёв
COPY backend/go.mod backend/go.sum ./
RUN go mod download
# исходники
COPY backend/ ./
# встраиваем собранную статику Vue
COPY --from=frontend /app/dist ./src/api/dist
# Миграции кладём туда, откуда Go их ищет
COPY backend/migrations ./migrations
# сборка бинарника
RUN CGO_ENABLED=0 GOOS=linux go build -o server ./src/
# ================================
# Stage 3: финальный образ
# ================================
FROM scratch
COPY --from=backend /app/server /server
COPY --from=backend /app/migrations /migrations
COPY --from=backend /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=backend /usr/share/zoneinfo /usr/share/zoneinfo
ENTRYPOINT ["/server"]
-24
View File
@@ -1,24 +0,0 @@
version: '3.9'
services:
db:
build:
context: ..
dockerfile: infra/docker/postgres-pg-cron/Dockerfile
container_name: postgres
restart: always
command:
- postgres
- -c
- shared_preload_libraries=pg_cron
- -c
- cron.database_name=familyHubDB
environment:
POSTGRES_USER: familyUser
POSTGRES_PASSWORD: familyPass
POSTGRES_DB: familyHubDB
ports:
- "5432:5432"
volumes:
- ./volumes/postgres:/var/lib/postgresql/data
- ./docker/postgres-pg-cron/init:/docker-entrypoint-initdb.d
+55
View File
@@ -0,0 +1,55 @@
version: '3.9'
services:
app:
image: git.myhomecloud.tech/admin/familyhub:latest
container_name: application
restart: unless-stopped
ports:
- "8000:8000"
environment:
- DB_HOST=${DB_HOST}
- DB_PORT=${DB_PORT}
- DB_USER=${DB_USER}
- DB_PASSWORD=${DB_PASSWORD}
- DB_NAME=${DB_NAME}
- BOT_TOKEN=${BOT_TOKEN}
- GOOGLE_APPLICATION_CREDENTIALS=${GOOGLE_APPLICATION_CREDENTIALS}
- RUN_MODE=${RUN_MODE}
- API_SECRET=${API_SECRET}
- DB_PATH=postgres://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?sslmode=disable
- OPEN_API_ENABLED=${OPEN_API_ENABLED}
- DEBUG_MODE=${DEBUG_MODE}
depends_on:
- db
networks:
- family-hub-net
db:
image: git.myhomecloud.tech/admin/familyhub-postgres:latest
container_name: postgres
restart: always
pull_policy: always
ports:
- "5432:5432"
command:
- postgres
- -c
- shared_preload_libraries=pg_cron
- -c
- cron.database_name=familyHubDB
environment:
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: ${DB_NAME}
volumes:
- postgres-data:/var/lib/postgresql/data
- ./init:/docker-entrypoint-initdb.d
networks:
- family-hub-net
networks:
family-hub-net:
volumes:
postgres-data:
-5
View File
@@ -1,5 +0,0 @@
FROM postgres:16
RUN apt-get update \
&& apt-get install -y --no-install-recommends postgresql-16-cron \
&& rm -rf /var/lib/apt/lists/*
+62
View File
@@ -0,0 +1,62 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: application
namespace: family-hub
spec:
replicas: 1
selector:
matchLabels:
app: application
template:
metadata:
labels:
app: application
spec:
containers:
- name: application
image: git.myhomecloud.tech/admin/familyhub:latest
ports:
- containerPort: 8000
envFrom:
- configMapRef:
name: family-hub-config
- secretRef:
name: family-hub-secrets
env:
- name: GOOGLE_APPLICATION_CREDENTIALS
value: /secrets/credentials.json
volumeMounts:
- name: google-credentials
mountPath: /secrets
readOnly: true
# livenessProbe:
# httpGet:
# path: /api/v1/health
# port: 8000
# initialDelaySeconds: 10
# periodSeconds: 30
# readinessProbe:
# httpGet:
# path: /api/v1/health
# port: 8000
# initialDelaySeconds: 5
# periodSeconds: 10
volumes:
- name: google-credentials
secret:
secretName: google-credentials
imagePullSecrets:
- name: gitea-registry
---
apiVersion: v1
kind: Service
metadata:
name: application
namespace: family-hub
spec:
selector:
app: application
ports:
- port: 9876
targetPort: 8000
+15
View File
@@ -0,0 +1,15 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: family-hub-config
namespace: family-hub
data:
DB_HOST: postgres
DB_PORT: "5432"
DB_NAME: familyHubDB
DB_USER: familyUser
API_PORT: "8000"
API_HOST: 0.0.0.0
RUN_MODE: standalone
OPEN_API_ENABLED: "true"
DEBUG_MODE: "false"
+19
View File
@@ -0,0 +1,19 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: application
namespace: family-hub
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: web
spec:
rules:
- host: application.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: application
port:
number: 9876
+4
View File
@@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: family-hub
+62
View File
@@ -0,0 +1,62 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres
namespace: family-hub
spec:
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: git.myhomecloud.tech/admin/familyhub-postgres:latest
env:
- name: POSTGRES_USER
value: familyUser
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: family-hub-secrets
key: DB_PASSWORD
- name: POSTGRES_DB
value: familyHubDB
ports:
- containerPort: 5432
volumeMounts:
- name: postgres-data
mountPath: /var/lib/postgresql/data
imagePullSecrets:
- name: gitea-registry
volumes:
- name: postgres-data
persistentVolumeClaim:
claimName: postgres-pvc
---
apiVersion: v1
kind: Service
metadata:
name: postgres
namespace: family-hub
spec:
selector:
app: postgres
ports:
- port: 5432
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-pvc
namespace: family-hub
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
+8
View File
@@ -0,0 +1,8 @@
FROM postgres:16
RUN apt-get update \
&& apt-get install -y --no-install-recommends postgresql-16-cron \
&& rm -rf /var/lib/apt/lists/* \
RUN echo "shared_preload_libraries = 'pg_cron'" >> /usr/share/postgresql/postgresql.conf.sample \
&& echo "cron.database_name = 'familyHubDB'" >> /usr/share/postgresql/postgresql.conf.sample