Merge pull request 'fix/docker-config' (#1) from fix/docker-config into main
Reviewed-on: #1
This commit is contained in:
commit
a91e9a74b3
@ -3,7 +3,10 @@
|
|||||||
# This hook runs linters and checks before allowing a commit
|
# This hook runs linters and checks before allowing a commit
|
||||||
|
|
||||||
echo "Running pre-commit checks..."
|
echo "Running pre-commit checks..."
|
||||||
|
echo "Current working directory: $(pwd)"
|
||||||
|
echo "Which golangci-lint: $(which golangci-lint)"
|
||||||
|
echo "golangci-lint version: $(golangci-lint version)"
|
||||||
|
cd "$PROJECT_ROOT" || exit 1 # Exit if cd fails
|
||||||
# Check for staged Go files
|
# Check for staged Go files
|
||||||
STAGED_GO_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep "\.go$")
|
STAGED_GO_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep "\.go$")
|
||||||
|
|
||||||
|
|||||||
@ -18,12 +18,18 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
go-version: '1.24.3'
|
go-version: '1.24.3'
|
||||||
cache-dependency-path: go.sum
|
cache-dependency-path: go.sum
|
||||||
|
- name: Cache golangci-lint
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ~/.cache/golangci-lint
|
||||||
|
key: ${{ runner.os }}-golangci-lint-${{ hashFiles('**/go.sum') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-golangci-lint-
|
||||||
- name: Lint
|
- name: Lint
|
||||||
uses: golangci/golangci-lint-action@v6
|
uses: golangci/golangci-lint-action@v6
|
||||||
with:
|
with:
|
||||||
version: latest
|
version: latest
|
||||||
args: --timeout=5m
|
args: --timeout=15m
|
||||||
|
|
||||||
- name: Notify on failure
|
- name: Notify on failure
|
||||||
if: failure()
|
if: failure()
|
||||||
|
|||||||
32
Dockerfile
32
Dockerfile
@ -1,6 +1,4 @@
|
|||||||
# syntax=docker/dockerfile:1.4
|
# Dockerfile
|
||||||
# Production-ready multi-stage build for server deployment
|
|
||||||
|
|
||||||
# Build arguments
|
# Build arguments
|
||||||
ARG GO_VERSION=1.24.3
|
ARG GO_VERSION=1.24.3
|
||||||
ARG ALPINE_VERSION=3.19
|
ARG ALPINE_VERSION=3.19
|
||||||
@ -16,7 +14,7 @@ RUN apk add --no-cache git make gcc libc-dev
|
|||||||
# Set working directory
|
# Set working directory
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
|
|
||||||
# Enable Go modules
|
# Enable Go modules and set build environment variables
|
||||||
ENV GO111MODULE=on \
|
ENV GO111MODULE=on \
|
||||||
CGO_ENABLED=0 \
|
CGO_ENABLED=0 \
|
||||||
GOOS=linux \
|
GOOS=linux \
|
||||||
@ -25,37 +23,35 @@ ENV GO111MODULE=on \
|
|||||||
# Copy dependency files first for better layer caching
|
# Copy dependency files first for better layer caching
|
||||||
COPY go.mod go.sum ./
|
COPY go.mod go.sum ./
|
||||||
|
|
||||||
|
# Download dependencies using cache mount for efficiency
|
||||||
# Download dependencies
|
|
||||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||||
go mod download
|
go mod download
|
||||||
|
|
||||||
# Copy source code
|
# Copy source code
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
|
# Create the configs directory in the build context (if it exists)
|
||||||
|
RUN if test -d "configs"; then mkdir -p /build/configs && cp -r configs/* /build/configs/; fi
|
||||||
|
|
||||||
# Create the configs directory in the build context
|
# Build the application with optimizations and version info
|
||||||
RUN mkdir -p /build/configs && \
|
|
||||||
cp -r configs/* /build/configs/ 2>/dev/null || :
|
|
||||||
|
|
||||||
# Build the application with optimizations
|
|
||||||
RUN --mount=type=cache,target=/root/.cache/go-build \
|
RUN --mount=type=cache,target=/root/.cache/go-build \
|
||||||
go build -ldflags="-w -s -X 'main.Version=$(git describe --tags --always 2>/dev/null || echo 'dev')'" \
|
go build -ldflags="-w -s -X 'main.Version=$(git describe --tags --always 2>/dev/null || echo 'dev')'" \
|
||||||
-o /build/bin/app ./cmd/app
|
-o /build/bin/app ./src/cmd/api # Update path to your main package
|
||||||
|
|
||||||
# --- Final Stage ---
|
# --- Final Stage ---
|
||||||
FROM alpine:${ALPINE_VERSION}
|
FROM alpine:${ALPINE_VERSION}
|
||||||
|
|
||||||
# Re-declare ARG to use in this stage
|
# Re-declare ARG to use in this stage
|
||||||
ARG APP_USER=appuser
|
ARG APP_USER
|
||||||
ARG APP_GROUP=appgroup
|
ARG APP_GROUP
|
||||||
ARG APP_HOME=/app
|
ARG APP_HOME=/app
|
||||||
|
|
||||||
# Set environment variables
|
# Set runtime environment variables
|
||||||
ENV TZ=Asia/Ho_Chi_Minh \
|
ENV TZ=Asia/Ho_Chi_Minh \
|
||||||
APP_USER=${APP_USER} \
|
APP_USER=${APP_USER} \
|
||||||
APP_GROUP=${APP_GROUP} \
|
APP_GROUP=${APP_GROUP} \
|
||||||
APP_HOME=${APP_HOME}
|
APP_HOME=${APP_HOME} \
|
||||||
|
APP_ENV=production
|
||||||
|
|
||||||
# Install runtime dependencies
|
# Install runtime dependencies
|
||||||
RUN apk add --no-cache \
|
RUN apk add --no-cache \
|
||||||
@ -68,7 +64,7 @@ RUN apk add --no-cache \
|
|||||||
RUN addgroup -S ${APP_GROUP} && \
|
RUN addgroup -S ${APP_GROUP} && \
|
||||||
adduser -S -G ${APP_GROUP} -h ${APP_HOME} -D ${APP_USER}
|
adduser -S -G ${APP_GROUP} -h ${APP_HOME} -D ${APP_USER}
|
||||||
|
|
||||||
# Create necessary directories
|
# Create necessary application directories
|
||||||
RUN mkdir -p ${APP_HOME}/configs ${APP_HOME}/logs ${APP_HOME}/storage
|
RUN mkdir -p ${APP_HOME}/configs ${APP_HOME}/logs ${APP_HOME}/storage
|
||||||
|
|
||||||
# Switch to app directory
|
# Switch to app directory
|
||||||
@ -99,4 +95,4 @@ EXPOSE 3000
|
|||||||
ENV APP_ENV=production
|
ENV APP_ENV=production
|
||||||
|
|
||||||
# Command to run the application
|
# Command to run the application
|
||||||
ENTRYPOINT ["/sbin/tini", "--"]
|
ENTRYPOINT ["/sbin/tini", "--"]
|
||||||
16
Makefile
16
Makefile
@ -86,7 +86,10 @@ clean:
|
|||||||
docker-build:
|
docker-build:
|
||||||
@echo "Building Docker image..."
|
@echo "Building Docker image..."
|
||||||
docker build -t ulflow-zee:latest .
|
docker build -t ulflow-zee:latest .
|
||||||
|
# Build Docker image for development
|
||||||
|
docker-build-dev:
|
||||||
|
@echo "Building Docker image for development..."
|
||||||
|
docker-compose -f docker-compose.dev.yml up --build
|
||||||
# Run application in Docker container
|
# Run application in Docker container
|
||||||
docker-run:
|
docker-run:
|
||||||
@echo "Running application in Docker container..."
|
@echo "Running application in Docker container..."
|
||||||
@ -97,6 +100,13 @@ docker-run:
|
|||||||
docker run -p 3000:3000 --env-file .env ulflow-zee:latest
|
docker run -p 3000:3000 --env-file .env ulflow-zee:latest
|
||||||
)
|
)
|
||||||
|
|
||||||
|
docker-up-dev:
|
||||||
|
@echo "Starting all services with Docker Compose for local development..."
|
||||||
|
docker-compose -f docker-compose.dev.yml up
|
||||||
|
|
||||||
|
docker-down-dev:
|
||||||
|
@echo "Stopping all development services..."
|
||||||
|
docker-compose -f docker-compose.dev.yml down -v
|
||||||
# Run Docker Compose for local development
|
# Run Docker Compose for local development
|
||||||
docker-compose-up:
|
docker-compose-up:
|
||||||
@echo "Starting all services with Docker Compose for local development..."
|
@echo "Starting all services with Docker Compose for local development..."
|
||||||
@ -106,7 +116,7 @@ docker-compose-up:
|
|||||||
# Stop Docker Compose services for local development
|
# Stop Docker Compose services for local development
|
||||||
docker-compose-down:
|
docker-compose-down:
|
||||||
@echo "Stopping all development services..."
|
@echo "Stopping all development services..."
|
||||||
docker-compose down
|
docker-compose down -v
|
||||||
|
|
||||||
# Run Docker Compose for production
|
# Run Docker Compose for production
|
||||||
docker-compose-prod-up:
|
docker-compose-prod-up:
|
||||||
@ -133,9 +143,7 @@ setup-git-hooks:
|
|||||||
@echo Copying prepare-commit-msg hook...
|
@echo Copying prepare-commit-msg hook...
|
||||||
@copy /Y .gitea\hooks\prepare-commit-msg .git\hooks\prepare-commit-msg >nul || (echo Warning: Could not copy prepare-commit-msg hook & exit /b 1)
|
@copy /Y .gitea\hooks\prepare-commit-msg .git\hooks\prepare-commit-msg >nul || (echo Warning: Could not copy prepare-commit-msg hook & exit /b 1)
|
||||||
|
|
||||||
git config --local core.hooksPath .git/hooks
|
|
||||||
@echo "Git setup complete!"
|
@echo "Git setup complete!"
|
||||||
|
|
||||||
# Create git message template
|
# Create git message template
|
||||||
setup-git-message:
|
setup-git-message:
|
||||||
@echo "Creating Git commit message template..."
|
@echo "Creating Git commit message template..."
|
||||||
|
|||||||
@ -24,10 +24,14 @@ type HTTPService struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewHTTPService(cfg *config.Config, db *database.Database) *HTTPService {
|
func NewHTTPService(cfg *config.Config, db *database.Database) *HTTPService {
|
||||||
|
var gormDB *gorm.DB
|
||||||
|
if db != nil {
|
||||||
|
gormDB = db.DB
|
||||||
|
}
|
||||||
return &HTTPService{
|
return &HTTPService{
|
||||||
server: http.NewServer(cfg, db.DB),
|
server: http.NewServer(cfg, gormDB), // gormDB can be nil here
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
db: db,
|
db: db, // db itself (the *database.Database wrapper) can also be nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,6 +70,7 @@ func (s *HTTPService) Shutdown(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
fmt.Println("Starting application 222...")
|
||||||
// Initialize config loader
|
// Initialize config loader
|
||||||
configLoader := config.NewConfigLoader()
|
configLoader := config.NewConfigLoader()
|
||||||
|
|
||||||
@ -108,26 +113,44 @@ func main() {
|
|||||||
}
|
}
|
||||||
lifecycleMgr := lifecycle.New(shutdownTimeout)
|
lifecycleMgr := lifecycle.New(shutdownTimeout)
|
||||||
|
|
||||||
// Initialize database connection
|
var gormDB *gorm.DB // Declare gormDB for database connection
|
||||||
db, err := database.NewConnection(&cfg.Database)
|
var dbInstance *database.Database // Declare dbInstance for HTTPService and other potential users
|
||||||
if err != nil {
|
|
||||||
logger.WithError(err).Fatal("Failed to connect to database")
|
if feature.IsEnabled(feature.EnableDatabase) {
|
||||||
|
logger.Info("Feature flag 'enable_database' is true. Initializing database connection...")
|
||||||
|
// Initialize database connection
|
||||||
|
var dbErr error
|
||||||
|
gormDB, dbErr = database.NewConnection(&cfg.Database)
|
||||||
|
if dbErr != nil {
|
||||||
|
logger.WithError(dbErr).Fatal("Failed to connect to database")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run database migrations
|
||||||
|
if err := database.Migrate(cfg.Database); err != nil { // Migrate still needs cfg.Database for path etc.
|
||||||
|
logger.WithError(err).Fatal("Failed to migrate database")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register database cleanup on shutdown
|
||||||
|
lifecycleMgr.Register(&databaseService{db: gormDB})
|
||||||
|
|
||||||
|
// Prepare dbInstance for HTTPService
|
||||||
|
dbInstance = &database.Database{DB: gormDB, Config: &cfg.Database}
|
||||||
|
} else {
|
||||||
|
logger.Info("Feature flag 'enable_database' is false. Skipping database initialization.")
|
||||||
|
// gormDB remains nil
|
||||||
|
// dbInstance remains nil
|
||||||
|
// No databaseService is registered with lifecycleMgr if DB is disabled.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run database migrations
|
// Initialize HTTP service
|
||||||
if err := database.Migrate(cfg.Database); err != nil {
|
// NewHTTPService and its chain (http.NewServer, http.SetupRouter)
|
||||||
logger.WithError(err).Fatal("Failed to migrate database")
|
// must be able to handle a nil dbInstance or a dbInstance with a nil DB.
|
||||||
}
|
// The NewHTTPService function already checks if its db argument (or db.DB) is nil.
|
||||||
|
httpService := NewHTTPService(cfg, dbInstance) // dbInstance can be nil here
|
||||||
// Register database cleanup on shutdown
|
|
||||||
lifecycleMgr.Register(&databaseService{db: db})
|
|
||||||
|
|
||||||
// Initialize HTTP service with database
|
|
||||||
httpService := NewHTTPService(cfg, &database.Database{DB: db})
|
|
||||||
if httpService == nil {
|
if httpService == nil {
|
||||||
logger.Fatal("Failed to create HTTP service")
|
logger.Fatal("Failed to create HTTP service")
|
||||||
}
|
}
|
||||||
lifecycleMgr.Register(httpService)
|
lifecycleMgr.Register(httpService) // HTTP service is always registered
|
||||||
|
|
||||||
// Start all services
|
// Start all services
|
||||||
logger.Info("Đang khởi động các dịch vụ...")
|
logger.Info("Đang khởi động các dịch vụ...")
|
||||||
|
|||||||
@ -19,11 +19,11 @@ server:
|
|||||||
|
|
||||||
database:
|
database:
|
||||||
driver: "postgres"
|
driver: "postgres"
|
||||||
host: "localhost"
|
host: "postgres"
|
||||||
port: 5432
|
port: 5432
|
||||||
username: "postgres"
|
username: "postgres"
|
||||||
password: "postgres"
|
password: "postgres"
|
||||||
database: "ulflow"
|
database: "ulflow_zee"
|
||||||
ssl_mode: "disable"
|
ssl_mode: "disable"
|
||||||
max_open_conns: 25
|
max_open_conns: 25
|
||||||
max_idle_conns: 5
|
max_idle_conns: 5
|
||||||
|
|||||||
59
docker-compose.dev.yml
Normal file
59
docker-compose.dev.yml
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
# docker-compose.dev.yml (Cho Local Development)
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
api:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile.dev # Sử dụng Dockerfile.dev
|
||||||
|
container_name: ulflow-api-dev
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
volumes:
|
||||||
|
- .:/app # Bind mount code cho hot reload
|
||||||
|
- go-modules:/go/pkg/mod
|
||||||
|
command: air # Chạy công cụ hot reload
|
||||||
|
env_file:
|
||||||
|
- ./.env # Load biến môi trường từ file .env
|
||||||
|
depends_on:
|
||||||
|
- postgres
|
||||||
|
networks:
|
||||||
|
- ulflow-network
|
||||||
|
restart: "no" # Không tự động restart trong dev
|
||||||
|
|
||||||
|
postgres:
|
||||||
|
image: postgres:15-alpine
|
||||||
|
container_name: ulflow-postgres-dev
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
# Lấy biến từ file .env
|
||||||
|
POSTGRES_USER: ${DATABASE_USERNAME}
|
||||||
|
POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
|
||||||
|
POSTGRES_DB: ${DATABASE_NAME}
|
||||||
|
ports:
|
||||||
|
- "5432:5432" # Để truy cập từ host nếu muốn
|
||||||
|
volumes:
|
||||||
|
- postgres-dev-data:/var/lib/postgresql/data # Volume riêng cho dev DB
|
||||||
|
networks:
|
||||||
|
- ulflow-network
|
||||||
|
|
||||||
|
adminer: # Adminer cũng nên có trong dev để quản lý DB dễ dàng
|
||||||
|
image: adminer:latest
|
||||||
|
container_name: ulflow-adminer-dev
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "8080:8080"
|
||||||
|
environment:
|
||||||
|
ADMINER_DEFAULT_SERVER: postgres # Tên service của postgres trong compose này
|
||||||
|
depends_on:
|
||||||
|
- postgres
|
||||||
|
networks:
|
||||||
|
- ulflow-network
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgres-dev-data: # Volume riêng cho dev DB
|
||||||
|
go-modules: # Volume để cache Go modules cho dev
|
||||||
|
|
||||||
|
networks:
|
||||||
|
ulflow-network:
|
||||||
|
driver: bridge
|
||||||
@ -1,83 +0,0 @@
|
|||||||
version: '3.8'
|
|
||||||
|
|
||||||
services:
|
|
||||||
# API Service - Production Configuration
|
|
||||||
api:
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
container_name: ulflow-api
|
|
||||||
restart: always
|
|
||||||
ports:
|
|
||||||
- "3000:3000"
|
|
||||||
env_file:
|
|
||||||
- .env
|
|
||||||
environment:
|
|
||||||
- APP_ENV=production
|
|
||||||
depends_on:
|
|
||||||
- postgres
|
|
||||||
networks:
|
|
||||||
- ulflow-network
|
|
||||||
deploy:
|
|
||||||
resources:
|
|
||||||
limits:
|
|
||||||
cpus: '1'
|
|
||||||
memory: 1G
|
|
||||||
restart_policy:
|
|
||||||
condition: on-failure
|
|
||||||
max_attempts: 3
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/health"]
|
|
||||||
interval: 30s
|
|
||||||
timeout: 10s
|
|
||||||
retries: 3
|
|
||||||
start_period: 40s
|
|
||||||
|
|
||||||
# PostgreSQL Database - Production Configuration
|
|
||||||
postgres:
|
|
||||||
image: postgres:15-alpine
|
|
||||||
container_name: ulflow-postgres
|
|
||||||
restart: always
|
|
||||||
environment:
|
|
||||||
POSTGRES_USER: ${DB_USER:-user}
|
|
||||||
POSTGRES_PASSWORD: ${DB_PASSWORD:-password}
|
|
||||||
POSTGRES_DB: ${DB_NAME:-ulflow_db}
|
|
||||||
volumes:
|
|
||||||
- postgres-data:/var/lib/postgresql/data
|
|
||||||
networks:
|
|
||||||
- ulflow-network
|
|
||||||
deploy:
|
|
||||||
resources:
|
|
||||||
limits:
|
|
||||||
cpus: '1'
|
|
||||||
memory: 1G
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-user} -d ${DB_NAME:-ulflow_db}"]
|
|
||||||
interval: 30s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 3
|
|
||||||
start_period: 10s
|
|
||||||
|
|
||||||
# Nginx Reverse Proxy
|
|
||||||
nginx:
|
|
||||||
image: nginx:alpine
|
|
||||||
container_name: ulflow-nginx
|
|
||||||
restart: always
|
|
||||||
ports:
|
|
||||||
- "80:80"
|
|
||||||
- "443:443"
|
|
||||||
volumes:
|
|
||||||
- ./nginx/conf.d:/etc/nginx/conf.d
|
|
||||||
- ./nginx/ssl:/etc/nginx/ssl
|
|
||||||
- ./nginx/logs:/var/log/nginx
|
|
||||||
depends_on:
|
|
||||||
- api
|
|
||||||
networks:
|
|
||||||
- ulflow-network
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
postgres-data:
|
|
||||||
|
|
||||||
networks:
|
|
||||||
ulflow-network:
|
|
||||||
driver: bridge
|
|
||||||
@ -1,85 +1,45 @@
|
|||||||
|
# docker-compose.prod.yml (Cho Production / CI/CD)
|
||||||
version: '3.8'
|
version: '3.8'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
# API Service
|
|
||||||
api:
|
api:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile.local
|
dockerfile: Dockerfile # Sử dụng Dockerfile production
|
||||||
container_name: ulflow-api
|
container_name: ulflow-api-prod
|
||||||
restart: unless-stopped
|
|
||||||
ports:
|
ports:
|
||||||
- "3000:3000"
|
- "3000:3000"
|
||||||
volumes:
|
# KHÔNG CÓ VOLUMES: - .:/app ở đây!
|
||||||
- .:/app
|
command: /app/app # Chạy binary đã build
|
||||||
- go-modules:/go/pkg/mod
|
# KHÔNG CÓ ENV_FILE Ở ĐÂY, BIẾN MÔI TRƯỜNG SẼ ĐƯỢC CUNG CẤP TỪ BÊN NGOÀI
|
||||||
env_file:
|
restart: always
|
||||||
- .env
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpus: '0.50'
|
||||||
|
memory: 256M
|
||||||
depends_on:
|
depends_on:
|
||||||
- postgres
|
- postgres
|
||||||
networks:
|
networks:
|
||||||
- ulflow-network
|
- ulflow-network
|
||||||
|
|
||||||
# PostgreSQL Database
|
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:15-alpine
|
image: postgres:15-alpine
|
||||||
container_name: ulflow-postgres
|
container_name: ulflow-postgres-prod
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_USER: ${DB_USER:-user}
|
POSTGRES_USER: ${DATABASE_USERNAME}
|
||||||
POSTGRES_PASSWORD: ${DB_PASSWORD:-password}
|
POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
|
||||||
POSTGRES_DB: ${DB_NAME:-ulflow_db}
|
POSTGRES_DB: ${DATABASE_NAME}
|
||||||
ports:
|
ports:
|
||||||
- "5432:5432"
|
- "5433:5432"
|
||||||
volumes:
|
volumes:
|
||||||
- postgres-data:/var/lib/postgresql/data
|
- postgres-prod-data:/var/lib/postgresql/data # Volume riêng cho prod DB
|
||||||
networks:
|
networks:
|
||||||
- ulflow-network
|
- ulflow-network
|
||||||
|
|
||||||
# Gitea (Git Server, CI/CD, Registry)
|
|
||||||
gitea:
|
|
||||||
image: gitea/gitea:1.21
|
|
||||||
container_name: ulflow-gitea
|
|
||||||
restart: unless-stopped
|
|
||||||
environment:
|
|
||||||
- USER_UID=1000
|
|
||||||
- USER_GID=1000
|
|
||||||
- GITEA__database__DB_TYPE=postgres
|
|
||||||
- GITEA__database__HOST=postgres:5432
|
|
||||||
- GITEA__database__NAME=gitea
|
|
||||||
- GITEA__database__USER=${DB_USER:-user}
|
|
||||||
- GITEA__database__PASSWD=${DB_PASSWORD:-password}
|
|
||||||
ports:
|
|
||||||
- "3001:3000"
|
|
||||||
- "2222:22"
|
|
||||||
volumes:
|
|
||||||
- gitea-data:/data
|
|
||||||
- /etc/timezone:/etc/timezone:ro
|
|
||||||
- /etc/localtime:/etc/localtime:ro
|
|
||||||
depends_on:
|
|
||||||
- postgres
|
|
||||||
networks:
|
|
||||||
- ulflow-network
|
|
||||||
|
|
||||||
# Gitea Runner for CI/CD
|
|
||||||
gitea-runner:
|
|
||||||
image: gitea/act_runner:latest
|
|
||||||
container_name: ulflow-gitea-runner
|
|
||||||
restart: unless-stopped
|
|
||||||
volumes:
|
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
|
||||||
- gitea-runner-data:/data
|
|
||||||
depends_on:
|
|
||||||
- gitea
|
|
||||||
networks:
|
|
||||||
- ulflow-network
|
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
postgres-data:
|
postgres-prod-data:
|
||||||
gitea-data:
|
|
||||||
gitea-runner-data:
|
|
||||||
go-modules:
|
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
ulflow-network:
|
ulflow-network:
|
||||||
driver: bridge
|
driver: bridge
|
||||||
@ -107,6 +107,7 @@ func (l *ViperConfigLoader) loadEnvFile(v *viper.Viper) error {
|
|||||||
if err := os.Setenv(key, val); err != nil {
|
if err := os.Setenv(key, val); err != nil {
|
||||||
return fmt.Errorf("failed to set environment variable %s: %w", key, err)
|
return fmt.Errorf("failed to set environment variable %s: %w", key, err)
|
||||||
}
|
}
|
||||||
|
fmt.Println("Loaded environment variable:", key, "=", val)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
"zee/internal/adapter/postgres"
|
"zee/internal/adapter/postgres"
|
||||||
"zee/internal/helper/config"
|
"zee/internal/helper/config"
|
||||||
|
"zee/internal/helper/logger" // Added logger import
|
||||||
"zee/internal/service"
|
"zee/internal/service"
|
||||||
"zee/internal/transport/http/handler"
|
"zee/internal/transport/http/handler"
|
||||||
"zee/internal/transport/http/middleware"
|
"zee/internal/transport/http/middleware"
|
||||||
@ -31,60 +32,72 @@ func SetupRouter(cfg *config.Config, db *gorm.DB) *gin.Engine {
|
|||||||
securityCfg := middleware.DefaultSecurityConfig()
|
securityCfg := middleware.DefaultSecurityConfig()
|
||||||
securityCfg.Apply(router)
|
securityCfg.Apply(router)
|
||||||
|
|
||||||
// Khởi tạo repositories
|
// Khởi tạo các handlers không phụ thuộc DB
|
||||||
userRepo := postgres.NewUserRepository(db)
|
|
||||||
roleRepo := postgres.NewRoleRepository(db)
|
|
||||||
|
|
||||||
// Get JWT configuration from config
|
|
||||||
jwtSecret := "your-secret-key" // Default fallback
|
|
||||||
accessTokenExpire := 24 * time.Hour
|
|
||||||
|
|
||||||
// Override with config values if available
|
|
||||||
if cfg.JWT.Secret != "" {
|
|
||||||
jwtSecret = cfg.JWT.Secret
|
|
||||||
}
|
|
||||||
if cfg.JWT.AccessTokenExpire > 0 {
|
|
||||||
accessTokenExpire = time.Duration(cfg.JWT.AccessTokenExpire) * time.Minute
|
|
||||||
}
|
|
||||||
|
|
||||||
// Khởi tạo services
|
|
||||||
authSvc := service.NewAuthService(
|
|
||||||
userRepo,
|
|
||||||
roleRepo,
|
|
||||||
jwtSecret,
|
|
||||||
accessTokenExpire,
|
|
||||||
)
|
|
||||||
|
|
||||||
// Khởi tạo middleware
|
|
||||||
authMiddleware := middleware.NewAuthMiddleware(authSvc)
|
|
||||||
_ = authMiddleware // TODO: Use authMiddleware when needed
|
|
||||||
|
|
||||||
// Khởi tạo các handlers
|
|
||||||
healthHandler := handler.NewHealthHandler(cfg)
|
healthHandler := handler.NewHealthHandler(cfg)
|
||||||
authHandler := handler.NewAuthHandler(authSvc)
|
|
||||||
|
|
||||||
// Đăng ký các routes
|
// Đăng ký các routes không phụ thuộc DB
|
||||||
|
|
||||||
// Health check routes (public)
|
|
||||||
router.GET("/ping", healthHandler.Ping)
|
router.GET("/ping", healthHandler.Ping)
|
||||||
router.GET("/health", healthHandler.HealthCheck)
|
router.GET("/health", healthHandler.HealthCheck)
|
||||||
|
|
||||||
// Auth routes (public)
|
// Các thành phần và routes phụ thuộc vào DB
|
||||||
authGroup := router.Group("/api/v1/auth")
|
if db != nil {
|
||||||
{
|
logger.Info("Database connection is available. Initializing DB-dependent services and routes...")
|
||||||
authGroup.POST("/register", authHandler.Register)
|
// Khởi tạo repositories
|
||||||
authGroup.POST("/login", authHandler.Login)
|
userRepo := postgres.NewUserRepository(db)
|
||||||
authGroup.POST("/refresh", authHandler.RefreshToken)
|
roleRepo := postgres.NewRoleRepository(db)
|
||||||
authGroup.POST("/logout", authMiddleware.Authenticate(), authHandler.Logout)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Protected API routes
|
// Get JWT configuration from config
|
||||||
api := router.Group("/api/v1")
|
jwtSecret := "your-secret-key" // Default fallback
|
||||||
api.Use(authMiddleware.Authenticate())
|
accessTokenExpire := 24 * time.Hour
|
||||||
{
|
|
||||||
// Ví dụ về protected endpoints
|
// Override with config values if available
|
||||||
// api.GET("/profile", userHandler.GetProfile)
|
if cfg.JWT.Secret != "" {
|
||||||
// api.PUT("/profile", userHandler.UpdateProfile)
|
jwtSecret = cfg.JWT.Secret
|
||||||
|
}
|
||||||
|
if cfg.JWT.AccessTokenExpire > 0 {
|
||||||
|
accessTokenExpire = time.Duration(cfg.JWT.AccessTokenExpire) * time.Minute
|
||||||
|
}
|
||||||
|
|
||||||
|
// Khởi tạo services
|
||||||
|
authSvc := service.NewAuthService(
|
||||||
|
userRepo,
|
||||||
|
roleRepo,
|
||||||
|
jwtSecret,
|
||||||
|
accessTokenExpire,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Khởi tạo middleware
|
||||||
|
authMiddleware := middleware.NewAuthMiddleware(authSvc)
|
||||||
|
|
||||||
|
// Khởi tạo các handlers phụ thuộc DB
|
||||||
|
authHandler := handler.NewAuthHandler(authSvc)
|
||||||
|
|
||||||
|
// Auth routes (public)
|
||||||
|
authGroup := router.Group("/api/v1/auth")
|
||||||
|
{
|
||||||
|
authGroup.POST("/register", authHandler.Register)
|
||||||
|
authGroup.POST("/login", authHandler.Login)
|
||||||
|
authGroup.POST("/refresh", authHandler.RefreshToken)
|
||||||
|
authGroup.POST("/logout", authMiddleware.Authenticate(), authHandler.Logout)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protected API routes
|
||||||
|
api := router.Group("/api/v1")
|
||||||
|
api.Use(authMiddleware.Authenticate())
|
||||||
|
{
|
||||||
|
// Ví dụ về protected endpoints
|
||||||
|
// api.GET("/profile", userHandler.GetProfile)
|
||||||
|
// api.PUT("/profile", userHandler.UpdateProfile)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.Info("Database is disabled via feature flag or connection failed. DB-dependent routes (e.g., /api/v1/auth, /api/v1/*) will not be available.")
|
||||||
|
// Optionally, register placeholder routes that return 503 Service Unavailable
|
||||||
|
// router.Any("/api/v1/auth/*any", func(c *gin.Context) {
|
||||||
|
// c.JSON(http.StatusServiceUnavailable, gin.H{"error": "Authentication service is currently disabled"})
|
||||||
|
// })
|
||||||
|
// router.Any("/api/v1/*any", func(c *gin.Context) {
|
||||||
|
// c.JSON(http.StatusServiceUnavailable, gin.H{"error": "API service is currently disabled due to database unavailability"})
|
||||||
|
// })
|
||||||
}
|
}
|
||||||
|
|
||||||
return router
|
return router
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user