Merge pull request #6 from Skrazzo/feat/docker-production
Feat/docker production
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"])
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
20
caddy/prod
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
|
||||
@@ -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"],
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { StrictMode } from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import { RouterProvider, createRouter } from "@tanstack/react-router";
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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
60
production.yml
Normal 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
|
||||
@@ -1,2 +1,3 @@
|
||||
#!/bin/bash
|
||||
export file="development.yml"
|
||||
# export file="production.yml"
|
||||
|
||||
Reference in New Issue
Block a user