feat: add fullstack TypeScript template with Docker support
- Created package.json for managing workspaces (frontend and backend) - Added scripts for development, build, testing, and Docker operations - Implemented kill-dev-processes.sh script to terminate development processes gracefully
This commit is contained in:
@@ -0,0 +1,25 @@
|
|||||||
|
FROM node:18
|
||||||
|
|
||||||
|
# Install git and other useful tools
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
git \
|
||||||
|
curl \
|
||||||
|
vim \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Set working directory
|
||||||
|
WORKDIR /workspace
|
||||||
|
|
||||||
|
# Install global packages
|
||||||
|
RUN npm install -g typescript ts-node nodemon
|
||||||
|
|
||||||
|
# Create user with same UID as host (for file permissions)
|
||||||
|
ARG USERNAME=node
|
||||||
|
ARG USER_UID=1000
|
||||||
|
ARG USER_GID=$USER_UID
|
||||||
|
|
||||||
|
RUN groupmod --gid $USER_GID $USERNAME \
|
||||||
|
&& usermod --uid $USER_UID --gid $USER_GID $USERNAME \
|
||||||
|
&& chown -R $USER_UID:$USER_GID /home/$USERNAME
|
||||||
|
|
||||||
|
USER $USERNAME
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"name": "Fotodrucker Full Stack",
|
||||||
|
"build": {
|
||||||
|
"dockerfile": "Dockerfile"
|
||||||
|
},
|
||||||
|
"workspaceFolder": "/workspace",
|
||||||
|
"workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=cached",
|
||||||
|
"customizations": {
|
||||||
|
"vscode": {
|
||||||
|
"extensions": [
|
||||||
|
"ms-vscode.vscode-typescript-next",
|
||||||
|
"esbenp.prettier-vscode",
|
||||||
|
"ms-vscode.vscode-json",
|
||||||
|
"bradlc.vscode-tailwindcss",
|
||||||
|
"ms-vscode.vscode-eslint",
|
||||||
|
"ms-vscode-remote.remote-containers"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"typescript.preferences.includePackageJsonAutoImports": "auto",
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"postCreateCommand": "cd backend && npm install && cd ../frontend && npm install",
|
||||||
|
"forwardPorts": [3000, 3001, 9229, 9230],
|
||||||
|
"portsAttributes": {
|
||||||
|
"3000": {
|
||||||
|
"label": "Frontend (React)",
|
||||||
|
"onAutoForward": "openBrowser"
|
||||||
|
},
|
||||||
|
"3001": {
|
||||||
|
"label": "Backend (Express)"
|
||||||
|
},
|
||||||
|
"9229": {
|
||||||
|
"label": "Backend Debug Port"
|
||||||
|
},
|
||||||
|
"9230": {
|
||||||
|
"label": "Frontend Debug Port"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
volumes:
|
||||||
|
- ..:/workspace:cached
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
- "3001:3001"
|
||||||
|
- "9229:9229"
|
||||||
|
- "9230:9230"
|
||||||
|
command: sleep infinity
|
||||||
|
working_dir: /workspace
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
# Dockerignore für optimierte Builds
|
||||||
|
# Verhindert, dass unnötige Dateien in den Build-Context kopiert werden
|
||||||
|
|
||||||
|
# Git
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
|
||||||
|
# Documentation (README.md sollte im Image sein für Referenz)
|
||||||
|
docs/
|
||||||
|
|
||||||
|
# IDE & Editor
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Dependencies (werden separat installiert)
|
||||||
|
node_modules/
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
package-lock.json
|
||||||
|
|
||||||
|
# Build outputs (werden im Container erstellt)
|
||||||
|
/backend/dist/
|
||||||
|
/frontend/build/
|
||||||
|
|
||||||
|
# Environment (production wird separat gesetzt)
|
||||||
|
.env*
|
||||||
|
|
||||||
|
# Testing & Coverage
|
||||||
|
coverage/
|
||||||
|
.nyc_output
|
||||||
|
**/*.test.*
|
||||||
|
**/*.spec.*
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs/
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Development Scripts
|
||||||
|
scripts/
|
||||||
|
|
||||||
|
# CI/CD
|
||||||
|
.woodpecker.yml
|
||||||
|
|
||||||
|
# Temporary & Cache
|
||||||
|
tmp/
|
||||||
|
temp/
|
||||||
|
.cache/
|
||||||
|
.parcel-cache
|
||||||
|
|
||||||
|
# Database Files & Volumes
|
||||||
|
*.db
|
||||||
|
*.sqlite*
|
||||||
|
postgres_data/
|
||||||
|
redis_data/
|
||||||
+83
@@ -0,0 +1,83 @@
|
|||||||
|
# Dependencies
|
||||||
|
node_modules/
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# Build outputs
|
||||||
|
/backend/dist/
|
||||||
|
/frontend/build/
|
||||||
|
|
||||||
|
# Environment variables
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage/
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# IDE / Editor
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# OS generated files
|
||||||
|
.DS_Store
|
||||||
|
.DS_Store?
|
||||||
|
._*
|
||||||
|
.Spotlight-V100
|
||||||
|
.Trashes
|
||||||
|
ehthumbs.db
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
.cache
|
||||||
|
.parcel-cache
|
||||||
|
|
||||||
|
# next.js build output
|
||||||
|
.next
|
||||||
|
|
||||||
|
# nuxt.js build output
|
||||||
|
.nuxt
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
.serverless
|
||||||
|
|
||||||
|
# Docker volumes (not the config files!)
|
||||||
|
postgres_data/
|
||||||
|
redis_data/
|
||||||
|
pgadmin_data/
|
||||||
|
|
||||||
|
# Docker runtime
|
||||||
|
.docker/
|
||||||
Vendored
+15
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"recommendations": [
|
||||||
|
"ms-vscode.vscode-typescript-next",
|
||||||
|
"esbenp.prettier-vscode",
|
||||||
|
"ms-vscode.vscode-json",
|
||||||
|
"ms-vscode.vscode-eslint",
|
||||||
|
"bradlc.vscode-tailwindcss",
|
||||||
|
"ms-vscode-remote.remote-containers",
|
||||||
|
"ms-vscode.node-debug2",
|
||||||
|
"ms-vscode.js-debug",
|
||||||
|
"formulahendry.auto-rename-tag",
|
||||||
|
"christian-kohler.path-intellisense",
|
||||||
|
"ms-vscode.vscode-typescript-tslint-plugin"
|
||||||
|
]
|
||||||
|
}
|
||||||
Vendored
+7
@@ -0,0 +1,7 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"key": "ctrl+shift+q",
|
||||||
|
"command": "workbench.action.tasks.runTask",
|
||||||
|
"args": "🛑 Cleanup Development Processes"
|
||||||
|
}
|
||||||
|
]
|
||||||
Vendored
+72
@@ -0,0 +1,72 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "🚀 Debug Backend",
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"runtimeExecutable": "npx",
|
||||||
|
"runtimeArgs": [
|
||||||
|
"ts-node-dev",
|
||||||
|
"--respawn",
|
||||||
|
"--transpile-only",
|
||||||
|
"--no-notify"
|
||||||
|
],
|
||||||
|
"args": ["src/index.ts"],
|
||||||
|
"cwd": "${workspaceFolder}/backend",
|
||||||
|
"env": {
|
||||||
|
"NODE_ENV": "development"
|
||||||
|
},
|
||||||
|
"console": "integratedTerminal",
|
||||||
|
"skipFiles": ["<node_internals>/**"],
|
||||||
|
"sourceMaps": true,
|
||||||
|
"restart": true,
|
||||||
|
"outputCapture": "std",
|
||||||
|
"presentation": {
|
||||||
|
"group": "fullstack",
|
||||||
|
"panel": "new"
|
||||||
|
},
|
||||||
|
"postDebugTask": "🛑 Terminate All Development Processes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "🌐 Debug Frontend (Chrome)",
|
||||||
|
"type": "chrome",
|
||||||
|
"request": "launch",
|
||||||
|
"url": "http://localhost:3000",
|
||||||
|
"webRoot": "${workspaceFolder}/frontend/src",
|
||||||
|
"skipFiles": ["<node_internals>/**"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "🔧 Debug Frontend (Edge)",
|
||||||
|
"type": "msedge",
|
||||||
|
"request": "launch",
|
||||||
|
"url": "http://localhost:3000",
|
||||||
|
"webRoot": "${workspaceFolder}/frontend/src",
|
||||||
|
"skipFiles": ["<node_internals>/**"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"compounds": [
|
||||||
|
{
|
||||||
|
"name": "🚀🌐 Debug Full Stack (Chrome)",
|
||||||
|
"preLaunchTask": "🌐 Frontend",
|
||||||
|
"configurations": ["🚀 Debug Backend", "🌐 Debug Frontend (Chrome)"],
|
||||||
|
"stopAll": true,
|
||||||
|
"presentation": {
|
||||||
|
"hidden": false,
|
||||||
|
"group": "fullstack",
|
||||||
|
"order": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "🚀🔧 Debug Full Stack (Edge)",
|
||||||
|
"preLaunchTask": "🌐 Frontend",
|
||||||
|
"configurations": ["🚀 Debug Backend", "🔧 Debug Frontend (Edge)"],
|
||||||
|
"stopAll": true,
|
||||||
|
"presentation": {
|
||||||
|
"hidden": false,
|
||||||
|
"group": "fullstack",
|
||||||
|
"order": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Vendored
+28
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"typescript.preferences.includePackageJsonAutoImports": "auto",
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.fixAll.eslint": "explicit"
|
||||||
|
},
|
||||||
|
"files.exclude": {
|
||||||
|
"**/node_modules": true,
|
||||||
|
"**/dist": true,
|
||||||
|
"**/.git": true,
|
||||||
|
"**/.DS_Store": true
|
||||||
|
},
|
||||||
|
"search.exclude": {
|
||||||
|
"**/node_modules": true,
|
||||||
|
"**/dist": true
|
||||||
|
},
|
||||||
|
"typescript.updateImportsOnFileMove.enabled": "always",
|
||||||
|
"emmet.includeLanguages": {
|
||||||
|
"typescript": "typescriptreact"
|
||||||
|
},
|
||||||
|
"debug.allowBreakpointsEverywhere": true,
|
||||||
|
"debug.node.autoAttach": "on",
|
||||||
|
"terminal.integrated.enablePersistentSessions": false,
|
||||||
|
"terminal.integrated.confirmOnKill": "editor",
|
||||||
|
"task.autoDetect": "off",
|
||||||
|
"task.showDecorations": true
|
||||||
|
}
|
||||||
Vendored
+322
@@ -0,0 +1,322 @@
|
|||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "Install Backend Dependencies",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "npm",
|
||||||
|
"args": ["install"],
|
||||||
|
"options": {
|
||||||
|
"cwd": "${workspaceFolder}/backend"
|
||||||
|
},
|
||||||
|
"group": "build",
|
||||||
|
"presentation": {
|
||||||
|
"echo": true,
|
||||||
|
"reveal": "always",
|
||||||
|
"focus": false,
|
||||||
|
"panel": "shared"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Install Frontend Dependencies",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "npm",
|
||||||
|
"args": ["install"],
|
||||||
|
"options": {
|
||||||
|
"cwd": "${workspaceFolder}/frontend"
|
||||||
|
},
|
||||||
|
"group": "build",
|
||||||
|
"presentation": {
|
||||||
|
"echo": true,
|
||||||
|
"reveal": "always",
|
||||||
|
"focus": false,
|
||||||
|
"panel": "shared"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "🖥️ Backend",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "npm",
|
||||||
|
"args": ["run", "dev"],
|
||||||
|
"options": {
|
||||||
|
"cwd": "${workspaceFolder}/backend"
|
||||||
|
},
|
||||||
|
"group": "build",
|
||||||
|
"isBackground": true,
|
||||||
|
"presentation": {
|
||||||
|
"echo": true,
|
||||||
|
"reveal": "always",
|
||||||
|
"focus": false,
|
||||||
|
"panel": "dedicated",
|
||||||
|
"group": "dev",
|
||||||
|
"showReuseMessage": false
|
||||||
|
},
|
||||||
|
"problemMatcher": {
|
||||||
|
"pattern": {
|
||||||
|
"regexp": "^.*$",
|
||||||
|
"file": 1,
|
||||||
|
"location": 2,
|
||||||
|
"message": 3
|
||||||
|
},
|
||||||
|
"background": {
|
||||||
|
"activeOnStart": true,
|
||||||
|
"beginsPattern": "^.*ts-node-dev.*$",
|
||||||
|
"endsPattern": "^🚀 Backend Server läuft auf Port \\d+$"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "🌐 Frontend",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "npm",
|
||||||
|
"args": ["start"],
|
||||||
|
"options": {
|
||||||
|
"cwd": "${workspaceFolder}/frontend",
|
||||||
|
"env": {
|
||||||
|
"BROWSER": "none"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"group": "build",
|
||||||
|
"isBackground": true,
|
||||||
|
"presentation": {
|
||||||
|
"echo": true,
|
||||||
|
"reveal": "always",
|
||||||
|
"focus": false,
|
||||||
|
"panel": "dedicated",
|
||||||
|
"group": "dev",
|
||||||
|
"showReuseMessage": false
|
||||||
|
},
|
||||||
|
"problemMatcher": {
|
||||||
|
"owner": "custom",
|
||||||
|
"pattern": {
|
||||||
|
"regexp": "^.*$",
|
||||||
|
"file": 1,
|
||||||
|
"location": 2,
|
||||||
|
"message": 3
|
||||||
|
},
|
||||||
|
"background": {
|
||||||
|
"activeOnStart": true,
|
||||||
|
"beginsPattern": "^.*Starting the development server.*$",
|
||||||
|
"endsPattern": "^.*webpack compiled.*$"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "🛑 Kill Frontend Process",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "pkill",
|
||||||
|
"args": ["-f", "npm start"],
|
||||||
|
"options": {
|
||||||
|
"cwd": "${workspaceFolder}/frontend"
|
||||||
|
},
|
||||||
|
"group": "build",
|
||||||
|
"presentation": {
|
||||||
|
"echo": true,
|
||||||
|
"reveal": "always",
|
||||||
|
"focus": false,
|
||||||
|
"panel": "shared"
|
||||||
|
},
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "🛑 Kill Backend Process",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "pkill",
|
||||||
|
"args": ["-f", "ts-node-dev"],
|
||||||
|
"options": {
|
||||||
|
"cwd": "${workspaceFolder}/backend"
|
||||||
|
},
|
||||||
|
"group": "build",
|
||||||
|
"presentation": {
|
||||||
|
"echo": true,
|
||||||
|
"reveal": "always",
|
||||||
|
"focus": false,
|
||||||
|
"panel": "shared"
|
||||||
|
},
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "📊 Show Development Processes Status",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "bash",
|
||||||
|
"args": [
|
||||||
|
"-c",
|
||||||
|
"echo '📊 Aktuelle Development-Prozesse:'; echo ''; ps aux | grep -E 'npm.*start|react-scripts|ts-node-dev' | grep -v grep | awk '{print \"PID: \" $2 \" - \" $11 \" \" $12 \" \" $13}' || echo 'Keine Development-Prozesse gefunden'"
|
||||||
|
],
|
||||||
|
"group": "build",
|
||||||
|
"presentation": {
|
||||||
|
"echo": true,
|
||||||
|
"reveal": "always",
|
||||||
|
"focus": false,
|
||||||
|
"panel": "shared"
|
||||||
|
},
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "🛑 Cleanup Development Processes",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "bash",
|
||||||
|
"args": [
|
||||||
|
"-c",
|
||||||
|
"pkill -f 'ts-node-dev' > /dev/null 2>&1 || true; pkill -f 'npm.*start' > /dev/null 2>&1 || true; pkill -f 'react-scripts' > /dev/null 2>&1 || true"
|
||||||
|
],
|
||||||
|
"group": "build",
|
||||||
|
"presentation": {
|
||||||
|
"echo": false,
|
||||||
|
"reveal": "never",
|
||||||
|
"focus": false,
|
||||||
|
"panel": "shared",
|
||||||
|
"clear": false,
|
||||||
|
"showReuseMessage": false,
|
||||||
|
"close": true
|
||||||
|
},
|
||||||
|
"problemMatcher": [],
|
||||||
|
"isBackground": false,
|
||||||
|
"runOptions": {
|
||||||
|
"reevaluateOnRerun": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "🧹 Post Debug Cleanup",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "bash",
|
||||||
|
"args": ["-c", "/workspace/scripts/post-debug-cleanup.sh"],
|
||||||
|
"group": "build",
|
||||||
|
"presentation": {
|
||||||
|
"echo": false,
|
||||||
|
"reveal": "never",
|
||||||
|
"focus": false,
|
||||||
|
"panel": "new",
|
||||||
|
"clear": false,
|
||||||
|
"showReuseMessage": false,
|
||||||
|
"close": true
|
||||||
|
},
|
||||||
|
"problemMatcher": [],
|
||||||
|
"isBackground": false,
|
||||||
|
"runOptions": {
|
||||||
|
"reevaluateOnRerun": true,
|
||||||
|
"runOn": "default"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "🔄 Force Kill All Dev Processes",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "bash",
|
||||||
|
"args": [
|
||||||
|
"-c",
|
||||||
|
"RANDOM_ID=$RANDOM; echo \"Cleanup ID: $RANDOM_ID\" > /dev/null; pkill -9 -f 'ts-node-dev' > /dev/null 2>&1 || true; pkill -9 -f 'npm.*start' > /dev/null 2>&1 || true; pkill -9 -f 'react-scripts' > /dev/null 2>&1 || true"
|
||||||
|
],
|
||||||
|
"group": "build",
|
||||||
|
"presentation": {
|
||||||
|
"echo": false,
|
||||||
|
"reveal": "never",
|
||||||
|
"focus": false,
|
||||||
|
"panel": "shared",
|
||||||
|
"clear": false,
|
||||||
|
"showReuseMessage": false,
|
||||||
|
"close": true
|
||||||
|
},
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "🛑 Terminate All Development Processes",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "/workspace/scripts/kill-dev-processes.sh",
|
||||||
|
"args": [],
|
||||||
|
"group": "build",
|
||||||
|
"presentation": {
|
||||||
|
"echo": false,
|
||||||
|
"reveal": "never",
|
||||||
|
"focus": false,
|
||||||
|
"panel": "shared",
|
||||||
|
"clear": false,
|
||||||
|
"showReuseMessage": false,
|
||||||
|
"close": true
|
||||||
|
},
|
||||||
|
"problemMatcher": [],
|
||||||
|
"runOptions": {
|
||||||
|
"runOn": "default"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "🛑 Terminate All Development Processes (Verbose)",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "/workspace/scripts/kill-dev-processes.sh",
|
||||||
|
"args": [],
|
||||||
|
"group": "build",
|
||||||
|
"presentation": {
|
||||||
|
"echo": true,
|
||||||
|
"reveal": "always",
|
||||||
|
"focus": false,
|
||||||
|
"panel": "dedicated",
|
||||||
|
"clear": true,
|
||||||
|
"showReuseMessage": false
|
||||||
|
},
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "🚀 Start Full Stack (Split Terminal)",
|
||||||
|
"dependsOrder": "parallel",
|
||||||
|
"dependsOn": ["🖥️ Backend", "🌐 Frontend"],
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
},
|
||||||
|
"presentation": {
|
||||||
|
"reveal": "always",
|
||||||
|
"panel": "new"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "🔄 Restart Full Stack",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "bash",
|
||||||
|
"args": [
|
||||||
|
"-c",
|
||||||
|
"/workspace/scripts/kill-dev-processes.sh && sleep 2 && echo '🚀 Starte Full Stack...' && code --command 'workbench.action.tasks.runTask' 'shell: 🚀 Start Full Stack (Split Terminal)'"
|
||||||
|
],
|
||||||
|
"group": "build",
|
||||||
|
"presentation": {
|
||||||
|
"echo": true,
|
||||||
|
"reveal": "always",
|
||||||
|
"focus": false,
|
||||||
|
"panel": "shared",
|
||||||
|
"clear": true
|
||||||
|
},
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Build Backend",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "npm",
|
||||||
|
"args": ["run", "build"],
|
||||||
|
"options": {
|
||||||
|
"cwd": "${workspaceFolder}/backend"
|
||||||
|
},
|
||||||
|
"group": "build",
|
||||||
|
"presentation": {
|
||||||
|
"echo": true,
|
||||||
|
"reveal": "always",
|
||||||
|
"focus": false,
|
||||||
|
"panel": "shared"
|
||||||
|
},
|
||||||
|
"problemMatcher": ["$tsc"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Build Frontend",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "npm",
|
||||||
|
"args": ["run", "build"],
|
||||||
|
"options": {
|
||||||
|
"cwd": "${workspaceFolder}/frontend"
|
||||||
|
},
|
||||||
|
"group": "build",
|
||||||
|
"presentation": {
|
||||||
|
"echo": true,
|
||||||
|
"reveal": "always",
|
||||||
|
"focus": false,
|
||||||
|
"panel": "shared"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
+164
@@ -0,0 +1,164 @@
|
|||||||
|
# Woodpecker CI Pipeline für Full Stack TypeScript Template
|
||||||
|
# Automatisiert Docker Builds für main, beta und alpha branches
|
||||||
|
|
||||||
|
when:
|
||||||
|
event: [push, pull_request]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
# =============================================================================
|
||||||
|
# Dependencies installieren und Tests ausführen
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
image: node:18-alpine
|
||||||
|
commands:
|
||||||
|
- echo "📦 Installing dependencies..."
|
||||||
|
- npm ci
|
||||||
|
- cd backend && npm ci && cd ..
|
||||||
|
- cd frontend && npm ci && cd ..
|
||||||
|
when:
|
||||||
|
event: [push, pull_request]
|
||||||
|
|
||||||
|
lint_and_test:
|
||||||
|
image: node:18-alpine
|
||||||
|
commands:
|
||||||
|
- echo "🔍 Running linting and tests..."
|
||||||
|
- cd backend && npm run test && cd ..
|
||||||
|
- cd frontend && npm run test -- --coverage --watchAll=false && cd ..
|
||||||
|
when:
|
||||||
|
event: [push, pull_request]
|
||||||
|
depends_on: [dependencies]
|
||||||
|
|
||||||
|
build_check:
|
||||||
|
image: node:18-alpine
|
||||||
|
commands:
|
||||||
|
- echo "🏗️ Testing production builds..."
|
||||||
|
- cd backend && npm run build && cd ..
|
||||||
|
- cd frontend && npm run build && cd ..
|
||||||
|
when:
|
||||||
|
event: [push, pull_request]
|
||||||
|
depends_on: [dependencies]
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Docker Build & Push für MAIN Branch (Production)
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
docker_build_push_main:
|
||||||
|
image: woodpeckerci/plugin-docker-buildx
|
||||||
|
settings:
|
||||||
|
repo: YOUR_REGISTRY/YOUR_ORG/fullstack-typescript-template
|
||||||
|
registry: YOUR_REGISTRY
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
tag: latest
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
context: .
|
||||||
|
username:
|
||||||
|
from_secret: docker_username
|
||||||
|
password:
|
||||||
|
from_secret: docker_password
|
||||||
|
build_args:
|
||||||
|
- NODE_ENV=production
|
||||||
|
- BUILD_VERSION=${CI_COMMIT_SHA:0:8}
|
||||||
|
when:
|
||||||
|
branch: main
|
||||||
|
event: push
|
||||||
|
depends_on: [lint_and_test, build_check]
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Docker Build & Push für BETA Branch (Staging)
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
docker_build_push_beta:
|
||||||
|
image: woodpeckerci/plugin-docker-buildx
|
||||||
|
settings:
|
||||||
|
repo: YOUR_REGISTRY/YOUR_ORG/fullstack-typescript-template
|
||||||
|
registry: YOUR_REGISTRY
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
tag: beta
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
context: .
|
||||||
|
username:
|
||||||
|
from_secret: docker_username
|
||||||
|
password:
|
||||||
|
from_secret: docker_password
|
||||||
|
build_args:
|
||||||
|
- NODE_ENV=staging
|
||||||
|
- BUILD_VERSION=${CI_COMMIT_SHA:0:8}
|
||||||
|
when:
|
||||||
|
branch: beta
|
||||||
|
event: push
|
||||||
|
depends_on: [lint_and_test, build_check]
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Docker Build & Push für ALPHA Branch (Development)
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
docker_build_push_alpha:
|
||||||
|
image: woodpeckerci/plugin-docker-buildx
|
||||||
|
settings:
|
||||||
|
repo: YOUR_REGISTRY/YOUR_ORG/fullstack-typescript-template
|
||||||
|
registry: YOUR_REGISTRY
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
tag: alpha
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
context: .
|
||||||
|
username:
|
||||||
|
from_secret: docker_username
|
||||||
|
password:
|
||||||
|
from_secret: docker_password
|
||||||
|
build_args:
|
||||||
|
- NODE_ENV=development
|
||||||
|
- BUILD_VERSION=${CI_COMMIT_SHA:0:8}
|
||||||
|
when:
|
||||||
|
branch: alpha
|
||||||
|
event: push
|
||||||
|
depends_on: [lint_and_test, build_check]
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Notification bei erfolgreichem Build
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
notify_success:
|
||||||
|
image: alpine:latest
|
||||||
|
commands:
|
||||||
|
- echo "✅ Pipeline erfolgreich abgeschlossen!"
|
||||||
|
- echo "🐳 Docker Image gebaut für Branch: ${CI_COMMIT_BRANCH}"
|
||||||
|
- echo "📦 Tag: ${CI_COMMIT_BRANCH == 'main' ? 'latest' : CI_COMMIT_BRANCH}"
|
||||||
|
- echo "🔗 Commit: ${CI_COMMIT_SHA:0:8}"
|
||||||
|
when:
|
||||||
|
status: success
|
||||||
|
event: push
|
||||||
|
branch: [main, beta, alpha]
|
||||||
|
depends_on: [docker_build_push_main, docker_build_push_beta, docker_build_push_alpha]
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Security Scan (Optional - kann aktiviert werden)
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# security_scan:
|
||||||
|
# image: aquasec/trivy:latest
|
||||||
|
# commands:
|
||||||
|
# - trivy image --exit-code 0 --severity HIGH,CRITICAL YOUR_REGISTRY/YOUR_ORG/fullstack-typescript-template:${CI_COMMIT_BRANCH == 'main' ? 'latest' : CI_COMMIT_BRANCH}
|
||||||
|
# when:
|
||||||
|
# branch: [main, beta, alpha]
|
||||||
|
# event: push
|
||||||
|
# depends_on: [docker_build_push_main, docker_build_push_beta, docker_build_push_alpha]
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Pipeline Services (falls externe Services für Tests benötigt werden)
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# services:
|
||||||
|
# postgres:
|
||||||
|
# image: postgres:15-alpine
|
||||||
|
# environment:
|
||||||
|
# POSTGRES_DB: testdb
|
||||||
|
# POSTGRES_USER: test
|
||||||
|
# POSTGRES_PASSWORD: test
|
||||||
|
# ports:
|
||||||
|
# - 5432:5432
|
||||||
|
#
|
||||||
|
# redis:
|
||||||
|
# image: redis:7-alpine
|
||||||
|
# ports:
|
||||||
|
# - 6379:6379
|
||||||
+230
@@ -0,0 +1,230 @@
|
|||||||
|
# Multi-Stage Dockerfile für Full Stack TypeScript Template
|
||||||
|
# Optimiert für Produktion mit Nginx + Node.js Backend für Traefik Deployment
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Build Stage für Frontend
|
||||||
|
# =============================================================================
|
||||||
|
FROM node:18-alpine AS frontend-builder
|
||||||
|
|
||||||
|
WORKDIR /app/frontend
|
||||||
|
|
||||||
|
# Package files kopieren für besseres Caching
|
||||||
|
COPY frontend/package*.json ./
|
||||||
|
RUN npm ci && npm cache clean --force
|
||||||
|
|
||||||
|
# Source Code kopieren und builden
|
||||||
|
COPY frontend/ ./
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Build Stage für Backend
|
||||||
|
# =============================================================================
|
||||||
|
FROM node:18-alpine AS backend-builder
|
||||||
|
|
||||||
|
WORKDIR /app/backend
|
||||||
|
|
||||||
|
# Package files kopieren
|
||||||
|
COPY backend/package*.json ./
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
# Source Code kopieren und builden
|
||||||
|
COPY backend/ ./
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Production Stage mit Nginx + Node.js + Supervisor
|
||||||
|
# =============================================================================
|
||||||
|
FROM node:18-alpine AS production
|
||||||
|
|
||||||
|
# Build Arguments
|
||||||
|
ARG NODE_ENV=production
|
||||||
|
ARG BUILD_VERSION=unknown
|
||||||
|
|
||||||
|
# Environment Variables setzen
|
||||||
|
ENV NODE_ENV=${NODE_ENV}
|
||||||
|
ENV BUILD_VERSION=${BUILD_VERSION}
|
||||||
|
ENV TZ=Europe/Berlin
|
||||||
|
|
||||||
|
# System-Dependencies installieren
|
||||||
|
RUN apk add --no-cache \
|
||||||
|
nginx \
|
||||||
|
supervisor \
|
||||||
|
curl \
|
||||||
|
tzdata \
|
||||||
|
tini \
|
||||||
|
&& rm -rf /var/cache/apk/*
|
||||||
|
|
||||||
|
# Arbeitsverzeichnisse erstellen
|
||||||
|
RUN mkdir -p /var/www/html \
|
||||||
|
&& mkdir -p /app/backend/dist \
|
||||||
|
&& mkdir -p /etc/supervisor.d \
|
||||||
|
&& mkdir -p /data
|
||||||
|
|
||||||
|
# Frontend Build von Builder Stage kopieren
|
||||||
|
COPY --from=frontend-builder /app/frontend/build /var/www/html
|
||||||
|
|
||||||
|
# Backend Build und Dependencies kopieren
|
||||||
|
COPY --from=backend-builder /app/backend/dist /app/backend/dist
|
||||||
|
COPY --from=backend-builder /app/backend/node_modules /app/backend/node_modules
|
||||||
|
|
||||||
|
# Nginx Konfiguration erstellen
|
||||||
|
RUN printf 'worker_processes auto;\n\
|
||||||
|
error_log /var/log/nginx/error.log warn;\n\
|
||||||
|
pid /var/run/nginx.pid;\n\
|
||||||
|
\n\
|
||||||
|
events {\n\
|
||||||
|
worker_connections 1024;\n\
|
||||||
|
use epoll;\n\
|
||||||
|
multi_accept on;\n\
|
||||||
|
}\n\
|
||||||
|
\n\
|
||||||
|
http {\n\
|
||||||
|
include /etc/nginx/mime.types;\n\
|
||||||
|
default_type application/octet-stream;\n\
|
||||||
|
\n\
|
||||||
|
# Logging\n\
|
||||||
|
log_format main '\''$remote_addr - $remote_user [$time_local] "$request" '\''\n\
|
||||||
|
'\''$status $body_bytes_sent "$http_referer" '\''\n\
|
||||||
|
'\''"$http_user_agent" "$http_x_forwarded_for"'\'';\n\
|
||||||
|
access_log /var/log/nginx/access.log main;\n\
|
||||||
|
\n\
|
||||||
|
# Performance\n\
|
||||||
|
sendfile on;\n\
|
||||||
|
tcp_nopush on;\n\
|
||||||
|
tcp_nodelay on;\n\
|
||||||
|
keepalive_timeout 65;\n\
|
||||||
|
gzip on;\n\
|
||||||
|
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;\n\
|
||||||
|
\n\
|
||||||
|
# Security Headers\n\
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN" always;\n\
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;\n\
|
||||||
|
add_header X-XSS-Protection "1; mode=block" always;\n\
|
||||||
|
add_header Referrer-Policy "strict-origin-when-cross-origin" always;\n\
|
||||||
|
\n\
|
||||||
|
server {\n\
|
||||||
|
listen 80;\n\
|
||||||
|
server_name _;\n\
|
||||||
|
\n\
|
||||||
|
client_max_body_size 50M;\n\
|
||||||
|
client_body_timeout 60s;\n\
|
||||||
|
client_header_timeout 60s;\n\
|
||||||
|
\n\
|
||||||
|
# Health Check Endpoint\n\
|
||||||
|
location /health {\n\
|
||||||
|
access_log off;\n\
|
||||||
|
return 200 "healthy";\n\
|
||||||
|
add_header Content-Type text/plain;\n\
|
||||||
|
}\n\
|
||||||
|
\n\
|
||||||
|
# Serve Frontend (SPA)\n\
|
||||||
|
location / {\n\
|
||||||
|
root /var/www/html;\n\
|
||||||
|
index index.html;\n\
|
||||||
|
try_files $uri $uri/ /index.html;\n\
|
||||||
|
\n\
|
||||||
|
# Cache static assets\n\
|
||||||
|
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {\n\
|
||||||
|
expires 1y;\n\
|
||||||
|
add_header Cache-Control "public, immutable";\n\
|
||||||
|
}\n\
|
||||||
|
}\n\
|
||||||
|
\n\
|
||||||
|
# Proxy API requests to backend\n\
|
||||||
|
location /api/ {\n\
|
||||||
|
proxy_pass http://127.0.0.1:3001/api/;\n\
|
||||||
|
proxy_http_version 1.1;\n\
|
||||||
|
proxy_set_header Upgrade $http_upgrade;\n\
|
||||||
|
proxy_set_header Connection "upgrade";\n\
|
||||||
|
proxy_set_header Host $host;\n\
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;\n\
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n\
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;\n\
|
||||||
|
proxy_set_header X-Forwarded-Host $host;\n\
|
||||||
|
proxy_cache_bypass $http_upgrade;\n\
|
||||||
|
\n\
|
||||||
|
# Timeouts\n\
|
||||||
|
proxy_connect_timeout 60s;\n\
|
||||||
|
proxy_send_timeout 60s;\n\
|
||||||
|
proxy_read_timeout 60s;\n\
|
||||||
|
}\n\
|
||||||
|
}\n\
|
||||||
|
}' > /etc/nginx/nginx.conf
|
||||||
|
|
||||||
|
# Supervisor Konfiguration erstellen
|
||||||
|
RUN printf '[supervisord]\n\
|
||||||
|
nodaemon=true\n\
|
||||||
|
loglevel=info\n\
|
||||||
|
logfile=/dev/stdout\n\
|
||||||
|
logfile_maxbytes=0\n\
|
||||||
|
\n\
|
||||||
|
[program:nginx]\n\
|
||||||
|
command=/usr/sbin/nginx -g "daemon off;"\n\
|
||||||
|
autorestart=true\n\
|
||||||
|
startretries=3\n\
|
||||||
|
stdout_logfile=/dev/stdout\n\
|
||||||
|
stdout_logfile_maxbytes=0\n\
|
||||||
|
stderr_logfile=/dev/stderr\n\
|
||||||
|
stderr_logfile_maxbytes=0\n\
|
||||||
|
priority=10\n\
|
||||||
|
\n\
|
||||||
|
[program:backend]\n\
|
||||||
|
command=node /app/backend/dist/index.js\n\
|
||||||
|
directory=/app/backend\n\
|
||||||
|
environment=NODE_ENV="production",DATA_DIR="/data",PORT="3001"\n\
|
||||||
|
autorestart=true\n\
|
||||||
|
startretries=3\n\
|
||||||
|
stdout_logfile=/dev/stdout\n\
|
||||||
|
stdout_logfile_maxbytes=0\n\
|
||||||
|
stderr_logfile=/dev/stderr\n\
|
||||||
|
stderr_logfile_maxbytes=0\n\
|
||||||
|
priority=20\n\
|
||||||
|
user=node\n' > /etc/supervisor.d/supervisord.ini
|
||||||
|
|
||||||
|
# Health Check Script erstellen
|
||||||
|
RUN printf '#!/bin/sh\n\
|
||||||
|
# Health check for both nginx and backend\n\
|
||||||
|
nginx_status=$(curl -f -s http://localhost/health > /dev/null && echo "ok" || echo "fail")\n\
|
||||||
|
backend_status=$(curl -f -s http://localhost:3001/api/health > /dev/null && echo "ok" || echo "fail")\n\
|
||||||
|
\n\
|
||||||
|
if [ "$nginx_status" = "ok" ] && [ "$backend_status" = "ok" ]; then\n\
|
||||||
|
exit 0\n\
|
||||||
|
else\n\
|
||||||
|
echo "Health check failed: nginx=$nginx_status, backend=$backend_status"\n\
|
||||||
|
exit 1\n\
|
||||||
|
fi' > /usr/local/bin/healthcheck.sh && chmod +x /usr/local/bin/healthcheck.sh
|
||||||
|
|
||||||
|
# Berechtigungen setzen
|
||||||
|
RUN chown -R node:node /data /app/backend
|
||||||
|
RUN chown nginx:nginx /var/www/html
|
||||||
|
|
||||||
|
# Health Check konfigurieren
|
||||||
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
|
||||||
|
CMD /usr/local/bin/healthcheck.sh
|
||||||
|
|
||||||
|
# Arbeitsverzeichnis setzen
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Startup mit Tini für proper signal handling
|
||||||
|
ENTRYPOINT ["/sbin/tini", "--"]
|
||||||
|
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor.d/supervisord.ini"]
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Traefik Labels für automatisches Service Discovery
|
||||||
|
# =============================================================================
|
||||||
|
LABEL traefik.enable=true
|
||||||
|
LABEL traefik.http.routers.app.rule="Host(\`your-domain.com\`)"
|
||||||
|
LABEL traefik.http.routers.app.entrypoints=websecure
|
||||||
|
LABEL traefik.http.routers.app.tls.certresolver=letsencrypt
|
||||||
|
LABEL traefik.http.services.app.loadbalancer.server.port=80
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Metadata Labels
|
||||||
|
# =============================================================================
|
||||||
|
LABEL maintainer="Your Name <your.email@example.com>"
|
||||||
|
LABEL version="${BUILD_VERSION}"
|
||||||
|
LABEL description="Full Stack TypeScript Template - Traefik Ready"
|
||||||
|
LABEL org.opencontainers.image.source="https://github.com/your-org/fullstack-typescript-template"
|
||||||
|
LABEL org.opencontainers.image.documentation="https://github.com/your-org/fullstack-typescript-template#readme"
|
||||||
|
LABEL org.opencontainers.image.vendor="Your Organization"
|
||||||
|
LABEL org.opencontainers.image.licenses="MIT"
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# License
|
||||||
|
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) [year] [fullname]
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -0,0 +1,254 @@
|
|||||||
|
# 🚀 Full Stack TypeScript Template
|
||||||
|
|
||||||
|
Ein modernes Full Stack Template mit React Frontend und Express Backend, komplett containerisiert mit Docker.
|
||||||
|
|
||||||
|
## 📋 Übersicht
|
||||||
|
|
||||||
|
- **Frontend**: React 18 + TypeScript + Create React App
|
||||||
|
- **Backend**: Express.js + TypeScript + Node.js
|
||||||
|
- **Database**: PostgreSQL 15
|
||||||
|
- **Container**: Docker + Docker Compose
|
||||||
|
- **Development**: Hot Reload für Frontend & Backend
|
||||||
|
- **CI/CD**: Woodpecker CI Ready
|
||||||
|
|
||||||
|
## 🛠️ Voraussetzungen
|
||||||
|
|
||||||
|
- Node.js >= 18.0.0
|
||||||
|
- npm >= 9.0.0
|
||||||
|
- Docker & Docker Compose
|
||||||
|
|
||||||
|
## 📦 Installation
|
||||||
|
|
||||||
|
### 1. Repository klonen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone <repository-url>
|
||||||
|
cd fullstack-typescript-template
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Dependencies installieren
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run install:all
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🏃♂️ Development
|
||||||
|
|
||||||
|
### Lokale Entwicklung (ohne Docker)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Alle Services starten
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# Einzeln starten
|
||||||
|
npm run dev:backend # Backend auf Port 3001
|
||||||
|
npm run dev:frontend # Frontend auf Port 3000
|
||||||
|
|
||||||
|
# Development-Prozesse beenden
|
||||||
|
npm run kill:dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mit Docker (empfohlen)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Development mit Hot Reload
|
||||||
|
npm run docker:dev
|
||||||
|
|
||||||
|
# Production-ähnliche Umgebung
|
||||||
|
npm run docker:prod
|
||||||
|
|
||||||
|
# Services stoppen
|
||||||
|
npm run docker:down
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🐳 Docker Commands
|
||||||
|
|
||||||
|
### Build & Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run docker:build # App Image erstellen
|
||||||
|
npm run docker:up # Services im Hintergrund starten
|
||||||
|
npm run docker:dev # Development-Modus mit Rebuild
|
||||||
|
npm run docker:prod # Production-Modus
|
||||||
|
```
|
||||||
|
|
||||||
|
### Management
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run docker:stop # Services stoppen (Container bleiben)
|
||||||
|
npm run docker:down # Services stoppen + Container entfernen
|
||||||
|
npm run docker:restart # Services neustarten
|
||||||
|
```
|
||||||
|
|
||||||
|
### Logs & Debugging
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run docker:logs # Alle Logs anzeigen
|
||||||
|
npm run docker:logs:app # Nur App-Logs
|
||||||
|
npm run docker:logs:db # Nur Datenbank-Logs
|
||||||
|
```
|
||||||
|
|
||||||
|
### Cleanup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run docker:clean # Alles aufräumen (Volumes, Container, Images)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Manuelle Docker Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Image bauen
|
||||||
|
docker build -t fullstack-app .
|
||||||
|
|
||||||
|
# Services starten
|
||||||
|
docker-compose up -d
|
||||||
|
|
||||||
|
# Logs verfolgen
|
||||||
|
docker-compose logs -f
|
||||||
|
|
||||||
|
# Services stoppen
|
||||||
|
docker-compose down
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🏗️ Build
|
||||||
|
|
||||||
|
### Development Build
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run build # Frontend + Backend
|
||||||
|
npm run build:frontend # Nur Frontend
|
||||||
|
npm run build:backend # Nur Backend
|
||||||
|
```
|
||||||
|
|
||||||
|
### Production Build
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Mit Docker (empfohlen)
|
||||||
|
npm run docker:prod
|
||||||
|
|
||||||
|
# Manuell
|
||||||
|
npm run build
|
||||||
|
NODE_ENV=production npm start
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🧪 Testing
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run test # Alle Tests
|
||||||
|
npm run test:frontend # Frontend Tests
|
||||||
|
npm run test:backend # Backend Tests
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🧹 Cleanup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run clean # Alle node_modules & Build-Ordner
|
||||||
|
npm run clean:build # Nur Build-Ordner
|
||||||
|
npm run docker:clean # Docker Cleanup
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🌍 Environment Variables
|
||||||
|
|
||||||
|
Erstelle eine `.env` Datei im Root-Verzeichnis:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Database
|
||||||
|
POSTGRES_DB=appdb
|
||||||
|
POSTGRES_USER=postgres
|
||||||
|
POSTGRES_PASSWORD=secure-password-change-this
|
||||||
|
DATABASE_URL=postgresql://postgres:secure-password-change-this@postgres:5432/appdb
|
||||||
|
|
||||||
|
# App
|
||||||
|
NODE_ENV=development
|
||||||
|
JWT_SECRET=your-jwt-secret-change-this
|
||||||
|
|
||||||
|
# Ports
|
||||||
|
APP_PORT=3000
|
||||||
|
POSTGRES_PORT=5432
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📡 Ports
|
||||||
|
|
||||||
|
- **Frontend**: http://localhost:3000
|
||||||
|
- **Backend**: http://localhost:3001
|
||||||
|
- **Database**: localhost:5432
|
||||||
|
|
||||||
|
## 🏗️ Projekt-Struktur
|
||||||
|
|
||||||
|
```
|
||||||
|
├── 📁 frontend/ # React App
|
||||||
|
│ ├── 📁 src/
|
||||||
|
│ ├── 📁 public/
|
||||||
|
│ └── 📄 package.json
|
||||||
|
├── 📁 backend/ # Express API
|
||||||
|
│ ├── 📁 src/
|
||||||
|
│ └── 📄 package.json
|
||||||
|
├── 📁 scripts/ # Helper Scripts
|
||||||
|
│ └── 🔧 kill-dev-processes.sh
|
||||||
|
├── 🐳 docker-compose.yml
|
||||||
|
├── 🐳 Dockerfile
|
||||||
|
├── ⚙️ .woodpecker.yml # CI/CD
|
||||||
|
└── 📦 package.json # Root Package
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔧 VS Code Tasks
|
||||||
|
|
||||||
|
Das Template enthält vorkonfigurierte VS Code Tasks:
|
||||||
|
|
||||||
|
- `🖥️ Backend` - Backend im Watch-Modus starten
|
||||||
|
- `🌐 Frontend` - Frontend starten
|
||||||
|
- `🛑 Terminate All Development Processes` - Alle Dev-Prozesse beenden
|
||||||
|
- `📊 Show Development Processes Status` - Status anzeigen
|
||||||
|
|
||||||
|
Öffne die Command Palette (`Ctrl+Shift+P`) und tippe "Tasks: Run Task".
|
||||||
|
|
||||||
|
## 🚢 Deployment
|
||||||
|
|
||||||
|
### Mit Docker (empfohlen)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Production Image erstellen
|
||||||
|
docker build -t fullstack-app .
|
||||||
|
|
||||||
|
# Mit Environment-Variablen starten
|
||||||
|
NODE_ENV=production npm run docker:prod
|
||||||
|
```
|
||||||
|
|
||||||
|
### CI/CD mit Woodpecker
|
||||||
|
|
||||||
|
Das Template ist ready für Woodpecker CI. Konfiguration in `.woodpecker.yml`.
|
||||||
|
|
||||||
|
## 🤝 Development Workflow
|
||||||
|
|
||||||
|
1. **Development starten**: `npm run docker:dev`
|
||||||
|
2. **Code ändern**: Hot Reload ist aktiv
|
||||||
|
3. **Tests ausführen**: `npm run test`
|
||||||
|
4. **Build testen**: `npm run build`
|
||||||
|
5. **Production testen**: `npm run docker:prod`
|
||||||
|
6. **Cleanup**: `npm run docker:clean`
|
||||||
|
|
||||||
|
## 📝 Troubleshooting
|
||||||
|
|
||||||
|
### Port bereits belegt
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run kill:dev
|
||||||
|
npm run docker:down
|
||||||
|
```
|
||||||
|
|
||||||
|
### Docker Problems
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run docker:clean
|
||||||
|
docker system prune -a
|
||||||
|
```
|
||||||
|
|
||||||
|
### Development-Prozesse hängen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./scripts/kill-dev-processes.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📄 License
|
||||||
|
|
||||||
|
[MIT](LICENSE)
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"name": "fullstack-typescript-template-backend",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "TypeScript Backend für Full Stack Template",
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "ts-node-dev --respawn --transpile-only --inspect=0.0.0.0:9229 src/index.ts",
|
||||||
|
"build": "tsc",
|
||||||
|
"start": "node dist/index.js",
|
||||||
|
"test": "jest"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/cors": "^2.8.17",
|
||||||
|
"@types/express": "^4.17.21",
|
||||||
|
"@types/jest": "^29.5.12",
|
||||||
|
"@types/node": "^20.11.24",
|
||||||
|
"jest": "^29.7.0",
|
||||||
|
"ts-jest": "^29.1.2",
|
||||||
|
"ts-node-dev": "^2.0.0",
|
||||||
|
"typescript": "^5.3.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"cors": "^2.8.5",
|
||||||
|
"express": "^4.18.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import express from "express";
|
||||||
|
import cors from "cors";
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
const PORT = process.env.PORT || 3001;
|
||||||
|
|
||||||
|
// Middleware
|
||||||
|
app.use(cors());
|
||||||
|
app.use(express.json());
|
||||||
|
|
||||||
|
// Routes
|
||||||
|
app.get("/api/health", (req, res) => {
|
||||||
|
// 🐛 Breakpoint-Test: Setze hier einen Breakpoint zum Testen
|
||||||
|
console.log("Health check aufgerufen!");
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
status: "OK",
|
||||||
|
message: "Backend is running",
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/api/photos", (req, res) => {
|
||||||
|
// Beispiel-Endpunkt für Fotos
|
||||||
|
res.json({
|
||||||
|
photos: [
|
||||||
|
{ id: 1, name: "photo1.jpg", url: "/images/photo1.jpg" },
|
||||||
|
{ id: 2, name: "photo2.jpg", url: "/images/photo2.jpg" },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post("/api/print", (req, res) => {
|
||||||
|
// Beispiel-Endpunkt für Druckaufträge
|
||||||
|
const { photoId, copies } = req.body;
|
||||||
|
|
||||||
|
console.log(`Druckauftrag erhalten: Photo ID ${photoId}, Kopien: ${copies}`);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
message: `Druckauftrag für Photo ${photoId} mit ${copies} Kopien wurde erstellt`,
|
||||||
|
jobId: Math.random().toString(36).substr(2, 9),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.listen(PORT, () => {
|
||||||
|
console.log(`🚀 Backend Server läuft auf Port ${PORT}`);
|
||||||
|
console.log(`📍 Health Check: http://localhost:${PORT}/api/health`);
|
||||||
|
console.log(`🐛 Debugger bereit auf Port 9229`);
|
||||||
|
});
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"module": "commonjs",
|
||||||
|
"lib": ["ES2020"],
|
||||||
|
"outDir": "./dist",
|
||||||
|
"rootDir": "./src",
|
||||||
|
"strict": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"removeComments": false,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"strictNullChecks": true,
|
||||||
|
"strictFunctionTypes": true,
|
||||||
|
"noImplicitThis": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"emitDecoratorMetadata": true
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
"dist",
|
||||||
|
"**/*.test.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
# Docker Compose für Full Stack Application
|
||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
image: ${APP_IMAGE:-fullstack-app:latest}
|
||||||
|
container_name: ${APP_CONTAINER_NAME:-fullstack-app}
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=${NODE_ENV:-production}
|
||||||
|
- DATABASE_URL=${DATABASE_URL:-postgresql://postgres:postgres@postgres:5432/appdb}
|
||||||
|
- JWT_SECRET=${JWT_SECRET:-your-jwt-secret-change-this}
|
||||||
|
networks:
|
||||||
|
- app-network
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
ports:
|
||||||
|
- "${APP_PORT:-3000}:3000"
|
||||||
|
|
||||||
|
postgres:
|
||||||
|
image: postgres:15-alpine
|
||||||
|
container_name: ${POSTGRES_CONTAINER_NAME:-postgres-db}
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: ${POSTGRES_DB:-appdb}
|
||||||
|
POSTGRES_USER: ${POSTGRES_USER:-postgres}
|
||||||
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres}
|
||||||
|
volumes:
|
||||||
|
- postgres_data:/var/lib/postgresql/data
|
||||||
|
networks:
|
||||||
|
- app-network
|
||||||
|
ports:
|
||||||
|
- "${POSTGRES_PORT:-5432}:5432"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-postgres}"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
networks:
|
||||||
|
app-network:
|
||||||
|
driver: bridge
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgres_data:
|
||||||
|
driver: local
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# Frontend Development Dockerfile
|
||||||
|
FROM node:18-alpine
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Package files kopieren
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN npm ci
|
||||||
|
|
||||||
|
# Source code kopieren
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Port exposieren
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
# Development server starten
|
||||||
|
CMD ["npm", "start"]
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
# Getting Started with Create React App
|
||||||
|
|
||||||
|
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
||||||
|
|
||||||
|
## Available Scripts
|
||||||
|
|
||||||
|
In the project directory, you can run:
|
||||||
|
|
||||||
|
### `npm start`
|
||||||
|
|
||||||
|
Runs the app in the development mode.\
|
||||||
|
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
|
||||||
|
|
||||||
|
The page will reload if you make edits.\
|
||||||
|
You will also see any lint errors in the console.
|
||||||
|
|
||||||
|
### `npm test`
|
||||||
|
|
||||||
|
Launches the test runner in the interactive watch mode.\
|
||||||
|
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
||||||
|
|
||||||
|
### `npm run build`
|
||||||
|
|
||||||
|
Builds the app for production to the `build` folder.\
|
||||||
|
It correctly bundles React in production mode and optimizes the build for the best performance.
|
||||||
|
|
||||||
|
The build is minified and the filenames include the hashes.\
|
||||||
|
Your app is ready to be deployed!
|
||||||
|
|
||||||
|
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
||||||
|
|
||||||
|
### `npm run eject`
|
||||||
|
|
||||||
|
**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
|
||||||
|
|
||||||
|
If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
||||||
|
|
||||||
|
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
|
||||||
|
|
||||||
|
You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
|
||||||
|
|
||||||
|
## Learn More
|
||||||
|
|
||||||
|
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
||||||
|
|
||||||
|
To learn React, check out the [React documentation](https://reactjs.org/).
|
||||||
Generated
+17554
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
"name": "fullstack-typescript-template-frontend",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@testing-library/dom": "^10.4.0",
|
||||||
|
"@testing-library/jest-dom": "^6.6.3",
|
||||||
|
"@testing-library/react": "^16.3.0",
|
||||||
|
"@testing-library/user-event": "^13.5.0",
|
||||||
|
"@types/jest": "^27.5.2",
|
||||||
|
"@types/node": "^16.18.126",
|
||||||
|
"@types/react": "^19.1.6",
|
||||||
|
"@types/react-dom": "^19.1.5",
|
||||||
|
"react": "^19.1.0",
|
||||||
|
"react-dom": "^19.1.0",
|
||||||
|
"react-scripts": "5.0.1",
|
||||||
|
"typescript": "^4.9.5",
|
||||||
|
"web-vitals": "^2.1.4"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "react-scripts start",
|
||||||
|
"build": "react-scripts build",
|
||||||
|
"test": "react-scripts test",
|
||||||
|
"eject": "react-scripts eject"
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"extends": [
|
||||||
|
"react-app",
|
||||||
|
"react-app/jest"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"browserslist": {
|
||||||
|
"production": [
|
||||||
|
">0.2%",
|
||||||
|
"not dead",
|
||||||
|
"not op_mini all"
|
||||||
|
],
|
||||||
|
"development": [
|
||||||
|
"last 1 chrome version",
|
||||||
|
"last 1 firefox version",
|
||||||
|
"last 1 safari version"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 3.8 KiB |
@@ -0,0 +1,43 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta name="theme-color" content="#000000" />
|
||||||
|
<meta
|
||||||
|
name="description"
|
||||||
|
content="Web site created using create-react-app"
|
||||||
|
/>
|
||||||
|
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||||
|
<!--
|
||||||
|
manifest.json provides metadata used when your web app is installed on a
|
||||||
|
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||||
|
-->
|
||||||
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||||
|
<!--
|
||||||
|
Notice the use of %PUBLIC_URL% in the tags above.
|
||||||
|
It will be replaced with the URL of the `public` folder during the build.
|
||||||
|
Only files inside the `public` folder can be referenced from the HTML.
|
||||||
|
|
||||||
|
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||||
|
work correctly both with client-side routing and a non-root public URL.
|
||||||
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
|
-->
|
||||||
|
<title>React App</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
<div id="root"></div>
|
||||||
|
<!--
|
||||||
|
This HTML file is a template.
|
||||||
|
If you open it directly in the browser, you will see an empty page.
|
||||||
|
|
||||||
|
You can add webfonts, meta tags, or analytics to this file.
|
||||||
|
The build step will place the bundled scripts into the <body> tag.
|
||||||
|
|
||||||
|
To begin the development, run `npm start` or `yarn start`.
|
||||||
|
To create a production bundle, use `npm run build` or `yarn build`.
|
||||||
|
-->
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 5.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 9.4 KiB |
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"short_name": "React App",
|
||||||
|
"name": "Create React App Sample",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "favicon.ico",
|
||||||
|
"sizes": "64x64 32x32 24x24 16x16",
|
||||||
|
"type": "image/x-icon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "logo192.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "192x192"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "logo512.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "512x512"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start_url": ".",
|
||||||
|
"display": "standalone",
|
||||||
|
"theme_color": "#000000",
|
||||||
|
"background_color": "#ffffff"
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# https://www.robotstxt.org/robotstxt.html
|
||||||
|
User-agent: *
|
||||||
|
Disallow:
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
.App {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.App-header {
|
||||||
|
background-color: #282c34;
|
||||||
|
padding: 20px;
|
||||||
|
color: white;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.health-status {
|
||||||
|
background-color: #4CAF50;
|
||||||
|
color: white;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin: 20px 0;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-ok {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photos-grid {
|
||||||
|
margin-top: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-card {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
color: #333;
|
||||||
|
margin: 10px;
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: 8px;
|
||||||
|
display: inline-block;
|
||||||
|
min-width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.print-button {
|
||||||
|
background-color: #008CBA;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 10px 20px;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 16px;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.print-button:hover {
|
||||||
|
background-color: #007B9A;
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { render, screen } from '@testing-library/react';
|
||||||
|
import App from './App';
|
||||||
|
|
||||||
|
test('renders learn react link', () => {
|
||||||
|
render(<App />);
|
||||||
|
const linkElement = screen.getByText(/learn react/i);
|
||||||
|
expect(linkElement).toBeInTheDocument();
|
||||||
|
});
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import './App.css';
|
||||||
|
|
||||||
|
interface Photo {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface HealthStatus {
|
||||||
|
status: string;
|
||||||
|
message: string;
|
||||||
|
timestamp: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const [photos, setPhotos] = useState<Photo[]>([]);
|
||||||
|
const [health, setHealth] = useState<HealthStatus | null>(null);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
|
const API_BASE_URL = process.env.REACT_APP_API_URL || 'http://localhost:3001';
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchData = async () => {
|
||||||
|
try {
|
||||||
|
// Health Check
|
||||||
|
const healthResponse = await fetch(`${API_BASE_URL}/api/health`);
|
||||||
|
const healthData = await healthResponse.json();
|
||||||
|
setHealth(healthData);
|
||||||
|
|
||||||
|
// Photos
|
||||||
|
const photosResponse = await fetch(`${API_BASE_URL}/api/photos`);
|
||||||
|
const photosData = await photosResponse.json();
|
||||||
|
setPhotos(photosData.photos);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fehler beim Laden der Daten:', error);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchData();
|
||||||
|
}, [API_BASE_URL]);
|
||||||
|
|
||||||
|
const handlePrint = async (photoId: number) => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${API_BASE_URL}/api/print`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ photoId, copies: 1 }),
|
||||||
|
});
|
||||||
|
const data = await response.json();
|
||||||
|
alert(`Druckauftrag erstellt: ${data.message}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fehler beim Drucken:', error);
|
||||||
|
alert('Fehler beim Erstellen des Druckauftrags');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="App">
|
||||||
|
<header className="App-header">
|
||||||
|
<h1>📸 Fotodrucker</h1>
|
||||||
|
|
||||||
|
{health && (
|
||||||
|
<div className="health-status">
|
||||||
|
<p>Backend Status: <span className="status-ok">{health.status}</span></p>
|
||||||
|
<small>Letzte Aktualisierung: {new Date(health.timestamp).toLocaleString()}</small>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{loading ? (
|
||||||
|
<p>Lade Fotos...</p>
|
||||||
|
) : (
|
||||||
|
<div className="photos-grid">
|
||||||
|
<h2>Verfügbare Fotos</h2>
|
||||||
|
{photos.map((photo) => (
|
||||||
|
<div key={photo.id} className="photo-card">
|
||||||
|
<h3>{photo.name}</h3>
|
||||||
|
<p>URL: {photo.url}</p>
|
||||||
|
<button
|
||||||
|
className="print-button"
|
||||||
|
onClick={() => handlePrint(photo.id)}
|
||||||
|
>
|
||||||
|
🖨️ Drucken
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</header>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App;
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||||
|
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||||
|
sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||||
|
monospace;
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom/client';
|
||||||
|
import './index.css';
|
||||||
|
import App from './App';
|
||||||
|
import reportWebVitals from './reportWebVitals';
|
||||||
|
|
||||||
|
const root = ReactDOM.createRoot(
|
||||||
|
document.getElementById('root') as HTMLElement
|
||||||
|
);
|
||||||
|
root.render(
|
||||||
|
<React.StrictMode>
|
||||||
|
<App />
|
||||||
|
</React.StrictMode>
|
||||||
|
);
|
||||||
|
|
||||||
|
// If you want to start measuring performance in your app, pass a function
|
||||||
|
// to log results (for example: reportWebVitals(console.log))
|
||||||
|
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
|
||||||
|
reportWebVitals();
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 2.6 KiB |
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
/// <reference types="react-scripts" />
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { ReportHandler } from 'web-vitals';
|
||||||
|
|
||||||
|
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
|
||||||
|
if (onPerfEntry && onPerfEntry instanceof Function) {
|
||||||
|
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
||||||
|
getCLS(onPerfEntry);
|
||||||
|
getFID(onPerfEntry);
|
||||||
|
getFCP(onPerfEntry);
|
||||||
|
getLCP(onPerfEntry);
|
||||||
|
getTTFB(onPerfEntry);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default reportWebVitals;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// jest-dom adds custom jest matchers for asserting on DOM nodes.
|
||||||
|
// allows you to do things like:
|
||||||
|
// expect(element).toHaveTextContent(/react/i)
|
||||||
|
// learn more: https://github.com/testing-library/jest-dom
|
||||||
|
import '@testing-library/jest-dom';
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"lib": [
|
||||||
|
"dom",
|
||||||
|
"dom.iterable",
|
||||||
|
"esnext"
|
||||||
|
],
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"strict": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "react-jsx"
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src"
|
||||||
|
]
|
||||||
|
}
|
||||||
Generated
+17295
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"name": "fullstack-typescript-template",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Full Stack TypeScript Template with React Frontend and Express Backend",
|
||||||
|
"private": true,
|
||||||
|
"workspaces": [
|
||||||
|
"backend",
|
||||||
|
"frontend"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"install:all": "npm install && npm install --workspace backend && npm install --workspace frontend",
|
||||||
|
"dev": "concurrently \"npm run dev:backend\" \"npm run dev:frontend\"",
|
||||||
|
"dev:backend": "npm run dev --workspace backend",
|
||||||
|
"dev:frontend": "npm run start --workspace frontend",
|
||||||
|
"build": "npm run build --workspace backend && npm run build --workspace frontend",
|
||||||
|
"build:backend": "npm run build --workspace backend",
|
||||||
|
"build:frontend": "npm run build --workspace frontend",
|
||||||
|
"test": "npm run test --workspace backend && npm run test --workspace frontend",
|
||||||
|
"test:backend": "npm run test --workspace backend",
|
||||||
|
"test:frontend": "npm run test --workspace frontend",
|
||||||
|
"clean": "rm -rf node_modules backend/node_modules frontend/node_modules backend/dist frontend/build",
|
||||||
|
"clean:build": "rm -rf backend/dist frontend/build",
|
||||||
|
"docker:build": "docker build -t fullstack-app .",
|
||||||
|
"docker:up": "docker-compose up -d",
|
||||||
|
"docker:down": "docker-compose down",
|
||||||
|
"docker:stop": "docker-compose stop",
|
||||||
|
"docker:restart": "docker-compose restart",
|
||||||
|
"docker:logs": "docker-compose logs -f",
|
||||||
|
"docker:logs:app": "docker-compose logs -f app",
|
||||||
|
"docker:logs:db": "docker-compose logs -f postgres",
|
||||||
|
"docker:clean": "docker-compose down -v --remove-orphans && docker system prune -f",
|
||||||
|
"docker:dev": "docker-compose up --build",
|
||||||
|
"docker:prod": "NODE_ENV=production docker-compose up -d --build",
|
||||||
|
"kill:dev": "./scripts/kill-dev-processes.sh"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"concurrently": "^8.2.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0",
|
||||||
|
"npm": ">=9.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable
+57
@@ -0,0 +1,57 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Einfacher Lock-Mechanismus ohne störende Ausgaben
|
||||||
|
LOCK_FILE="/tmp/kill-dev-processes.lock"
|
||||||
|
|
||||||
|
# Prüfe auf bereits laufende Instanz
|
||||||
|
if [ -f "$LOCK_FILE" ]; then
|
||||||
|
# Prüfe ob Prozess tatsächlich noch läuft
|
||||||
|
if [ -f "$LOCK_FILE" ] && kill -0 $(cat "$LOCK_FILE") 2>/dev/null; then
|
||||||
|
# Stilles Beenden wenn bereits aktiv
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
# Aufräumen alter Lock-Dateien
|
||||||
|
rm -f "$LOCK_FILE" 2>/dev/null
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Erstelle Lock-File mit aktueller PID
|
||||||
|
echo $$ > "$LOCK_FILE"
|
||||||
|
trap "rm -f $LOCK_FILE 2>/dev/null" EXIT
|
||||||
|
|
||||||
|
echo "🛑 Beende alle Development-Prozesse..."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Finde alle Development-Prozesse
|
||||||
|
FRONTEND_PIDS=$(ps aux | grep -E 'npm start|react-scripts' | grep -v grep | awk '{print $2}')
|
||||||
|
BACKEND_PIDS=$(ps aux | grep 'ts-node-dev' | grep -v grep | awk '{print $2}')
|
||||||
|
|
||||||
|
echo "Frontend-Prozesse (PIDs): $FRONTEND_PIDS"
|
||||||
|
echo "Backend-Prozesse (PIDs): $BACKEND_PIDS"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Beende Frontend-Prozesse
|
||||||
|
if [ ! -z "$FRONTEND_PIDS" ]; then
|
||||||
|
echo "Beende Frontend-Prozesse..."
|
||||||
|
echo $FRONTEND_PIDS | xargs kill -TERM 2>/dev/null
|
||||||
|
sleep 2
|
||||||
|
echo $FRONTEND_PIDS | xargs kill -KILL 2>/dev/null
|
||||||
|
echo "✅ Frontend-Prozesse beendet"
|
||||||
|
else
|
||||||
|
echo "❌ Keine Frontend-Prozesse gefunden"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Beende Backend-Prozesse
|
||||||
|
if [ ! -z "$BACKEND_PIDS" ]; then
|
||||||
|
echo "Beende Backend-Prozesse..."
|
||||||
|
echo $BACKEND_PIDS | xargs kill -TERM 2>/dev/null
|
||||||
|
sleep 1
|
||||||
|
echo $BACKEND_PIDS | xargs kill -KILL 2>/dev/null
|
||||||
|
echo "✅ Backend-Prozesse beendet"
|
||||||
|
else
|
||||||
|
echo "❌ Keine Backend-Prozesse gefunden"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Verbleibende Development-Prozesse:"
|
||||||
|
ps aux | grep -E 'npm start|react-scripts|ts-node-dev' | grep -v grep || echo "✅ Keine Development-Prozesse mehr vorhanden"
|
||||||
Reference in New Issue
Block a user