Merge pull request #6 from Skrazzo/feat/docker-production

Feat/docker production
This commit is contained in:
Leons Aleksandrovs
2025-07-13 22:00:19 +03:00
committed by GitHub
15 changed files with 135 additions and 12 deletions

View File

@@ -1,4 +1,9 @@
# API key for chatgpt
CHATGPT_KEY=api key for chatgpt
# Chat gpt model
CHATGPT_MODEL=gpt-4o
# This is secret key for jwt signature
JWT_SECRET=just a random string here
# Set to false to disable registration
ALLOW_REGISTER=true

View File

@@ -7,13 +7,21 @@ WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
# ---- Production mode ----
FROM base AS prod
# ---- Build mode ----
FROM base AS build
WORKDIR /app
# Copy code, and compile
COPY . .
RUN go build -o server main.go
# ---- Production mode ----
FROM alpine@sha256:8a1f59ffb675680d47db6337b49d22281a139e9d709335b492be023728e11715 AS prod
WORKDIR /app
# Copy built binary
COPY --from=build /app/server .
# Expose 8080 port
EXPOSE 8080

View File

@@ -3,12 +3,29 @@ FROM oven/bun:1.2.18-alpine@sha256:a7df687a2f684ee2f7404e2592039e192d75d26a04f84
WORKDIR /app
# Install dependencies
COPY package.json bun.lock ./
COPY frontend/package.json frontend/bun.lock ./
RUN bun install --frozen-lockfile
# ------ Build stage ------
FROM deps AS build
WORKDIR /app
# Copy code, and compile
COPY frontend/ .
RUN bun run build
# ------ Development stage ------
FROM deps AS dev
# Run
CMD ["bun", "run", "dev"]
# ------ Production stage ------
FROM caddy:2.10.0-alpine@sha256:e2e3a089760c453bc51c4e718342bd7032d6714f15b437db7121bfc2de2654a6 AS prod
WORKDIR /app
# Copy built code
COPY --from=build /app/dist .
# Copy configuration for caddy
COPY caddy/prod /etc/caddy/Caddyfile

View File

@@ -1,6 +1,9 @@
package config
import "os"
import (
"log"
"os"
)
func defaultValue(val string, def string) string {
if val == "" {
@@ -20,4 +23,8 @@ func LoadEnv() {
Env["JWT_SECRET"] = defaultValue(os.Getenv("JWT_SECRET"), "just a random string here")
Env["Environment"] = defaultValue(os.Getenv("Environment"), "dev")
Env["CHATGPT_KEY"] = defaultValue(os.Getenv("CHATGPT_KEY"), "")
Env["REGISTER"] = defaultValue(os.Getenv("ALLOW_REGISTER"), "true")
log.Printf("[INFO] Register set to '%s'\n", Env["REGISTER"])
Env["CHATGPT_MODEL"] = defaultValue(os.Getenv("CHATGPT_MODEL"), "gpt-4o")
log.Printf("[INFO] ChatGPT model set to '%s'\n", Env["CHATGPT_MODEL"])
}

View File

@@ -25,6 +25,12 @@ type RegisterForm struct {
}
func Register(c *gin.Context) {
// Check for register environment
if config.Env["REGISTER"] != "true" {
res.Error(c, "Registration is disabled", http.StatusForbidden)
return
}
// Receive data from frontend, check if data is okay, hash password, call model
var data RegisterForm
if err := c.ShouldBindJSON(&data); err != nil {

View File

@@ -37,7 +37,7 @@ func GenerateCoverLetter(templateHTML string, jobHTML string) (GeneratedCover, e
}
payload := ChatRequest{
Model: "gpt-4o", // o4-mini
Model: config.Env["CHATGPT_MODEL"],
ResponseFormat: &ResponseFormat{Type: "json_object"},
Messages: []ChatMessage{
{

20
caddy/prod Normal file
View File

@@ -0,0 +1,20 @@
{
admin off
auto_https off
}
:8080 {
encode
# Proxy to backend
handle_path /api* {
reverse_proxy backend:8080
}
# Server static files
handle_path /* {
root * /app
try_files {path} /index.html
file_server
}
}

View File

@@ -25,8 +25,8 @@ services:
condition: service_healthy
frontend:
build:
context: ./frontend
dockerfile: ../Dockerfile.frontend
context: .
dockerfile: Dockerfile.frontend
target: dev # Development stage
restart: unless-stopped
container_name: cover-letter-frontend
@@ -44,7 +44,7 @@ services:
networks:
- cover-letter-network
volumes:
- ./caddy/Caddyfile:/etc/caddy/Caddyfile
- ./caddy/dev:/etc/caddy/Caddyfile
ports:
- 8000:8080
depends_on:

View File

@@ -6,10 +6,9 @@ import type { TokenUserInfo } from "@/types/api";
interface Props {
children: React.ReactNode;
className?: string;
}
export default function Authorised({ children, className = "" }: Props) {
export default function Authorised({ children }: Props) {
// Check authentication
useQuery({
queryKey: ["user_info"],

View File

@@ -1,4 +1,3 @@
import { StrictMode } from "react";
import ReactDOM from "react-dom/client";
import { RouterProvider, createRouter } from "@tanstack/react-router";

View File

@@ -3,7 +3,7 @@ import { Card, CardAction, CardContent, CardDescription, CardHeader, CardTitle }
import { useAppForm } from "@/hooks/formHook";
import Guest from "@/layouts/Guest";
import requests from "@/lib/requests";
import { createFileRoute, Link, redirect, useNavigate } from "@tanstack/react-router";
import { createFileRoute, Link, useNavigate } from "@tanstack/react-router";
import { useState } from "react";
import toast from "react-hot-toast";
import * as z from "zod/v4";

View File

@@ -1,3 +1,4 @@
// @ts-nocheck
import { defineConfig } from "vite";
import viteReact from "@vitejs/plugin-react";
import tailwindcss from "@tailwindcss/vite";

60
production.yml Normal file
View File

@@ -0,0 +1,60 @@
services:
backend:
build:
context: ./backend
dockerfile: ../Dockerfile.backend
target: prod # Development mode with hot reload
restart: unless-stopped
container_name: cover-letter-backend
env_file:
- .env
environment:
# - GIN_MODE=release # For production
- GIN_MODE=release
# - POSTGRES_DB=postgresql://username:password@host:port/database_name
- POSTGRES_DB=postgresql://postgres:postgres@db:5432/cover-letter
# - Environment=prod # For production
- Environment=prod
networks:
- cover-letter-network
depends_on:
db: # Wait for database to be ready (Pass healthcheck)
condition: service_healthy
frontend:
build:
context: .
dockerfile: Dockerfile.frontend
target: prod # Development stage
restart: unless-stopped
container_name: cover-letter-frontend
ports:
- 8000:8080
networks:
- cover-letter-network
db:
image: postgres:13.21-alpine3.22
restart: unless-stopped
container_name: cover-letter-db
networks:
- cover-letter-network
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
# - POSTGRES_HOST_AUTH_METHOD=trust # No password needed
- POSTGRES_DB=cover-letter
ports:
- 5432:5432
volumes:
- ./data/db:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 2s
timeout: 2s
retries: 5
networks:
cover-letter-network:
driver: bridge

View File

@@ -1,2 +1,3 @@
#!/bin/bash
export file="development.yml"
# export file="production.yml"