Compare commits
9 Commits
608508a155
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 1abcfc9c4c | |||
| d052ad4b52 | |||
| 5d81b0dbb0 | |||
| 6e5f833d57 | |||
| f36f645e8c | |||
| ace040a012 | |||
| f56a2e7fb6 | |||
| 5228365299 | |||
| d79cf4c160 |
@@ -97,5 +97,10 @@ Soutenange-creation-entreprise : Mise en place des groupe et création des compt
|
|||||||
# Juin
|
# Juin
|
||||||
|
|
||||||
- UE2.14_Management_interculturel : Cours Magitral Notions clés, Enjeux, Prise en compte du MI dans les pratiques manageriales de l’entreprise. Exercice pratique de groupe poir deja appréhender l’examen qui est deja en approche.
|
- UE2.14_Management_interculturel : Cours Magitral Notions clés, Enjeux, Prise en compte du MI dans les pratiques manageriales de l’entreprise. Exercice pratique de groupe poir deja appréhender l’examen qui est deja en approche.
|
||||||
- UE2.35 - Suite et fin TP Sécurisation Avancée AD DS, Les protocole RADIUS
|
- UE2.35 - Suite et fin TP Sécurisation Avancée AD DS, Les protocole RADIUS.
|
||||||
|
- UE2.34 - Analyse de la securite des reseaux : TP SNORT, installation, utilisation envoie de paquet avec scapy.
|
||||||
|
- UE2.10 - Anglais : Exercice sur le travail en groupe et cas pratique sur la gestion d'équipe.
|
||||||
|
- UE2.18 - Communication Marketing : Etude de marché, offre et demande.
|
||||||
|
- UE2.6 - Activités Professionnelles : HACHATON.
|
||||||
|
- UE2.24 - Systèmes distribués et applications sous Windows et Linux : TP1 Mise en place d'un systeme distribués simple pour trouver un hash md5. TP2 Mise en place d’un Load Balancer avec Nginx
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
# 03 04 2026
|
|
||||||
|
|
||||||
<details><summary>Info prof</summary>
|
|
||||||
<pre>
|
|
||||||
Mr Kalonji Jean.Claude - jean-claude.Kalonji@seineetmarne.cci.fr</pre>
|
|
||||||
</details>
|
|
||||||
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# UE2.18 Communication Marketing
|
||||||
|
|
||||||
|
## 03 04 2026
|
||||||
|
|
||||||
|
<details><summary>Info prof</summary>
|
||||||
|
<pre>
|
||||||
|
Mr Kalonji Jean.Claude - jean-claude.kalonji@seineetmarne.cci.fr</pre>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## 04 06 2026
|
||||||
|
|
||||||
|
> Etude de marché, offre et demande
|
||||||
|
|
||||||
|
## 24 06 2026
|
||||||
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,4 @@
|
|||||||
|
import xmlrpc.client
|
||||||
|
proxy = xmlrpc.client.ServerProxy("http://192.168.1.210:9000/")
|
||||||
|
num = int(input("Entrez un nombre : "))
|
||||||
|
print("Résultat : ", proxy.square(num))
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
from xmlrpc.server import SimpleXMLRPCServer
|
||||||
|
def square(x):
|
||||||
|
return x * x
|
||||||
|
server = SimpleXMLRPCServer(("0.0.0.0", 9000))
|
||||||
|
print("Server listening on port 9000...")
|
||||||
|
server.register_function(square, "square")
|
||||||
|
server.serve_forever()
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
from xmlrpc.server import SimpleXMLRPCServer
|
||||||
|
import itertools
|
||||||
|
import string
|
||||||
|
|
||||||
|
class HashCrackerServer:
|
||||||
|
def __init__(self, target_hash, algo):
|
||||||
|
self.target_hash = target_hash
|
||||||
|
self.algo = algo
|
||||||
|
self.workers = []
|
||||||
|
self.password_found = False
|
||||||
|
|
||||||
|
print("Génération de l'espace de recherche (1 à 4 lettres)...")
|
||||||
|
self.blocks = self._generate_blocks()
|
||||||
|
print(f"Espace divisé en {len(self.blocks)} blocs de travail.")
|
||||||
|
|
||||||
|
def _generate_blocks(self, block_size=5000):
|
||||||
|
chars = string.ascii_lowercase
|
||||||
|
all_combinations = []
|
||||||
|
# Génère les mots de 1 à 4 lettres
|
||||||
|
for length in range(1, 5):
|
||||||
|
for p in itertools.product(chars, repeat=length):
|
||||||
|
all_combinations.append("".join(p))
|
||||||
|
|
||||||
|
# Découpage de la liste globale en sous-listes (blocs)
|
||||||
|
return [all_combinations[i:i + block_size] for i in range(0, len(all_combinations), block_size)]
|
||||||
|
|
||||||
|
def register_worker(self, worker_name):
|
||||||
|
if worker_name not in self.workers:
|
||||||
|
self.workers.append(worker_name)
|
||||||
|
print(f"[INFO] Nouveau Worker enregistré : {worker_name}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_work_block(self):
|
||||||
|
if self.password_found:
|
||||||
|
return {"status": "STOP"} # Quelqu'un a déjà trouvé
|
||||||
|
if not self.blocks:
|
||||||
|
return {"status": "DONE"} # Plus de travail disponible
|
||||||
|
|
||||||
|
# On retire le premier bloc de la liste et on l'envoie
|
||||||
|
return {
|
||||||
|
"status": "WORK",
|
||||||
|
"algo": self.algo,
|
||||||
|
"target": self.target_hash,
|
||||||
|
"words": self.blocks.pop(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
def submit_result(self, worker_name, password):
|
||||||
|
if password:
|
||||||
|
self.password_found = True
|
||||||
|
print(f"\n[SUCCÈS] Le worker '{worker_name}' a cracké le hash ! Mot de passe : {password}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Hash MD5 pour le mot "chat"
|
||||||
|
TARGET_HASH = "aa8af3ebe14831a7cd1b6d1383a03755"
|
||||||
|
ALGO = "md5"
|
||||||
|
|
||||||
|
server = SimpleXMLRPCServer(("0.0.0.0", 9000), allow_none=True)
|
||||||
|
cracker_instance = HashCrackerServer(TARGET_HASH, ALGO)
|
||||||
|
|
||||||
|
# Enregistrement de l'instance entière (expose toutes ses méthodes publiques)
|
||||||
|
server.register_instance(cracker_instance)
|
||||||
|
|
||||||
|
print("Serveur Maître RPC en écoute sur le port 9000...")
|
||||||
|
server.serve_forever()
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import xmlrpc.client
|
||||||
|
import hashlib
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Pense à remplacer par l'IP de ta VM1
|
||||||
|
PROXY_URL = "http://192.168.1.210:9000/"
|
||||||
|
WORKER_NAME = "UE2-24-worker1"
|
||||||
|
#WORKER_NAME = "UE2-24-worker2"
|
||||||
|
|
||||||
|
try:
|
||||||
|
proxy = xmlrpc.client.ServerProxy(PROXY_URL, allow_none=True)
|
||||||
|
proxy.register_worker(WORKER_NAME)
|
||||||
|
print(f"Connecté au serveur en tant que {WORKER_NAME}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Impossible de se connecter au serveur : {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
job = proxy.get_work_block()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Erreur de communication avec le serveur : {e}")
|
||||||
|
break
|
||||||
|
|
||||||
|
if job["status"] == "STOP":
|
||||||
|
print("Un autre worker a trouvé le mot de passe. Fin du programme.")
|
||||||
|
break
|
||||||
|
elif job["status"] == "DONE":
|
||||||
|
print("L'espace de recherche est épuisé. Le mot de passe n'a pas été trouvé.")
|
||||||
|
break
|
||||||
|
|
||||||
|
words = job["words"]
|
||||||
|
target = job["target"]
|
||||||
|
algo = job["algo"]
|
||||||
|
|
||||||
|
print(f"Réception d'un bloc de {len(words)} mots à tester...")
|
||||||
|
found_password = None
|
||||||
|
|
||||||
|
# Boucle de force brute sur le bloc
|
||||||
|
for word in words:
|
||||||
|
h = hashlib.new(algo)
|
||||||
|
h.update(word.encode('utf-8'))
|
||||||
|
|
||||||
|
if h.hexdigest() == target:
|
||||||
|
found_password = word
|
||||||
|
break
|
||||||
|
|
||||||
|
# Soumission du résultat (mot de passe ou None si échec sur ce bloc)
|
||||||
|
if found_password:
|
||||||
|
proxy.submit_result(WORKER_NAME, found_password)
|
||||||
|
print(f">>> MOT DE PASSE TROUVÉ : {found_password} <<<")
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
proxy.submit_result(WORKER_NAME, None)
|
||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
+58
@@ -0,0 +1,58 @@
|
|||||||
|
services:
|
||||||
|
db:
|
||||||
|
image: mariadb:latest
|
||||||
|
container_name: db_container
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- MYSQL_ROOT_PASSWORD=rootpassword
|
||||||
|
- MYSQL_DATABASE=mydb
|
||||||
|
- MYSQL_USER=user
|
||||||
|
- MYSQL_PASSWORD=userpassword
|
||||||
|
volumes:
|
||||||
|
- db_data:/var/lib/mysql
|
||||||
|
networks:
|
||||||
|
- app-network
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "mariadb -uroot -prootpassword -e 'SELECT 1' || exit 1"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
|
||||||
|
web:
|
||||||
|
image: nginx:alpine
|
||||||
|
container_name: web_container
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "8080:80"
|
||||||
|
volumes:
|
||||||
|
- ./html:/usr/share/nginx/html:ro
|
||||||
|
networks:
|
||||||
|
- app-network
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "curl -f http://localhost || exit 1"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
|
||||||
|
phpmyadmin:
|
||||||
|
image: phpmyadmin:latest
|
||||||
|
container_name: phpmyadmin_container
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- PMA_HOST=db
|
||||||
|
ports:
|
||||||
|
- "8081:80"
|
||||||
|
networks:
|
||||||
|
- app-network
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "curl -f http://localhost || exit 1"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
db_data:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
app-network:
|
||||||
|
driver: bridge
|
||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
+7
@@ -0,0 +1,7 @@
|
|||||||
|
FROM node:18-alpine
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN npm install
|
||||||
|
COPY . .
|
||||||
|
EXPOSE 3000
|
||||||
|
CMD ["node", "server.js"]
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"name": "api-stock-it",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "server.js",
|
||||||
|
"dependencies": {
|
||||||
|
"cors": "^2.8.5",
|
||||||
|
"express": "^4.18.2",
|
||||||
|
"mysql2": "^3.6.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
+63
@@ -0,0 +1,63 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const mysql = require('mysql2');
|
||||||
|
const cors = require('cors');
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
app.use(cors());
|
||||||
|
app.use(express.json());
|
||||||
|
|
||||||
|
// Connexion à la base de données via variables d'environnement
|
||||||
|
const db = mysql.createPool({
|
||||||
|
host: process.env.DB_HOST || 'localhost',
|
||||||
|
user: process.env.DB_USER || 'root',
|
||||||
|
password: process.env.DB_PASSWORD || 'password',
|
||||||
|
database: process.env.DB_NAME || 'stock_it'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Route GET à la racine : Health Check
|
||||||
|
app.get('/', (req, res) => {
|
||||||
|
res.json({
|
||||||
|
status: 'OK',
|
||||||
|
message: 'L\'API de gestion de stock est bien en ligne et fonctionnelle !',
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Route GET : Récupérer tout le stock
|
||||||
|
app.get('/api/materiel', (req, res) => {
|
||||||
|
db.query('SELECT * FROM materiel', (err, results) => {
|
||||||
|
if (err) return res.status(500).json({ error: err.message });
|
||||||
|
res.json(results);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Route POST : Ajouter un nouvel équipement
|
||||||
|
app.post('/api/materiel', (req, res) => {
|
||||||
|
const { nom, quantite } = req.body;
|
||||||
|
db.query('INSERT INTO materiel (nom, quantite) VALUES (?, ?)', [nom, quantite], (err, results) => {
|
||||||
|
if (err) return res.status(500).json({ error: err.message });
|
||||||
|
res.status(201).json({ id: results.insertId, nom, quantite });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Route PUT : Mettre à jour la quantité d'un équipement
|
||||||
|
app.put('/api/materiel/:id', (req, res) => {
|
||||||
|
const { quantite } = req.body;
|
||||||
|
db.query('UPDATE materiel SET quantite = ? WHERE id = ?', [quantite, req.params.id], (err, results) => {
|
||||||
|
if (err) return res.status(500).json({ error: err.message });
|
||||||
|
res.json({ message: 'Quantité mise à jour avec succès' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Route DELETE : Supprimer un équipement
|
||||||
|
app.delete('/api/materiel/:id', (req, res) => {
|
||||||
|
db.query('DELETE FROM materiel WHERE id = ?', [req.params.id], (err, results) => {
|
||||||
|
if (err) return res.status(500).json({ error: err.message });
|
||||||
|
res.json({ message: 'Équipement supprimé avec succès' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const PORT = 3000;
|
||||||
|
app.listen(PORT, () => {
|
||||||
|
console.log(`API Backend démarrée sur le port ${PORT}`);
|
||||||
|
});
|
||||||
+94
@@ -0,0 +1,94 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: mariadb-pvc
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 1Gi
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: db-init-script
|
||||||
|
data:
|
||||||
|
init.sql: |
|
||||||
|
CREATE DATABASE IF NOT EXISTS stock_it;
|
||||||
|
USE stock_it;
|
||||||
|
CREATE TABLE IF NOT EXISTS materiel (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
nom VARCHAR(255) NOT NULL,
|
||||||
|
quantite INT NOT NULL
|
||||||
|
);
|
||||||
|
INSERT INTO materiel (nom, quantite) VALUES
|
||||||
|
('Serveur Dell PowerEdge', 3),
|
||||||
|
('Switch Cisco 24 ports', 5),
|
||||||
|
('Baie de brassage', 2);
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: db-backend
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: mariadb
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: mariadb
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: mariadb
|
||||||
|
image: mariadb:10.11
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: "128Mi"
|
||||||
|
cpu: "100m"
|
||||||
|
limits:
|
||||||
|
memory: "256Mi"
|
||||||
|
cpu: "250m"
|
||||||
|
env:
|
||||||
|
- name: MYSQL_ROOT_PASSWORD
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: db-credentials
|
||||||
|
key: root-password
|
||||||
|
- name: MYSQL_DATABASE
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: app-config
|
||||||
|
key: db-name
|
||||||
|
ports:
|
||||||
|
- containerPort: 3306
|
||||||
|
volumeMounts:
|
||||||
|
- name: init-script-volume
|
||||||
|
mountPath: /docker-entrypoint-initdb.d/init.sql
|
||||||
|
subPath: init.sql
|
||||||
|
- name: mariadb-storage
|
||||||
|
mountPath: /var/lib/mysql
|
||||||
|
volumes:
|
||||||
|
- name: init-script-volume
|
||||||
|
configMap:
|
||||||
|
name: db-init-script
|
||||||
|
- name: mariadb-storage
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: mariadb-pvc
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: db-service
|
||||||
|
spec:
|
||||||
|
type: ClusterIP
|
||||||
|
selector:
|
||||||
|
app: mariadb
|
||||||
|
ports:
|
||||||
|
- port: 3306
|
||||||
|
targetPort: 3306
|
||||||
+68
@@ -0,0 +1,68 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: api-backend
|
||||||
|
spec:
|
||||||
|
replicas: 2
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: node-api
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: node-api
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: node-api
|
||||||
|
image: gsz116/api-stock:v2
|
||||||
|
imagePullPolicy: Always
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: "64Mi"
|
||||||
|
cpu: "50m"
|
||||||
|
limits:
|
||||||
|
memory: "128Mi"
|
||||||
|
cpu: "100m"
|
||||||
|
env:
|
||||||
|
- name: DB_HOST
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: app-config
|
||||||
|
key: db-host
|
||||||
|
- name: DB_USER
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: app-config
|
||||||
|
key: db-user
|
||||||
|
- name: DB_NAME
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: app-config
|
||||||
|
key: db-name
|
||||||
|
- name: PORT
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: app-config
|
||||||
|
key: backend-port
|
||||||
|
# Variable issue du Secret
|
||||||
|
- name: DB_PASSWORD
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: db-credentials
|
||||||
|
key: root-password
|
||||||
|
ports:
|
||||||
|
- containerPort: 3000
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: api-service
|
||||||
|
spec:
|
||||||
|
type: NodePort
|
||||||
|
selector:
|
||||||
|
app: node-api
|
||||||
|
ports:
|
||||||
|
- port: 3000
|
||||||
|
targetPort: 3000
|
||||||
|
nodePort: 30001
|
||||||
+47
@@ -0,0 +1,47 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: web-frontend
|
||||||
|
spec:
|
||||||
|
replicas: 2
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: nginx-front
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: nginx-front
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: nginx-front
|
||||||
|
image: gsz116/front-stock:v2
|
||||||
|
imagePullPolicy: Always
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: "32Mi"
|
||||||
|
cpu: "50m"
|
||||||
|
limits:
|
||||||
|
memory: "64Mi"
|
||||||
|
cpu: "100m"
|
||||||
|
env:
|
||||||
|
- name: API_URL
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: app-config
|
||||||
|
key: api-url
|
||||||
|
ports:
|
||||||
|
- containerPort: 80
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: frontend-service
|
||||||
|
spec:
|
||||||
|
type: NodePort
|
||||||
|
selector:
|
||||||
|
app: nginx-front
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
targetPort: 80
|
||||||
|
nodePort: 30002
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
FROM nginx:alpine
|
||||||
|
COPY index.html /usr/share/nginx/html/index.html
|
||||||
|
EXPOSE 80
|
||||||
+153
@@ -0,0 +1,153 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="fr">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Gestion de Stock IT</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
margin: 40px;
|
||||||
|
background-color: #f4f4f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
background: white;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||||
|
max-width: 600px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input,
|
||||||
|
button {
|
||||||
|
padding: 10px;
|
||||||
|
margin: 5px 0;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
background-color: #007bff;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background-color: #0056b3;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
background: #eee;
|
||||||
|
margin: 5px 0;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1>Gestion de Stock Informatique</h1>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<h2>Ajouter un équipement</h2>
|
||||||
|
<form id="addForm">
|
||||||
|
<input type="text" id="nom" placeholder="Nom du matériel (ex: Routeur MicroTik)" required>
|
||||||
|
<input type="number" id="quantite" placeholder="Quantité" required min="1">
|
||||||
|
<button type="submit">Ajouter au stock</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<h2>Inventaire actuel</h2>
|
||||||
|
<ul id="stockList">Chargement du stock...</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const API_URL = 'http://192.168.1.15:30001/api/materiel';
|
||||||
|
|
||||||
|
async function fetchStock() {
|
||||||
|
try {
|
||||||
|
const response = await fetch(API_URL);
|
||||||
|
const data = await response.json();
|
||||||
|
const list = document.getElementById('stockList');
|
||||||
|
list.innerHTML = '';
|
||||||
|
data.forEach(item => {
|
||||||
|
const li = document.createElement('li');
|
||||||
|
li.innerHTML = `
|
||||||
|
<span><strong>${item.nom}</strong></span>
|
||||||
|
<span>Quantité: ${item.quantite}</span>
|
||||||
|
<div>
|
||||||
|
<button onclick="modifierQuantite(${item.id}, ${item.quantite})" style="background-color: #ffc107; color: black; margin-right: 5px; width: auto;">Modifier</button>
|
||||||
|
<button onclick="supprimerMateriel(${item.id})" style="background-color: #dc3545; width: auto;">Supprimer</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
list.appendChild(li);
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
document.getElementById('stockList').innerHTML = '<li style="color:red;">Erreur de connexion à l\'API</li>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('addForm').addEventListener('submit', async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const nom = document.getElementById('nom').value;
|
||||||
|
const quantite = document.getElementById('quantite').value;
|
||||||
|
|
||||||
|
await fetch(API_URL, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ nom, quantite })
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('nom').value = '';
|
||||||
|
document.getElementById('quantite').value = '';
|
||||||
|
fetchStock();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Nouvelle fonction : Modifier la quantité
|
||||||
|
window.modifierQuantite = async (id, ancienneQuantite) => {
|
||||||
|
const nouvelleQuantite = prompt("Entrez la nouvelle quantité :", ancienneQuantite);
|
||||||
|
|
||||||
|
// Si l'utilisateur n'a pas annulé et a entré un nombre valide
|
||||||
|
if (nouvelleQuantite !== null && nouvelleQuantite !== "" && !isNaN(nouvelleQuantite)) {
|
||||||
|
await fetch(`${API_URL}/${id}`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ quantite: nouvelleQuantite })
|
||||||
|
});
|
||||||
|
fetchStock(); // Rafraîchit la liste
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Nouvelle fonction : Supprimer un matériel
|
||||||
|
window.supprimerMateriel = async (id) => {
|
||||||
|
if (confirm("Êtes-vous sûr de vouloir supprimer ce matériel de l'inventaire ?")) {
|
||||||
|
await fetch(`${API_URL}/${id}`, {
|
||||||
|
method: 'DELETE'
|
||||||
|
});
|
||||||
|
fetchStock(); // Rafraîchit la liste
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Charger le stock au démarrage
|
||||||
|
fetchStock();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user