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
180 changes: 180 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
# Environment Configuration Template for Bit Issues Backend
# Copy this file to .env and fill in your actual values
# This file contains all required environment variables with examples and documentation

# =============================================================================
# SERVER CONFIGURATION
# =============================================================================

# HTTP Server Address
# Purpose: Host and port the HTTP server will listen on
# Format: host:port (e.g., 0.0.0.0:3000, 127.0.0.1:3000)
# Default: 127.0.0.1:3000
# Example: 0.0.0.0:3000
HTTP__ADDRESS=127.0.0.1:3000

# Proxy Header Name
# Purpose: Header name used to identify the original client IP when behind a proxy
# Format: string (e.g., X-Forwarded-For, X-Real-IP)
# Default: X-Forwarded-For
HTTP__PROXY_HEADER=X-Forwarded-For

# Trusted Proxy Addresses
# Purpose: List of trusted proxy addresses (CIDR notation supported)
# Format: comma-separated list of addresses
# Example: 10.0.0.0/8,172.16.0.0/12
HTTP__PROXIES=

# =============================================================================
# OPENAPI CONFIGURATION
# =============================================================================

# OpenAPI Documentation Enabled
# Purpose: Enable/disable OpenAPI documentation endpoint
# Format: boolean (true/false)
# Default: true
HTTP__OPENAPI__ENABLED=true

# OpenAPI Public Host
# Purpose: Public host URL for OpenAPI documentation (used in OpenAPI spec)
# Format: hostname (e.g., api.example.com)
# Example: api.bitissues.dev
HTTP__OPENAPI__PUBLIC_HOST=

# OpenAPI Documentation Path
# Purpose: URL path for OpenAPI documentation (e.g., /docs, /api/docs)
# Default: (empty - uses default path)
# Example: /api/docs
HTTP__OPENAPI__PUBLIC_PATH=

# =============================================================================
# DATABASE CONFIGURATION
# =============================================================================

# Database Connection URL
# Purpose: Connection string for MariaDB/MySQL database
# Format: mariadb://[username:password@]host[:port]/database[?query_params]
# Example: mariadb://user:password@localhost:3306/dbname?charset=utf8mb4&parseTime=True&loc=UTC&clientFoundRows=true
# Example: mariadb://bit-issues:bit-issues@127.0.0.1:3306/bit-issues?charset=utf8mb4&parseTime=True&loc=UTC&clientFoundRows=true
DATABASE__URL=mariadb://bit-issues:bit-issues@127.0.0.1:3306/bit-issues?charset=utf8mb4&parseTime=True&loc=UTC&clientFoundRows=true

# Database Connection Max Idle Time
# Purpose: Maximum amount of time a database connection may be idle
# Format: duration (e.g., 1m, 30s, 1h)
# Default: 0 (no limit)
# Example: 1m
DATABASE__CONN_MAX_IDLE_TIME=0

# Database Connection Max Lifetime
# Purpose: Maximum amount of time a database connection may be reused
# Format: duration (e.g., 1h, 30m)
# Default: 0 (no limit)
# Example: 1h
DATABASE__CONN_MAX_LIFETIME=0

# Database Max Open Connections
# Purpose: Maximum number of open database connections
# Format: integer
# Default: 0 (unlimited)
# Example: 25
DATABASE__MAX_OPEN_CONNS=0

# Database Max Idle Connections
# Purpose: Maximum number of idle database connections
# Format: integer
# Default: 0 (unlimited)
# Example: 10
DATABASE__MAX_IDLE_CONNS=0

# =============================================================================
# JWT AUTHENTICATION CONFIGURATION
# =============================================================================

# JWT Secret Key
# Purpose: Secret key used to sign and verify JWT tokens (minimum 32 bytes)
# Format: string (at least 32 characters)
# IMPORTANT: Change this to a secure random string in production!
# Default: secret
# Example: your-secure-secret-key-at-least-32-bytes-long
JWT__SECRET=secret

# JWT Access Token TTL
# Purpose: Time-to-live for access tokens
# Format: duration (e.g., 15m, 1h, 24h)
# Default: 15m
# Example: 15m
JWT__ACCESS_TTL=15m

# JWT Issuer
# Purpose: Issuer claim for JWT tokens
# Format: string (e.g., domain name)
# Default: bitissues.dev
# Example: bitissues.dev
JWT__ISSUER=bitissues.dev

# =============================================================================
# STORAGE CONFIGURATION (S3 Compatible)
# =============================================================================

# S3-Compatible Storage URL
# Purpose: Connection URL for S3-compatible storage (MinIO, AWS S3, etc.)
# Format: s3://bucket_name?endpoint=host:port&region=region[&insecure=true]
# Example: s3://attachments?endpoint=localhost:9000&region=us-east-1&insecure=true
# Example: s3://bit-issues/uploads?endpoint=storage.example.com&region=us-east-1
STORAGE__URL=s3://bit-issues/uploads?endpoint=localhost:9000&region=us-east-1&insecure=true

# Presigned Link TTL
# Purpose: Time-to-live for presigned storage URLs
# Format: duration (e.g., 15m, 1h)
# Default: 15m
# Example: 15m
STORAGE__LINKS_TTL=15m

# AWS Access Key ID
# Purpose: AWS/MinIO access key for storage operations
# Format: string
# Example: minioadmin
AWS_ACCESS_KEY_ID=minioadmin

# AWS Secret Access Key
# Purpose: AWS/MinIO secret key for storage operations
# Format: string
# Example: minioadmin
AWS_SECRET_ACCESS_KEY=minioadmin

# AWS Region
# Purpose: AWS region for S3 storage
# Format: string (e.g., us-east-1, eu-west-1)
# Default: us-east-1
# Example: us-east-1
AWS_REGION=us-east-1

# =============================================================================
# ATTACHMENTS CONFIGURATION
# =============================================================================

# Maximum Attachment Size
# Purpose: Maximum size allowed for file attachments (in bytes)
# Format: integer (bytes)
# Default: 10485760 (10 MB)
# Example: 10485760 (10 MB)
# Example: 52428800 (50 MB)
ATTACHMENTS__MAX_SIZE=10485760

# =============================================================================
# GENERAL CONFIGURATION
# =============================================================================

# Timezone
# Purpose: Server timezone for logging and timestamps
# Format: IANA timezone (e.g., UTC, America/New_York, Europe/London)
# Default: UTC
# Example: UTC
# Example: Asia/Krasnoyarsk
TIMEZONE=UTC

# Config Path (Optional)
# Purpose: Path to an optional YAML configuration file that overrides env vars
# Format: filesystem path
# Example: ./config/local.yaml
CONFIG_PATH=
31 changes: 31 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
FROM golang:1.25-alpine AS builder

WORKDIR /src

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

COPY . .

RUN CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags="-s -w" -o /out/server ./main.go
Comment thread
coderabbitai[bot] marked this conversation as resolved.


FROM alpine:latest

# Install certificates and timezone data
RUN apk add --no-cache ca-certificates tzdata

# Create non-root user
RUN addgroup -g 1000 appuser && \
adduser -D -u 1000 -G appuser --home /app appuser

# Set the Current Working Directory inside the container
WORKDIR /app

# Copy the binary file from the previous stage
COPY --from=builder --chown=appuser:appuser /out/server /app/server

USER appuser

# Command to run the executable
ENTRYPOINT ["/app/server"]
91 changes: 91 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
name: bit-issues_backend
services:
backend:
build:
context: .
dockerfile: Dockerfile
restart: on-failure
depends_on:
mariadb:
condition: service_healthy
minio-init:
condition: service_completed_successfully
environment:
TZ: ${TIMEZONE:-UTC}
AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID:-minioadmin}
AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY:-minioadmin}
DATABASE__URL: mariadb://bit-issues:bit-issues@mariadb:3306/bit-issues?charset=utf8mb4&parseTime=True&loc=Local&clientFoundRows=true
HTTP__ADDRESS: 0.0.0.0:3000
JWT__SECRET: ${JWT__SECRET:-secret}
STORAGE__URL: s3://bit-issues/uploads?endpoint=localhost:9000&region=us-east-1&insecure=true
ports:
- "3000:3000"
extra_hosts:
- "localhost:host-gateway"
Comment thread
capcom6 marked this conversation as resolved.

minio:
image: minio/minio:latest
command: server /data
environment:
TZ: ${TIMEZONE:-UTC}
MINIO_ROOT_USER: ${AWS_ACCESS_KEY_ID:-minioadmin}
MINIO_ROOT_PASSWORD: ${AWS_SECRET_ACCESS_KEY:-minioadmin}
MINIO_REGION_NAME: ${AWS_REGION:-us-east-1}
MINIO_API_CORS_ALLOW_ORIGIN: "*"
healthcheck:
test: ["CMD", "mc", "ready", "local"]
interval: 30s
timeout: 5s
retries: 3
ports:
- "9000:9000"
volumes:
- minio-data:/data

minio-init:
image: minio/mc:latest
depends_on:
minio:
condition: service_healthy
entrypoint: ["/bin/sh", "-c"]
command:
- |
set -eu
echo "waiting for minio..."
until (/usr/bin/mc alias set local http://minio:9000 "$${AWS_ACCESS_KEY_ID:-minioadmin}" "$${AWS_SECRET_ACCESS_KEY:-minioadmin}") >/dev/null 2>&1; do
sleep 1
done
/usr/bin/mc mb --ignore-existing local/bit-issues
echo "minio ready"
environment:
TZ: ${TIMEZONE:-UTC}
AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID:-minioadmin}
AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY:-minioadmin}

mariadb:
image: mariadb:lts
environment:
TZ: ${TIMEZONE:-UTC}
MARIADB_ROOT_PASSWORD: root
MARIADB_DATABASE: bit-issues
MARIADB_USER: bit-issues
MARIADB_PASSWORD: bit-issues
MARIADB_AUTO_UPGRADE: ON
volumes:
- mariadb-data:/var/lib/mysql
healthcheck:
test:
[
"CMD",
"healthcheck.sh",
"--su-mysql",
"--connect",
"--innodb_initialized",
]
interval: 10s
timeout: 5s
retries: 10

volumes:
mariadb-data:
minio-data:
1 change: 1 addition & 0 deletions internal/storage/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ func Module() fx.Option {
return miniofx.Config{
Endpoint: u.Query().Get("endpoint"),
Region: u.Query().Get("region"),
Secure: u.Query().Get("insecure") != "true",
}, nil
}),
fx.Provide(NewService),
Expand Down
6 changes: 2 additions & 4 deletions pkg/miniofx/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,9 @@ import (
)

func NewClient(config Config) (*minio.Client, error) {
endpoint := config.Endpoint

client, err := minio.New(endpoint, &minio.Options{
client, err := minio.New(config.Endpoint, &minio.Options{
Creds: credentials.NewEnvAWS(),
Secure: true,
Secure: config.Secure,
Region: config.Region,
})
if err != nil {
Expand Down
1 change: 1 addition & 0 deletions pkg/miniofx/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ package miniofx
type Config struct {
Endpoint string
Region string
Secure bool
}
Loading