CESI: Architecture N Tiers, exemple microservices
This commit is contained in:
parent
8d6f44d938
commit
b975949318
1
cesi/architecture_n_tiers/ressources/exercices/ex_microbloggr_solution/.gitignore
vendored
Normal file
1
cesi/architecture_n_tiers/ressources/exercices/ex_microbloggr_solution/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
node_modules
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
|
@ -0,0 +1,14 @@
|
|||
version: '2'
|
||||
services:
|
||||
gateway:
|
||||
build:
|
||||
context: containers/gateway
|
||||
ports:
|
||||
- 8080:80
|
||||
|
||||
home_page:
|
||||
build:
|
||||
context: containers/home_page
|
||||
login_page:
|
||||
build:
|
||||
context: containers/login_page
|
|
@ -0,0 +1,8 @@
|
|||
FROM alpine:3.2
|
||||
|
||||
RUN apk add --no-cache nginx
|
||||
COPY microbloggr.conf /etc/nginx/microbloggr.conf
|
||||
|
||||
RUN mkdir -p /var/run/nginx
|
||||
|
||||
CMD ["nginx", "-c", "/etc/nginx/microbloggr.conf"]
|
|
@ -0,0 +1,45 @@
|
|||
daemon off;
|
||||
|
||||
worker_processes 1;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
include mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
sendfile on;
|
||||
keepalive_timeout 65;
|
||||
|
||||
gzip on;
|
||||
|
||||
server {
|
||||
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
location / {
|
||||
proxy_pass http://home_page:8080/;
|
||||
}
|
||||
|
||||
location /login {
|
||||
proxy_pass http://login_page:8080/;
|
||||
}
|
||||
|
||||
# location /profile {
|
||||
# proxy_pass http://profile_page:8080/;
|
||||
# }
|
||||
#
|
||||
# location /register {
|
||||
# proxy_pass http://register_page:8080/;
|
||||
# }
|
||||
#
|
||||
# location /statuses {
|
||||
# proxy_pass http://statuses_page:8080/;
|
||||
# }
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
/app/node_modules
|
|
@ -0,0 +1,10 @@
|
|||
FROM alpine:3.7
|
||||
|
||||
RUN apk add --no-cache nodejs
|
||||
|
||||
COPY app /app
|
||||
WORKDIR /app
|
||||
|
||||
RUN npm install --production
|
||||
|
||||
CMD ["node", "server.js"]
|
|
@ -0,0 +1 @@
|
|||
/node_modules
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"name": "login_page",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"ejs": "^2.5.7",
|
||||
"express": "^4.16.2",
|
||||
"express-session": "^1.15.6",
|
||||
"request": "^2.83.0",
|
||||
"request-promise-native": "^1.0.5"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
const express = require('express');
|
||||
const session = require('express-session');
|
||||
const app = express();
|
||||
|
||||
app.set('view engine', 'ejs');
|
||||
|
||||
app.use(session({
|
||||
secret: 'AbsolutlyNotSecret', // Ce secret doit être partagé par tous les microservices *_page
|
||||
resave: false,
|
||||
saveUninitialized: true,
|
||||
}));
|
||||
|
||||
// GET /
|
||||
// Retourne la page d'accueil de MicroBloggr ou redirige vers la
|
||||
// page de login si l'utilisateur n'est pas identifié
|
||||
app.get('/', (req, res) => {
|
||||
|
||||
const sess = req.session;
|
||||
|
||||
// On vérifie si un utilisateur a été associé à la session HTTP
|
||||
if (req.session.user) {
|
||||
// Si oui, on affichage la page d'accueil
|
||||
return res.render('index');
|
||||
} else {
|
||||
// Si non, on redirige l'utilisateur vers la page de login
|
||||
return res.redirect(301, '/login');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
app.listen(8080, () => console.log('listening on port 8080'));
|
|
@ -0,0 +1,9 @@
|
|||
<html>
|
||||
<body>
|
||||
<div class="layout">
|
||||
<nav class="main">
|
||||
<h1 class="brand">MicroBloggr</div>
|
||||
</nav>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1 @@
|
|||
/app/node_modules
|
|
@ -0,0 +1,10 @@
|
|||
FROM alpine:3.7
|
||||
|
||||
RUN apk add --no-cache nodejs
|
||||
|
||||
COPY app /app
|
||||
WORKDIR /app
|
||||
|
||||
RUN npm install --production
|
||||
|
||||
CMD ["node", "server.js"]
|
|
@ -0,0 +1 @@
|
|||
/node_modules
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"name": "login_page",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"body-parser": "^1.18.2",
|
||||
"ejs": "^2.5.7",
|
||||
"express": "^4.16.2",
|
||||
"express-session": "^1.15.6",
|
||||
"request": "^2.83.0",
|
||||
"request-promise-native": "^1.0.5"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
const express = require('express');
|
||||
const app = express();
|
||||
const session = require('express-session');
|
||||
const bodyParser = require('body-parser');
|
||||
const request = require('request-promise-native');
|
||||
|
||||
app.set('view engine', 'ejs');
|
||||
|
||||
app.use(session({
|
||||
secret: 'AbsolutlyNotSecret', // Ce secret doit être partagé par tous les microservices *_page
|
||||
resave: false,
|
||||
saveUninitialized: true,
|
||||
}));
|
||||
|
||||
// On utilise le module "body-parser"
|
||||
// pour faciliter la désérialisation des données
|
||||
// du formulaire
|
||||
app.use(bodyParser.urlencoded({extended: false}));
|
||||
|
||||
// GET /
|
||||
// Affiche la page de login
|
||||
app.get('/', (req, res) => {
|
||||
res.render('index', { error: null });
|
||||
});
|
||||
|
||||
|
||||
// POST /
|
||||
// Récupère les données du formulaire de login
|
||||
app.post('/', (req, res) => {
|
||||
|
||||
const form = req.body;
|
||||
|
||||
// Si le formulaire ne contient pas les données attendues
|
||||
// on renvoie un code HTTP 400 pour indiquer une requête invalide.
|
||||
if(!form.username || !form.password) {
|
||||
return res.render('index', { error: 'Identifiant ou mot de passe manquant.'});
|
||||
}
|
||||
|
||||
// On interroge le microservices "users"
|
||||
// pour récupérer l'utilisateur associé à l'identifiant
|
||||
// fourni dans le formulaire
|
||||
findUserByName(form.username)
|
||||
.then(user => {
|
||||
|
||||
// Si l'utilisateur n'existe pas, on affiche un message d'erreur
|
||||
if(!user) {
|
||||
return res.render('index', { error: 'Identifiant invalide !'});
|
||||
}
|
||||
|
||||
// Si le mot de passe de l'utilisateur correspond à celui enregistré sur le
|
||||
// microservice "users", on attache l'identifiant de l'utilisateur à la session
|
||||
// HTTP puis on le redirige sur la page d'accueil
|
||||
if(user.password === form.password) {
|
||||
req.session.user = user;
|
||||
return res.redirect(302, '/');
|
||||
} else {
|
||||
// Sinon, on affiche un message d'erreur
|
||||
return res.render('index', { error: 'Mot de passe invalide !'});
|
||||
}
|
||||
|
||||
})
|
||||
.catch(err => {
|
||||
// Une erreur s'est produite en essayant de contacter le microservices "users"
|
||||
// On affiche un message d'erreur à l'utilisateur
|
||||
return res.render('index', {
|
||||
error: 'Une erreur s\'est produite pendant le processus d\'authentification.'
|
||||
});
|
||||
})
|
||||
;
|
||||
|
||||
});
|
||||
|
||||
app.listen(8080, () => console.log('listening on port 8080'));
|
||||
|
||||
// Récupère un utilisateur via son identifiant
|
||||
// sur le microservice "users"
|
||||
function findUserByName(username) {
|
||||
return request({
|
||||
uri: `http://users:8080/users/${username}`,
|
||||
json: true
|
||||
})
|
||||
.catch(err => {
|
||||
// Si le microservice "users" répond avec un code HTTP 404
|
||||
// c'est que l'utilisateur n'existe pas
|
||||
if (err.response.statusCode === 404) {
|
||||
return null;
|
||||
}
|
||||
throw err;
|
||||
})
|
||||
;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<html>
|
||||
<body>
|
||||
<h1>Login</h1>
|
||||
<% if (error) { %>
|
||||
<div class="error"><p><%= error %></p></div>
|
||||
<% } %>
|
||||
<form method="POST" enctype="application/x-www-form-urlencoded">
|
||||
<div>
|
||||
<label for="username">Username</label>
|
||||
<input type="text" name="username" id="username" required />
|
||||
</div>
|
||||
<div>
|
||||
<label for="username">Password</label>
|
||||
<input type="password" name="password" id="password" required />
|
||||
</div>
|
||||
<button type="submit">Login</button>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1 @@
|
|||
/app/node_modules
|
|
@ -0,0 +1,10 @@
|
|||
FROM alpine:3.7
|
||||
|
||||
RUN apk add --no-cache nodejs
|
||||
|
||||
COPY app /app
|
||||
WORKDIR /app
|
||||
|
||||
RUN npm install --production
|
||||
|
||||
CMD ["node", "server.js"]
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"name": "sessions",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"body-parser": "^1.18.2",
|
||||
"express": "^4.16.2"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
const express = require('express');
|
||||
const bodyParser = require('body-parser');
|
||||
const app = express();
|
||||
|
||||
const sessions = {};
|
||||
let sessionCounter = 0;
|
||||
|
||||
// On utilise le middleware "body-parser"
|
||||
// pour désérialiser automatiquement
|
||||
// les objets JSON potentiellement
|
||||
// fournis par les requêtes
|
||||
app.use(bodyParser.json());
|
||||
|
||||
// GET /sessions
|
||||
// Permet de récupérer la liste des sessions en cours
|
||||
app.get('/sessions', (req, res) => {
|
||||
res.json(sessions);
|
||||
});
|
||||
|
||||
|
||||
// GET /sessions/:id
|
||||
// Permet de récupérer une session via son identifiant
|
||||
app.get('/sessions/:id', (req, res) => {
|
||||
|
||||
// On récupère l'identifiant de session
|
||||
// depuis l'URL
|
||||
const sessionId = req.params.id;
|
||||
|
||||
// On vérifie que la session existe.
|
||||
// Si ce n'est pas le cas, on retourne un code
|
||||
// HTTP 404 pour indiquer que la ressource
|
||||
// n'existe pas
|
||||
if (!sessions.hasOwnProperty(sessionId)) {
|
||||
return res.status(404).end('Not found.');
|
||||
}
|
||||
|
||||
// On retourne la session sérialisée en JSON
|
||||
return res.json(sessions[sessionId]);
|
||||
|
||||
})
|
||||
|
||||
// POST /sessions
|
||||
// Body: {"user": <user_id> }
|
||||
// Permet de créer une nouvelle session pour l'utilisateur donné
|
||||
app.post('/sessions', (req, res) => {
|
||||
|
||||
const payload = req.body;
|
||||
|
||||
// On valide la présence de l'attribut "user"
|
||||
// sur les données fournies.
|
||||
// Si il n'est pas présent, on retourne un code HTTP
|
||||
// 400 pour indiquer que la requête est invalide.
|
||||
if (!payload.user) {
|
||||
return res.status(400).end('Bad request.');
|
||||
}
|
||||
|
||||
// On créait une nouvelle session
|
||||
const sess = {
|
||||
user: payload.user,
|
||||
id: getSessionId(),
|
||||
};
|
||||
|
||||
// On la "stocke" dans notre registre
|
||||
sessions[session.id] = sess;
|
||||
|
||||
// On retourne la session
|
||||
// nouvellement créée
|
||||
res.json(sess);
|
||||
|
||||
})
|
||||
|
||||
|
||||
// DELETE /sessions/:id
|
||||
// Permet de supprimer une session dans le registre via son identifiant
|
||||
app.delete('/sessions/:id', (req, res) => {
|
||||
|
||||
// On récupère l'identifiant de session
|
||||
// depuis l'URL
|
||||
const sessionId = req.params.id;
|
||||
|
||||
// On vérifie que la session existe.
|
||||
// Si ce n'est pas le cas, on retourne un code
|
||||
// HTTP 404 pour indiquer que la ressource
|
||||
// n'existe pas
|
||||
if (!sessions.hasOwnProperty(sessionId)) {
|
||||
return res.status(404).end('Not found.');
|
||||
}
|
||||
|
||||
delete sessions[sessionId];
|
||||
|
||||
// On retourne un code HTTP 204 pour indiquer que l'opération
|
||||
// s'est déroulée correctement mais qu'aucun "contenu" n'est disponible
|
||||
return res.status(204);
|
||||
|
||||
});
|
||||
|
||||
|
||||
app.listen(8080, () => console.log('listening on port 8080'));
|
||||
|
||||
function getSessionId() {
|
||||
return `${Date.now()}-${sessionCounter++}`;
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 48 KiB |
Binary file not shown.
Loading…
Reference in New Issue