Skip to content

Frontend (vote) #392

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions .github/workflows/ci-cd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: DevSecOps Pipeline

on: [push]

jobs:
build-test-scan:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Install dependencies
run: npm install
working-directory: backend/

- name: Run Unit Tests
run: npm test
working-directory: backend/

- name: Snyk scan (Security)
uses: snyk/actions/node@master
with:
command: test
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
69 changes: 10 additions & 59 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,65 +1,16 @@
# Example Voting App

A simple distributed application running across multiple Docker containers.
Creo la carpeta vonting-app donde meto toda aplicacion nueva y mas modular mas segura con la siguiente estructura

## Getting started
voting-app/
├── frontend/ # React App
├── backend/ # Express API
├── docker-compose.yml
├── .github/workflows/ci-cd.yml # Pipeline de DevSecOps
├── README.md

Download [Docker Desktop](https://www.docker.com/products/docker-desktop) for Mac or Windows. [Docker Compose](https://docs.docker.com/compose) will be automatically installed. On Linux, make sure you have the latest version of [Compose](https://docs.docker.com/compose/install/).

This solution uses Python, Node.js, .NET, with Redis for messaging and Postgres for storage.
## Creo workflow en gitops para hacer actualizaciones automaticas en devopsec

Run in this directory to build and run the app:

```shell
docker compose up
```

The `vote` app will be running at [http://localhost:8080](http://localhost:8080), and the `results` will be at [http://localhost:8081](http://localhost:8081).

Alternately, if you want to run it on a [Docker Swarm](https://docs.docker.com/engine/swarm/), first make sure you have a swarm. If you don't, run:

```shell
docker swarm init
```

Once you have your swarm, in this directory run:

```shell
docker stack deploy --compose-file docker-stack.yml vote
```

## Run the app in Kubernetes

The folder k8s-specifications contains the YAML specifications of the Voting App's services.

Run the following command to create the deployments and services. Note it will create these resources in your current namespace (`default` if you haven't changed it.)

```shell
kubectl create -f k8s-specifications/
```

The `vote` web app is then available on port 31000 on each host of the cluster, the `result` web app is available on port 31001.

To remove them, run:

```shell
kubectl delete -f k8s-specifications/
```

## Architecture

![Architecture diagram](architecture.excalidraw.png)

* A front-end web app in [Python](/vote) which lets you vote between two options
* A [Redis](https://hub.docker.com/_/redis/) which collects new votes
* A [.NET](/worker/) worker which consumes votes and stores them in…
* A [Postgres](https://hub.docker.com/_/postgres/) database backed by a Docker volume
* A [Node.js](/result) web app which shows the results of the voting in real time

## Notes

The voting application only accepts one vote per client browser. It does not register additional votes if a vote has already been submitted from a client.

This isn't an example of a properly architected perfectly designed distributed app... it's just a simple
example of the various types of pieces and languages you might see (queues, persistent data, etc), and how to
deal with them in Docker at a basic level.
.github/workflows/ci-cd.yml
---
8 changes: 8 additions & 0 deletions voting-app/backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# backend/Dockerfile
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install --only=production
COPY . .
EXPOSE 3000
CMD ["node", "index.js"]
22 changes: 22 additions & 0 deletions voting-app/backend/Node.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// backend/index.js
const express = require('express');
const helmet = require('helmet'); // Seguridad de cabecera
const rateLimit = require('express-rate-limit'); // Previene ataques de fuerza bruta

const app = express();
app.use(helmet());
app.use(express.json());

// Limitador de solicitudes
const limiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 100 });
app.use(limiter);

// Votación API
app.post('/vote', (req, res) => {
const { choice } = req.body;
if (!choice) return res.status(400).send('Choice is required');
// (Aquí guardaríamos en base de datos)
res.send('Vote registered');
});

app.listen(3000, () => console.log('Backend running on port 3000'));
34 changes: 34 additions & 0 deletions voting-app/backend/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const express = require('express');
const app = express();
app.use(express.json()); // Para manejar datos JSON en las solicitudes POST

let votes = [
{ id: 1, option: 'Option 1', count: 10 },
{ id: 2, option: 'Option 2', count: 5 },
{ id: 3, option: 'Option 3', count: 3 }
];

// Ruta GET para obtener los votos
app.get('/votes', (req, res) => {
res.json(votes); // Devuelve los votos actuales
});

// Ruta POST para recibir el voto, pero no incrementar el contador aquí
app.post('/votes', (req, res) => {
const { optionId } = req.body;

// Encontrar el voto basado en el ID
const vote = votes.find(vote => vote.id === optionId);

if (vote) {
res.status(200).json(vote); // Solo devolvemos la opción de voto sin incrementarla
} else {
res.status(404).send('Opción no encontrada');
}
});

// Configuración del puerto
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Backend corriendo en http://localhost:${PORT}`);
});
16 changes: 16 additions & 0 deletions voting-app/backend/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "backend",
"version": "1.0.0",
"description": "Voting backend",
"main": "index.js",
"scripts": {
"start": "node index.js",
"test": "echo \"No tests yet\" && exit 0"
},
"dependencies": {
"express": "^4.18.2",
"helmet": "^7.0.0",
"express-rate-limit": "^7.0.0"
}
}

28 changes: 28 additions & 0 deletions voting-app/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
version: '3.8'

services:
backend:
build: ./backend
ports:
- "3000:3000"
restart: always
environment:
- NODE_ENV=production
depends_on:
- db

db:
image: postgres:15-alpine
restart: always
environment:
- POSTGRES_USER=voting_user
- POSTGRES_PASSWORD=supersecurepassword
- POSTGRES_DB=voting_db

frontend:
build: ./frontend
ports:
- "80:80"
restart: always
depends_on:
- backend
15 changes: 15 additions & 0 deletions voting-app/frontend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# frontend/Dockerfile

FROM node:18-alpine AS build

WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

# Ahora sirve la app con un servidor de archivos estáticos (por ejemplo nginx)
FROM nginx:alpine
COPY --from=build /app/build /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Loading