Neatly packaged for docker

This commit is contained in:
Frederik Beimgraben 2025-09-01 04:55:11 +02:00
parent d0d5b811b4
commit 1cae3b3479
5 changed files with 91 additions and 6 deletions

31
frontend/Dockerfile Normal file
View File

@ -0,0 +1,31 @@
# Build stage
FROM node:18-alpine AS builder
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci
# Copy source code
COPY . .
# Build the application
RUN npm run build
# Production stage
FROM nginx:alpine
# Copy built assets from builder stage
COPY --from=builder /app/dist /usr/share/nginx/html
# Copy nginx configuration
COPY nginx.conf /etc/nginx/conf.d/default.conf
# Expose port 80
EXPOSE 80
# Start nginx
CMD ["nginx", "-g", "daemon off;"]

56
frontend/nginx.conf Normal file
View File

@ -0,0 +1,56 @@
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
# Enable gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# API proxy configuration
location /api/ {
proxy_pass http://api:8000/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
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_cache_bypass $http_upgrade;
# Increase timeouts for large file uploads
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
send_timeout 600;
# Increase max body size for file uploads (100MB)
client_max_body_size 100M;
}
# SPA fallback - serve index.html for all routes
location / {
try_files $uri $uri/ /index.html;
}
# Cache static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Don't cache index.html
location = /index.html {
expires -1;
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0";
}
}

View File

@ -36,8 +36,6 @@ import {
StarBorder, StarBorder,
} from "@mui/icons-material"; } from "@mui/icons-material";
import Radio from "@mui/material/Radio"; import Radio from "@mui/material/Radio";
import RadioGroup from "@mui/material/RadioGroup";
import Tooltip from "@mui/material/Tooltip";
import { useApplicationStore } from "../../store/applicationStore"; import { useApplicationStore } from "../../store/applicationStore";
interface ComparisonOffer { interface ComparisonOffer {
@ -748,7 +746,9 @@ const ComparisonOffersDialog: React.FC<ComparisonOffersDialogProps> = ({
} }
error={ error={
(!newOffer.url && !newOffer.attachment_id) || (!newOffer.url && !newOffer.attachment_id) ||
(newOffer.url !== "" && !newOffer.url.includes(".")) (!!newOffer.url &&
newOffer.url !== "" &&
!newOffer.url.includes("."))
} }
/> />
<FormControl size="small" fullWidth> <FormControl size="small" fullWidth>

View File

@ -6,7 +6,6 @@ import {
ListItemSecondaryAction, ListItemSecondaryAction,
Box, Box,
Typography, Typography,
Tooltip,
CircularProgress, CircularProgress,
Button, Button,
} from "@mui/material"; } from "@mui/material";
@ -14,7 +13,6 @@ import {
Warning, Warning,
CheckCircle, CheckCircle,
Receipt, Receipt,
Info,
DoNotDisturb, DoNotDisturb,
} from "@mui/icons-material"; } from "@mui/icons-material";
import ComparisonOffersDialog from "./ComparisonOffersDialog"; import ComparisonOffersDialog from "./ComparisonOffersDialog";
@ -180,7 +178,6 @@ const CostPositionsList: React.FC<CostPositionsListProps> = ({
.filter(({ cost }) => cost.name && cost.amountEur > 0) .filter(({ cost }) => cost.name && cost.amountEur > 0)
.map(({ cost, originalIndex }) => { .map(({ cost, originalIndex }) => {
const status = getPositionStatus(originalIndex); const status = getPositionStatus(originalIndex);
const needsAttention = status === "incomplete" && !loading;
return ( return (
<ListItem <ListItem

View File

@ -67,6 +67,7 @@ const AdminApplicationView: React.FC = () => {
downloadApplicationPdfAdmin, downloadApplicationPdfAdmin,
resetApplicationCredentials, resetApplicationCredentials,
isAdmin, isAdmin,
masterKey,
} = useApplicationStore(); } = useApplicationStore();
// Local state // Local state