Add production Docker setup and update backend/frontend configs
Introduces .dockerignore, production Dockerfile and nginx config for frontend, and refactors docker-compose.yml for multi-service deployment. Updates backend and frontend code to support public API tagging, improves refund handling, adds test email endpoint, and migrates Orval config to TypeScript. Removes unused frontend Dockerfile and updates dependencies for React Query and Orval.
This commit is contained in:
@@ -1,11 +0,0 @@
|
||||
FROM node:20-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
|
||||
COPY . .
|
||||
|
||||
EXPOSE 5173
|
||||
CMD ["npm", "run", "dev"]
|
||||
16
frontend/Dockerfile.prod
Normal file
16
frontend/Dockerfile.prod
Normal file
@@ -0,0 +1,16 @@
|
||||
# Step 1: Build React (Vite) app
|
||||
FROM node:22-alpine AS build
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
# If package-lock.json exists, npm ci is faster and reproducible
|
||||
RUN npm ci || npm install
|
||||
COPY . .
|
||||
ENV NODE_ENV=production
|
||||
RUN npm run build
|
||||
|
||||
# Step 2: Nginx runtime
|
||||
FROM nginx:1.27-alpine
|
||||
COPY nginx/nginx.conf /etc/nginx/conf.d/default.conf
|
||||
COPY --from=build /app/dist /usr/share/nginx/html
|
||||
EXPOSE 80
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
75
frontend/nginx/nginx.conf
Normal file
75
frontend/nginx/nginx.conf
Normal file
@@ -0,0 +1,75 @@
|
||||
# nginx.conf
|
||||
worker_processes auto;
|
||||
user nginx;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
client_max_body_size 50m;
|
||||
|
||||
sendfile on;
|
||||
keepalive_timeout 65;
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
|
||||
# -------------------------
|
||||
# React frontend
|
||||
# -------------------------
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
location / {
|
||||
try_files $uri /index.html;
|
||||
# Ensure CSP is present on SPA document responses too
|
||||
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob:; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https://api.paylibo.com; connect-src 'self' http://127.0.0.1:8000 http://localhost:8000 ws: wss: https://api.paylibo.com; font-src 'self' data:" always;
|
||||
}
|
||||
|
||||
# -------------------------
|
||||
# Django backend API
|
||||
# -------------------------
|
||||
|
||||
# Serve Django static and media volumes mounted into the container
|
||||
location /static/ {
|
||||
alias /app/collectedstaticfiles/;
|
||||
}
|
||||
|
||||
location /media/ {
|
||||
alias /app/media/;
|
||||
}
|
||||
|
||||
# Same-origin proxy for API -> avoids CORS and allows cookies
|
||||
location /api {
|
||||
return 301 /api/;
|
||||
}
|
||||
location /api/ {
|
||||
proxy_pass http://backend:8000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
proxy_buffering off;
|
||||
client_max_body_size 50m;
|
||||
|
||||
# Ensure CSP is also present on proxied responses
|
||||
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob:; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https://api.paylibo.com; connect-src 'self' http://127.0.0.1:8000 http://localhost:8000 ws: wss: https://api.paylibo.com; font-src 'self' data:" always;
|
||||
}
|
||||
|
||||
# -------------------------
|
||||
# Security headers
|
||||
# -------------------------
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
|
||||
# Minimal, valid CSP for development (apply on all responses)
|
||||
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob:; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https://api.paylibo.com; connect-src 'self' http://127.0.0.1:8000 http://localhost:8000 ws: wss: https://api.paylibo.com; font-src 'self' data:" always;
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
module.exports = {
|
||||
public: {
|
||||
input: { target: "http://localhost:8000/api/schema/" },
|
||||
output: {
|
||||
target: "src/api/generated/public.ts",
|
||||
schemas: "src/api/generated/models",
|
||||
client: "axios",
|
||||
override: {
|
||||
mutator: {
|
||||
path: "src/api/publicClient.ts",
|
||||
name: "publicApi",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
private: {
|
||||
input: { target: "http://localhost:8000/api/schema/" },
|
||||
output: {
|
||||
target: "src/api/generated/private.ts",
|
||||
schemas: "src/api/generated/models",
|
||||
client: "axios",
|
||||
override: {
|
||||
mutator: {
|
||||
path: "src/api/privateClient.ts",
|
||||
name: "privateApi",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
59
frontend/orval.config.ts
Normal file
59
frontend/orval.config.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { defineConfig } from "orval";
|
||||
import "dotenv/config";
|
||||
|
||||
const backendUrl = process.env.VITE_API_BASE_URL || "http://localhost:8000";
|
||||
|
||||
// může se hodit pokud nechceme při buildu generovat klienta (nechat false pro produkci nebo vynechat)
|
||||
const SKIP_ORVAL = process.env.SKIP_ORVAL === "true";
|
||||
|
||||
if (SKIP_ORVAL){
|
||||
|
||||
}
|
||||
export default defineConfig({
|
||||
public: {
|
||||
input: {
|
||||
target: `${backendUrl}/api/schema/`,
|
||||
|
||||
filters: {
|
||||
mode: "include",
|
||||
tags: ["public"],
|
||||
},
|
||||
},
|
||||
output: {
|
||||
target: "src/api/generated/public.ts",
|
||||
schemas: "src/api/generated/public/models",
|
||||
|
||||
client: "react-query",
|
||||
httpClient: "axios",
|
||||
|
||||
override: {
|
||||
mutator: {
|
||||
path: "src/api/publicClient.ts", //IMPORTANTE
|
||||
name: "publicApi",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
private: {
|
||||
input: {
|
||||
target: `${backendUrl}/api/schema/`
|
||||
|
||||
// No filters, include all endpoints
|
||||
},
|
||||
output: {
|
||||
|
||||
target: "src/api/generated/private.ts", //IMPORTANTE
|
||||
schemas: "src/api/generated/private/models",
|
||||
|
||||
client: "react-query",
|
||||
httpClient: "axios",
|
||||
|
||||
override: {
|
||||
mutator: {
|
||||
path: "src/api/privateClient.ts",
|
||||
name: "privateApi",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
3473
frontend/package-lock.json
generated
3473
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -5,15 +5,19 @@
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc -b && vite build",
|
||||
"build": "tsc -b && tsc -b && vite build",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview",
|
||||
"api:gen": "orval --config orval.config.js"
|
||||
|
||||
"api:gen": "orval --config orval.config.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tailwindcss/vite": "^4.1.16",
|
||||
"@tanstack/react-query": "^5.90.12",
|
||||
"@types/react-router": "^5.1.20",
|
||||
"axios": "^1.13.0",
|
||||
"dotenv": "^17.2.3",
|
||||
"orval": "^7.13.2",
|
||||
"react": "^19.1.1",
|
||||
"react-dom": "^19.1.1",
|
||||
"react-icons": "^5.5.0",
|
||||
@@ -34,7 +38,6 @@
|
||||
"globals": "^16.3.0",
|
||||
"typescript": "~5.8.3",
|
||||
"typescript-eslint": "^8.39.1",
|
||||
"vite": "^7.1.2",
|
||||
"openapi-generator-cli": "^2.9.0"
|
||||
"vite": "^7.1.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// User API model for searching users by username
|
||||
// Structure matches other model files (see order.js for reference)
|
||||
|
||||
import Client from '../legacy/Client';
|
||||
import Client from '../Client';
|
||||
|
||||
const API_BASE_URL = "/account/users";
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { createContext, useState, useEffect } from 'react';
|
||||
|
||||
import userAPI from '../api/models/User';
|
||||
import userAPI from '../api/legacy/models/User';
|
||||
|
||||
// definice uživatele
|
||||
export interface User {
|
||||
|
||||
Reference in New Issue
Block a user