V1.1.0 Responsive
Footer angepasst Lauffähigkeit auf Server verbessert deploy.sh mit for loop
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
FROM python:3.11-slim
|
||||
|
||||
WORKDIR /app
|
||||
@@ -5,6 +7,7 @@ WORKDIR /app
|
||||
# System-Abhängigkeiten installieren
|
||||
RUN apt-get update && apt-get install -y \
|
||||
gcc \
|
||||
curl \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Python-Abhängigkeiten installieren
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
FROM python:3.13-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
89
deploy.sh
Executable file
89
deploy.sh
Executable file
@@ -0,0 +1,89 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Deploy Script für laufschrift
|
||||
# Baut das Docker Image und lädt es zu docker.citysensor.de hoch
|
||||
|
||||
set -e
|
||||
|
||||
|
||||
# Konfiguration
|
||||
REGISTRY="docker.citysensor.de"
|
||||
PROJEKT="wetterstation"
|
||||
IMAGE_NAME=("${PROJEKT}-frontend" "${PROJEKT}-collector" "${PROJEKT}-api")
|
||||
TAG="${TAG:-$(date +%Y%m%d%H%M)}" # default Datum
|
||||
|
||||
# Build-Datum
|
||||
BUILD_DATE=$(date +%d.%m.%Y)
|
||||
|
||||
echo "=========================================="
|
||||
echo " Deploy Script"
|
||||
echo "=========================================="
|
||||
echo "Registry: ${REGISTRY}"
|
||||
echo "Images: ${IMAGE_NAME[*]}"
|
||||
echo "Tag: ${TAG}"
|
||||
echo "Build-Datum: ${BUILD_DATE}"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
# 1. Login zur Registry (falls noch nicht eingeloggt)
|
||||
echo ">>> Login zu ${REGISTRY}..."
|
||||
docker login "${REGISTRY}"
|
||||
echo ""
|
||||
|
||||
# 2. Multiplatform Builder einrichten (docker-container driver erforderlich)
|
||||
echo ">>> Richte Multiplatform Builder ein..."
|
||||
if ! docker buildx inspect multiplatform-builder &>/dev/null; then
|
||||
docker buildx create --name multiplatform-builder --driver docker-container --bootstrap
|
||||
fi
|
||||
docker buildx use multiplatform-builder
|
||||
echo ""
|
||||
|
||||
for image in "${IMAGE_NAME[@]}"; do
|
||||
# Entferne Projekt-Präfix für Verzeichnisnamen
|
||||
IMAGE_DIR="${image#${PROJEKT}-}"
|
||||
FULL_IMAGE="${REGISTRY}/${image}:${TAG}"
|
||||
|
||||
echo "=========================================="
|
||||
echo ">>> Baue ${image}..."
|
||||
echo ">>> Image: ${FULL_IMAGE}"
|
||||
echo "=========================================="
|
||||
|
||||
# Build-Args vorbereiten (für Frontend Version und Build-Date)
|
||||
BUILD_ARGS="--build-arg BUILD_DATE=${BUILD_DATE}"
|
||||
if [ "${IMAGE_DIR}" = "frontend" ]; then
|
||||
VERSION=$(grep '"version"' "${IMAGE_DIR}/package.json" | head -1 | sed 's/.*"version": "\(.*\)".*/\1/')
|
||||
BUILD_ARGS="${BUILD_ARGS} --build-arg VERSION=${VERSION}"
|
||||
fi
|
||||
|
||||
# 3. Docker Image bauen und pushen (Multiplatform)
|
||||
docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
${BUILD_ARGS} \
|
||||
-t "${FULL_IMAGE}" \
|
||||
--push \
|
||||
"./${IMAGE_DIR}"
|
||||
|
||||
# 4. Tagge auch als :latest
|
||||
echo ">>> Tagge ${image} als :latest..."
|
||||
docker buildx imagetools create \
|
||||
-t "${REGISTRY}/${image}:latest" \
|
||||
"${FULL_IMAGE}"
|
||||
|
||||
echo "✓ ${image} erfolgreich gebaut und gepusht!"
|
||||
echo ""
|
||||
done
|
||||
|
||||
echo ">>> Alle Builds erfolgreich!"
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "✓ Deploy erfolgreich abgeschlossen!"
|
||||
echo "=========================================="
|
||||
echo "Registry: ${REGISTRY}"
|
||||
echo "Projekt: ${PROJEKT}"
|
||||
echo "Tag: ${TAG}"
|
||||
echo ""
|
||||
echo "Auf dem Server ausführen:"
|
||||
echo " docker compose -f docker-compose.prod.yml pull"
|
||||
echo " docker compose -f docker-compose.prod.yml up -d"
|
||||
echo ""
|
||||
@@ -56,6 +56,12 @@ services:
|
||||
networks:
|
||||
- internal
|
||||
- proxy
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 10s
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.docker.network=dockge_default"
|
||||
@@ -72,7 +78,8 @@ services:
|
||||
container_name: wetterstation_frontend_prod
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- api
|
||||
api:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- internal
|
||||
- proxy
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
# Build stage
|
||||
FROM node:20-alpine AS builder
|
||||
FROM --platform=$BUILDPLATFORM node:20-alpine AS builder
|
||||
|
||||
# Build arguments
|
||||
ARG BUILD_DATE=unknown
|
||||
ARG VERSION=unknown
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
@@ -12,14 +18,18 @@ RUN npm ci
|
||||
# Copy source code
|
||||
COPY . .
|
||||
|
||||
# Build app
|
||||
# Build app with build info
|
||||
ENV VITE_BUILD_DATE=${BUILD_DATE}
|
||||
ENV VITE_VERSION=${VERSION}
|
||||
RUN npm run build
|
||||
|
||||
# Production stage
|
||||
FROM nginx:alpine
|
||||
|
||||
WORKDIR /usr/share/nginx/html
|
||||
|
||||
# Copy built app from builder
|
||||
COPY --from=builder /app/dist /usr/share/nginx/html
|
||||
COPY --from=builder /app/dist .
|
||||
|
||||
# Copy nginx configuration
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
@@ -4,6 +4,10 @@ server {
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
# Docker DNS resolver für dynamische Service-Auflösung
|
||||
resolver 127.0.0.11 valid=30s;
|
||||
resolver_timeout 5s;
|
||||
|
||||
# Gzip compression
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
@@ -12,7 +16,8 @@ server {
|
||||
|
||||
# API proxy (wird im Docker-Compose-Netzwerk aufgelöst)
|
||||
location /api/ {
|
||||
proxy_pass http://api:8000/;
|
||||
set $upstream_api api:8000;
|
||||
proxy_pass http://$upstream_api/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "wetterstation-frontend",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"version": "1.1.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.dashboard {
|
||||
width: 100%;
|
||||
max-width: 795px;
|
||||
max-width: 1900px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@@ -65,3 +65,83 @@
|
||||
color: #666;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.dashboard-footer {
|
||||
/* margin-top: 2rem;
|
||||
*/ padding-top: 1rem;
|
||||
}
|
||||
|
||||
.footer-divider {
|
||||
border: none;
|
||||
border-top: 1px solid #ccc;
|
||||
margin: 0 0 1rem 0;
|
||||
}
|
||||
|
||||
.footer-credits {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 0.85rem;
|
||||
color: #666;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.footer-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.footer-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.footer-sponsor {
|
||||
text-align: center;
|
||||
font-size: 0.85rem;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.footer-sponsor a {
|
||||
color: #0066cc;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.footer-sponsor a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.version-line {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.version-short {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.version-full {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
/* Responsive Design für schmale Bildschirme (Smartphones) */
|
||||
@media (max-width: 768px) {
|
||||
.charts-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.current-values {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.dashboard {
|
||||
padding: 0 0.5rem;
|
||||
}
|
||||
|
||||
.version-short {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.version-full {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,9 @@ import HighchartsReact from 'highcharts-react-official'
|
||||
import { format } from 'date-fns'
|
||||
import { de } from 'date-fns/locale'
|
||||
import './WeatherDashboard.css'
|
||||
|
||||
// Build-Informationen (werden beim Build eingef\u00fcgt)
|
||||
const buildDate = __BUILD_DATE__
|
||||
const version = __VERSION__
|
||||
// Deutsche Lokalisierung für Highcharts
|
||||
Highcharts.setOptions({
|
||||
lang: {
|
||||
@@ -426,6 +428,30 @@ const WeatherDashboard = ({ data }) => {
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
<div className="dashboard-footer">
|
||||
<div className="version-line">
|
||||
<div><a href="mailto:rxf@gmx.de">
|
||||
mailto:rxf@gmx.de
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<span className="version-full">Version</span>
|
||||
<span className="version-short">V</span>
|
||||
{' '}{version} – {buildDate}
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<div className="footer-credits">
|
||||
<div className="footer-left">Daten-Erfassung mit einer Davis VantagePro.</div>
|
||||
<div className="footer-right">Grafiken erzeugt mit HighCharts</div>
|
||||
</div>
|
||||
<div className="footer-sponsor">
|
||||
Die Wetterstation wurde vom Zeitungsverlag Waiblingen <a href="https://www.zvw.de" target="_blank" rel="noopener noreferrer">www.zvw.de</a> gestiftet.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,8 +3,10 @@ import react from '@vitejs/plugin-react'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
base: './',
|
||||
server: {
|
||||
base: './', define: {
|
||||
__BUILD_DATE__: JSON.stringify(process.env.VITE_BUILD_DATE || 'dev'),
|
||||
__VERSION__: JSON.stringify(process.env.VITE_VERSION || '0.0.0')
|
||||
}, server: {
|
||||
host: '0.0.0.0',
|
||||
port: 3000,
|
||||
proxy: {
|
||||
|
||||
@@ -1,24 +1,38 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Script zum Bauen und Pushen der Docker Images zur Registry
|
||||
# Script zum Bauen und Pushen der Docker Images zur Registry (Multi-Plattform)
|
||||
set -e
|
||||
|
||||
REGISTRY="docker.citysensor.de"
|
||||
PROJECT="wetterstation"
|
||||
PLATFORMS="linux/amd64,linux/arm64"
|
||||
|
||||
echo "🔨 Building Docker images..."
|
||||
docker compose build collector
|
||||
docker compose build api
|
||||
docker compose build frontend
|
||||
echo "🔧 Setting up buildx builder..."
|
||||
# Erstelle oder verwende existierenden Builder
|
||||
docker buildx create --name multiplatform --use 2>/dev/null || docker buildx use multiplatform
|
||||
|
||||
echo ""
|
||||
echo "📤 Pushing images to ${REGISTRY}..."
|
||||
docker compose push collector
|
||||
docker compose push api
|
||||
docker compose push frontend
|
||||
echo "🔨 Building and pushing Docker images for ${PLATFORMS}..."
|
||||
|
||||
# Baue und pushe alle Images mit buildx
|
||||
docker buildx build --platform ${PLATFORMS} \
|
||||
-t ${REGISTRY}/${PROJECT}/collector:latest \
|
||||
--push \
|
||||
./collector
|
||||
|
||||
docker buildx build --platform ${PLATFORMS} \
|
||||
-t ${REGISTRY}/${PROJECT}/api:latest \
|
||||
--push \
|
||||
./api
|
||||
|
||||
docker buildx build --platform ${PLATFORMS} \
|
||||
-t ${REGISTRY}/${PROJECT}/frontend:latest \
|
||||
--push \
|
||||
./frontend
|
||||
|
||||
echo ""
|
||||
echo "✅ Done! Images successfully pushed to ${REGISTRY}"
|
||||
echo "✅ Done! Multi-platform images successfully pushed to ${REGISTRY}"
|
||||
echo " Platforms: ${PLATFORMS}"
|
||||
echo ""
|
||||
echo "To pull and run on another machine:"
|
||||
echo " docker compose pull"
|
||||
|
||||
Reference in New Issue
Block a user