fix(backend): solve 500 errors with auto-migrations and standardized filenames

This commit is contained in:
Michael committed 2026-02-04 16:32:28 +01:00
1 parent 8ed1782de6
commit 502b76c5d6
6 files changed
+83 -36

No files matched your search

+19 -20
View File
@@ -1,34 +1,33 @@
# Build stage
FROM golang:1.22-alpine AS builder
# Multi-stage Dockerfile for FlowReader
# Step 1: Build the React Frontend
FROM node:20-alpine AS web-builder
WORKDIR /app/web
COPY web/package*.json ./
RUN npm install
COPY web/ ./
RUN npm run build
# Step 2: Build the Go Backend
FROM golang:1.24-alpine AS builder
WORKDIR /app
# Install dependencies
RUN apk add --no-cache git
# Copy go mod files
COPY go.mod go.sum* ./
COPY go.mod go.sum ./
RUN go mod download
# Copy source code
COPY . .
# Build the application
# Copy the built frontend from Step 1
COPY --from=web-builder /app/web/dist ./web/dist
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -o /server ./cmd/server
# Final stage
# Step 3: Final Production Image
FROM alpine:3.19
WORKDIR /app
# Install wget for healthcheck
RUN apk add --no-cache wget ca-certificates
# Copy binary from builder
COPY --from=builder /server /app/server
# Copy the static files for the Go server to serve
COPY --from=builder /app/web/dist /app/web/dist
# Copy migration files for auto-migration
COPY --from=builder /app/migrations /app/migrations
# Expose port
EXPOSE 8080
# Run the application
CMD ["/app/server"]
+15
View File
@@ -139,6 +139,21 @@ func main() {
})
})
// Serve Static Files (Frontend)
staticPath := "./web/dist"
if _, err := os.Stat(staticPath); err == nil {
fs := http.FileServer(http.Dir(staticPath))
r.Handle("/*", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// If the file exists, serve it, otherwise serve index.html (for SPA routing)
path := staticPath + r.URL.Path
if _, err := os.Stat(path); os.IsNotExist(err) {
http.ServeFile(w, r, staticPath+"/index.html")
return
}
fs.ServeHTTP(w, r)
}))
}
// Create server
srv := &http.Server{
Addr: ":" + cfg.Port,
+10 -9
View File
@@ -1,30 +1,31 @@
module github.com/michael/flowreader
go 1.22
go 1.24.0
require (
github.com/go-chi/chi/v5 v5.0.11
github.com/google/uuid v1.6.0
github.com/gorilla/websocket v1.5.3
github.com/jackc/pgx/v5 v5.5.2
github.com/microcosm-cc/bluemonday v1.0.27
github.com/mmcdole/gofeed v1.3.0
golang.org/x/crypto v0.47.0
)
require (
github.com/PuerkitoBio/goquery v1.8.0 // indirect
github.com/andybalholm/cascadia v1.3.1 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/css v1.0.1 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/jackc/puddle/v2 v2.2.1 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/microcosm-cc/bluemonday v1.0.27 // indirect
github.com/mmcdole/gofeed v1.3.0 // indirect
github.com/mmcdole/goxpp v1.1.1-0.20240225020742-a0c311522b23 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
golang.org/x/crypto v0.24.0 // indirect
golang.org/x/net v0.26.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/net v0.48.0 // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.40.0 // indirect
golang.org/x/text v0.33.0 // indirect
)
+39 -7
View File
@@ -6,6 +6,11 @@ import (
"fmt"
"log"
"os"
"path/filepath"
"sort"
"strings"
"github.com/jackc/pgx/v5/pgxpool"
)
@@ -36,26 +41,53 @@ func Connect(ctx context.Context, databaseURL string) (*pgxpool.Pool, error) {
}
// RunMigrations executes pending database migrations.
// Note: For production, use golang-migrate CLI. This is for development convenience.
func RunMigrations(ctx context.Context, pool *pgxpool.Pool) error {
// Check if schema_migrations table exists
// Check if users table exists
var exists bool
err := pool.QueryRow(ctx, `
SELECT EXISTS (
SELECT FROM information_schema.tables
WHERE table_schema = 'public'
AND table_name = 'schema_migrations'
AND table_name = 'users'
)
`).Scan(&exists)
if err != nil {
return fmt.Errorf("checking schema_migrations: %w", err)
return fmt.Errorf("checking users table: %w", err)
}
if !exists {
log.Println("Migrations table not found. Run 'make migrate-up' to apply migrations.")
if exists {
log.Println("Database already initialized")
return nil
}
log.Println("Database migrations verified")
log.Println("Initializing database and applying migrations...")
// Read migration files
entries, err := os.ReadDir("./migrations")
if err != nil {
return fmt.Errorf("reading migrations dir: %w", err)
}
var upFiles []string
for _, entry := range entries {
if !entry.IsDir() && strings.HasSuffix(entry.Name(), ".up.sql") {
upFiles = append(upFiles, entry.Name())
}
}
sort.Strings(upFiles)
for _, file := range upFiles {
log.Printf("Applying migration: %s", file)
content, err := os.ReadFile(filepath.Join("./migrations", file))
if err != nil {
return fmt.Errorf("reading migration %s: %w", file, err)
}
if _, err := pool.Exec(ctx, string(content)); err != nil {
return fmt.Errorf("executing migration %s: %w", file, err)
}
}
log.Println("All migrations applied successfully")
return nil
}
File renamed without changes.