Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/danielsl4/TFG_DAM_2526/llms.txt

Use this file to discover all available pages before exploring further.

FutsalManager is a classic three-tier web application. The Angular single-page application runs entirely in the browser and communicates with the Node.js/Express REST API over HTTP. The API persists data to a PostgreSQL database and uses Redis to cache frequently read responses and to enforce rate limits on authentication endpoints. Images are stored in Cloudinary and served via CDN.

Technology stack

LayerTechnologyVersion
Frontend frameworkAngular20
Frontend UIBootstrap5
Backend runtimeNode.js
Backend frameworkExpress4
DatabasePostgreSQL
Cache / rate-limit storeRedis (ioredis)
AuthenticationJSON Web Tokens (jsonwebtoken)
Password hashingbcrypt
Image storageCloudinary
File uploadsMulter + sharp

Frontend (Angular SPA)

The Angular application is a standalone SPA with client-side routing managed by @angular/router. All HTTP communication goes through the ApiService, which reads the backend base URL from environment.apiUrl at build time. Route guards control access to protected pages. Key routes in the Angular app:
PathAccessDescription
/PublicHome page with match schedule
/standingsPublicLeague standings per group
/matchesPublicMatch calendar
/matches/detail/:idPublicFull match detail with events
/statisticsPublicTop scorers and leaderboard
/teams/:idPublicTeam profile
/players/:idPublicPlayer profile
/login, /registerUnauthenticated onlyAuth pages (guarded by noAuthGuard)
/profileAuthenticatedUser profile (guarded by authGuard)
/adminAdmin onlyAdmin panel (guarded by adminGuard)

Backend (Node.js/Express API)

The Express server mounts modular route files. Each domain area has its own router, keeping the codebase easy to extend. Mounted routes:
Mount pointRouter fileDomain
/login, /registersrc/routes/auth.jsAuthentication
/users, /user/profilesrc/routes/users.jsUser management
/matchessrc/routes/matches.jsMatch lifecycle and events
/teamssrc/routes/teams.jsTeam profiles and following
/playerssrc/routes/players.jsPlayer profiles and registration
/standingssrc/routes/standings.jsLeague table calculations
/statisticssrc/routes/statistics.jsScorer/card rankings, prediction stats
/adminsrc/routes/admin.jsAdmin dashboard and image uploads
/seasonssrc/routes/seasons.jsSeason CRUD and import
/groupssrc/routes/groups.jsGroup and team assignment management
/fieldssrc/routes/fields.jsVenue/field management

Authentication and authorization

Authentication uses JSON Web Tokens signed with the JWT_SECRET environment variable. The token is passed in the Authorization: Bearer <token> header on every protected request. Token lifetimes by role:
RoleToken expiryRationale
admin6 hoursShort-lived to limit exposure of privileged credentials
referee6 hoursShort-lived for the same reason
user7 daysLonger session for regular fans who do not have write access
Three middleware functions gate access at the route level:
  • verifyToken — required on all protected routes; returns 401 if the token is missing or invalid.
  • verifyReferee — permits referee and admin roles only; used on match editing endpoints.
  • verifyAdmin — permits admin role only; used on all admin panel endpoints.
  • verifyMatchLock — checks whether a match is currently locked by another user. Locks expire after 2 minutes of inactivity and are cleaned up automatically.

Database (PostgreSQL)

The backend uses pg (node-postgres) with a connection pool configured for up to 50 concurrent connections, a 30-second idle timeout, and a 5-second connection timeout. The connection is configured via the DATABASE_URL environment variable with SSL enabled. All database access goes through a shared db module (backend/db.js) that exposes a query helper and a getClient method for transaction-level control.

Caching (Redis)

Redis serves two purposes: response caching and rate limiting. Response caching reduces database load on read-heavy endpoints. Cached keys follow a consistent naming scheme:
Key patternContents
all_matchesFull match list (global)
matches:currentCurrent-season match list
matches:season:<id>Match list for a specific season
match:<id>Individual match detail
match:<id>:user:<uid>Match detail with user-specific vote data
standingsGlobal standings
standings:<id>Standings for a specific season
global_last_activityTimestamp of the last admin action
Cache invalidation happens immediately when a match event occurs. The invalidateMatchCache function deletes all related keys (match detail, season list, standings, and all user-scoped match entries) to prevent stale reads. Rate limiting on /login and /register uses express-rate-limit with a Redis store (rate-limit-redis). Each IP is limited to 10 requests per 15-minute window to guard against brute-force attacks.

Image uploads (Cloudinary)

Team and player images are uploaded through the /admin/upload endpoint. The backend accepts a multipart/form-data request, processes the file with sharp for optimisation, and sends it to Cloudinary using the cloudinary SDK. The returned CDN URL is stored in the database and served directly to the frontend.

CORS

CORS is currently configured with a wildcard origin (Access-Control-Allow-Origin: *), which permits requests from any domain. This is intentional for maximum compatibility during development and self-hosted deployments. If you deploy to production and need to restrict access, replace the wildcard with an explicit list of allowed origins in backend/index.js.
The allowed methods are GET, POST, PUT, PATCH, DELETE, OPTIONS. Preflight OPTIONS requests receive an immediate 200 response without hitting any route handlers.

Next steps

Quickstart

Run both services locally and verify your setup.

API reference

Browse all endpoints with request and response schemas.

Deployment: backend

Deploy the Node.js API to a production environment.

Deployment: frontend

Build and deploy the Angular SPA.

Build docs developers (and LLMs) love