MCS Lose

A small full-stack demo app to manage "Lose" entries (Losnummer records) using a React + TypeScript frontend, an Express (Node) backend, and a MySQL database. The project is containerised with Docker Compose for easy local development.

Quick overview

  • Backend: Express.js (ES Modules), serves JSON APIs and EJS root view.
  • Frontend: React + TypeScript + Vite (UI for importing CSVs and managing entries).
  • Database: MySQL 8.0 with a simple schema (lose and admin_user).
  • Orchestration: docker-compose.yml to run backend and MySQL (frontend is prepared but commented out in compose).

Goals

  • Import and manage losnummer (ticket) entries.
  • Allow admins to login and perform protected operations (create, delete, update rows).
  • Keep the setup easy to run locally via Docker.

Prerequisites

  • Docker & Docker Compose (tested on macOS in this workspace).
  • Alternatively, Node 18+ and npm/yarn to run services locally without containers.

Start backend + MySQL using Docker Compose:

docker compose up -d --build

Stop and remove containers:

docker compose down

The backend will be available at http://localhost:8002 by default (see docker-compose.yml). MySQL is exposed on host port 3308 -> container 3306.

Running services locally (without Docker)

Backend

cd backend
npm install
# set env vars (see Environment section) or create a .env file
npm start

Frontend

cd frontend
npm install
npm run dev

Environment variables

The backend reads configuration from environment variables (some are present in docker-compose.yml):

  • DB_HOST - MySQL host (e.g. mysql in compose)
  • DB_USER - MySQL user (root in compose)
  • DB_PASSWORD - MySQL root password
  • DB_NAME - MySQL database name (e.g. mcs_lose)
  • SECRET_KEY - secret used to sign JWT tokens (required for authentication)

Important: do not commit secrets to source control. In production, provide SECRET_KEY through a secure secret manager.

API (backend)

All endpoints are served by the Express backend in backend/server.js.

Public

  • GET / — renders index.ejs (simple root view)
  • POST /lose — accepts an update payload and attempts a conditional UPDATE on lose rows (no auth)
  • POST /login — admin login; returns a JWT token on success

Protected (require Authorization: Bearer )

  • GET /table-data — returns all rows from lose
  • POST /create-entry — bulk-insert losnummer values (body: { losnummer: [...] })
  • DELETE /remove-entries — delete rows by losnummer (body: { losnummern: [...] })
  • PUT /save-row — update a single row (body contains row fields)
  • DELETE /reset-data — delete all rows from lose

Authentication

  • Tokens are generated using jose and signed with SECRET_KEY in backend/services/tokenService.js.
  • The authenticate middleware expects an Authorization header with Bearer <token>.

Database schema

Schema available in backend/scheme.sql — main tables:

  • lose (columns):
    • losnummer VARCHAR(255) UNIQUE
    • vorname, nachname, adresse, plz, email (nullable)
  • admin_user (columns): username (unique), password (plain text in current implementation)

Notes:

  • The current SQL uses simple types and a single losnummer unique constraint. Consider adding an auto-increment id column if needed.

Project structure (high level)

  • backend/

    • server.js — Express app, routes and middleware
    • services/database.js — MySQL pool and query helpers
    • services/tokenService.js — token generation and authentication middleware
    • scheme.sql — DDL for tables
  • frontend/

    • React + TypeScript app scaffolded with Vite
    • src/components — UI components (Admin, Table, Import GUI, Forms)
  • docker-compose.yml — config for backend and MySQL containers

Notable implementation details & known issues

  • Body size limits are increased in server.js to accept large CSV/JSON payloads (limit: 10mb).
  • Passwords for admin_user are stored and checked as plaintext in database.js — this is insecure. Use salted password hashing (bcrypt) for real deployments.
  • tokenService.js currently logs the generated token to console — remove that in production.
  • getTableData() in services/database.js has an incorrect code path checking result.entries.length (should check result.length). This may cause unexpected behavior when the table is empty.
  • Queries use parameterized queries with mysql2 which protects against SQL injection for provided query parameters.

Suggestions / TODOs

  • Fix the getTableData() empty-check bug.
  • Hash admin passwords and add a registration/migration path for admin accounts.
  • Add input validation on API endpoints and better error handling/user feedback.
  • Consider adding database migrations (Flyway, knex, sequelize migrations).
  • Add tests and CI for backend endpoints.
  • Optionally enable the frontend service in docker-compose.yml (it's currently commented out).

Development tips

  • To inspect the MySQL data created by Docker Compose, you can connect with your preferred MySQL client to localhost:3308 (user and password from docker-compose.yml).
  • To generate a token from the backend for testing, create an admin user entry in admin_user and POST to /login.

License

No license specified. Add a LICENSE file if you want to open-source this project.

Contacts / credits

Project created in this workspace. See backend and frontend folders for authorship and details.

Description
No description provided
https://lose.the1s.de
Readme 536 KiB
Languages
TypeScript 85.4%
JavaScript 12.4%
HTML 1.4%
Dockerfile 0.4%
EJS 0.3%