From 0a2f1e650d6fccfcbe686631671dd0ff8189fcf7 Mon Sep 17 00:00:00 2001 From: "theis.gaedigk" Date: Mon, 11 Aug 2025 19:56:43 +0200 Subject: [PATCH] Enhance backend and frontend setup with MySQL integration, Docker configurations, and toast notifications - Updated .gitignore to include additional environment and build files - Configured Dockerfiles for backend and frontend with npm install and port exposure - Added MySQL connection pool and query function in backend services - Implemented form submission with toast notifications in MainForm component - Updated package.json and package-lock.json for new dependencies - Enhanced routing and layout in frontend with toast notifications --- .gitignore | 116 ++++++++++++++++++++++++++- backend/Dockerfile | 12 +++ backend/package-lock.json | 112 +++++++++++++++++++++++++- backend/package.json | 3 +- backend/server.js | 12 ++- backend/services/database.js | 34 ++++++++ docker-compose.yml | 43 ++++++++++ frontend/Dockerfile | 12 +++ frontend/package-lock.json | 23 ++++++ frontend/package.json | 1 + frontend/src/App.tsx | 5 +- frontend/src/components/Home.tsx | 1 + frontend/src/components/MainForm.tsx | 13 ++- frontend/src/layout/Layout.tsx | 2 + frontend/src/utils/register.ts | 18 ++++- frontend/src/utils/toastify.ts | 17 ++++ frontend/vite.config.ts | 2 +- 17 files changed, 411 insertions(+), 15 deletions(-) create mode 100644 backend/services/database.js create mode 100644 frontend/src/utils/toastify.ts diff --git a/.gitignore b/.gitignore index db0377c..5fd7033 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,114 @@ +# Dependencies +node_modules/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +# Production builds +dist/ +build/ +out/ + +# Environment variables +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +# Editor directories and files +.vscode/ +.idea/ +*.swp +*.swo +*~ +weather-app.code-workspace + +# OS generated files .DS_Store -backend/node_modules/ -client/node_modules/ -todo.txt \ No newline at end of file +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +# Logs +logs/ +*.log + +# Runtime data +pids/ +*.pid +*.seed +*.pid.lock + +# Coverage directory used by tools like istanbul +coverage/ +*.lcov + +# nyc test coverage +.nyc_output + +# Dependency directories +jspm_packages/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# 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 / generate output +.nuxt + +# Storybook build outputs +.out +.storybook-out + +# Temporary folders +tmp/ +temp/ + +# Frontend specific (React/Vite) +frontend/dist/ +frontend/build/ +frontend/.vite/ + +# Backend specific (Express) +backend/uploads/ +backend/public/uploads/ + +# Database +*.db +*.sqlite +*.sqlite3 + +# API keys and secrets (additional protection) +config/ +secrets/ +keys/ \ No newline at end of file diff --git a/backend/Dockerfile b/backend/Dockerfile index e69de29..7167e5a 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -0,0 +1,12 @@ +FROM node:20-alpine + +WORKDIR /backend + +COPY package*.json ./ +RUN npm install + +COPY . . + +EXPOSE 8002 + +CMD ["npm", "start"] \ No newline at end of file diff --git a/backend/package-lock.json b/backend/package-lock.json index 04a3fd2..c466d41 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -12,7 +12,8 @@ "cors": "^2.8.5", "dotenv": "^17.2.1", "ejs": "^3.1.10", - "express": "^5.1.0" + "express": "^5.1.0", + "mysql2": "^3.14.3" } }, "node_modules/accepts": { @@ -34,6 +35,15 @@ "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", "license": "MIT" }, + "node_modules/aws-ssl-profiles": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", + "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -176,6 +186,15 @@ } } }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -381,6 +400,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "license": "MIT", + "dependencies": { + "is-property": "^1.0.2" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -512,6 +540,12 @@ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "license": "MIT" }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==", + "license": "MIT" + }, "node_modules/jake": { "version": "10.9.4", "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", @@ -529,6 +563,36 @@ "node": ">=10" } }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, + "node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/lru.min": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.2.tgz", + "integrity": "sha512-Nv9KddBcQSlQopmBHXSsZVY5xsdlZkdH/Iey0BlcBYggMd4two7cZnKOK9vmy3nY0O5RGH99z1PCeTpPqszUYg==", + "license": "MIT", + "engines": { + "bun": ">=1.0.0", + "deno": ">=1.30.0", + "node": ">=8.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wellwelwel" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -598,6 +662,38 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, + "node_modules/mysql2": { + "version": "3.14.3", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.14.3.tgz", + "integrity": "sha512-fD6MLV8XJ1KiNFIF0bS7Msl8eZyhlTDCDl75ajU5SJtpdx9ZPEACulJcqJWr1Y8OYyxsFc4j3+nflpmhxCU5aQ==", + "license": "MIT", + "dependencies": { + "aws-ssl-profiles": "^1.1.1", + "denque": "^2.1.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.6.3", + "long": "^5.2.1", + "lru.min": "^1.0.0", + "named-placeholders": "^1.1.3", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.2" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/named-placeholders": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz", + "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==", + "license": "MIT", + "dependencies": { + "lru-cache": "^7.14.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/negotiator": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", @@ -789,6 +885,11 @@ "node": ">= 18" } }, + "node_modules/seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==" + }, "node_modules/serve-static": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", @@ -882,6 +983,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", diff --git a/backend/package.json b/backend/package.json index 81405a9..e1d502e 100644 --- a/backend/package.json +++ b/backend/package.json @@ -14,6 +14,7 @@ "cors": "^2.8.5", "dotenv": "^17.2.1", "ejs": "^3.1.10", - "express": "^5.1.0" + "express": "^5.1.0", + "mysql2": "^3.14.3" } } diff --git a/backend/server.js b/backend/server.js index 0535cae..17f2b59 100644 --- a/backend/server.js +++ b/backend/server.js @@ -1,9 +1,10 @@ import express from "express"; import cors from "cors"; import env from "dotenv"; +import { query } from "./services/database.js"; env.config(); const app = express(); -const port = 8001; +const port = 8002; app.use(cors()); app.use(express.urlencoded({ extended: true })); @@ -14,6 +15,15 @@ app.get("/", (req, res) => { res.render("index.ejs", { title: port }); }); +app.post("/lose", async (req, res) => { + const result = await query(req.body); + if (result.success) { + res.status(200).send("Update successful"); + } else { + res.status(400).send("Update failed"); + } +}); + app.listen(port, () => { console.log(`Server is running on port: ${port}`); }); diff --git a/backend/services/database.js b/backend/services/database.js new file mode 100644 index 0000000..00385ac --- /dev/null +++ b/backend/services/database.js @@ -0,0 +1,34 @@ +import mysql from "mysql2"; +import dotenv from "dotenv"; +dotenv.config(); + +// Create a MySQL connection pool using environment variables for configuration +const pool = mysql + .createPool({ + host: process.env.DB_HOST, + user: process.env.DB_USER, + password: process.env.DB_PASSWORD, + database: process.env.DB_NAME, + }) + .promise(); + +export async function query(params) { + const [result] = await pool.query( + "UPDATE lose SET vorname = ?, nachname = ?, adresse = ?, plz = ?, email = ? WHERE losnummer = ? AND vorname IS NULL AND nachname IS NULL AND adresse IS NULL AND plz IS NULL AND email IS NULL", + [ + params.vorname, + params.nachname, + params.adresse, + params.plz, + params.email, + params.losnummer, + ] + ); + + if (result.affectedRows > 0) { + console.log("Update successful:", result); + return { success: true }; + } else { + return { success: false }; + } +} diff --git a/docker-compose.yml b/docker-compose.yml index e69de29..d23a129 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -0,0 +1,43 @@ +services: + # mcs_lose-frontend: + # container_name: mcs_lose-frontend + # build: ./frontend + # ports: + # - "8001:8001" + # environment: + # - CHOKIDAR_USEPOLLING=true + # volumes: + # - ./frontend:/app + # - /app/node_modules + # restart: unless-stopped + + mcs_lose-backend: + container_name: mcs_lose-backend_express + build: ./backend + ports: + - "8002:8002" + environment: + DB_HOST: mysql + DB_USER: root + DB_PASSWORD: D7Ze0lwV9hMrNQHdz1Q8yi0MIQuOO8 + DB_NAME: mcs_lose + depends_on: + - mysql + volumes: + - ./backend:/mcs_lose-backend + restart: unless-stopped + + mysql: + container_name: mcs_lose-mysql + image: mysql:8.0 + restart: unless-stopped + environment: + MYSQL_ROOT_PASSWORD: D7Ze0lwV9hMrNQHdz1Q8yi0MIQuOO8 + MYSQL_DATABASE: mcs_lose + volumes: + - mysql-data:/var/lib/mysql + ports: + - "3308:3306" + +volumes: + mysql-data: diff --git a/frontend/Dockerfile b/frontend/Dockerfile index e69de29..b7dad9b 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -0,0 +1,12 @@ +FROM node:20-alpine + +WORKDIR /app + +COPY package*.json ./ +RUN npm install + +COPY . . + +EXPOSE 8001 + +CMD ["npm", "run", "dev"] \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 3c9487d..1a44c8d 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -13,6 +13,7 @@ "react": "^19.1.1", "react-dom": "^19.1.1", "react-router-dom": "^7.8.0", + "react-toastify": "^11.0.5", "tailwind-merge": "^3.3.1", "tailwindcss": "^4.1.11", "tailwindcss-animate": "^1.0.7", @@ -2371,6 +2372,15 @@ "node": ">=18" } }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -3851,6 +3861,19 @@ "react-dom": ">=18" } }, + "node_modules/react-toastify": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-11.0.5.tgz", + "integrity": "sha512-EpqHBGvnSTtHYhCPLxML05NLY2ZX0JURbAdNYa6BUkk+amz4wbKBQvoKQAB0ardvSarUBuY4Q4s1sluAzZwkmA==", + "license": "MIT", + "dependencies": { + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": "^18 || ^19", + "react-dom": "^18 || ^19" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index a7bd765..9531c43 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -15,6 +15,7 @@ "react": "^19.1.1", "react-dom": "^19.1.1", "react-router-dom": "^7.8.0", + "react-toastify": "^11.0.5", "tailwind-merge": "^3.3.1", "tailwindcss": "^4.1.11", "tailwindcss-animate": "^1.0.7", diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index f9e3161..3d56513 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,6 +1,7 @@ import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; -import Admin from './components/Admin'; // Beispiel-Komponente -import Home from './components/Home'; // Beispiel-Komponente +import Admin from './components/Admin'; +import Home from './components/Home'; +import "react-toastify/dist/ReactToastify.css"; function App() { return ( diff --git a/frontend/src/components/Home.tsx b/frontend/src/components/Home.tsx index 9c47457..dc0b16d 100644 --- a/frontend/src/components/Home.tsx +++ b/frontend/src/components/Home.tsx @@ -1,6 +1,7 @@ import "../App.css"; import Layout from "../layout/Layout"; import MainForm from "./MainForm"; +import "react-toastify/dist/ReactToastify.css"; function App() { return ( diff --git a/frontend/src/components/MainForm.tsx b/frontend/src/components/MainForm.tsx index a878db1..e33856e 100644 --- a/frontend/src/components/MainForm.tsx +++ b/frontend/src/components/MainForm.tsx @@ -1,11 +1,16 @@ import React from "react"; import { registerLos } from "../utils/register"; +import { toast } from "react-toastify"; const handleSubmit = async (event: React.FormEvent) => { event.preventDefault(); const formData = new FormData(event.currentTarget); const data = Object.fromEntries(formData.entries()); - await registerLos(data); + toast.promise(registerLos(data), { + pending: "Registriere Los...", + success: "Los erfolgreich registriert!", + error: "Fehler bei der Registrierung des Loses.", + }); }; const MainForm: React.FC = () => { @@ -72,15 +77,15 @@ const MainForm: React.FC = () => {
= ({ children }) => {
{children}
+ ); }; diff --git a/frontend/src/utils/register.ts b/frontend/src/utils/register.ts index f558656..82e7e25 100644 --- a/frontend/src/utils/register.ts +++ b/frontend/src/utils/register.ts @@ -1,3 +1,17 @@ +import { myToast } from "./toastify"; + export const registerLos = async (data: any) => { - console.log(data); -} \ No newline at end of file + await fetch("http://localhost:8002/lose", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(data), + }).then((response) => { + if (response.ok) { + myToast("Los erfolgreich registriert!", "success"); + } else { + myToast("Fehler bei der Registrierung des Loses.", "error"); + } + }); +}; diff --git a/frontend/src/utils/toastify.ts b/frontend/src/utils/toastify.ts new file mode 100644 index 0000000..3607ed8 --- /dev/null +++ b/frontend/src/utils/toastify.ts @@ -0,0 +1,17 @@ +import { toast, type ToastOptions } from "react-toastify"; + +export type ToastType = "success" | "error" | "info" | "warning"; + +export const myToast = (message: string, msgType: ToastType) => { + let config: ToastOptions = { + position: "top-right", + autoClose: 5000, + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + theme: "dark", + }; + toast[msgType](message, config); +}; diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 7145de2..dd0234a 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -7,7 +7,7 @@ export default defineConfig({ plugins: [react(), svgr(), tailwindcss()], server: { host: "0.0.0.0", - port: 5001, + port: 8001, watch: { usePolling: true, },