Skip to main content

Overview

The permits management system is a Node.js web application built for managing municipal permits (alcohol sales, special events, and advertising). The system uses a traditional MVC architecture with server-side rendering and real-time updates.

Technology Stack

Core Dependencies

"dependencies": {
  "express": "^4.17.3",
  "mysql": "^2.18.1",
  "ejs": "^3.1.6",
  "socket.io": "^4.4.1",
  "bcryptjs": "^2.4.3",
  "multer": "^1.4.4",
  "html-pdf": "^3.0.1",
  "phantomjs": "^2.1.7",
  "express-session": "^1.17.2"
}
  • Express.js: Web framework for routing and middleware
  • MySQL: Relational database (asuntos_publicos)
  • EJS: Server-side templating engine
  • Socket.io: Real-time bidirectional communication
  • bcryptjs: Password hashing and authentication
  • multer: Multipart form data handling (file uploads)
  • html-pdf + phantomjs: PDF generation from HTML templates
  • express-session: Session-based authentication

Directory Structure

src/
├── index.js              # Server entry point, Socket.io setup
├── database.js           # MySQL connection pool
├── globalFunctions.js    # Shared utility functions
├── keys.js              # Configuration keys
├── routes/              # Route handlers
│   ├── authentications.js
│   ├── bebidas.js       # Alcohol permits
│   ├── eventos.js       # Event permits
│   ├── publicidad.js    # Advertising permits
│   └── usuarios.js      # User management
├── middlewares/         # Express middlewares
│   ├── verifySession.js # Session validation
│   └── verifyRoles.js   # Role-based access control
├── views/              # EJS templates
├── public/             # Static assets (JS, CSS, images)
│   └── server-files/   # Uploaded files and generated PDFs
└── lib/
    └── createAdmin.js  # Admin initialization

Server Initialization

The application starts by initializing Express and configuring middleware:
const express = require('express'),
  session = require('express-session'),
  mysql = require('mysql'),
  pool = require('./database.js'),
  path = require('path');

const app = express();
require('./lib/createAdmin.js')(pool);

// Settings
app.set('port', 4000);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

// Middlewares
app.use(express.urlencoded({ extended: false }));
app.use(express.json());
app.use(session({
  key: 'permisos_municipales_pass',
  secret: 'permisos_municipales',
  resave: false,
  saveUninitialized: false
}));

// Routes
app.use(require('./routes/authentications.js'));
app.use('/venta_de_bebidas', require('./routes/bebidas.js'));
app.use('/eventos_especiales', require('./routes/eventos.js'));
app.use('/publicidad_y_propaganda', require('./routes/publicidad.js'));
app.use('/usuarios', require('./routes/usuarios.js'));

// Static files
app.use(express.static(path.join(__dirname, 'public')));

// Start server
const server = app.listen(app.get('port'), () => {
  console.log('¡Servidor Iniciado Correctamente!');
});
Source: src/index.js:1-80

Request Flow

The application follows a standard middleware-based request pipeline:
1

Client Request

User sends HTTP request to Express server
2

Session Middleware

express-session validates and loads session data
3

Authentication Check

verifySession middleware ensures user is logged in
module.exports = function(req, res, next) {
  if (!req.session.usuario) {
    return res.status(401, '¡No ha iniciado sessión!');
  }
  return next();
}
Source: src/middlewares/verifySession.js
4

Authorization Check

verifyRoles middleware checks user permissions
module.exports = function verifyRoles(...roles) {
  return (req, res, next) => {
    if (!roles.some(role => role === req.session.usuario.tipo_usuario)) {
      return res.status(403).json({
        message: '¡Usted no tiene los permisos necesarios para realizar ésta operación!'
      });
    }
    return next();
  }
}
Source: src/middlewares/verifyRoles.js
5

Route Handler

Route controller processes request, queries database, and prepares response
6

Database Query

MySQL pool executes query against asuntos_publicos database
7

Response

Server renders EJS template or returns JSON response
8

Real-time Update

Socket.io broadcasts changes to all connected clients (if applicable)

File Upload Handling

The system uses multer for processing file uploads with a two-stage approach:

1. Temporary Storage

Files are initially saved to a temporary directory:
const storageProyect = multer.diskStorage({
  destination: path.join(__dirname, '../public/server-files/temps/'),
  filename: (req, file, funcion) => {
    if ('usuario' in req.session) {
      funcion(null, 
        file.fieldname + '_de_pago_' + Date.now() + 
        file.originalname.substr(file.originalname.lastIndexOf('.'), 
                                 file.originalname.length - 1)
      );
    }
  }
});

const uploadProyect = multer({
  storage: storageProyect
}).single('comprobante');
Source: src/routes/bebidas.js:16-37

2. File Movement

After validation, files are moved from temps/ to their final location in the appropriate permit directory.
  1. Client submits multipart/form-data with file
  2. Multer middleware intercepts request
  3. File saved to /public/server-files/temps/ with timestamped name
  4. Route handler validates file and request data
  5. File moved to final destination (e.g., /server-files/asuntos_publicos/permisos_municipales/bebidas/)
  6. Database updated with file path reference

PDF Generation Pipeline

The system generates PDF permits using the html-pdf library with phantomjs as the rendering engine:
1

Template Rendering

EJS template is populated with permit data
2

HTML Generation

Rendered template produces complete HTML with inline CSS
3

PDF Conversion

html-pdf uses phantomjs to convert HTML to PDF
const pdf = require('html-pdf');

pdf.create(htmlContent, options).toFile(outputPath, (err, res) => {
  if (err) return console.log(err);
  // PDF saved successfully
});
4

File Storage

Generated PDF saved to server-files directory
5

Database Update

File path stored in permiso_autorizado field

Database Connection

The application uses a MySQL connection pool for efficient database access:
const sessionOptions = {
  host: 'localhost',
  port: 4000,
  user: 'root',
  password: '',
  database: 'asuntos_publicos'
};
Source: src/index.js:24-30

Security Features

Session Management

express-session with server-side storage

Password Hashing

bcryptjs for secure password storage

Role-Based Access

verifyRoles middleware for authorization

Session Validation

verifySession guards protected routes

Database Schema

Complete database structure and relationships

WebSocket Integration

Real-time communication with Socket.io

Build docs developers (and LLMs) love