tsk-6: Added docker support (#14)
Closes #6 Reviewed-on: phoenix/textsender-api#14 Co-authored-by: phoenix <kundeng00@pm.me> Co-committed-by: phoenix <kundeng00@pm.me>
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
vendor/
|
||||
|
||||
# Ignore git directory
|
||||
.git/
|
||||
|
||||
.gitea/
|
||||
|
||||
# Ignore environment files (configure via docker-compose instead)
|
||||
.env*
|
||||
@@ -0,0 +1,7 @@
|
||||
JWT_SECRET=NULqYIzgt28bTiyziCd7IOO7b6LnWDW!
|
||||
DB_NAME=textsender_db
|
||||
DB_USER=textsender
|
||||
DB_PASSWORD=password
|
||||
DB_HOST=main_db
|
||||
DB_PORT=5432
|
||||
DB_SSLMODE=disable
|
||||
@@ -3,3 +3,4 @@
|
||||
|
||||
.env
|
||||
.env.local
|
||||
.env.docker
|
||||
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
# Multi-stage Dockerfile for Go application
|
||||
FROM golang:1.25.3 AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
ca-certificates \
|
||||
openssh-client git
|
||||
|
||||
RUN mkdir -p -m 0700 ~/.ssh && \
|
||||
ssh-keyscan git.kundeng.us >> ~/.ssh/known_hosts
|
||||
|
||||
# Configure Git to use SSH for GitHub
|
||||
RUN git config --global url."ssh://git@git.kundeng.us".insteadOf "https://git.kundeng.us"
|
||||
|
||||
# Set up the Go environment for private modules
|
||||
ENV GOPRIVATE=git.kundeng.us
|
||||
|
||||
|
||||
# Copy go mod and sum files
|
||||
COPY go.mod go.sum ./
|
||||
|
||||
RUN --mount=type=ssh mkdir src && \
|
||||
go mod download
|
||||
|
||||
# Copy source code
|
||||
COPY ./cmd ./cmd
|
||||
COPY ./internal ./internal
|
||||
COPY ./Makefile .
|
||||
COPY ./.env .
|
||||
COPY ./migrations ./migrations
|
||||
|
||||
# Build the application
|
||||
RUN CGO_ENABLED=0 GOOS=linux make build
|
||||
|
||||
# Runtime stage
|
||||
FROM alpine:latest AS production
|
||||
|
||||
RUN apk --no-cache add ca-certificates
|
||||
|
||||
WORKDIR /root/
|
||||
|
||||
# Copy the pre-built binary file from the previous stage
|
||||
COPY --from=builder /app/textsender-api .
|
||||
COPY --from=builder /app/.env ./
|
||||
COPY --from=builder /app/migrations ./migrations
|
||||
|
||||
# Expose port
|
||||
EXPOSE 8080
|
||||
|
||||
# Command to run the executable
|
||||
CMD ["./textsender-api"]
|
||||
+15
-5
@@ -14,7 +14,7 @@ import (
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
|
||||
"git.kundeng.us/phoenix/textsender-api/internal/config"
|
||||
"git.kundeng.us/phoenix/textsender-api/internal/db"
|
||||
database "git.kundeng.us/phoenix/textsender-api/internal/db"
|
||||
"git.kundeng.us/phoenix/textsender-api/internal/handler"
|
||||
"git.kundeng.us/phoenix/textsender-api/internal/handler/endpoint"
|
||||
mdlware "git.kundeng.us/phoenix/textsender-api/internal/middleware"
|
||||
@@ -32,26 +32,36 @@ func main() {
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
database, err := db.NewDatabase(cfg.GetDBConnString())
|
||||
db, err := database.NewDatabase(cfg.GetDBConnString())
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to connect to database: %v", err)
|
||||
}
|
||||
defer database.Close()
|
||||
defer db.Close()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
if cfg.ResetDB {
|
||||
log.Println("Resetting database")
|
||||
if err := database.ResetDatabase(ctx); err != nil {
|
||||
if err := db.ResetDatabase(ctx); err != nil {
|
||||
log.Fatalf("Failed to reset database: %v", err)
|
||||
}
|
||||
log.Println("Database reset completed. Exiting.")
|
||||
return
|
||||
} else {
|
||||
if exists, err := database.TableExists(ctx, db.Pool, "contacts "); err == nil && !exists {
|
||||
fmt.Println("Resetting database")
|
||||
err = db.ResetDatabase(ctx)
|
||||
if err != nil {
|
||||
fmt.Printf("Error:%v", err)
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("Error:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
jwtService := services.NewJWTService(cfg.JWTSecret)
|
||||
|
||||
contactStore := store.NewContactStore(database.Pool)
|
||||
contactStore := store.NewContactStore(db.Pool)
|
||||
contactHandler := handler.NewContactHandler(contactStore)
|
||||
|
||||
router := chi.NewRouter()
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
version: '3.8' # Use a recent version
|
||||
|
||||
services:
|
||||
api:
|
||||
build: # Tells docker-compose to build the Dockerfile in the current directory
|
||||
context: .
|
||||
ssh: ["default"] # Uses host's SSH agent
|
||||
container_name: textsender # Optional: Give the container a specific name
|
||||
ports:
|
||||
# Map host port 8000 to container port 3000 (adjust as needed)
|
||||
- "8080:8080"
|
||||
env_file:
|
||||
- .env
|
||||
depends_on:
|
||||
main_db:
|
||||
condition: service_healthy # Wait for the DB to be healthy before starting the app
|
||||
networks:
|
||||
- textsender-network
|
||||
restart: unless-stopped # Optional: Restart policy
|
||||
|
||||
auth_api:
|
||||
build: # Tells docker-compose to build the Dockerfile in the current directory
|
||||
context: ../textsender-auth
|
||||
ssh: ["default"] # Uses host's SSH agent
|
||||
dockerfile: Dockerfile
|
||||
container_name: textsender_auth # Optional: Give the container a specific name
|
||||
ports:
|
||||
# Map host port 8000 to container port 3000 (adjust as needed)
|
||||
- "9080:9080"
|
||||
env_file:
|
||||
- ../textsender-auth/.env
|
||||
depends_on:
|
||||
auth_db:
|
||||
condition: service_healthy # Wait for the DB to be healthy before starting the app
|
||||
networks:
|
||||
- textsender-network
|
||||
restart: unless-stopped # Optional: Restart policy
|
||||
|
||||
# PostgreSQL Database Service
|
||||
main_db:
|
||||
image: postgres:18.0-alpine # Use an official Postgres image (Alpine variant is smaller)
|
||||
container_name: textsender_db # Optional: Give the container a specific name
|
||||
environment:
|
||||
# These MUST match the user, password, and database name in the DATABASE_URL above
|
||||
POSTGRES_USER: ${POSTGRES_AUTH_USER:-textsender}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_AUTH_PASSWORD:-password}
|
||||
POSTGRES_DB: ${POSTGRES_AUTH_DB:-textsender_db}
|
||||
volumes:
|
||||
# Persist database data using a named volume
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
ports:
|
||||
- "5432:5432"
|
||||
healthcheck:
|
||||
# Checks if Postgres is ready to accept connections
|
||||
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 10s
|
||||
networks:
|
||||
- textsender-network
|
||||
restart: always # Optional: Restart policy
|
||||
|
||||
auth_db:
|
||||
image: postgres:18.0-alpine # Use an official Postgres image (Alpine variant is smaller)
|
||||
container_name: textsender_auth_db # Optional: Give the container a specific name
|
||||
environment:
|
||||
# These MUST match the user, password, and database name in the DATABASE_URL above
|
||||
POSTGRES_USER: ${POSTGRES_AUTH_USER:-textsender_auth}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_AUTH_PASSWORD:-password}
|
||||
POSTGRES_DB: ${POSTGRES_AUTH_DB:-textsender_auth_db}
|
||||
volumes:
|
||||
# Persist database data using a named volume
|
||||
- postgres_data_auth:/var/lib/postgresql/data
|
||||
ports:
|
||||
- "5433:5432"
|
||||
healthcheck:
|
||||
# Checks if Postgres is ready to accept connections
|
||||
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 10s
|
||||
networks:
|
||||
- textsender-network
|
||||
restart: always # Optional: Restart policy
|
||||
|
||||
# Define the named volume for data persistence
|
||||
volumes:
|
||||
postgres_data:
|
||||
driver: local # Use the default local driver
|
||||
postgres_data_auth:
|
||||
driver: local # Use the default local driver
|
||||
|
||||
networks:
|
||||
textsender-network:
|
||||
driver: bridge
|
||||
@@ -54,6 +54,25 @@ func NewDatabase(connString string) (*Database, error) {
|
||||
return &Database{Pool: pool}, nil
|
||||
}
|
||||
|
||||
func TableExists(ctx context.Context, conn *pgxpool.Pool, tableName string) (bool, error) {
|
||||
var exists bool
|
||||
|
||||
query := `
|
||||
SELECT EXISTS (
|
||||
SELECT FROM information_schema.tables
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name = $1
|
||||
);
|
||||
`
|
||||
|
||||
err := conn.QueryRow(ctx, query, tableName).Scan(&exists)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("error checking if table exists: %w", err)
|
||||
}
|
||||
|
||||
return exists, nil
|
||||
}
|
||||
|
||||
func (db *Database) Close() {
|
||||
if db.Pool != nil {
|
||||
db.Pool.Close()
|
||||
@@ -115,4 +134,3 @@ func (db *Database) ResetDatabase(ctx context.Context) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user