Add full-stack Hello World app with CI/CD

- Go backend with PostgreSQL connection
- Next.js frontend with TypeScript
- Docker Compose for local development
- Woodpecker CI pipeline for build and push
- Kubernetes manifests with Kustomize
- ArgoCD application for GitOps deployment

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Erdenebat Tsenddorj
2026-01-21 21:06:08 +08:00
parent 2c4e7d2c38
commit f311439621
26 changed files with 955 additions and 1 deletions

19
backend/Dockerfile Normal file
View File

@@ -0,0 +1,19 @@
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod ./
RUN go mod download
COPY . .
RUN go mod tidy && CGO_ENABLED=0 GOOS=linux go build -o main .
FROM alpine:latest
WORKDIR /app
COPY --from=builder /app/main .
EXPOSE 8080
CMD ["./main"]

5
backend/go.mod Normal file
View File

@@ -0,0 +1,5 @@
module hello-world-backend
go 1.21
require github.com/lib/pq v1.10.9

93
backend/main.go Normal file
View File

@@ -0,0 +1,93 @@
package main
import (
"database/sql"
"encoding/json"
"fmt"
"log"
"net/http"
"os"
_ "github.com/lib/pq"
)
type Response struct {
Message string `json:"message"`
DBStatus string `json:"db_status"`
}
var db *sql.DB
func main() {
// PostgreSQL connection
dbHost := getEnv("DB_HOST", "localhost")
dbPort := getEnv("DB_PORT", "5432")
dbUser := getEnv("DB_USER", "postgres")
dbPassword := getEnv("DB_PASSWORD", "postgres")
dbName := getEnv("DB_NAME", "hellodb")
connStr := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable",
dbHost, dbPort, dbUser, dbPassword, dbName)
var err error
db, err = sql.Open("postgres", connStr)
if err != nil {
log.Printf("Warning: Could not connect to database: %v", err)
} else {
defer db.Close()
}
// HTTP handlers
http.HandleFunc("/api/hello", corsMiddleware(helloHandler))
http.HandleFunc("/api/health", corsMiddleware(healthHandler))
port := getEnv("PORT", "8080")
log.Printf("Server starting on port %s", port)
log.Fatal(http.ListenAndServe(":"+port, nil))
}
func helloHandler(w http.ResponseWriter, r *http.Request) {
dbStatus := "disconnected"
if db != nil {
err := db.Ping()
if err == nil {
dbStatus = "connected"
}
}
response := Response{
Message: "Hello World! - Go + PostgreSQL + Next.js",
DBStatus: dbStatus,
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}
func healthHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
}
func corsMiddleware(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
if r.Method == "OPTIONS" {
w.WriteHeader(http.StatusOK)
return
}
next(w, r)
}
}
func getEnv(key, defaultValue string) string {
if value := os.Getenv(key); value != "" {
return value
}
return defaultValue
}