Skip to main content

System Architecture

The MarsAI film festival platform follows a modern three-tier architecture separating presentation, business logic, and data layers.

Architecture Overview

The platform is built using a client-server architecture with clear separation of concerns:
┌─────────────────┐
│  React Frontend │  (Vite + React 19)
│    Port 5173    │
└────────┬────────┘
         │ HTTP/REST API

┌─────────────────┐
│ Express Backend │  (Node.js + Express 5)
│    Port 3000    │
└────────┬────────┘
         │ Sequelize ORM

┌─────────────────┐
│  MySQL Database │  (MySQL 2.18)
│    Port 3306    │
└─────────────────┘

Technology Stack

Frontend Dependencies

The frontend is built with React 19 and modern tooling from ~/workspace/source/front/package.json:1:
{
  "react": "^19.2.0",
  "react-dom": "^19.2.0",
  "react-router": "^7.12.0",
  "vite": "^7.2.4",
  "tailwindcss": "^4.1.18"
}

Backend Dependencies

The backend uses Express and Sequelize from ~/workspace/source/back/package.json:1:
{
  "express": "^5.2.1",
  "sequelize": "^6.37.7",
  "mysql2": "^3.16.1",
  "dotenv": "^17.2.3"
}

Backend Structure

The backend follows the MVC pattern with clear separation:
back/
├── index.js                 # Main entry point
├── config/
│   └── config.cjs          # Database configuration
├── migrations/             # Database migrations
├── src/
│   ├── constants/          # Application constants
│   ├── controllers/        # Business logic
│   │   ├── AuthController.js
│   │   ├── MovieController.js
│   │   ├── UserController.js
│   │   └── ...
│   ├── db/
│   │   └── connection.js   # Database connection
│   ├── middlewares/        # Request middlewares
│   │   └── AuthMiddleware.js
│   ├── models/             # Sequelize models
│   │   ├── User.js
│   │   ├── Movie.js
│   │   ├── Vote.js
│   │   └── index.js
│   ├── routes/             # API route definitions
│   │   ├── index.js
│   │   ├── Auth.route.js
│   │   ├── Movie.route.js
│   │   └── ...
│   └── utils/              # Helper utilities
└── uploads/                # File storage

Main Application Entry

The server initializes in ~/workspace/source/back/index.js:1:
import express from "express";
import cors from "cors";
import dotenv from "dotenv";
import sequelize from "./src/db/connection.js";
import routes from "./src/routes/index.js";

dotenv.config();

const app = express();
const PORT = process.env.PORT || 3000;

// Middleware configuration
app.use(cors({
  origin: process.env.FRONTEND_URL || "http://localhost:5173",
  credentials: true
}));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use("/uploads", express.static(path.join(process.cwd(), "uploads")));

// Mount all routes
app.use("/", routes);

// Start server with database connection
async function startServer() {
  await sequelize.authenticate();
  app.listen(PORT);
}

Route Organization

All routes are centralized in ~/workspace/source/back/src/routes/index.js:1:
import express from "express";
import userRouter from "./User.route.js";
import movieRouter from "./Movie.route.js";
import authRouter from "./Auth.route.js";
import awardRouter from "./Award.route.js";
import voteRouter from "./Vote.route.js";
import categorieRouter from "./Categorie.route.js";
import reservationRouter from "./Reservation.route.js";
import eventRouter from "./Event.route.js";
import dashboardRouter from "./Dashboard.route.js";
import sponsorRouter from "./Sponsor.route.js";

const router = express.Router();

router.use("/auth", authRouter);
router.use("/users", userRouter);
router.use("/movies", movieRouter);
router.use("/awards", awardRouter);
router.use("/votes", voteRouter);
router.use("/categories", categorieRouter);
router.use("/reservations", reservationRouter);
router.use("/events", eventRouter);
router.use("/admin/dashboard", dashboardRouter);
router.use("/sponsors", sponsorRouter);

export default router;
All API endpoints are prefixed with their resource name (e.g., /movies, /users, /votes)

Frontend Structure

The React frontend is organized by feature and responsibility:
front/
├── src/
│   ├── api/                # API client & service layer
│   ├── assets/
│   │   ├── data/           # Static data files
│   │   ├── images/         # Image assets
│   │   └── videos/         # Video assets
│   ├── components/         # Reusable components
│   │   ├── admin/          # Admin-specific components
│   │   ├── home/           # Homepage components
│   │   └── navbar/         # Navigation components
│   ├── context/            # React Context providers
│   ├── hooks/              # Custom React hooks
│   ├── layouts/            # Page layout components
│   ├── locales/            # i18n translation files
│   ├── middlewares/        # Route middlewares
│   ├── pages/              # Page components
│   │   ├── admin/          # Admin dashboard pages
│   │   ├── auth/           # Login/Register pages
│   │   ├── jury/           # Jury voting pages
│   │   ├── producer/       # Producer submission pages
│   │   └── public/         # Public-facing pages
│   └── utils/              # Helper utilities
├── main.jsx                # React entry point
└── vite.config.js          # Vite configuration
The frontend follows a role-based page structure with separate areas for admin, jury, producer, and public users.

Request Flow

A typical API request follows this flow:
// User action triggers API call via axios
import axios from 'axios';

const response = await axios.get(
  'http://localhost:3000/movies',
  { withCredentials: true }
);
// Route handler in src/routes/Movie.route.js
import { Router } from 'express';
import MovieController from '../controllers/MovieController.js';
import { authenticateToken } from '../middlewares/AuthMiddleware.js';

const router = Router();

router.get('/', authenticateToken, MovieController.getAllMovies);
// AuthMiddleware validates JWT token
export const authenticateToken = (req, res, next) => {
  const token = req.headers.authorization?.split(' ')[1];
  
  if (!token) {
    return res.status(401).json({ error: 'Access denied' });
  }
  
  jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
    if (err) return res.status(403).json({ error: 'Invalid token' });
    req.user = user;
    next();
  });
};
// Controller processes business logic
import db from '../models/index.js';

class MovieController {
  async getAllMovies(req, res) {
    try {
      const movies = await db.Movie.findAll({
        include: [
          { model: db.User, as: 'Producer' },
          { model: db.Categorie }
        ]
      });
      res.json(movies);
    } catch (error) {
      res.status(500).json({ error: error.message });
    }
  }
}
// Sequelize executes SQL query
SELECT 
  movies.*,
  users.first_name,
  users.last_name
FROM movies
LEFT JOIN users ON movies.id_user = users.id_user;
{
  "id_movie": 1,
  "title": "AI Dreams",
  "duration": 120,
  "Producer": {
    "id_user": 5,
    "first_name": "John",
    "last_name": "Doe"
  }
}

Database Configuration

Database connection is managed through Sequelize configuration in ~/workspace/source/back/config/config.cjs:1:
require("dotenv").config();

module.exports = {
  development: {
    username: process.env.DB_USER,
    password: process.env.DB_PASSWORD,
    database: process.env.DB_NAME,
    host: process.env.DB_HOST,
    port: Number(process.env.DB_PORT) || 3306,
    dialect: "mysql",
    logging: false
  },
  production: {
    username: process.env.DB_USER,
    password: process.env.DB_PASSWORD,
    database: process.env.DB_NAME,
    host: process.env.DB_HOST,
    port: Number(process.env.DB_PORT) || 3306,
    dialect: "mysql",
    logging: false
  }
};
All database credentials are stored in environment variables for security.

Key Features

Authentication

  • JWT-based authentication
  • Role-based access control (ADMIN, JURY, PRODUCER)
  • Password hashing with bcrypt (10 salt rounds)

File Management

  • Multer for file uploads
  • Static file serving from /uploads
  • AWS S3 integration for cloud storage
  • FFmpeg for video processing

External Integrations

  • YouTube API: Video management and auto-upload
  • Brevo: Email marketing and transactional emails
  • Google APIs: OAuth and YouTube integration
  • File Watcher: Automatic YouTube upload on file changes

Internationalization

  • i18next for multilingual support
  • Translations stored in front/src/locales/
  • Support for French and English

Environment Variables

Required environment variables:
# Database
DB_USER=marsai
DB_PASSWORD=Mars2026!
DB_NAME=marsai_db
DB_HOST=localhost
DB_PORT=3306

# Server
PORT=3000
FRONTEND_URL=http://localhost:5173

# JWT
JWT_SECRET=your_secret_key

# AWS S3
AWS_ACCESS_KEY_ID=your_key
AWS_SECRET_ACCESS_KEY=your_secret
AWS_REGION=us-east-1

# Google/YouTube
GOOGLE_CLIENT_ID=your_client_id
GOOGLE_CLIENT_SECRET=your_client_secret

Performance Considerations

  • Database Connection Pooling: Sequelize manages connection pools automatically
  • Static Asset Serving: Express serves uploaded files directly
  • CORS Configuration: Restricted to frontend URL only
  • Query Optimization: Models use eager loading with include for related data
  • Logging: Disabled in production for performance (logging: false)

Security Measures

  1. Authentication: JWT tokens with expiration
  2. Password Security: Bcrypt hashing with salt rounds
  3. CORS: Restricted to specific origins
  4. Input Validation: Express body parsing with size limits
  5. SQL Injection Prevention: Sequelize ORM with parameterized queries
  6. Environment Secrets: All sensitive data in .env files