diff --git a/README_Z2.md b/README_Z2.md new file mode 100644 index 0000000..2ff605f --- /dev/null +++ b/README_Z2.md @@ -0,0 +1,57 @@ +# Application Kubernetes Deployment - SmartBuilding + +## 1. Description of the Application +This application is a Smart Building Management Dashboard built with Node.js and an Express web server. It primarily relies on a MySQL database to securely track system infrastructure components, connected devices (like sensors, thermostats, air conditioning), and different member privilege tiers. Members can authenticate to control and view resources associated with the buildings they manage. + +## 2. Containers Used +- **`web-app:1.0` (Node.js)**: A custom Docker image built from `node:18-alpine` using the provided `Dockerfile`. It serves the Express application and manages API routing, web views (using EJS), and database interactions. +- **`mysql:8.0`**: The official MySQL container image. It is initialized safely with custom user schemas for our objects, users, and sensor mappings (using our `user.sql` script mounted as a ConfigMap). + +## 3. Kubernetes Objects +- **Namespace (`smartbuilding-namespace`)**: Logically isolates all deployment resources within the Kubernetes cluster. +- **PersistentVolume (`mysql-pv`)**: Defines physical storage capacity dynamically provisioned for the database onto the node. +- **PersistentVolumeClaim (`mysql-pvc`)**: Formulates the formal storage request by the `StatefulSet`, linking it tightly with the above `PersistentVolume` ensuring persistence. +- **StatefulSet (`mysql-statefulset`)**: Responsible for preserving the single stateful instance of our database, reliably linking persistent network identity across potential pod restarts. +- **ConfigMap (`mysql-initdb-config`)**: Converts the local `user.sql` file natively into Kubernetes configuration format to be mounted intelligently inside `docker-entrypoint-initdb.d` within the MySQL Pod to initialize the database upon container recreation. +- **Service (`mysql-service`)**: A Headless Service mapping dns directly for the StatefulSet, preserving an internal DNS mapping so the web API node securely resolves the database host organically. +- **Deployment (`webapp-deployment`)**: Defines the stateless scaling tier managing the custom Node.js web-app container instances. Environment variables to control database location (`DB_HOST`) are natively overridden using specs referencing `mysql-service`. +- **Service (`webapp-service`)**: Exposes our load-balancer / NodePort for the Node.js frontend so standard browsers can interface with the cluster externally. + +## 4. Virtual Networks and Named Volumes +- **Virtual Networks**: The application securely utilizes the isolated cluster SDN (Software-Defined Network) isolating nodes using headless services. Specifically, `webapp-service` maps port 3000 on the containers locally to a global `NodePort` (30000) allowing bridge access from external networks. +- **Named Volumes**: The MySQL state is physically bound to `mysql-persistent-storage`. The initial schema (`mysql-initdb`) behaves as a simulated mounted file dynamically injected directly from the ConfigMap volume into `/docker-entrypoint-initdb.d`. + +## 5. Container Configuration +- **Node app configuration**: Containerized with a lightweight Alpine filesystem. Dependencies (`package.json`) are pulled securely during building. Native port exposure binds to TCP `3000`. The code was modified specifically (`config/db.js`) to parse connection configurations dynamically relying gracefully on the environment variables defined within `deployment.yaml` (`DB_HOST`, `DB_USER`, `DB_PASSWORD`, `DB_NAME`). +- **MySQL app configuration**: Managed uniquely with secure environment hooks. Initializing automatically utilizing the `MYSQL_ROOT_PASSWORD` variable and implicitly creates initial mappings executing our SQL via the configured ConfigMap entry. + +## 6. How to Prepare, Run, Pause, and Delete the Application +The `webapp` directory must be in the same path as these scripts. Ensure Minikube and Docker Daemon are configured. + +- **Prepare Application**: Execute the associated preparation script. This directs your Docker CLI locally to Minikube's Docker Daemon natively and packages the image seamlessly. + ```bash + ./prepare-app.sh + ``` +- **Run Application**: Execute the start script to orchestrate all objects inside the Minikube environment. Wait slightly upon deployment creation. + ```bash + ./start-app.sh + ``` +- **Pause Application**: To temporarily freeze deployments natively scale them gracefully to zero instances without permanently destroying metadata bindings. + ```bash + kubectl scale deployment webapp-deployment --replicas=0 -n smartbuilding-namespace + kubectl scale statefulset mysql-statefulset --replicas=0 -n smartbuilding-namespace + ``` +- **Delete Application**: Ensure resources inside the workspace securely dismantle. + ```bash + ./stop-app.sh + ``` + +## 7. How to view the application on the web +Since it operates dynamically via Minikube scaling out an external NodePort, use Minikube's native proxy redirect function. After ensuring deployments are completely `Ready`, open the terminal natively and type: +```bash +minikube service webapp-service -n smartbuilding-namespace +``` +This correctly resolves external mappings locally exposing a URL automatically inside your standard Web Browser to begin authenticating into SmartBuilding. + +## How Artificial Intelligence was natively used +In this project ai was used to help understand kubernetes intrications between files. It was also used to help Write the README.me file (add state clearly what does this application do, list all the dependencies and write it smoothly). diff --git a/z2/deployment.yaml b/z2/deployment.yaml new file mode 100644 index 0000000..7021cc5 --- /dev/null +++ b/z2/deployment.yaml @@ -0,0 +1,36 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: webapp-deployment + namespace: smartbuilding-namespace + labels: + app: webapp +spec: + replicas: 1 + selector: + matchLabels: + app: webapp + template: + metadata: + labels: + app: webapp + spec: + initContainers: + - name: init-mysql + image: busybox:1.28 + command: ['sh', '-c', 'until nc -z mysql-service 3306; do echo waiting for mysql; sleep 2; done;'] + containers: + - name: webapp + image: web-app:1.0 + imagePullPolicy: IfNotPresent + ports: + - containerPort: 3000 + env: + - name: DB_HOST + value: "mysql-service" + - name: DB_USER + value: "root" + - name: DB_PASSWORD + value: "cytech0001" + - name: DB_NAME + value: "user" diff --git a/z2/namespace.yaml b/z2/namespace.yaml new file mode 100644 index 0000000..6402897 --- /dev/null +++ b/z2/namespace.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: smartbuilding-namespace + labels: + app: smartbuilding diff --git a/z2/prepare-app.sh b/z2/prepare-app.sh new file mode 100755 index 0000000..ed8f64f --- /dev/null +++ b/z2/prepare-app.sh @@ -0,0 +1,23 @@ +#!/bin/bash +set -e + +echo "=== Preparing the Kubernetes environment and building image ===" + +if ! minikube status | grep -q "Running"; then + echo "Error: Minikube is not running. Please start it with 'minikube start'." + exit 1 +fi + +echo "[1/3] Pointing Docker CLI to minikube's Docker daemon..." +eval $(minikube docker-env) + +echo "[2/3] Building the Docker image for the web application..." +cd webapp +docker build -t web-app:1.0 . +cd .. + +echo "[3/3] Creating physical folders in Minikube for Persistent Volumes..." +minikube ssh 'sudo mkdir -p /mnt/data/mysql' +minikube ssh 'sudo chmod 777 /mnt/data/mysql' + +echo "=== Application successfully prepared! ===" diff --git a/z2/service.yaml b/z2/service.yaml new file mode 100644 index 0000000..fccb822 --- /dev/null +++ b/z2/service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: webapp-service + namespace: smartbuilding-namespace +spec: + type: NodePort + ports: + - port: 3000 + targetPort: 3000 + nodePort: 30000 + selector: + app: webapp diff --git a/z2/start-app.sh b/z2/start-app.sh new file mode 100755 index 0000000..5f01a54 --- /dev/null +++ b/z2/start-app.sh @@ -0,0 +1,26 @@ +#!/bin/bash +set -e + +echo "=== Starting Kubernetes Application Deployment ===" + +echo "[1/4] Creating Namespace..." +kubectl apply -f namespace.yaml + +echo "[2/4] Generating ConfigMap for the MySQL database init script..." +kubectl create configmap mysql-initdb-config --namespace=smartbuilding-namespace --from-file=webapp/user.sql --dry-run=client -o yaml | kubectl apply -f - + +echo "[3/4] Applying StatefulSet, PV, PVC, and Headless Service..." +kubectl apply -f statefulset.yaml + +echo "[4/4] Applying Node.js Deployment and public Service..." +kubectl apply -f deployment.yaml +kubectl apply -f service.yaml + +echo "" +echo "=== Initialization Complete! ===" +echo "Note: The database container may take a minute to initialize." +echo "You can check the status of your pods by running:" +echo " kubectl get pods -n smartbuilding-namespace -w" +echo "" +echo "To access the web application in Minikube, open a new terminal and run:" +echo " minikube service webapp-service -n smartbuilding-namespace" diff --git a/z2/statefulset.yaml b/z2/statefulset.yaml new file mode 100644 index 0000000..a3cd9c8 --- /dev/null +++ b/z2/statefulset.yaml @@ -0,0 +1,83 @@ +apiVersion: v1 +kind: Service +metadata: + name: mysql-service + namespace: smartbuilding-namespace + labels: + app: mysql +spec: + ports: + - port: 3306 + targetPort: 3306 + clusterIP: None + selector: + app: mysql +--- +apiVersion: v1 +kind: PersistentVolume +metadata: + name: mysql-pv + namespace: smartbuilding-namespace + labels: + type: local +spec: + storageClassName: manual + capacity: + storage: 1Gi + accessModes: + - ReadWriteOnce + hostPath: + path: "/mnt/data/mysql" +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: mysql-pvc + namespace: smartbuilding-namespace +spec: + storageClassName: manual + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: mysql-statefulset + namespace: smartbuilding-namespace +spec: + serviceName: "mysql-service" + replicas: 1 + selector: + matchLabels: + app: mysql + template: + metadata: + labels: + app: mysql + spec: + containers: + - name: mysql + image: mysql:8.0 + env: + - name: MYSQL_ROOT_PASSWORD + value: "cytech0001" + - name: MYSQL_DATABASE + value: "user" + ports: + - containerPort: 3306 + name: mysql + volumeMounts: + - name: mysql-persistent-storage + mountPath: /var/lib/mysql + - name: mysql-initdb + mountPath: /docker-entrypoint-initdb.d + volumes: + - name: mysql-persistent-storage + persistentVolumeClaim: + claimName: mysql-pvc + - name: mysql-initdb + configMap: + name: mysql-initdb-config diff --git a/z2/stop-app.sh b/z2/stop-app.sh new file mode 100755 index 0000000..4b83b80 --- /dev/null +++ b/z2/stop-app.sh @@ -0,0 +1,17 @@ +#!/bin/bash +echo "=== Destroying Kubernetes Application ===" + +echo "Deleting Deployment and web App Service..." +kubectl delete -f deployment.yaml --ignore-not-found=true +kubectl delete -f service.yaml --ignore-not-found=true + +echo "Deleting StatefulSet, PVC, PV and Headless Service..." +kubectl delete -f statefulset.yaml --ignore-not-found=true + +echo "Deleting ConfigMap..." +kubectl delete configmap mysql-initdb-config --namespace=smartbuilding-namespace --ignore-not-found=true + +echo "Deleting Namespace (this might take a moment)..." +kubectl delete -f namespace.yaml --ignore-not-found=true + +echo "=== All Kubernetes objects have been dropped successfully! ===" diff --git a/z2/webapp/.dockerignore b/z2/webapp/.dockerignore new file mode 100644 index 0000000..b8b83d3 --- /dev/null +++ b/z2/webapp/.dockerignore @@ -0,0 +1,4 @@ +node_modules +npm-debug.log +.git +.env diff --git a/z2/webapp/Dockerfile b/z2/webapp/Dockerfile new file mode 100644 index 0000000..08ff5fb --- /dev/null +++ b/z2/webapp/Dockerfile @@ -0,0 +1,12 @@ +FROM node:18-alpine + +WORKDIR /app + +COPY package*.json ./ +RUN npm install + +COPY . . + +EXPOSE 3000 + +CMD ["npm", "start"] diff --git a/z2/webapp/config/db.js b/z2/webapp/config/db.js new file mode 100644 index 0000000..e382c41 --- /dev/null +++ b/z2/webapp/config/db.js @@ -0,0 +1,19 @@ +const mysql = require('mysql2'); + +const connection = mysql.createConnection({ + host: process.env.DB_HOST || 'localhost', + user: process.env.DB_USER || 'root', // Ton utilisateur MySQL + password: process.env.DB_PASSWORD !== undefined ? process.env.DB_PASSWORD : 'cytech0001', // Ton mot de passe + database: process.env.DB_NAME || 'user', // Le nom de ta base + charset: 'utf8mb4' +}); + +connection.connect((err) => { + if (err) { + console.error('❌ Erreur de connexion à MySQL :', err); + } else { + console.log('✅ Connecté à la base de données MySQL'); + } +}); + +module.exports = connection; diff --git a/z2/webapp/img/1744554542632_Screenshot from 2025-04-11 21-19-09.png b/z2/webapp/img/1744554542632_Screenshot from 2025-04-11 21-19-09.png new file mode 100644 index 0000000..b322374 Binary files /dev/null and b/z2/webapp/img/1744554542632_Screenshot from 2025-04-11 21-19-09.png differ diff --git a/z2/webapp/img/option.png b/z2/webapp/img/option.png new file mode 100755 index 0000000..795ee05 Binary files /dev/null and b/z2/webapp/img/option.png differ diff --git a/z2/webapp/img/stylo.png b/z2/webapp/img/stylo.png new file mode 100755 index 0000000..659c3f4 Binary files /dev/null and b/z2/webapp/img/stylo.png differ diff --git a/z2/webapp/index.js b/z2/webapp/index.js new file mode 100644 index 0000000..bd1fe61 --- /dev/null +++ b/z2/webapp/index.js @@ -0,0 +1,205 @@ +const express = require('express'); +const session = require('express-session'); +const path = require('path'); +const bcrypt = require('bcrypt'); +const db = require('./config/db'); + +const app = express(); +const PORT = 3000; + +// -------------------- +// Middleware +// -------------------- +app.use(express.static('public')); +app.use('/img', express.static('img')); +app.use(express.urlencoded({ extended: true })); +app.use(express.json()); + +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + +app.use(session({ + secret: 'votre-secret', + resave: false, + saveUninitialized: false +})); + +app.use((req, res, next) => { + res.locals.session = req.session; + res.locals.currentRoute = req.path; + next(); +}); + +// -------------------- +// Middleware admin +// -------------------- +function requireAdmin(req, res, next) { + if (!req.session.utilisateur || req.session.utilisateur.statut !== 'administrateur') { + return res.redirect('/non-admin'); + } + next(); +} + +// -------------------- +// Importation des routes +// -------------------- +const profilRouter = require('./routes/profil'); +const inscriptionRouter = require('./routes/inscription'); +const connexionRouter = require('./routes/connexion'); +const objetsRoutes = require('./routes/api/objet'); +const utilisateursRoutes = require('./routes/api/utilisateur'); +const ressourceRoutes = require('./routes/api/ressource'); +const complexeRoutes = require('./routes/complexe'); +const adminRoutes = require('./routes/admin'); + +// -------------------- +// Utilisation des routes +// -------------------- +app.use('/profil', profilRouter); +app.use('/inscription', inscriptionRouter); +app.use('/connexion', connexionRouter); +app.use('/api/objets', objetsRoutes); +app.use('/api/utilisateurs', utilisateursRoutes); +app.use('/api/ressources', ressourceRoutes); +app.use('/complexe', complexeRoutes); +app.use('/admin', adminRoutes); + +// -------------------- +// Pages de vue +// -------------------- +app.get('/', (req, res) => res.render('accueil')); +app.get('/objets', (req, res) => res.render('objets')); +app.get('/ressources', (req, res) => res.render('ressources')); +app.get('/description', (req, res) => res.render('description')); +app.get('/contact', (req, res) => res.render('contact')); + +app.get('/membres', (req, res) => { + db.query('SELECT * FROM utilisateur', (err, membres) => { + if (err) return res.status(500).send('Erreur BDD'); + res.render('membres', { membres }); + }); +}); + +app.get('/membres/:id', (req, res) => { + const id = req.params.id; + db.query('SELECT * FROM utilisateur WHERE id = ?', [id], (err, results) => { + if (err || results.length === 0) return res.status(404).send('Utilisateur non trouvé'); + res.render('membre', { membre: results[0] }); + }); +}); + +app.get('/dashboard-complexe', (req, res) => { + if (!req.session.utilisateur || req.session.utilisateur.statut !== 'complexe') { + return res.send(` + + `); + } + + db.query('SELECT * FROM objet', (err, objets) => { + if (err) return res.status(500).send("Erreur objets"); + res.render('dashboard-complexe', { objets }); + }); +}); + +app.get('/dashboard-simple', (req, res) => { + if (!req.session.utilisateur || req.session.utilisateur.statut !== 'simple') { + return res.redirect('/non-admin'); + } + res.redirect('/objets'); +}); + +app.get('/non-admin', (req, res) => { + res.send(` + + `); +}); + +// -------------------- +// Inscription utilisateur +// -------------------- +app.post('/admin/ajouter-utilisateur', async (req, res) => { + const { + nom, prenom, sexe, age, date_naissance, + identifiant, email, mot_de_passe, situation, statut + } = req.body; + + try { + const checkSql = 'SELECT * FROM utilisateur WHERE email = ? OR identifiant = ?'; + db.query(checkSql, [email, identifiant], async (err, results) => { + if (err) return res.status(500).send("Erreur serveur"); + + if (results.length > 0) { + return res.send(` + + `); + } + + const hashed = await bcrypt.hash(mot_de_passe, 10); + const insertSql = ` + INSERT INTO utilisateur + (nom, prenom, sexe, age, date_naissance, identifiant, email, mot_de_passe, situation, statut, etat) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'en attente') + `; + db.query(insertSql, [ + nom, prenom, sexe, age, date_naissance, + identifiant, email, hashed, situation, statut + ], (err) => { + if (err) return res.status(500).send("Erreur lors de l'ajout de l'utilisateur"); + res.redirect('/admin'); + }); + }); + } catch (error) { + res.status(500).send('Erreur serveur'); + } +}); + +// -------------------- +// Ajout objet (admin) +// -------------------- +app.post('/admin/ajouter-objet', (req, res) => { + const { denomination, adresse_ip, type, niveau, etat } = req.body; + const sql = ` + INSERT INTO objet (denomination, adresse_ip, type, niveau, etat) + VALUES (?, ?, ?, ?, ?)`; + db.query(sql, [denomination, adresse_ip, type, niveau, etat], (err) => { + if (err) return res.status(500).send("Erreur lors de l'ajout de l'objet"); + res.redirect('/admin'); + }); +}); + +// -------------------- +// Envoi de message contact +// -------------------- +app.post('/contact', (req, res) => { + const { nom, email, message } = req.body; + const sql = 'INSERT INTO contact (nom, email, message) VALUES (?, ?, ?)'; + db.query(sql, [nom, email, message], (err) => { + if (err) return res.status(500).send("Erreur lors de l'envoi du message"); + res.redirect('/'); + }); +}); + +// -------------------- +// Déconnexion +// -------------------- +app.get('/deconnexion', (req, res) => { + req.session.destroy(() => { + res.redirect('/'); + }); +}); + +// -------------------- +// Lancement du serveur +// -------------------- +app.listen(PORT, () => { + console.log(`✅ Serveur lancé sur http://localhost:${PORT}`); +}); diff --git a/z2/webapp/middleware/auth.js b/z2/webapp/middleware/auth.js new file mode 100644 index 0000000..a15462b --- /dev/null +++ b/z2/webapp/middleware/auth.js @@ -0,0 +1,10 @@ +function ensureAuthenticated(req, res, next) { + if (req.session.utilisateur) { + return next(); + } else { + return res.redirect('/connexion'); + } + } + + module.exports = ensureAuthenticated; + \ No newline at end of file diff --git a/z2/webapp/package-lock.json b/z2/webapp/package-lock.json new file mode 100644 index 0000000..bc74947 --- /dev/null +++ b/z2/webapp/package-lock.json @@ -0,0 +1,2208 @@ +{ + "name": "mon-projet", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "mon-projet", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "bcrypt": "^5.1.1", + "bcryptjs": "^3.0.2", + "connect-flash": "^0.1.1", + "ejs": "^3.1.10", + "express": "^5.1.0", + "express-session": "^1.18.1", + "multer": "^1.4.5-lts.2", + "mysql2": "^3.14.0" + }, + "devDependencies": { + "nodemon": "^3.1.9" + } + }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "license": "BSD-3-Clause", + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "license": "ISC" + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", + "license": "MIT" + }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "license": "ISC" + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/are-we-there-yet/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, + "node_modules/aws-ssl-profiles": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", + "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/bcrypt": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", + "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.11", + "node-addon-api": "^5.0.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/bcryptjs": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.2.tgz", + "integrity": "sha512-k38b3XOZKv60C4E2hVsXTolJWfkGRMbILBIe2IBITXciy5bOsTKot5kDrf3ZfufQtQOUN5mXceUEpU1rTl9Uog==", + "license": "BSD-3-Clause", + "bin": { + "bcrypt": "bin/bcrypt" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/body-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "license": "ISC", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT" + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/connect-flash": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/connect-flash/-/connect-flash-0.1.1.tgz", + "integrity": "sha512-2rcfELQt/ZMP+SM/pG8PyhJRaLKp+6Hk2IUBNkEit09X+vwn3QsAL3ZbYtxUn7NVPzbMTSLRDhqe0B/eh30RYA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "license": "ISC" + }, + "node_modules/content-disposition": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "license": "MIT" + }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-session": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.1.tgz", + "integrity": "sha512-a5mtTqEaZvBCL9A9aqkrtfz+3SMDhOVUnjafjo+s7A9Txkq+SVX2DLvSp1Zrv4uCXa3lMSK3viWnh9Gg07PBUA==", + "license": "MIT", + "dependencies": { + "cookie": "0.7.2", + "cookie-signature": "1.0.7", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-headers": "~1.0.2", + "parseurl": "~1.3.3", + "safe-buffer": "5.2.1", + "uid-safe": "~2.1.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/express-session/node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" + }, + "node_modules/express-session/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express-session/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "license": "MIT", + "dependencies": { + "is-property": "^1.0.2" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "license": "ISC" + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true, + "license": "ISC" + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==", + "license": "MIT" + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/long": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.1.tgz", + "integrity": "sha512-ka87Jz3gcx/I7Hal94xaN2tZEOPoUOEVftkQqZx2EeQRN7LGdfLlI3FvZ+7WDplm+vK2Urx9ULrvSowtdCieng==", + "license": "Apache-2.0" + }, + "node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/lru.min": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.2.tgz", + "integrity": "sha512-Nv9KddBcQSlQopmBHXSsZVY5xsdlZkdH/Iey0BlcBYggMd4two7cZnKOK9vmy3nY0O5RGH99z1PCeTpPqszUYg==", + "license": "MIT", + "engines": { + "bun": ">=1.0.0", + "deno": ">=1.30.0", + "node": ">=8.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wellwelwel" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "license": "MIT", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/multer": { + "version": "1.4.5-lts.2", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.2.tgz", + "integrity": "sha512-VzGiVigcG9zUAoCNU+xShztrlr1auZOlurXynNvO9GiWD1/mTBbUljOKY+qMeazBqXgRnjzeEgJI/wyjJUHg9A==", + "license": "MIT", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.0.0", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.4", + "object-assign": "^4.1.1", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/multer/node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mysql2": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.14.0.tgz", + "integrity": "sha512-8eMhmG6gt/hRkU1G+8KlGOdQi2w+CgtNoD1ksXZq9gQfkfDsX4LHaBwTe1SY0Imx//t2iZA03DFnyYKPinxSRw==", + "license": "MIT", + "dependencies": { + "aws-ssl-profiles": "^1.1.1", + "denque": "^2.1.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.6.3", + "long": "^5.2.1", + "lru.min": "^1.0.0", + "named-placeholders": "^1.1.3", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.2" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/named-placeholders": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz", + "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==", + "license": "MIT", + "dependencies": { + "lru-cache": "^7.14.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==", + "license": "MIT" + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/nodemon": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.9.tgz", + "integrity": "sha512-hdr1oIb2p6ZSxu3PB2JWWYS7ZQ0qvaZsc3hK8DR8f02kRzc8rjYmxAIvdz+aYC+8F2IjNaB7HMcSDg8nQpJxyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/nodemon/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "license": "ISC", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-to-regexp": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", + "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true, + "license": "MIT" + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", + "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.6.3", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==" + }, + "node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "license": "ISC", + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, + "node_modules/uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "license": "MIT", + "dependencies": { + "random-bytes": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true, + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "license": "ISC", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + } + } +} diff --git a/z2/webapp/package.json b/z2/webapp/package.json new file mode 100644 index 0000000..444d4e7 --- /dev/null +++ b/z2/webapp/package.json @@ -0,0 +1,26 @@ +{ + "name": "mon-projet", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "start": "node index.js", + "dev": "nodemon index.js" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "bcrypt": "^5.1.1", + "bcryptjs": "^3.0.2", + "connect-flash": "^0.1.1", + "ejs": "^3.1.10", + "express": "^5.1.0", + "express-session": "^1.18.1", + "multer": "^1.4.5-lts.2", + "mysql2": "^3.14.0" + }, + "devDependencies": { + "nodemon": "^3.1.9" + } +} diff --git a/z2/webapp/public/images/batiment.png b/z2/webapp/public/images/batiment.png new file mode 100755 index 0000000..a3faf48 Binary files /dev/null and b/z2/webapp/public/images/batiment.png differ diff --git a/z2/webapp/public/images/building.jpg b/z2/webapp/public/images/building.jpg new file mode 100755 index 0000000..595e53e Binary files /dev/null and b/z2/webapp/public/images/building.jpg differ diff --git a/z2/webapp/public/images/smart_building.png b/z2/webapp/public/images/smart_building.png new file mode 100755 index 0000000..03fb2f3 Binary files /dev/null and b/z2/webapp/public/images/smart_building.png differ diff --git a/z2/webapp/public/images/tour.jpg b/z2/webapp/public/images/tour.jpg new file mode 100755 index 0000000..b13a701 Binary files /dev/null and b/z2/webapp/public/images/tour.jpg differ diff --git a/z2/webapp/public/style.css b/z2/webapp/public/style.css new file mode 100644 index 0000000..6e9d048 --- /dev/null +++ b/z2/webapp/public/style.css @@ -0,0 +1,419 @@ +/* ------------- */ +/* HTML */ +/* ------------- */ + +html, +body { + font-family: "Inter", sans-serif; + background-color: #1e1e1e; + color: #eff1f3; + margin: 0; + padding: 0; + overflow: auto; + height: 100%; /* Définit la hauteur de la page à 100% */ + display: flex; /* Flexbox sur le body */ + flex-direction: column; /* Aligne le contenu du body en colonne */ +} + +/* --------------- */ +/* Header */ +/* --------------- */ + +header { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + padding: 10px 40px; +} + +header a { + text-decoration: none; + color: #eff1f3; +} + +header ul { + list-style: none; + display: flex; + align-items: center; +} + +header li { + cursor: pointer; +} + +.header_text { + display: flex; + flex-grow: 1; /* Le conteneur des liens prend toute la place disponible */ + justify-content: center; /* Centre les liens horizontalement */ + gap: 15%; /* Espace entre les liens */ + font-weight: 600; +} + +.header_text a { + padding: 0 10px; +} + +.header_text li a:hover { + color: #00a8e8; +} + +.bouton_connexion { + padding: 10px 20px; + border-radius: 900px; + font-size: 16px; /* Taille de la police */ + border-color: #00a8e8; + color: #eff1f3; + background-color: #00a8e8; + margin-top: 0; + cursor: pointer; +} + +.bouton_connexion:hover { + border-color: #eff1f3; + color: #00a8e8; + background-color: #eff1f3; /* Changer la couleur au survol */ +} + +#smart_building { + width: 13%; + margin-left: -40px; + height: auto; +} + +/* ------------- */ +/* Main */ +/* ------------- */ + +main { + display: flex; + flex-grow: 1; /* Permet à la section principale d'occuper l'espace restant */ + justify-content: center; /* Centre verticalement le contenu */ + align-items: center; /* Centre horizontalement le contenu */ + padding-bottom: 50px; + gap: 30px; /* Espace entre les 2 sections */ +} + +.left-panel { + flex: 3; /* 30% de la largeur totale */ + display: flex; + justify-content: center; /* Centrer l'image horizontalement */ + align-items: center; /* Centrer l'image verticalement */ +} + +.left-panel img { + width: 100%; /* L'image prend toute la largeur de son conteneur */ + max-width: 450px; /* Maximum de 300px de largeur */ + height: auto; /* La hauteur de l'image s'ajuste automatiquement */ + border-radius: 32px; +} + +.right-panel { + flex: 7; /* 70% de la largeur totale */ +} + +.right-panel h1 { + font-size: 60px; + margin-bottom: 60px; + padding-left: 70px; /* Ajoute un espacement à gauche du titre */ +} + +.features { + display: grid; + grid-template-columns: repeat(4, 1fr); /* 4 ronds par ligne */ + gap: 30px; + justify-items: center; +} + +.feature { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; +} + +.features a{ + text-decoration: none; + color: #eff1f3; +} + +.circle { + width: 150px; + height: 150px; + background-color: #eff1f3; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + transition: background-color 0.3s, color 0.3s; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2); +} + +.circle:hover { + background-color: #2c2c2c; + cursor: pointer; +} + +.circle i { + font-size: 70px; + fill: #2c2c2c; + color: #2c2c2c; + transition: fill 0.3s, color 0.3s; +} + +.circle:hover i { + fill: #eff1f3; + color: #eff1f3; +} + +/* --------------- */ +/* Footer */ +/* --------------- */ + +footer { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + font-size: 14px; + text-transform: uppercase; + padding: 20px; + margin-top: auto; /* Cela force le footer à se pousser vers le bas */ +} + +.copyright { + padding: 10px 20px; +} + +footer a { + text-decoration: none; + color: #eff1f3; +} + +.footer_link { + display: flex; + flex-grow: 1; + gap: 10%; +} + +.footer_link a { + padding: 0 10px; +} + +.copyright { + padding: 10px 20px; +} + +/* ------------------------- */ +/* Responsive Design */ +/* ------------------------- */ + +/* GRAND ÉCRAN (> 1440px) */ +@media (min-width: 1440px) { + .right-panel h1 { + font-size: 72px; + } + + .circle { + width: 180px; + height: 180px; + } + + .circle i { + font-size: 80px; + } +} + + +/* TABLETTE - Moyenne (1025px à 1440px) */ +@media (max-width: 1440px) and (min-width: 1025px) { + .features { + grid-template-columns: repeat(3, 1fr); + } + + .right-panel h1 { + font-size: 48px; + padding-left: 30px; + } + + .circle { + width: 130px; + height: 130px; + } + + .circle i { + font-size: 50px; + } +} + + +/* TABLETTE - Petite (769px à 1024px) */ +@media (max-width: 1024px) and (min-width: 769px) { + header { + flex-wrap: wrap; + justify-content: center; + padding: 20px; + gap: 20px; + } + + #smart_building { + width: 100px; + margin: 0; + } + + .header_text { + flex-direction: row; + flex-wrap: wrap; + justify-content: center; + gap: 30px; + } + + .bouton_connexion { + padding: 8px 16px; + font-size: 14px; + } + + main { + flex-direction: column; + padding: 40px; + } + + .left-panel, + .right-panel { + width: 100%; + flex: none; + } + + .features { + grid-template-columns: repeat(2, 1fr); + } + + .right-panel h1 { + font-size: 42px; + padding-left: 0; + text-align: center; + } +} + + +/* MOBILE (≤ 768px) */ +@media (max-width: 768px) { + header { + flex-direction: column; + align-items: center; + gap: 15px; + } + + #smart_building { + width: 90px; + height: auto; + margin: 0 auto; + } + + .header_text { + flex-direction: column; + align-items: center; + gap: 10px; + font-size: 16px; + } + + .bouton_connexion { + padding: 10px 20px; + width: auto; + text-align: center; + } + + main { + flex-direction: column; + padding: 20px; + } + + .left-panel, + .right-panel { + width: 100%; + flex: unset; + } + + .right-panel h1 { + font-size: 36px; + padding-left: 0; + text-align: center; + margin-bottom: 30px; + } + + .features { + grid-template-columns: 1fr; + gap: 20px; + } + + .circle { + width: 100px; + height: 100px; + } + + .circle i { + font-size: 40px; + } + + footer { + flex-direction: column; + text-align: center; + gap: 10px; + align-items: center; + } + + .footer_link { + flex-direction: column; + align-items: center; + gap: 10px; + } + + .copyright { + padding: 0; + } +} + + +/* TÉLÉPHONE TRÈS PETIT (≤ 480px) */ +@media (max-width: 480px) { + main { + padding: 20px 10px; + } + + .right-panel h1 { + font-size: 28px; + margin-bottom: 20px; + } + + .circle { + width: 80px; + height: 80px; + } + + .circle i { + font-size: 32px; + } + + .form-control-inscription { + font-size: 14px; + padding: 8px; + } + + #smart_building { + width: 70px; + } + + .bouton_connexion { + font-size: 14px; + padding: 8px 16px; + } +} + + +/* ------------------------- */ +/* Transitions douces */ +/* ------------------------- */ + +* { + transition: all 0.3s ease-in-out; +} \ No newline at end of file diff --git a/z2/webapp/public/styleAdmin.css b/z2/webapp/public/styleAdmin.css new file mode 100644 index 0000000..7c7a818 --- /dev/null +++ b/z2/webapp/public/styleAdmin.css @@ -0,0 +1,530 @@ +/* ---------------- */ +/* GLOBAL */ +/* ---------------- */ + +body { + margin: 0; + padding: 0; + font-family: 'Inter', sans-serif; + background-color: #1e1e1e; + color: #eff1f3; + min-height: 100vh; + display: flex; + flex-direction: column; +} + +main { + flex: 1; + padding: 40px 60px; +} + +/* --------------- */ +/* Header */ +/* --------------- */ + +header { +display: flex; +flex-wrap: wrap; +align-items: center; +justify-content: space-between; +padding: 10px 40px; +} + +header a { +text-decoration: none; +color: #eff1f3; +} + +header ul { +list-style: none; +display: flex; +align-items: center; +} + +header li { +cursor: pointer; +} + +.header_text li a:hover { + color: #00a8e8; +} + + +.header_text { +display: flex; +flex-grow: 1; /* Le conteneur des liens prend toute la place disponible */ +justify-content: center; /* Centre les liens horizontalement */ +gap: 15%; /* Espace entre les liens */ +font-weight: 600; +} + +.header_text a { +padding: 0 10px; +} + +.bouton_connexion { +padding: 10px 20px; +border-radius: 900px; +font-size: 16px; /* Taille de la police */ +border-color: #00a8e8; +color: #eff1f3; +background-color: #00a8e8; +margin-top: 0; +cursor: pointer; +} + +.bouton_connexion:hover { +border-color: #eff1f3; +color: #00a8e8; +background-color: #eff1f3; /* Changer la couleur au survol */ +} + +#smart_building { +width: 13%; +margin-left: -40px; +height: auto; +} + + +/* ---------------- */ +/* TITRES */ +/* ---------------- */ + +h1 { + font-size: 42px; + margin-bottom: 30px; + color: #00a8e8; +} + +/* ---------------- */ +/* TABLES */ +/* ---------------- */ + +table { + width: 100%; + border-collapse: collapse; + margin-bottom: 60px; + background-color: #2c2c2c; + border-radius: 12px; + overflow: hidden; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); +} + +th, +td { + padding: 15px; + text-align: center; + border-bottom: 1px solid #444; +} + +th { + background-color: #1e1e1e; + color: #00a8e8; + font-size: 18px; +} + +tr:hover { + background-color: #333; +} + +select { + padding: 6px 10px; + border-radius: 20px; + border: none; + background-color: #eff1f3; + color: #1e1e1e; + font-weight: bold; +} + +button { + background-color: #00a8e8; + color: #eff1f3; + border: none; + padding: 8px 20px; + border-radius: 20px; + font-weight: bold; + cursor: pointer; + transition: background-color 0.2s ease; +} + +button:hover { + background-color: #0077b6; +} + +.toggle-btn { + display: inline-block; /* Assurer que l'élément peut être transformé */ + transition: transform 0.2s ease; /* Transition pour un zoom fluide */ +} + +.toggle-btn:hover { + cursor: pointer; + transform: scale(1.4); /* Agrandir de 1.5x lors du survol */ +} + +/* ---------------- */ +/* FORMULAIRE */ +/* ---------------- */ + +/* Si tu veux aussi centrer directement dans la section d'ajout */ +.ajout-section { + display: flex; + flex-direction: column; /* Permet de garder la structure de la section */ + justify-content: center; + align-items: center; + text-align: center; + margin: 0; +} + +#form-ajout-objet, +#form-ajout-user { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + border-radius: 24px; + background-color: #2c2c2c; + padding: 40px 100px; /* Augmente l'espace intérieur */ + width: 100%; /* Prend toute la largeur disponible */ + max-width: 400px; /* Définit une largeur max pour éviter qu'elle ne devienne trop étroite */ + box-shadow: 0 4px 8px 0 rgba(21, 21, 21, 0.2); + margin-bottom: 40px; +} + +label { + font-weight: bold; + font-size: 20px; + color: #eff1f3; + white-space: nowrap; +} + +select { + appearance: none; /* Supprime le style natif */ + background: white; + cursor: pointer; +} + +.form-group { + width: 100%; + position: relative; /* Position relative */ + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 20px; +} + +.form-group .icon { + position: absolute; + left: 15px; /* Ajuste la position des icônes */ + top: 50%; + transform: translateY(-50%); +} + +.form-group .icon svg { + width: 24px; + height: 24px; + fill: #191717; +} + +.form-control-admin { + width: 100%; + padding-left: 50px; /* Remplissage à gauche de 55px */ + padding-right: 40px; + height: 48px; /* Hauteur de 40px */ + border-radius: 34px; /* Bordure arrondie */ + font-size: 16px; /* Taille de la police */ + letter-spacing: 0.5px; /* Espacement des lettres */ + background-color: #eff1f3; /* Couleur de fond */ + border: none; /* Supprimer la bordure */ +} + +.form-row { + display: flex; + align-items: center; + gap: 10px; /* espace entre label et champ */ + flex-wrap: nowrap; /* évite le retour à la ligne */ +} + +.bouton-admin { + padding: 10px 20px; + border-radius: 900px; + font-size: 16px; /* Taille de la police */ + border-color: #00a8e8; + color: #eff1f3; + background-color: #00a8e8; + margin-top: 0; /* Centrage vertical */ + cursor: pointer; +} + +.bouton-admin:hover { + border-color: #eff1f3; + color: #00a8e8; + background-color: #eff1f3; /* Changer la couleur au survol */ +} + +/* ---------------- */ +/* FOOTER */ +/* ---------------- */ + +footer { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + font-size: 14px; + text-transform: uppercase; + padding: 20px; + margin-top: auto; /* Cela force le footer à se pousser vers le bas */ +} + +.copyright { + padding: 10px 20px; +} + +footer a { + text-decoration: none; + color: #eff1f3; +} + +.footer_link { + display: flex; + flex-grow: 1; + gap: 10%; +} + +.footer_link a { + padding: 0 10px; +} + +.copyright { + padding: 10px 20px; +} + +/* ------------------------- */ +/* Responsive Design */ +/* ------------------------- */ + +/* GRAND ÉCRAN (> 1440px) */ +@media (min-width: 1440px) { + header { + padding: 20px 40px; + } + + .header_text { + gap: 25%; /* Plus d'espace entre les liens */ + } + + h1 { + font-size: 50px; + } + + table { + margin-bottom: 80px; + } + + .footer_link { + gap: 20%; + } + + footer { + padding: 30px; + } + + .bouton_connexion { + font-size: 18px; + padding: 12px 25px; + } + + #smart_building { + width: 15%; + } +} + + +/* TABLETTE - Moyenne (1025px à 1440px) */ +@media (max-width: 1440px) and (min-width: 1025px) { + header { + padding: 20px 30px; + } + + .header_text { + gap: 20%; /* Moins d'espace entre les liens */ + } + + h1 { + font-size: 48px; + } + + table { + margin-bottom: 60px; + } + + footer { + padding: 25px; + } + + .footer_link { + gap: 15%; + } + + .bouton_connexion { + font-size: 16px; + padding: 10px 22px; + } + + #smart_building { + width: 14%; + } +} + + +/* TABLETTE - Petite (769px à 1024px) */ +@media (max-width: 1024px) and (min-width: 769px) { + header { + flex-wrap: wrap; + justify-content: center; + padding: 20px; + gap: 20px; + } + + .header_text { + flex-direction: row; + flex-wrap: wrap; + justify-content: center; + gap: 30px; + } + + .bouton_connexion { + padding: 8px 16px; + font-size: 14px; + } + + main { + flex-direction: column; + padding: 40px; + } + + table { + margin-bottom: 50px; + } + + .footer_link { + gap: 10%; + } + + #smart_building { + width: 100px; + margin: 0; + } + + footer { + padding: 20px; + } + + .footer_link { + gap: 10%; + } +} + + +/* MOBILE (≤ 768px) */ +@media (max-width: 768px) { + header { + flex-direction: column; + align-items: center; + gap: 15px; + } + + #smart_building { + width: 90px; + height: auto; + margin: 0 auto; + } + + .header_text { + flex-direction: column; + align-items: center; + gap: 10px; + font-size: 16px; + } + + .bouton_connexion { + padding: 10px 20px; + width: auto; + text-align: center; + } + + main { + flex-direction: column; + padding: 20px; + } + + footer { + flex-direction: column; + text-align: center; + gap: 10px; + align-items: center; + } + + .footer_link { + flex-direction: column; + align-items: center; + gap: 10px; + } + + .copyright { + padding: 0; + } + + table { + margin-bottom: 30px; + } +} + + +/* TÉLÉPHONE TRÈS PETIT (≤ 480px) */ +@media (max-width: 480px) { + main { + padding: 20px 10px; + } + + h1 { + font-size: 28px; + margin-bottom: 20px; + } + + #smart_building { + width: 70px; + } + + .bouton_connexion { + font-size: 14px; + padding: 8px 16px; + } + + table { + margin-bottom: 20px; + } + + .footer_link { + flex-direction: column; + gap: 8%; + } + + footer { + padding: 15px; + } + + .footer_link a { + padding: 5px 10px; + } + + .header_text { + font-size: 14px; + } + + .bouton_connexion { + font-size: 12px; + padding: 8px 15px; + } +} \ No newline at end of file diff --git a/z2/webapp/public/styleComplexe.css b/z2/webapp/public/styleComplexe.css new file mode 100644 index 0000000..44092cc --- /dev/null +++ b/z2/webapp/public/styleComplexe.css @@ -0,0 +1,354 @@ +/* ------------- */ +/* HTML */ +/* ------------- */ + +html, +body { + font-family: "Inter", sans-serif; + background-color: #1e1e1e; + color: #eff1f3; + margin: 0; + padding: 0; + overflow: auto; + height: 100%; /* Définit la hauteur de la page à 100% */ + display: flex; /* Flexbox sur le body */ + flex-direction: column; /* Aligne le contenu du body en colonne */ +} + +/* --------------- */ +/* Header */ +/* --------------- */ + +header { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + padding: 10px 40px; +} + +header a { + text-decoration: none; + color: #eff1f3; +} + +header ul { + list-style: none; + display: flex; + align-items: center; +} + +header li { + cursor: pointer; +} + +.header_text { + display: flex; + flex-grow: 1; /* Le conteneur des liens prend toute la place disponible */ + justify-content: center; /* Centre les liens horizontalement */ + gap: 15%; /* Espace entre les liens */ + font-weight: 600; +} + +.header_text a { + padding: 0 10px; +} + +.header_text li a:hover { + color: #00a8e8; +} + +.bouton_connexion { + padding: 10px 20px; + border-radius: 900px; + font-size: 16px; /* Taille de la police */ + border-color: #00a8e8; + color: #eff1f3; + background-color: #00a8e8; + margin-top: 0; + cursor: pointer; +} + +.bouton_connexion:hover { + border-color: #eff1f3; + color: #00a8e8; + background-color: #eff1f3; /* Changer la couleur au survol */ +} + +#smart_building { + width: 13%; + margin-left: -40px; + height: auto; +} + +/* ----------------- */ +/* Complexe */ +/* ----------------- */ + +body { + background-color: #1e1e1e; + color: #eff1f3; + font-family: Arial, sans-serif; + margin: 0; + padding: 0; +} + +main.admin-container { + padding: 40px; + max-width: 1000px; + margin: auto; +} + +h1 { + text-align: center; + margin-bottom: 30px; + font-size: 36px; + color: #00a8e8; +} + +table { + width: 100%; + border-collapse: collapse; + background-color: #2c2c2c; + border-radius: 12px; + overflow: hidden; +} + +th, +td { + padding: 12px 16px; + text-align: center; + border-bottom: 1px solid #444; +} + +th { + background-color: #0077b6; + color: #fff; +} + +tr:nth-child(even) { + background-color: #353535; +} + +select, +button { + padding: 6px 10px; + border-radius: 6px; + border: none; +} + +button { + background-color: #00a8e8; + color: white; + cursor: pointer; +} + +button:hover { + background-color: #0077b6; +} + +/* --------------- */ +/* Footer */ +/* --------------- */ + +footer { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + font-size: 14px; + text-transform: uppercase; + padding: 20px; + margin-top: auto; /* Cela force le footer à se pousser vers le bas */ +} + +.copyright { + padding: 10px 20px; +} + +footer a { + text-decoration: none; + color: #eff1f3; +} + +.footer_link { + display: flex; + flex-grow: 1; + gap: 10%; +} + +.footer_link a { + padding: 0 10px; +} + +.copyright { + padding: 10px 20px; +} + +/* ------------------------- */ +/* Responsive Design */ +/* ------------------------- */ + +/* GRAND ÉCRAN (> 1440px) */ +@media (min-width: 1440px) { + main.admin-container { + padding: 60px; + max-width: 1200px; + } + + h1 { + font-size: 48px; + } + + table th, table td { + padding: 20px; + } + + .bouton_connexion { + padding: 12px 24px; + font-size: 18px; + } +} + +/* TABLETTE - Moyenne (1025px à 1440px) */ +@media (max-width: 1440px) and (min-width: 1025px) { + + table th, table td { + padding: 15px; + } + + h1 { + font-size: 40px; + } +} + +/* TABLETTE - Petite (769px à 1024px) */ +@media (max-width: 1024px) and (min-width: 769px) { + header { + flex-wrap: wrap; + justify-content: center; + padding: 20px; + gap: 20px; + } + + #smart_building { + width: 100px; + margin: 0; + } + + .header_text { + flex-direction: row; + flex-wrap: wrap; + justify-content: center; + gap: 30px; + } + + .bouton_connexion { + padding: 8px 16px; + font-size: 14px; + } + + main.admin-container { + padding: 30px; + } + + h1 { + font-size: 36px; + margin-bottom: 20px; + } + + table th, table td { + padding: 10px; + } +} + +/* MOBILE (≤ 768px) */ +@media (max-width: 768px) { + header { + flex-direction: column; + align-items: center; + gap: 15px; + } + + #smart_building { + width: 90px; + height: auto; + margin: 0 auto; + } + + .header_text { + flex-direction: column; + align-items: center; + gap: 10px; + font-size: 16px; + } + + .bouton_connexion { + padding: 10px 20px; + width: auto; + text-align: center; + } + + main.admin-container { + padding: 20px; + } + + h1 { + font-size: 28px; + margin-bottom: 20px; + } + + table th, table td { + padding: 8px; + } + + footer { + flex-direction: column; + text-align: center; + gap: 10px; + align-items: center; + } + + .footer_link { + flex-direction: column; + align-items: center; + gap: 10px; + } + + .copyright { + padding: 0; + } +} + +/* TÉLÉPHONE TRÈS PETIT (≤ 480px) */ +@media (max-width: 480px) { + main.admin-container { + padding: 10px; + } + + h1 { + font-size: 24px; + margin-bottom: 20px; + } + + .bouton_connexion { + padding: 8px 16px; + font-size: 14px; + } + + #smart_building { + width: 70px; + } + + table th, table td { + padding: 6px; + } +} + +/* ------------------------- */ +/* Transitions douces */ +/* ------------------------- */ + +* { + transition: all 0.3s ease-in-out; +} \ No newline at end of file diff --git a/z2/webapp/public/styleConnexion.css b/z2/webapp/public/styleConnexion.css new file mode 100644 index 0000000..535dc52 --- /dev/null +++ b/z2/webapp/public/styleConnexion.css @@ -0,0 +1,453 @@ +/* ------------- */ +/* HTML */ +/* ------------- */ + +html, +body { + background-color: #1e1e1e; + color: #eff1f3; + margin: 0; + padding: 0; + overflow: auto; + height: 100%; + display: flex; + flex-direction: column; +} + +/* --------------- */ +/* Header */ +/* --------------- */ + +header { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + padding: 10px 40px; +} + +header a { + text-decoration: none; + color: #eff1f3; +} + +header ul { + list-style: none; + display: flex; + align-items: center; +} + +header li { + cursor: pointer; +} + +.header_text { + display: flex; + flex-grow: 1; /* Le conteneur des liens prend toute la place disponible */ + justify-content: center; /* Centre les liens horizontalement */ + gap: 15%; /* Espace entre les liens */ + font-weight: 600; +} + +.header_text a { + padding: 0 10px; +} + +.header_text li a:hover { + color: #00a8e8; +} + +.bouton_connexion { + padding: 10px 20px; + border-radius: 900px; + font-size: 16px; /* Taille de la police */ + border-color: #00a8e8; + color: #eff1f3; + background-color: #00a8e8; + margin-top: 0; + cursor: pointer; +} + +.bouton_connexion:hover { + border-color: #eff1f3; + color: #00a8e8; + background-color: #eff1f3; /* Changer la couleur au survol */ +} + +#smart_building { + width: 13%; + margin-left: -40px; + height: auto; +} + +/* ------------------- */ +/* Connexion */ +/* ------------------- */ + +h1 { + font-size: 45px; /* Taille de la police */ + color: #eff1f3; + white-space: nowrap; +} + +#connexion { + display: flex; /* Ajouté pour aligner les enfants */ + justify-content: center; /* Centrer les éléments verticalement */ + align-items: center; /* Centrer les éléments horizontalement */ + flex: 1;; /* Prend toute la hauteur de l'écran */ +} + +.building { + width: 23%; + height: 640px; + border-radius: 33px; + padding-left: 8%; + background-image: url("/images/building.jpg"); + background-size: cover; +} + +.page-connexion { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + border-radius: 24px; + background-color: #2c2c2c; + padding: 40px 60px; /* Augmente l'espace intérieur */ + width: 100%; /* Prend toute la largeur disponible */ + max-width: 400px; /* Définit une largeur max pour éviter qu'elle ne devienne trop étroite */ + box-shadow: 0 4px 8px 0 rgba(21, 21, 21, 0.2); +} + +.form-group { + width: 100%; + position: relative; /* Position relative */ + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 20px; +} + +.form-group .icon { + position: absolute; + left: 15px; /* Ajuste la position des icônes */ + top: 50%; + transform: translateY(-50%); +} + +.form-group .icon svg { + width: 24px; + height: 24px; + fill: #191717; +} + +.form-control-connexion { + width: 100%; + padding-left: 50px; /* Remplissage à gauche de 55px */ + padding-right: 80px; + height: 48px; /* Hauteur de 40px */ + border-radius: 34px; /* Bordure arrondie */ + font-size: 16px; /* Taille de la police */ + letter-spacing: 0.5px; /* Espacement des lettres */ + background-color: #eff1f3; /* Couleur de fond */ + border: none; /* Supprimer la bordure */ +} + +.bouton-connexion { + padding: 10px 20px; + border-radius: 900px; + font-size: 16px; /* Taille de la police */ + border-color: #00a8e8; + color: #eff1f3; + background-color: #00a8e8; + margin-top: 10px; /* Centrage vertical */ + cursor: pointer; +} + +.bouton-connexion:hover { + border-color: #eff1f3; + color: #00a8e8; + background-color: #eff1f3; /* Changer la couleur au survol */ +} + +.lien { + display: inline-block; /* Permet de mieux gérer la taille du soulignement */ + text-align: center; + margin-bottom: 15px; + font-size: 20px; /* Taille de la police */ + color: #eff1f3; + text-decoration: none; /* Supprime le soulignement par défaut */ + text-underline-offset: 10px; /* Augmente l'espace entre le texte et le soulignement */ + cursor: pointer; + padding-bottom: 10px; /* Ajoute un petit espace sous le texte */ + position: relative; /* Positionner l'élément par rapport à son parent */ +} + +.lien::after { + content: ""; + display: block; + width: 150%; /* La ligne est maintenant 1.5 fois plus grande que le texte */ + height: 2px; /* Épaisseur du soulignement */ + background-color: #eff1f3; /* Couleur du soulignement */ + position: absolute; + left: 50%; /* Centre la ligne horizontalement */ + transform: translateX( + -50% + ); /* Déplace la ligne pour qu'elle soit exactement centrée */ + bottom: 0; +} + +.lien:hover { + color: #0077b6; +} + +.cellule { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + margin-left: 9%; +} + +/* --------------- */ +/* Footer */ +/* --------------- */ + +footer { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + font-size: 14px; + text-transform: uppercase; + padding: 20px; + margin-top: auto; /* Cela force le footer à se pousser vers le bas */ +} + +.copyright { + padding: 10px 20px; +} + +footer a { + text-decoration: none; + color: #eff1f3; +} + +.footer_link { + display: flex; + flex-grow: 1; + gap: 10%; +} + +.footer_link a { + padding: 0 10px; +} + +.copyright { + padding: 10px 20px; +} + +/* ------------------------- */ +/* Responsive Design */ +/* ------------------------- */ + +/* GRAND ÉCRAN (> 1440px) */ +@media (min-width: 1440px) { + h1 { + font-size: 60px; + } + + .building { + height: 700px; + } + + .form-control-connexion { + font-size: 18px; + height: 52px; + } +} + + +/* TABLETTE - Moyenne (1025px à 1440px) */ +@media (max-width: 1440px) and (min-width: 1025px) { + h1 { + font-size: 48px; + } + + .building { + height: 600px; + width: 28%; + padding-left: 5%; + } + + .page-connexion { + padding: 40px 40px; + } + + .form-control-connexion { + font-size: 16px; + } +} + + +/* TABLETTE - Petite (769px à 1024px) */ +@media (max-width: 1024px) and (min-width: 769px) { + header { + flex-wrap: wrap; + justify-content: center; + padding: 20px; + gap: 20px; + } + + #smart_building { + width: 100px; + margin: 0; + } + + .header_text { + flex-direction: row; + flex-wrap: wrap; + justify-content: center; + gap: 30px; + } + + .bouton_connexion { + padding: 8px 16px; + font-size: 14px; + } + + h1 { + font-size: 40px; + text-align: center; + } + + #connexion { + flex-direction: column; + padding: 40px 20px; + } + + .building { + width: 100%; + height: 300px; + border-radius: 20px; + padding: 0; + margin-bottom: 30px; + } + + .page-connexion { + max-width: 100%; + } + + .form-control-connexion { + font-size: 15px; + } +} + + +/* MOBILE (≤ 768px) */ +@media (max-width: 768px) { + header { + flex-direction: column; + align-items: center; + gap: 15px; + padding: 20px; + } + + .header_text { + flex-direction: column; + align-items: center; + gap: 10px; + font-size: 16px; + } + + .bouton_connexion { + width: auto; + padding: 10px 16px; + font-size: 14px; + } + + #smart_building { + width: 80px; + margin-left: 0; + } + + h1 { + font-size: 32px; + text-align: center; + } + + #connexion { + flex-direction: column; + padding: 20px; + } + + .building { + width: 100%; + height: 250px; + padding: 0; + margin-bottom: 20px; + } + + .page-connexion { + padding: 30px 20px; + max-width: 100%; + } + + .form-control-connexion { + font-size: 14px; + height: 44px; + } + + footer { + flex-direction: column; + gap: 10px; + text-align: center; + align-items: center; + } + + .footer_link { + flex-direction: column; + gap: 10px; + } + + .copyright { + padding: 0; + } +} + + +/* TÉLÉPHONE TRÈS PETIT (≤ 480px) */ +@media (max-width: 480px) { + h1 { + font-size: 26px; + } + + .form-control-connexion { + font-size: 13px; + padding-left: 40px; + height: 40px; + } + + .form-group .icon svg { + width: 20px; + height: 20px; + } + + .page-connexion { + padding: 20px 10px; + } + + .bouton-connexion { + font-size: 14px; + padding: 8px 14px; + } + + .lien { + font-size: 16px; + } + + #smart_building { + width: 60px; + } +} \ No newline at end of file diff --git a/z2/webapp/public/styleContact.css b/z2/webapp/public/styleContact.css new file mode 100644 index 0000000..28e39ad --- /dev/null +++ b/z2/webapp/public/styleContact.css @@ -0,0 +1,240 @@ + +/* --------------- */ +/* Header */ +/* --------------- */ + +header { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + padding: 10px 40px; + } + + header a { + text-decoration: none; + color: #eff1f3; + } + + header ul { + list-style: none; + display: flex; + align-items: center; + } + + header li { + cursor: pointer; + } + + .header_text { + display: flex; + flex-grow: 1; /* Le conteneur des liens prend toute la place disponible */ + justify-content: center; /* Centre les liens horizontalement */ + gap: 15%; /* Espace entre les liens */ + font-weight: 600; + } + + .header_text a { + padding: 0 10px; + } + + .bouton_connexion { + padding: 10px 20px; + border-radius: 900px; + font-size: 16px; /* Taille de la police */ + border-color: #00a8e8; + color: #eff1f3; + background-color: #00a8e8; + margin-top: 0; + cursor: pointer; + } + + .bouton_connexion:hover { + border-color: #eff1f3; + color: #00a8e8; + background-color: #eff1f3; /* Changer la couleur au survol */ + } + + #smart_building { + width: 13%; + margin-left: -40px; + height: auto; + } + + +body { + margin: 0; + font-family: 'Segoe UI', sans-serif; + background-color: #121212; + color: #f1f1f1; + } + + .contact-container { + max-width: 600px; + margin: 60px auto; + padding: 20px; + background-color: #1e1e1e; + border-radius: 10px; + box-shadow: 0 0 20px rgba(0,0,0,0.4); + } + + .contact-container h1 { + text-align: center; + margin-bottom: 10px; + font-size: 2em; + } + + .contact-container p { + text-align: center; + margin-bottom: 30px; + color: #cccccc; + } + + .contact-form { + display: flex; + flex-direction: column; + } + + .contact-form label { + margin-bottom: 5px; + font-weight: bold; + color: #ffffff; + } + + .contact-form input, + .contact-form textarea { + padding: 10px; + margin-bottom: 20px; + background-color: #2a2a2a; + color: #f1f1f1; + border: 1px solid #444; + border-radius: 5px; + } + + .contact-form input:focus, + .contact-form textarea:focus { + outline: none; + border: 1px solid #00bcd4; + } + + .contact-form button { + background-color: #00bcd4; + color: #121212; + padding: 12px; + border: none; + border-radius: 5px; + font-weight: bold; + cursor: pointer; + transition: 0.3s; + } + + .contact-form button:hover { + background-color: #0097a7; + } + + + /* ---------------- */ +/* FOOTER */ +/* ---------------- */ + +footer { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + font-size: 14px; + text-transform: uppercase; + padding: 20px; + margin-top: auto; /* Cela force le footer à se pousser vers le bas */ + } + + .copyright { + padding: 10px 20px; + } + + footer a { + text-decoration: none; + color: #eff1f3; + } + + .footer_link { + display: flex; + flex-grow: 1; + gap: 10%; + } + + .footer_link a { + padding: 0 10px; + } + + .copyright { + padding: 10px 20px; + } + + + @media (max-width: 768px) { + /* HEADER */ + header { + flex-direction: column; + align-items: flex-start; + padding: 15px 20px; + } + + .header_text { + flex-direction: column; + align-items: center; + gap: 10px; + width: 100%; + margin-top: 10px; + } + + .bouton_connexion { + width: 100%; + text-align: center; + margin-top: 10px; + } + + #smart_building { + width: 40%; + margin-left: 0; + align-self: center; + } + + /* CONTACT */ + .contact-container { + width: 90%; + margin: 40px auto; + padding: 15px; + } + + .contact-container h1 { + font-size: 1.5em; + } + + .contact-form input, + .contact-form textarea { + font-size: 1rem; + } + + .contact-form button { + font-size: 1rem; + } + + /* FOOTER */ + footer { + flex-direction: column; + align-items: center; + text-align: center; + gap: 10px; + } + + .footer_link { + flex-direction: column; + gap: 8px; + } + + .footer_link a { + padding: 0; + } + } + \ No newline at end of file diff --git a/z2/webapp/public/styleInscription.css b/z2/webapp/public/styleInscription.css new file mode 100644 index 0000000..07bfff0 --- /dev/null +++ b/z2/webapp/public/styleInscription.css @@ -0,0 +1,484 @@ +/* ------------- */ +/* HTML */ +/* ------------- */ + +html, +body { + background-color: #1e1e1e; + color: #eff1f3; + margin: 0; + padding: 0; + overflow: auto; + height: 100%; + display: flex; + flex-direction: column; +} + +/* --------------- */ +/* Header */ +/* --------------- */ + +header { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + padding: 10px 40px; +} + +header a { + text-decoration: none; + color: #eff1f3; +} + +header ul { + list-style: none; + display: flex; + align-items: center; +} + +header li { + cursor: pointer; +} + +.header_text { + display: flex; + flex-grow: 1; /* Le conteneur des liens prend toute la place disponible */ + justify-content: center; /* Centre les liens horizontalement */ + gap: 15%; /* Espace entre les liens */ + font-weight: 600; +} + +.header_text a { + padding: 0 10px; +} + +.header_text li a:hover { + color: #00a8e8; +} + +.bouton_connexion { + padding: 10px 20px; + border-radius: 900px; + font-size: 16px; /* Taille de la police */ + border-color: #00a8e8; + color: #eff1f3; + background-color: #00a8e8; + margin-top: 0; + cursor: pointer; +} + +.bouton_connexion:hover { + border-color: #eff1f3; + color: #00a8e8; + background-color: #eff1f3; /* Changer la couleur au survol */ +} + +#smart_building { + width: 13%; + margin-left: -40px; + height: auto; +} + +/* --------------------- */ +/* Inscription */ +/* --------------------- */ + +h1 { + font-size: 45px; /* Taille de la police */ + color: #eff1f3; + white-space: nowrap; +} + +label { + font-weight: bold; + font-size: 20px; + color: #eff1f3; + white-space: nowrap; +} + +select { + appearance: none; /* Supprime le style natif */ + background: white; + cursor: pointer; +} + +#inscription { + display: flex; /* Ajouté pour aligner les enfants */ + justify-content: center; /* Centrer les éléments verticalement */ + align-items: center; /* Centrer les éléments horizontalement */ + flex: 1; /* Prend toute la hauteur de l'écran */ + margin-bottom: 50px; +} + +.tour { + width: 23%; + height: 640px; + border-radius: 33px; + padding-left: 8%; + background-image: url("/images/tour.jpg"); + background-size: cover; +} + +.page-inscription { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + border-radius: 24px; + background-color: #2c2c2c; + padding: 40px 100px; /* Augmente l'espace intérieur */ + width: 100%; /* Prend toute la largeur disponible */ + max-width: 400px; /* Définit une largeur max pour éviter qu'elle ne devienne trop étroite */ + box-shadow: 0 4px 8px 0 rgba(21, 21, 21, 0.2); +} + +.form-group { + width: 100%; + position: relative; /* Position relative */ + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 20px; +} + +.form-group .icon { + position: absolute; + left: 15px; /* Ajuste la position des icônes */ + top: 50%; + transform: translateY(-50%); +} + +.form-group .icon svg { + width: 24px; + height: 24px; + fill: #191717; +} + +.form-control-inscription { + width: 100%; + padding-left: 50px; /* Remplissage à gauche de 50px */ + padding-right: 40px; + height: 48px; /* Hauteur de 48px */ + border-radius: 34px; /* Bordure arrondie */ + font-size: 16px; /* Taille de la police */ + letter-spacing: 0.5px; /* Espacement des lettres */ + background-color: #eff1f3; /* Couleur de fond */ + border: none; /* Supprimer la bordure */ +} + +.form-row { + display: flex; + align-items: center; + gap: 10px; /* espace entre label et champ */ + flex-wrap: nowrap; /* évite le retour à la ligne */ +} + +.bouton-inscription { + padding: 10px 20px; + border-radius: 900px; + font-size: 16px; /* Taille de la police */ + border-color: #00a8e8; + color: #eff1f3; + background-color: #00a8e8; + margin-top: 0; /* Centrage vertical */ + cursor: pointer; +} + +.bouton-inscription:hover { + border-color: #eff1f3; + color: #00a8e8; + background-color: #eff1f3; /* Changer la couleur au survol */ +} + +.boutons { + display: flex; + justify-content: center; + align-items: center; + gap: 20px; + margin-top: 20px; +} + +/* Alignement parfait du bouton "Photo" */ +.custom-file-upload { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 8px; + background-color: #004fa9; + color: #eff1f3; + padding: 10px 20px; + border-radius: 900px; + cursor: pointer; + font-size: 16px; + line-height: 1; +} + +.custom-file-upload:hover { + background-color: #eff1f3; + color: #004fa9; + border: 1px solid #004fa9; +} + +/* Alignement du SVG */ +.custom-file-upload svg { + width: 20px; + height: 20px; + vertical-align: middle; + padding-right: 4px; +} + +.case { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + margin-right: 9%; +} + +.lien { + background: none; + border: none; + font-family: inherit; /* pour garder la cohérence du texte */ + display: inline-block; /* Permet de mieux gérer la taille du soulignement */ + text-align: center; + margin-bottom: 15px; + font-size: 23px; /* Taille de la police */ + color: #eff1f3; + text-decoration: none; /* Supprime le soulignement par défaut */ + text-underline-offset: 10px; /* Augmente l'espace entre le texte et le soulignement */ + cursor: pointer; + padding-bottom: 10px; /* Ajoute un petit espace sous le texte */ + position: relative; /* Positionner l'élément par rapport à son parent */ +} + +.lien::after { + content: ""; + display: block; + width: 120%; /* La ligne est maintenant 1.5 fois plus grande que le texte */ + height: 2px; /* Épaisseur du soulignement */ + background-color: #eff1f3; /* Couleur du soulignement */ + position: absolute; + left: 50%; /* Centre la ligne horizontalement */ + transform: translateX( + -50% + ); /* Déplace la ligne pour qu'elle soit exactement centrée */ + bottom: 0; +} + +.lien:hover { + color: #0077b6; +} + +/* --------------- */ +/* Footer */ +/* --------------- */ + +footer { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + font-size: 14px; + text-transform: uppercase; + padding: 20px; + margin-top: auto; /* Cela force le footer à se pousser vers le bas */ +} + +.copyright { + padding: 10px 20px; +} + +footer a { + text-decoration: none; + color: #eff1f3; +} + +.footer_link { + display: flex; + flex-grow: 1; + gap: 10%; +} + +.footer_link a { + padding: 0 10px; +} + +.copyright { + padding: 10px 20px; +} + +/* ---------------------- */ +/* Media Queries */ +/* ---------------------- */ + +/* GRAND ÉCRAN (> 1440px) */ +@media (min-width: 1440px) { + h1 { + font-size: 48px; + } + + .tour { + height: 700px; + } + + .form-control-inscription { + font-size: 18px; + height: 52px; + } +} + +/* TABLETTE - Moyenne (1025px à 1440px) */ +@media (max-width: 1440px) and (min-width: 1025px) { + h1 { + font-size: 48px; + } + + .tour { + height: 600px; + } + + .page-inscription { + padding: 40px; + } + + .form-control-inscription { + font-size: 16px; + } +} + +/* TABLETTE - Petite (769px à 1024px) */ +@media (max-width: 1024px) and (min-width: 769px) { + header { + flex-wrap: wrap; + justify-content: center; + padding: 20px; + gap: 20px; + } + + #smart_building { + width: 100px; + margin: 0; + } + + .header_text { + flex-direction: row; + flex-wrap: wrap; + justify-content: center; + gap: 30px; + } + + .bouton_connexion { + padding: 8px 16px; + font-size: 14px; + } + + h1 { + font-size: 40px; + text-align: center; + } + + .tour { + width: 100%; + height: 300px; + } + + .form-control-inscription { + font-size: 15px; + } +} + +/* MOBILE (≤ 768px) */ +@media (max-width: 768px) { + header { + flex-direction: column; + align-items: center; + gap: 15px; + padding: 20px; + } + + .header_text { + flex-direction: column; + align-items: center; + gap: 10px; + font-size: 16px; + } + + .bouton_connexion { + width: auto; + padding: 10px 16px; + font-size: 14px; + } + + #smart_building { + width: 80px; + margin-left: 0; + } + + h1 { + font-size: 32px; + text-align: center; + } + + .tour { + height: 250px; + margin-bottom: 20px; + } + + .form-control-inscription { + font-size: 14px; + height: 44px; + } + + footer { + flex-direction: column; + gap: 10px; + text-align: center; + align-items: center; + } + + .footer_link { + flex-direction: column; + gap: 10px; + } + + .copyright { + padding: 0; + } +} + +/* TÉLÉPHONE TRÈS PETIT (≤ 480px) */ +@media (max-width: 480px) { + h1 { + font-size: 26px; + } + + .form-control-inscription { + font-size: 13px; + padding-left: 40px; + height: 40px; + } + + .form-group .icon svg { + width: 20px; + height: 20px; + } + + .page-inscription { + padding: 20px 10px; + } + + .bouton-inscription { + font-size: 14px; + padding: 8px 14px; + } + + .lien { + font-size: 16px; + } + + .tour { + width: 60px; + } +} \ No newline at end of file diff --git a/z2/webapp/public/styleMembres.css b/z2/webapp/public/styleMembres.css new file mode 100644 index 0000000..35c04aa --- /dev/null +++ b/z2/webapp/public/styleMembres.css @@ -0,0 +1,576 @@ +/* ------------- */ +/* HTML */ +/* ------------- */ + +html, +body { + font-family: "Inter", sans-serif; + background-color: #1e1e1e; + color: #eff1f3; + margin: 0; + padding: 0; + overflow: auto; + height: 100%; /* Définit la hauteur de la page à 100% */ + display: flex; /* Flexbox sur le body */ + flex-direction: column; /* Aligne le contenu du body en colonne */ +} + +/* --------------- */ +/* Header */ +/* --------------- */ + +header { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + padding: 10px 40px; +} + +header a { + text-decoration: none; + color: #eff1f3; +} + +header ul { + list-style: none; + display: flex; + align-items: center; +} + +header li { + cursor: pointer; +} + +.header_text { + display: flex; + flex-grow: 1; /* Le conteneur des liens prend toute la place disponible */ + justify-content: center; /* Centre les liens horizontalement */ + gap: 15%; /* Espace entre les liens */ + font-weight: 600; +} + +.header_text a { + padding: 0 10px; +} + +.header_text li a:hover { + color: #00a8e8; +} + +.bouton_connexion { + padding: 10px 20px; + border-radius: 900px; + font-size: 16px; /* Taille de la police */ + border-color: #00a8e8; + color: #eff1f3; + background-color: #00a8e8; + margin-top: 0; + cursor: pointer; +} + +.bouton_connexion:hover { + border-color: #eff1f3; + color: #00a8e8; + background-color: #eff1f3; /* Changer la couleur au survol */ +} + +#smart_building { + width: 13%; + margin-left: -40px; + height: auto; +} + +/* ---------------- */ +/* Membres */ +/* ---------------- */ + +main { + display: flex; + flex-grow: 1; /* Permet à la section principale d'occuper l'espace restant */ + justify-content: center; /* Centre verticalement le contenu */ + align-items: center; /* Centre horizontalement le contenu */ + padding-bottom: 50px; + gap: 30px; /* Espace entre les 2 sections */ +} + +.left-panel { + flex: 3; /* 30% de la largeur totale */ + display: flex; + justify-content: center; /* Centrer l'image horizontalement */ + align-items: center; /* Centrer l'image verticalement */ +} + +.left-panel img { + width: 100%; /* L'image prend toute la largeur de son conteneur */ + max-width: 450px; /* Maximum de 300px de largeur */ + height: auto; /* La hauteur de l'image s'ajuste automatiquement */ + border-radius: 32px; +} + +.right-panel { + flex: 7; /* 70% de la largeur totale */ +} + +.right-panel h1 { + font-size: 60px; + margin-bottom: 60px; + padding-left: 50px; /* Ajoute un espacement à gauche du titre */ +} + +#membres-container { + display: grid; + grid-template-columns: repeat(2, 1fr); /* 2 cartes par ligne */ + gap: 30px; + justify-items: center; + padding: 20px 60px; +} + +.membre { + width: 100%; /* occupe toute la colonne */ + max-width: 400px; /* limite visuelle de largeur */ + padding: 20px; + background-color: #2c2c2c; + color: #eff1f3; + border-radius: 20px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + transition: transform 0.2s ease, background-color 0.3s ease; + text-align: center; +} + +.membre:hover { + background-color: #dfe3e6; + transform: translateY(-5px); + color: #2c2c2c; + cursor: pointer; +} + +.membre h3 { + font-size: 22px; + margin: 10px 0 5px; +} + +.membre h4 { + font-size: 18px; + margin: 5px 0; + font-weight: 500; +} + +.membre p { + margin: 6px 0; + font-size: 16px; +} + +/* --------------- */ +/* Footer */ +/* --------------- */ + +footer { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + font-size: 14px; + text-transform: uppercase; + padding: 20px; + margin-top: auto; /* Cela force le footer à se pousser vers le bas */ +} + +.copyright { + padding: 10px 20px; +} + +footer a { + text-decoration: none; + color: #eff1f3; +} + +.footer_link { + display: flex; + flex-grow: 1; + gap: 10%; +} + +.footer_link a { + padding: 0 10px; +} + +.copyright { + padding: 10px 20px; +} + +/* -------------------- */ +/* Barre Recherche */ +/* -------------------- */ + +.form { + width: 300px; + margin: 0 50px; + font-size: 0.9rem; + display: flex; + gap: 0.5rem; + align-items: center; + position: relative; + isolation: isolate; + --input-text-color: #363636; + --input-bg-color: #eff1f3; + --focus-input-bg-color: transparent; + --text-color: #2a2a2a; + --active-color: #eff1f3; + --width-of-input: 250px; + --inline-padding-of-input: 1.2em; + --gap: 0.9rem; +} + +.fancy-bg { + position: absolute; + width: 100%; + inset: 0; + background: var(--input-bg-color); + border-radius: 30px; + height: 100%; + z-index: -1; + pointer-events: none; + box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 4px; +} + +label { + padding: 0.2em; + height: 40px; + padding-inline: var(--inline-padding-of-input); + display: flex; + align-items: center; +} + +.search, +.close-btn { + position: absolute; +} + +.search { + fill: var(--text-color); + left: var(--inline-padding-of-input); +} + +svg { + width: 17px; + display: block; +} + +.input { + color: var(--input-text-color); + width: 100%; + background: none; + border: none; + font-size: 1rem; + margin-inline: min(2em, calc(var(--inline-padding-of-input) + var(--gap))); +} + +.input:focus { + outline: none; + color: #eff1f3; +} + +.input:focus ~ .fancy-bg { + border: 1px solid var(--active-color); + background: var(--focus-input-bg-color); +} + +.input:focus ~ .search { + fill: var(--active-color); +} + +.input:valid ~ .close-btn { + visibility: visible; + color: #eff1f3; +} + +.close-btn { + right: var(--inline-padding-of-input); + background: none; + border: none; + box-shadow: none; + width: auto; + height: auto; + padding: 0; + opacity: 1; + color: #eff1f3; + visibility: hidden; + font-size: 20px; + cursor: pointer; +} + +.close-btn:hover, +.close-btn:focus { + background: none; + font-weight: bold; + transform: scale(1.2); +} + +.input:not(:focus):not(:placeholder-shown) { + color: #2a2a2a; +} + +.input:not(:focus):not(:placeholder-shown) ~ .search { + fill: #2a2a2a; +} + +.input:not(:focus):not(:placeholder-shown) ~ .close-btn { + color: #2a2a2a; +} + +.header-membres { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0 70px; + margin-bottom: 60px; + flex-wrap: wrap; +} + +.header-membres h1 { + font-size: 60px; + margin: 0; + color: #eff1f3; +} + +/* ----------------------------- */ +/* Responsive Design */ +/* ----------------------------- */ + +/* GRAND ÉCRAN (> 1440px) */ +@media (min-width: 1440px) { + .right-panel h1, + .header-membres h1 { + font-size: 72px; + } + + .left-panel img { + max-width: 500px; + } +} + + +/* TABLETTE LARGE (1025px à 1440px) */ +@media (max-width: 1440px) and (min-width: 1025px) { + .right-panel h1, + .header-membres h1 { + font-size: 56px; + padding-left: 80px; + } + + .header-membres { + padding: 0 50px; + } + + #membres-container { + padding: 20px 40px; + } + + .left-panel img { + max-width: 400px; + } +} + + +/* TABLETTE MOYENNE (769px à 1024px) */ +@media (max-width: 1024px) and (min-width: 769px) { + header { + flex-wrap: wrap; + justify-content: center; + padding: 20px; + gap: 20px; + } + + #smart_building { + width: 100px; + margin: 0; + } + + .header_text { + flex-direction: row; + flex-wrap: wrap; + justify-content: center; + gap: 30px; + } + + .bouton_connexion { + padding: 8px 16px; + font-size: 14px; + } + + main { + flex-direction: column; + padding: 40px; + } + + .right-panel, + .left-panel { + width: 100%; + flex: none; + } + + .features { + grid-template-columns: repeat(2, 1fr); + } + + .right-panel h1, + .header-membres h1 { + font-size: 42px; + padding-left: 0; + text-align: center; + } + + .header-membres { + padding: 0 30px; + justify-content: center; + gap: 30px; + } + + .form { + margin: 0 auto; + } + + #membres-container { + grid-template-columns: repeat(2, 1fr); + padding: 20px; + } +} + + +/* MOBILE (≤ 768px) */ +@media (max-width: 768px) { + header { + flex-direction: column; + align-items: center; + gap: 15px; + } + + #smart_building { + width: 90px; + height: auto; + margin: 0 auto; + } + + .header_text { + flex-direction: column; + align-items: center; + gap: 10px; + font-size: 16px; + } + + .bouton_connexion { + padding: 10px 20px; + width: auto; + text-align: center; + } + + main { + flex-direction: column; + padding: 20px; + } + + .left-panel, + .right-panel { + width: 100%; + flex: unset; + } + + .right-panel h1, + .header-membres h1 { + font-size: 36px; + padding-left: 0; + text-align: center; + margin-bottom: 30px; + } + + .header-membres { + flex-direction: column; + align-items: center; + gap: 20px; + padding: 0 20px; + } + + .form { + width: 100%; + max-width: 300px; + } + + #membres-container { + grid-template-columns: 1fr; + padding: 10px; + } + + .membre { + max-width: 100%; + } + + footer { + flex-direction: column; + text-align: center; + gap: 10px; + align-items: center; + } + + .footer_link { + flex-direction: column; + gap: 10px; + } + + .copyright { + padding: 0; + } +} + + +/* MOBILE TRÈS PETIT (≤ 480px) */ +@media (max-width: 480px) { + .right-panel h1, + .header-membres h1 { + font-size: 28px; + margin-bottom: 20px; + } + + .form { + width: 90%; + max-width: 280px; + } + + .input { + font-size: 0.9rem; + } + + .close-btn { + font-size: 16px; + } + + #membres-container { + gap: 20px; + } + + .membre { + padding: 16px; + font-size: 14px; + } + + .membre h3 { + font-size: 20px; + } + + .membre h4 { + font-size: 16px; + } + + .membre p { + font-size: 14px; + } +} + + +/* ------------------------ */ +/* Transitions Générales */ +/* ------------------------ */ + +* { + transition: all 0.3s ease-in-out; +} \ No newline at end of file diff --git a/z2/webapp/public/styleObjets.css b/z2/webapp/public/styleObjets.css new file mode 100644 index 0000000..3527f87 --- /dev/null +++ b/z2/webapp/public/styleObjets.css @@ -0,0 +1,472 @@ +/* ------------- */ +/* HTML */ +/* ------------- */ + +html, +body { + font-family: "Inter", sans-serif; + background-color: #1e1e1e; + color: #eff1f3; + margin: 0; + padding: 0; + overflow: auto; + height: 100%; /* Définit la hauteur de la page à 100% */ + display: flex; /* Flexbox sur le body */ + flex-direction: column; /* Aligne le contenu du body en colonne */ +} + +/* --------------- */ +/* Header */ +/* --------------- */ + +header { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + padding: 10px 40px; +} + +header a { + text-decoration: none; + color: #eff1f3; +} + +header ul { + list-style: none; + display: flex; + align-items: center; +} + +header li { + cursor: pointer; +} + +.header_text { + display: flex; + flex-grow: 1; /* Le conteneur des liens prend toute la place disponible */ + justify-content: center; /* Centre les liens horizontalement */ + gap: 15%; /* Espace entre les liens */ + font-weight: 600; +} + +.header_text a { + padding: 0 10px; +} + +.bouton_connexion { + padding: 10px 20px; + border-radius: 900px; + font-size: 16px; /* Taille de la police */ + border-color: #00a8e8; + color: #eff1f3; + background-color: #00a8e8; + margin-top: 0; + cursor: pointer; +} + +.bouton_connexion:hover { + border-color: #eff1f3; + color: #00a8e8; + background-color: #eff1f3; /* Changer la couleur au survol */ +} + +#smart_building { + width: 13%; + margin-left: -40px; + height: auto; +} + +/* --------------- */ +/* Objets */ +/* --------------- */ + +main { + display: flex; + flex-grow: 1; /* Permet à la section principale d'occuper l'espace restant */ + justify-content: center; /* Centre verticalement le contenu */ + align-items: center; /* Centre horizontalement le contenu */ + padding-bottom: 50px; + gap: 30px; /* Espace entre les 2 sections */ +} + +.left-panel { + flex: 3; /* 30% de la largeur totale */ + display: flex; + justify-content: center; /* Centrer l'image horizontalement */ + align-items: center; /* Centrer l'image verticalement */ +} + +.left-panel img { + width: 100%; /* L'image prend toute la largeur de son conteneur */ + max-width: 450px; /* Maximum de 300px de largeur */ + height: auto; /* La hauteur de l'image s'ajuste automatiquement */ + border-radius: 32px; +} + +.right-panel { + flex: 7; /* 70% de la largeur totale */ +} + +.right-panel h1 { + font-size: 60px; + margin-bottom: 60px; +} + +.header-objets { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0 70px; + margin-bottom: 60px; + flex-wrap: wrap; +} + +.header-objets h1 { + font-size: 60px; + margin: 0; + color: #eff1f3; +} + +#objets-container { + display: grid; + grid-template-columns: repeat(3, 1fr); /* 3 cartes par ligne */ + gap: 20px; + justify-items: center; + padding: 20px 60px; +} + +.objet { + width: 100%; + max-width: 300px; /* Limite la largeur des cartes pour que ça soit plus joli */ + padding: 20px; + background-color: #2c2c2c; + color: #eff1f3; + border-radius: 10px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + transition: transform 0.2s ease, background-color 0.3s ease; + text-align: center; +} + +.objet:hover { + transform: translateY(-5px); + background-color: #dfe3e6; + color: #2c2c2c; + cursor: pointer; +} + +.objet h3 { + font-size: 22px; + margin: 10px 0 5px; +} + +.etat-point { + width: 12px; + height: 12px; + border-radius: 50%; + position: absolute; + bottom: 5px; + right: 5px; +} + +.etat-point.actif { + background-color: rgb(100, 236, 100); +} + +.etat-point.inactif { + background-color: rgb(240, 84, 84); +} + +.setting-btn { + background: none; + border: none; + text-align: center; + cursor: pointer; +} + +.setting-btn i { + font-size: 20px; + color: #a8a3a3; +} + +.setting-btn:hover i { + transform: scale(1.4); + transition: transform 0.2s ease; +} + +.objet:hover i { + color: #2c2c2c; +} + + +.info_objet{ + display: flex; + align-items: center; + justify-content: space-between; +} + +.info_objet h2{ + margin-bottom: 100px; +} + +.info_objet h4{ + margin:0px; +} + +.actions{ + display: flex; + align-items: center; + justify-content: space-between; +} + +.edit-button { + background: none; + border: none; + padding: 0; + margin: 0; + cursor: pointer; + display: flex; + align-items: center; +} + +.edit-button i { + font-size: 14px; + color: #eff1f3; + transition: transform 0.2s ease; + transform: translateY(-2px); +} + +.edit-button:hover i { + transform: scale(1.3) translateY(-2px); +} + +.inline-edit { + display: flex; + align-items: center; + gap: 10px; +} + +.inline-edit h2, +.inline-edit p { + margin: 0; +} + +[contenteditable="true"] { + border: 1px solid #eff1f3; /* Bordure fine autour de l'élément modifiable */ + border-radius: 4px; /* Bords arrondis pour un style plus doux */ + padding: 3px 5px; /* Un peu de padding pour l'esthétique */ + box-shadow: 0 0 5px rgba(0, 0, 0, 0.1); /* Ombre légère autour de l'élément */ +} + +/* Style modal */ +.modal { + display: none; + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background: rgba(60, 60, 60, 0.98); /* Plus clair que le fond de base */ + backdrop-filter: blur(12px); /* Plus de flou = plus de séparation */ + padding: 30px; + border-radius: 20px; + box-shadow: 0 12px 40px rgba(0, 0, 0, 0.8); /* Ombre plus forte */ + color: #eff1f3; + max-width: 450px; + width: 90%; + z-index: 1000; + animation: fadeIn 0.3s ease; + border: 1px solid rgba(255, 255, 255, 0.1); /* Léger contour clair */ +} + +.modal-content { + display: flex; + flex-direction: column; + gap: 20px; +} + +.modal h2 { + font-size: 24px; + margin-bottom: 10px; + color: #eff1f3; +} + +.modal p { + font-size: 16px; + line-height: 1.4; +} + +.modal .close { + position: absolute; + top: 10px; + right: 15px; + font-size: 22px; + font-weight: bold; + color: #eff1f3; + cursor: pointer; + transition: transform 0.2s ease, color 0.2s ease; +} + +.modal .close:hover { + color: #ff4d4d; + transform: scale(1.2); +} + +/* -------------------- */ +/* Barre Recherche */ +/* -------------------- */ + +.form { + width: 300px; + margin: 0; + font-size: 0.9rem; + display: flex; + gap: 0.5rem; + align-items: center; + position: relative; + isolation: isolate; + --input-text-color: #363636; + --input-bg-color: #eff1f3; + --focus-input-bg-color: transparent; + --text-color: #2a2a2a; + --active-color: #eff1f3; + --width-of-input: 250px; + --inline-padding-of-input: 1.2em; + --gap: 0.9rem; +} + +.fancy-bg { + position: absolute; + width: 100%; + inset: 0; + background: var(--input-bg-color); + border-radius: 30px; + height: 100%; + z-index: -1; + pointer-events: none; + box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 4px; +} + +label { + padding: 0.2em; + height: 40px; + padding-inline: var(--inline-padding-of-input); + display: flex; + align-items: center; +} + +.search, +.close-btn { + position: absolute; +} + +.search { + fill: var(--text-color); + left: var(--inline-padding-of-input); +} + +svg { + width: 17px; + display: block; +} + +.input { + color: var(--input-text-color); /* La couleur du texte par défaut */ + width: 100%; + margin-inline: min(2em, calc(var(--inline-padding-of-input) + var(--gap))); + background: none; + border: none; +} + +.input:focus { + outline: none; + color: #eff1f3; +} + +/* input background change in focus */ +.input:focus ~ .fancy-bg { + border: 1px solid var(--active-color); + background: var(--focus-input-bg-color); +} + +/* search icon color change in focus */ +.input:focus ~ .search { + fill: var(--active-color); +} + +/* showing close button when typing */ +.input:valid ~ .close-btn { + visibility: visible; /* Si l'input est valide, rendre le bouton visible */ + color: #eff1f3; /* Garde l'icône en blanc */ +} + +.close-btn { + right: var(--inline-padding-of-input); + background: none; + border: none; + box-shadow: none; + width: auto; + height: auto; + padding: 0; + opacity: 1; + color: #eff1f3; + visibility: hidden; + font-size: 20px; + cursor: pointer; +} + +.close-btn:hover, +.close-btn:focus { + background: none; + font-weight: bold; + transform: scale(1.2); /* Légèrement agrandit l'icône pour la rendre plus évidente */ +} + +.input:not(:focus):not(:placeholder-shown) { + color: #2a2a2a; /* Couleur texte sombre si champ rempli mais pas focus */ +} + +.input:not(:focus):not(:placeholder-shown) ~ .search { + fill: #2a2a2a; /* Loupe sombre quand input rempli mais pas focus */ +} + +.input:not(:focus):not(:placeholder-shown) ~ .close-btn { + color: #2a2a2a; /* Croix sombre également */ +} + +/* --------------- */ +/* Footer */ +/* --------------- */ + +footer { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + font-size: 14px; + text-transform: uppercase; + padding: 20px; + margin-top: auto; /* Cela force le footer à se pousser vers le bas */ +} + +.copyright { + padding: 10px 20px; +} + +footer a { + text-decoration: none; + color: #eff1f3; +} + +.footer_link { + display: flex; + flex-grow: 1; + gap: 10%; +} + +.footer_link a { + padding: 0 10px; +} + +.copyright { + padding: 10px 20px; +} \ No newline at end of file diff --git a/z2/webapp/public/styleProfil.css b/z2/webapp/public/styleProfil.css new file mode 100644 index 0000000..5e20af0 --- /dev/null +++ b/z2/webapp/public/styleProfil.css @@ -0,0 +1,378 @@ +/* ------------- */ +/* HTML */ +/* ------------- */ + +html, +body { + font-family: "Inter", sans-serif; + background-color: #1e1e1e; + color: #eff1f3; + margin: 0; + padding: 0; + overflow: auto; + height: 100%; /* Définit la hauteur de la page à 100% */ + display: flex; /* Flexbox sur le body */ + flex-direction: column; /* Aligne le contenu du body en colonne */ +} + +/* --------------- */ +/* Header */ +/* --------------- */ + +header { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + padding: 10px 40px; +} + +header a { + text-decoration: none; + color: #eff1f3; +} + +header ul { + list-style: none; + display: flex; + align-items: center; +} + +header li { + cursor: pointer; +} + +.header_text { + display: flex; + flex-grow: 1; /* Le conteneur des liens prend toute la place disponible */ + justify-content: center; /* Centre les liens horizontalement */ + gap: 15%; /* Espace entre les liens */ + font-weight: 600; +} + +.header_text a { + padding: 0 10px; +} + +.header_text li a:hover { + color: #00a8e8; +} + +.bouton_connexion { + padding: 10px 20px; + border-radius: 900px; + font-size: 16px; /* Taille de la police */ + border-color: #00a8e8; + color: #eff1f3; + background-color: #00a8e8; + margin-top: 0; + cursor: pointer; +} + +.bouton_connexion:hover { + border-color: #eff1f3; + color: #00a8e8; + background-color: #eff1f3; /* Changer la couleur au survol */ +} + +#smart_building { + width: 13%; + margin-left: -40px; + height: auto; +} + +/* --------------- */ +/* Profil */ +/* --------------- */ + +/* Conteneur Profil */ +.container { + width: 80%; /* Adaptation à la nouvelle structure, largeur plus grande */ + max-width: 800px; /* Limite la taille maximale à 800px */ + margin: 50px auto; /* Centre le conteneur et ajoute un espacement */ + background-color: #2c2c2c; + padding: 30px 40px; + border-radius: 15px; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.6); + display: flex; + flex-direction: column; /* Assure un alignement vertical des éléments */ + gap: 20px; /* Espacement entre les éléments internes */ + z-index: 1; /* Pour s'assurer que ce contenu reste bien en-dessous de l'en-tête */ +} + +/* Titre */ +h1 { + text-align: center; + margin-bottom: 30px; + color: #ffffff; +} + +/* Formulaire Profil */ +form div { + margin-bottom: 20px; +} + +label { + display: block; + margin-bottom: 8px; + font-weight: bold; + color: #ccc; +} + +/* Champs de saisie */ +input[type="text"], +input[type="password"], +input[type="file"], +select { + width: 100%; + padding: 10px 12px; + font-size: 14px; + background-color: #3a3a3a; + color: #ffffff; + border: 1px solid #555; + border-radius: 8px; + box-sizing: border-box; + transition: border-color 0.3s; +} + +/* Champs désactivés */ +input:disabled, +select:disabled { + background-color: #2c2c2c; + color: #888; + border: 1px solid #444; + cursor: not-allowed; +} + +/* Image de profil */ +.container img { + margin-top: 10px; + border-radius: 10px; + border: 2px solid #444; + max-width: 100px; + height: auto; +} + +/* Boutons (Editer, Sauvegarder) */ +button { + padding: 10px 16px; + border: none; + border-radius: 8px; + font-size: 15px; + cursor: pointer; + transition: background-color 0.3s ease; + color: white; +} + +/* Bouton Modifier */ +#editBtn { + background-color: #27ae60; +} + +#editBtn:hover { + background-color: #219150; +} + +/* Bouton Sauvegarder */ +#saveBtn { + background-color: #e67e22; +} + +#saveBtn:hover { + background-color: #d35400; +} + +/* --------------- */ +/* Footer */ +/* --------------- */ + +footer { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + font-size: 14px; + text-transform: uppercase; + padding: 20px; + margin-top: auto; /* Cela force le footer à se pousser vers le bas */ +} + +.copyright { + padding: 10px 20px; +} + +footer a { + text-decoration: none; + color: #eff1f3; +} + +.footer_link { + display: flex; + flex-grow: 1; + gap: 10%; +} + +.footer_link a { + padding: 0 10px; +} + +.copyright { + padding: 10px 20px; +} + +/* ------------------------- */ +/* Responsive Design */ +/* ------------------------- */ + +/* GRAND ÉCRAN (> 1440px) */ +@media (min-width: 1440px) { + main { + padding: 60px; + max-width: 1200px; + } + + .container { + width: 70%; + } + + h1 { + font-size: 48px; + } + + .bouton_connexion { + padding: 12px 24px; + font-size: 18px; + } +} + +/* TABLETTE - Moyenne (1025px à 1440px) */ +@media (max-width: 1440px) and (min-width: 1025px) { + .container { + width: 75%; + max-width: 1000px; + } + + h1 { + font-size: 40px; + } + + .bouton_connexion { + padding: 10px 20px; + font-size: 16px; + } +} + +/* TABLETTE - Petite (769px à 1024px) */ +@media (max-width: 1024px) and (min-width: 769px) { + header { + flex-wrap: wrap; + justify-content: center; + padding: 20px; + gap: 20px; + } + + #smart_building { + width: 100px; + margin: 0; + } + + .header_text { + flex-direction: row; + flex-wrap: wrap; + justify-content: center; + gap: 30px; + } + + .bouton_connexion { + padding: 8px 16px; + font-size: 14px; + } + + .container { + width: 90%; + max-width: 1000px; + } + + h1 { + font-size: 36px; + margin-bottom: 20px; + } +} + +/* MOBILE (≤ 768px) */ +@media (max-width: 768px) { + header { + flex-direction: column; + align-items: center; + gap: 15px; + } + + #smart_building { + width: 90px; + height: auto; + margin: 0 auto; + } + + .header_text { + flex-direction: column; + align-items: center; + gap: 10px; + font-size: 16px; + } + + .bouton_connexion { + padding: 10px 20px; + width: auto; + text-align: center; + } + + .container { + width: 100%; + max-width: 100%; + padding: 20px; + } + + h1 { + font-size: 28px; + margin-bottom: 20px; + } + + footer { + flex-direction: column; + text-align: center; + gap: 10px; + align-items: center; + } + + .footer_link { + flex-direction: column; + align-items: center; + gap: 10px; + } + + .copyright { + padding: 0; + } +} + +/* TÉLÉPHONE TRÈS PETIT (≤ 480px) */ +@media (max-width: 480px) { + .container { + width: 100%; + padding: 10px; + } + + h1 { + font-size: 24px; + margin-bottom: 20px; + } + + .bouton_connexion { + padding: 8px 16px; + font-size: 14px; + } + + #smart_building { + width: 70px; + } +} \ No newline at end of file diff --git a/z2/webapp/query b/z2/webapp/query new file mode 100644 index 0000000..0eaebf1 --- /dev/null +++ b/z2/webapp/query @@ -0,0 +1 @@ +mysql diff --git a/z2/webapp/routes/admin.js b/z2/webapp/routes/admin.js new file mode 100644 index 0000000..411f158 --- /dev/null +++ b/z2/webapp/routes/admin.js @@ -0,0 +1,115 @@ +const express = require('express'); +const router = express.Router(); +const db = require('../config/db'); + +// Middleware de protection admin (défini ici si non global) +const requireAdmin = (req, res, next) => { + if (!req.session.utilisateur || req.session.utilisateur.statut !== 'administrateur') { + return res.send(` + + `); + } + next(); +}; + +// Applique le middleware à toutes les routes de ce fichier +router.use(requireAdmin); + +// Route principale - admin dashboard +router.get('/', (req, res) => { + const usersQuery = 'SELECT * FROM utilisateur'; + const objetsQuery = 'SELECT * FROM objet'; + const contactsQuery = 'SELECT * FROM contact'; // Récupérer les messages de contact + + db.query(usersQuery, (err, utilisateurs) => { + if (err) return res.status(500).send('Erreur récupération utilisateurs'); + + db.query(objetsQuery, (err2, objets) => { + if (err2) return res.status(500).send('Erreur récupération objets'); + + db.query(contactsQuery, (err3, contacts) => { + if (err3) return res.status(500).send('Erreur récupération messages'); + res.render('admin', { utilisateurs, objets, contacts }); + }); + }); + }); +}); + +// Mise à jour utilisateur +router.post('/update-user', (req, res) => { + const { id, etat, statut } = req.body; + const sql = 'UPDATE utilisateur SET etat = ?, statut = ? WHERE id = ?'; + + db.query(sql, [etat, statut, id], (err) => { + if (err) { + console.error('Erreur update utilisateur :', err); + return res.status(500).send('Erreur update utilisateur'); + } + res.redirect('/admin'); + }); +}); + +// Suppression utilisateur +router.post('/supprimer-user', (req, res) => { + const { id } = req.body; + const sql = 'DELETE FROM utilisateur WHERE id = ?'; + + db.query(sql, [id], (err) => { + if (err) { + console.error('Erreur suppression utilisateur :', err); + return res.status(500).send("Erreur lors de la suppression de l'utilisateur"); + } + res.redirect('/admin'); + }); +}); + +// Mise à jour objet +router.post('/update-objet', (req, res) => { + const { adresse_ip, niveau, etat, type } = req.body; + const sql = 'UPDATE objet SET niveau = ?, etat = ?, type = ? WHERE adresse_ip = ?'; + + db.query(sql, [niveau, etat, type, adresse_ip], (err) => { + if (err) { + console.error('Erreur update objet :', err); + return res.status(500).send('Erreur update objet'); + } + res.redirect('/admin'); + }); +}); + +// Suppression objet + dépendances +router.post('/supprimer-objet', (req, res) => { + const { adresse_ip } = req.body; + const deleteChildTables = ['climatisation_chauffage', 'thermostat', 'appareil_menager', 'securite']; + + const deleteFromTable = (table) => { + return new Promise((resolve, reject) => { + const sql = `DELETE FROM ${table} WHERE adresse_ip = ?`; + db.query(sql, [adresse_ip], (err) => { + if (err) return reject(err); + resolve(); + }); + }); + }; + + Promise.all(deleteChildTables.map(deleteFromTable)) + .then(() => { + const sql = 'DELETE FROM objet WHERE adresse_ip = ?'; + db.query(sql, [adresse_ip], (err) => { + if (err) { + console.error('Erreur suppression objet :', err); + return res.status(500).send("Erreur lors de la suppression de l'objet"); + } + res.redirect('/admin'); + }); + }) + .catch(err => { + console.error("Erreur suppression dépendances : ", err); + res.status(500).send("Erreur lors de la suppression des dépendances liées à l'objet"); + }); +}); + +module.exports = router; diff --git a/z2/webapp/routes/api/objet.js b/z2/webapp/routes/api/objet.js new file mode 100644 index 0000000..db2b9d1 --- /dev/null +++ b/z2/webapp/routes/api/objet.js @@ -0,0 +1,56 @@ +const express = require('express'); +const router = express.Router(); +const db = require('../../config/db'); + +router.get('/', (req, res) => { + const sql = ` + SELECT + objet.adresse_ip, + objet.niveau, + objet.denomination, + objet.etat, + objet.derniere_interaction, + objet.type, + thermostat.temperature_actuelle, + thermostat.temperature_cible, + thermostat.mode, + appareil_menager.depart_differe, + appareil_menager.etat_de_la_tache, + securite.situation AS situation_securite + FROM objet + LEFT JOIN thermostat ON objet.adresse_ip = thermostat.adresse_ip + LEFT JOIN appareil_menager ON objet.adresse_ip = appareil_menager.adresse_ip + LEFT JOIN securite ON objet.adresse_ip = securite.adresse_ip + `; + + db.query(sql, (err, result) => { + if (err) { + console.error('Erreur lors de la récupération des objets :', err); + return res.status(500).send('Erreur serveur'); + } + res.json(result); + }); +}); + + +router.put('/:adresse_ip', (req, res) => { + const adresse_ip = req.params.adresse_ip; + const champs = req.body; + + const setValues = Object.keys(champs) + .map(key => `${key} = ?`) + .join(', '); + + const sql = `UPDATE objet SET ${setValues} WHERE adresse_ip = ?`; + const values = [...Object.values(champs), adresse_ip]; + + db.query(sql, values, (err) => { + if (err) { + console.error('Erreur lors de la mise à jour de l\'objet :', err); + return res.status(500).send('Erreur serveur'); + } + res.sendStatus(200); + }); +}); + +module.exports = router; diff --git a/z2/webapp/routes/api/ressource.js b/z2/webapp/routes/api/ressource.js new file mode 100644 index 0000000..f935f1c --- /dev/null +++ b/z2/webapp/routes/api/ressource.js @@ -0,0 +1,34 @@ +const express = require('express'); +const router = express.Router(); +const db = require('../../config/db'); // Connexion à la base de données + +// Route pour récupérer les ressources +router.get('/', (req, res) => { + const search = req.query.search || ''; + + if (search) { + const sql = 'SELECT * FROM ressources WHERE nom LIKE ?'; + const values = [`%${search}%`]; + + db.query(sql, values, (err, results) => { + if (err) { + console.error('Erreur lors de la recherche des ressources :', err); + res.status(500).send('Erreur serveur'); + } else { + res.json(results); + } + }); + } else { + const sql = 'SELECT * FROM ressources'; + db.query(sql, (err, results) => { + if (err) { + console.error('Erreur lors de la récupération des ressources :', err); + res.status(500).send('Erreur serveur'); + } else { + res.json(results); + } + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/z2/webapp/routes/api/utilisateur.js b/z2/webapp/routes/api/utilisateur.js new file mode 100644 index 0000000..f09e046 --- /dev/null +++ b/z2/webapp/routes/api/utilisateur.js @@ -0,0 +1,18 @@ +const express = require('express'); +const router = express.Router(); +const db = require('../../config/db'); // Connexion à la base de données + +// Route pour récupérer les objets +router.get('/', (req, res) => { + const sql = 'SELECT * FROM utilisateur'; + db.query(sql, (err, results) => { + if (err) { + console.error('Erreur lors de la récupération des objets :', err); + res.status(500).send('Erreur serveur'); + } else { + res.json(results); // Retourne les objets sous forme de JSON + } + }); +}); + +module.exports = router; diff --git a/z2/webapp/routes/complexe.js b/z2/webapp/routes/complexe.js new file mode 100644 index 0000000..66efdf3 --- /dev/null +++ b/z2/webapp/routes/complexe.js @@ -0,0 +1,77 @@ +const express = require('express'); +const router = express.Router(); +const db = require('../config/db'); + +// Modifier un objet +router.post('/update-objet', (req, res) => { + const { adresse_ip, niveau, etat } = req.body; + const sql = 'UPDATE objet SET niveau = ?, etat = ? WHERE adresse_ip = ?'; + + db.query(sql, [niveau, etat, adresse_ip], (err) => { + if (err) { + console.error("Erreur lors de la modification de l'objet :", err); + return res.status(500).send("Erreur lors de la modification de l'objet"); + } + res.redirect('/dashboard-complexe'); + }); +}); + +// Supprimer un objet (et ses dépendances) +router.post('/supprimer-objet', (req, res) => { + const { adresse_ip } = req.body; + console.log('Adresse IP reçue pour suppression :', adresse_ip); + + const deleteChildTables = [ + 'climatisation_chauffage', + 'thermostat', + 'appareil_menager', + 'securite' + ]; + + const deleteFromTable = (table) => { + return new Promise((resolve, reject) => { + const sql = `DELETE FROM ${table} WHERE adresse_ip = ?`; + db.query(sql, [adresse_ip], (err) => { + if (err) { + console.error(`Erreur suppression dans ${table} :`, err); + return reject(err); + } + resolve(); + }); + }); + }; + + Promise.all(deleteChildTables.map(deleteFromTable)) + .then(() => { + db.query('DELETE FROM objet WHERE adresse_ip = ?', [adresse_ip], (err) => { + if (err) { + console.error('Erreur suppression objet :', err); + return res.status(500).send("Erreur lors de la suppression de l'objet"); + } + res.redirect('/dashboard-complexe'); + }); + }) + .catch(err => { + res.status(500).send("Erreur lors de la suppression d'une dépendance liée à l'objet"); + }); +}); + +// Ajouter un objet +router.post('/ajouter-objet', (req, res) => { + const { denomination, adresse_ip, type, niveau, etat } = req.body; + + const sql = ` + INSERT INTO objet (denomination, adresse_ip, type, niveau, etat) + VALUES (?, ?, ?, ?, ?)`; + + db.query(sql, [denomination, adresse_ip, type, niveau, etat], (err) => { + if (err) { + console.error('Erreur ajout objet :', err); + return res.status(500).send("Erreur lors de l'ajout de l'objet"); + } + + res.redirect('/dashboard-complexe'); + }); +}); + +module.exports = router; diff --git a/z2/webapp/routes/connexion.js b/z2/webapp/routes/connexion.js new file mode 100644 index 0000000..3b107c0 --- /dev/null +++ b/z2/webapp/routes/connexion.js @@ -0,0 +1,52 @@ +const express = require('express'); +const router = express.Router(); +const bcrypt = require('bcrypt'); +const db = require('../config/db'); + +// Connexion - Formulaire +router.get('/', (req, res) => { + res.render('connexion'); +}); + +// Connexion - Traitement +router.post('/', (req, res) => { + const { identifiant, mot_de_passe } = req.body; + + const sql = 'SELECT * FROM utilisateur WHERE identifiant = ?'; + db.query(sql, [identifiant], async (err, results) => { + if (err) { + console.error('Erreur lors de la requête :', err); + return res.send('Erreur serveur'); + } + + if (results.length === 0) { + return res.send('Identifiant incorrect'); + } + + const utilisateur = results[0]; + const match = await bcrypt.compare(mot_de_passe, utilisateur.mot_de_passe); + + if (!match) { + return res.send('Mot de passe incorrect'); + } + + // Enregistrement en session + req.session.utilisateur = utilisateur; + console.log("Utilisateur connecté :", req.session.utilisateur); + + // Redirection selon le statut + switch (utilisateur.statut) { + case 'administrateur': + return res.redirect('/admin'); + case 'complexe': + return res.redirect('/dashboard-complexe'); + case 'simple': + return res.redirect('/objets'); // 🔄 Redirection mise à jour ici + case 'visiteur': + default: + return res.redirect('/'); + } + }); +}); + +module.exports = router; diff --git a/z2/webapp/routes/inscription.js b/z2/webapp/routes/inscription.js new file mode 100644 index 0000000..31e9303 --- /dev/null +++ b/z2/webapp/routes/inscription.js @@ -0,0 +1,44 @@ +const express = require('express'); +const router = express.Router(); +const bcrypt = require('bcrypt'); +const db = require('../config/db'); +const multer = require('multer'); +const path = require('path'); + + +const storage = multer.diskStorage({ + destination: (req, file, cb) => { + const uploadDir = path.join(__dirname, '../img'); + cb(null, uploadDir); + }, + filename: (req, file, cb) => { + const uniqueName = `${Date.now()}_${file.originalname}`; + cb(null, uniqueName); + } +}); + +const upload = multer({ storage }); + + +router.get('/', (req, res) => { + res.render('inscription'); +}); + +router.post('/', upload.single('photo_profil'), async (req, res) => { + const { nom, prenom, sexe, age, date_naissance, identifiant, mot_de_passe, situation, email } = req.body; + const photo = req.file ? req.file.filename : null; + const hashedPassword = await bcrypt.hash(mot_de_passe, 10); + + const sql = `INSERT INTO utilisateur (nom, prenom, sexe, age, date_naissance, identifiant, mot_de_passe, photo, situation, email) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`; + + db.query(sql, [nom, prenom, sexe, age, date_naissance, identifiant, hashedPassword, photo, situation, email], (err, result) => { + if (err) { + console.error(err); + return res.send('Erreur lors de l’inscription'); + } + res.redirect('/connexion'); + }); +}); + +module.exports = router; diff --git a/z2/webapp/routes/profil.js b/z2/webapp/routes/profil.js new file mode 100644 index 0000000..dd65ba3 --- /dev/null +++ b/z2/webapp/routes/profil.js @@ -0,0 +1,56 @@ +const express = require('express'); +const router = express.Router(); +const ensureAuthenticated = require('../middleware/auth'); +const db = require('../config/db'); +const multer = require('multer'); +const bcrypt = require('bcrypt'); +const storage = multer.diskStorage({ + destination: (req, file, cb) => { + cb(null, 'photos'); + }, + filename: (req, file, cb) => { + const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9); + cb(null, uniqueSuffix + '-' + file.originalname); // Nom unique pour éviter les conflits + } +}); + +const upload = multer({ + storage: storage, + limits: { fileSize: 5 * 1024 * 1024 }, // Limite de taille (5 Mo) +}).fields([ + { name: 'photo', maxCount: 1 }, // Champ pour le fichier +]); + + + +router.get('/', ensureAuthenticated, (req, res) => { + const utilisateur = req.session.utilisateur; + res.render('profil', { utilisateur }); +}); + +router.post('/update', ensureAuthenticated, upload, (req, res) => { + const mot_de_passe = req.body.mot_de_passe ? bcrypt.hashSync(req.body.mot_de_passe, 10) : req.session.utilisateur.mot_de_passe; + const { identifiant, nom, prenom, sexe, situation } = req.body; + const photo = req.files && req.files.photo ? req.files.photo[0].filename : req.session.utilisateur.photo; + const id = req.session.utilisateur.id; + + const sql = 'UPDATE utilisateur SET identifiant = ?, nom = ?, prenom = ?, sexe = ?, situation = ?, photo = ?, mot_de_passe = ? WHERE id = ?'; + db.query(sql, [identifiant, nom, prenom, sexe, situation, photo, mot_de_passe, id], (err, result) => { + if (err) { + console.error(err); + return res.status(500).send("Erreur lors de la mise à jour"); + } + + req.session.utilisateur.identifiant = identifiant; + req.session.utilisateur.nom = nom; + req.session.utilisateur.prenom = prenom; + req.session.utilisateur.sexe = sexe; + req.session.utilisateur.situation = situation; + req.session.utilisateur.photo = photo; + req.session.utilisateur.mot_de_passe = mot_de_passe; + + res.redirect('/profil'); + }); +}); + +module.exports = router; diff --git a/z2/webapp/user.sql b/z2/webapp/user.sql new file mode 100644 index 0000000..341cfd4 --- /dev/null +++ b/z2/webapp/user.sql @@ -0,0 +1,156 @@ +-- Création de la base de données et sélection +CREATE DATABASE IF NOT EXISTS user; +USE user; + +SET NAMES utf8mb4; + +-- Table `utilisateur` +CREATE TABLE IF NOT EXISTS `utilisateur` ( + `id` INT NOT NULL AUTO_INCREMENT, + `nom` VARCHAR(100) NOT NULL, + `prenom` VARCHAR(100) NOT NULL, + `sexe` ENUM('Homme', 'Femme', 'Autre') NOT NULL, + `age` INT DEFAULT NULL, + `date_naissance` DATE NOT NULL, + `identifiant` VARCHAR(50) NOT NULL UNIQUE, + `email` VARCHAR(255) NOT NULL UNIQUE, + `mot_de_passe` VARCHAR(255) NOT NULL, + `photo` VARCHAR(255) DEFAULT NULL, + `situation` VARCHAR(255) DEFAULT NULL, + `etat` ENUM('en attente', 'validé') DEFAULT 'en attente', + `statut` ENUM('visiteur','simple','complexe','administrateur') DEFAULT 'visiteur', + PRIMARY KEY (`id`) +); + +INSERT INTO `utilisateur` VALUES +(1, 'Dupont', 'Jean', 'Homme', 30, '1994-05-12', 'jdupont', 'jean.dupont@mail.com', '$2b$10$sOFuc/cZzpK1P8fINfmsAOQ33K0xyCeEXD.OCilFH0SPyuMC.3LNC', './photos/jean.jpg', 'Ingénieur', 'validé', 'complexe'), -- mots de passe dupont +(2, 'Martin', 'Sophie', 'Femme', 25, '1999-08-21', 'smartin', 'sophie.martin@mail.com', '$2b$10$ydWLQyCvL8PVAN18KCWBWe.oMdtm7Y.R4oCVVJenP9fJqfyBLR26C', './photos/sophie.jpg', 'Étudiante', 'en attente', 'visiteur'), -- mots de passe martin +(3, 'Durand', 'Paul', 'Homme', 45, '1979-11-30', 'pdurand', 'paul.durand@mail.com', '$2b$10$HnhXjkKOPNLE.2erAwzg.e5zVVRK/wNeegd8GrNxWv2f3U660xQbW', './photos/paul.jpg', 'Directeur', 'validé', 'administrateur'), -- mots de passe durand +(4, 'Bernard', 'Alice', 'Femme', 28, '1996-02-15', 'aliceb', 'alice.bernard@mail.com', '$2b$10$6BslSk7oJc6fGzQx3YQRHu6Op8xtVQIGFznA0vsmZX40Ix4ywBTfy', './photos/alice.jpg', 'Développeuse', 'validé', 'simple'), -- mots de passe bernard +(5, 'Lemoine', 'Alex', 'Autre', 32, '1992-07-19', 'alemoine', 'alex.lemoine@mail.com', '$2b$10$lZebS.LIRJi9Z5yDaLSNxujY0piysZAb9BRjGOLVW3Hr2wDPoYTgO', './photos/alex.jpg', 'Freelance', 'en attente', 'simple'), -- mots de passe lemoine +(6, 'Arricastres', 'Guillaume', 'Homme', 26, '1998-01-15', 'garricastres', 'guillaume.arricastres@mail.com', '$2b$10$StE4wQ2Mox/CUpuR.o/1nObQMT/rAAndmKXOgAJ7r.V/YGvEs7isy', './photos/guillaume.jpg', 'Étudiant', 'validé', 'simple'), -- mots de passe 1234 +(7, 'Chosson', 'Clément', 'Homme', 25, '1999-06-15', 'clement_cx', 'clement.chosson@mail.com', '$2b$10$StE4wQ2Mox/CUpuR.o/1nObQMT/rAAndmKXOgAJ7r.V/YGvEs7isy', NULL, 'Technicien', 'validé', 'complexe'), -- mots de passe 1234 +(8, 'Admin', 'Root', 'Homme', 30, '1993-12-31', 'admin', 'admin@smartbuilding.com', '$2b$10$StE4wQ2Mox/CUpuR.o/1nObQMT/rAAndmKXOgAJ7r.V/YGvEs7isy', NULL, 'Administrateur', 'validé', 'administrateur'); -- mots de passe 1234 + + +-- Table `objet` +CREATE TABLE IF NOT EXISTS `objet` ( + `adresse_ip` VARCHAR(45) NOT NULL, + `niveau` ENUM('débutant','intermédiaire','avancé','expert') NOT NULL, + `denomination` VARCHAR(255) NOT NULL, + `etat` ENUM('Actif','Inactif') NOT NULL, + `derniere_interaction` DATETIME DEFAULT NULL, + `type` ENUM('Climatisation','Lumière','thermostat','securite','Enceinte connectée','Capteur','appareil_menager','Prise') NOT NULL, + PRIMARY KEY (`adresse_ip`) +); + +INSERT INTO `objet` VALUES +('192.168.1.10', 'débutant', 'Ampoule connectée', 'Inactif', '2025-04-08 10:40:50', 'Lumière'), +('192.168.1.11', 'débutant', 'Cuisinière', 'Inactif', '2025-04-08 10:40:50', 'appareil_menager'), +('192.168.1.12', 'débutant', 'Machine à café', 'Inactif', '2025-04-08 10:40:50', 'appareil_menager'), +('192.168.1.20', 'intermédiaire', 'Thermostat intelligent', 'Actif', '2025-04-08 10:40:50', 'thermostat'), +('192.168.1.30', 'avancé', 'Caméra de surveillance', 'Actif', '2025-04-08 10:40:50', 'Capteur'), +('192.168.1.40', 'débutant', 'Machine à laver', 'Inactif', '2025-04-08 10:40:50', 'appareil_menager'), +('192.168.1.50', 'débutant', 'Enceinte intelligente', 'Inactif', '2025-04-08 10:40:50', 'Enceinte connectée'), +('192.168.1.60', 'intermédiaire', 'Prise connectée', 'Actif', '2025-04-08 10:40:50', 'Prise'), +('192.168.1.70', 'avancé', 'Serrure connectée', 'Actif', '2025-04-08 10:40:50', 'securite'), +('192.168.1.80', 'expert', 'Porte connectée salle de contrôle', 'Actif', '2025-04-08 10:40:50', 'securite'), +('192.168.1.90', 'avancé', 'Climatisation', 'Actif', '2025-04-08 10:40:50', 'Climatisation'); + +-- Table `thermostat` +CREATE TABLE `thermostat` ( + `adresse_ip` VARCHAR(45) NOT NULL, + `temperature_actuelle` FLOAT DEFAULT NULL, + `temperature_cible` FLOAT DEFAULT NULL, + `mode` ENUM('Automatique','Manuel') NOT NULL, + PRIMARY KEY (`adresse_ip`), + FOREIGN KEY (`adresse_ip`) REFERENCES `objet` (`adresse_ip`) +); + +INSERT INTO `thermostat` VALUES +('192.168.1.20', 17, 20, 'Automatique'); + +-- Table `climatisation_chauffage` +CREATE TABLE `climatisation_chauffage` ( + `adresse_ip` VARCHAR(45) NOT NULL, + `thermostat` VARCHAR(45) NOT NULL, + PRIMARY KEY (`adresse_ip`), + FOREIGN KEY (`adresse_ip`) REFERENCES `objet` (`adresse_ip`), + FOREIGN KEY (`thermostat`) REFERENCES `thermostat` (`adresse_ip`) +); + +INSERT INTO `climatisation_chauffage` VALUES +('192.168.1.90', '192.168.1.20'); + +-- Table `appareil_menager` +CREATE TABLE `appareil_menager` ( + `adresse_ip` VARCHAR(45) NOT NULL, + `depart_differe` TIME DEFAULT NULL, + `etat_de_la_tache` ENUM('Complété','En cours','En attente','Aucune') NOT NULL, + PRIMARY KEY (`adresse_ip`), + FOREIGN KEY (`adresse_ip`) REFERENCES `objet` (`adresse_ip`) ON DELETE RESTRICT ON UPDATE CASCADE +); + +INSERT INTO `appareil_menager` VALUES +('192.168.1.11', NULL, 'Aucune'), +('192.168.1.12', NULL, 'Aucune'), +('192.168.1.40', NULL, 'Aucune'); + +-- Table `securite` +CREATE TABLE `securite` ( + `adresse_ip` VARCHAR(45) NOT NULL, + `situation` ENUM('Verouillé','Ouvert') NOT NULL, + PRIMARY KEY (`adresse_ip`), + FOREIGN KEY (`adresse_ip`) REFERENCES `objet` (`adresse_ip`) ON DELETE RESTRICT ON UPDATE CASCADE +); + +INSERT INTO `securite` VALUES +('192.168.1.70', 'Ouvert'), +('192.168.1.80', 'Verouillé'); + +-- Table `gestion` +CREATE TABLE `gestion` ( + `id` INT NOT NULL AUTO_INCREMENT, + `id_utilisateur` INT NOT NULL, + `competence` VARCHAR(255) NOT NULL, + `niveau` ENUM('débutant','intermédiaire','avancé','expert') NOT NULL, + `categorie_experience` FLOAT DEFAULT NULL, + PRIMARY KEY (`id`), + FOREIGN KEY (`id_utilisateur`) REFERENCES `utilisateur` (`id`) ON DELETE CASCADE +); + +INSERT INTO `gestion` (`id`, `id_utilisateur`, `competence`, `niveau`, `categorie_experience`) VALUES +(1, 1, 'Informatique', 'expert', 5), +(2, 1, 'Electronique', 'avancé', 3.5), +(3, 2, 'Internet', 'intermédiaire', 2), +(4, 2, 'Assistance numerique', 'débutant', 1), +(5, 3, 'Cuisine', 'expert', 10), +(6, 3, 'Aménagement', 'avancé', 7), +(7, 4, 'Securite', 'avancé', 4), +(8, 4, 'Acceuil', 'intermédiaire', 2.5), +(9, 5, 'Entretien', 'intermédiaire', 3), +(10, 5, 'Securite', 'débutant', 1.5); + + +CREATE TABLE `ressources` ( + `id` int NOT NULL, + `nom` varchar(50) NOT NULL, + `consommation` varchar(50) NOT NULL, + `consommation_max` varchar(50) NOT NULL, + `fournisseur` varchar(100) NOT NULL, + `abonnement` varchar(100) NOT NULL, + `echeance` varchar(50) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +INSERT INTO `ressources` (`id`, `nom`, `consommation`, `consommation_max`, `fournisseur`, `abonnement`, `echeance`) VALUES +(0, 'Électricité', '250 kWh', '500 kWh', 'EDF', '50 € / mois', '2025-12-30'), +(1, 'Gaz', '90 m³', '200 m³', 'Engie', '40 € / mois', '2025-06-30'), +(2, 'Eau', '1200 L', '3000 L', 'Veolia', '20 € / mois', '2025-08-15'); + +CREATE TABLE IF NOT EXISTS contact ( + id INT AUTO_INCREMENT PRIMARY KEY, + nom VARCHAR(100) NOT NULL, + email VARCHAR(255) NOT NULL, + message TEXT NOT NULL, + date_envoi TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); diff --git a/z2/webapp/views/accueil.ejs b/z2/webapp/views/accueil.ejs new file mode 100644 index 0000000..b0f95db --- /dev/null +++ b/z2/webapp/views/accueil.ejs @@ -0,0 +1,67 @@ + + + + + Page d'accueil - Smart Building + + + + + + + + <%- include('partials/header') %> + +
+
+ Bâtiment +
+ +
+

Accueil

+ +
+
+ + <%- include('partials/footer') %> + + \ No newline at end of file diff --git a/z2/webapp/views/admin.ejs b/z2/webapp/views/admin.ejs new file mode 100644 index 0000000..1142f06 --- /dev/null +++ b/z2/webapp/views/admin.ejs @@ -0,0 +1,401 @@ + + + + + Admin - Smart Building + + + + +<%- include('partials/header') %> + +
+

Gestion des utilisateurs

+ +
+

Ajouter un utilisateur

+ +
+ + + + + + + + + + + + + + <% utilisateurs.forEach(user => { %> + + + + + + + + + + + + <% }) %> + +
NomPrénomStatutÉtatModifierSupprimer
<%= user.nom %><%= user.prenom %> + + + + +
+ + +
+
+ +

Gestion des objets

+ +
+

Ajouter un objet

+ + + + + + + + + + + + + + + + <% objets.forEach(objet => { %> + + + + + + + + + + + + + <% }) %> + +
NomAdresse IPTypeNiveauÉtatModifierSupprimer
<%= objet.denomination %><%= objet.adresse_ip %><%= objet.type %> + + + + +
+ + +
+
+ +

Messages de contact

+ + + + + + + + + + + + <% contacts.forEach(c => { %> + + + + + + + <% }) %> + +
NomEmailMessageDate
<%= c.nom %><%= c.email %><%= c.message %><%= new Date(c.date_envoi).toLocaleString('fr-FR') %>
+ +
+ +<%- include('partials/footer') %> + + \ No newline at end of file diff --git a/z2/webapp/views/connexion.ejs b/z2/webapp/views/connexion.ejs new file mode 100644 index 0000000..271a846 --- /dev/null +++ b/z2/webapp/views/connexion.ejs @@ -0,0 +1,71 @@ + + + + + Page d'accueil - Smart Building + + + + + <%- include('partials/header') %> +
+
+
+
+

Connexion

+
+
+ + + + + +

+
+ +
+ + + + + +

+
+ + +
+
+
+
+ + <%- include('partials/footer') %> + + diff --git a/z2/webapp/views/contact.ejs b/z2/webapp/views/contact.ejs new file mode 100644 index 0000000..c152a87 --- /dev/null +++ b/z2/webapp/views/contact.ejs @@ -0,0 +1,31 @@ + + + + + Contact - Smart Building + + + + <%- include('partials/header') %> + +
+

Nous contacter

+

Une question ? Une suggestion ? N'hésitez pas à nous écrire !

+ +
+ + + + + + + + + + +
+
+ + <%- include('partials/footer') %> + + diff --git a/z2/webapp/views/dashboard-complexe.ejs b/z2/webapp/views/dashboard-complexe.ejs new file mode 100644 index 0000000..00ccdc9 --- /dev/null +++ b/z2/webapp/views/dashboard-complexe.ejs @@ -0,0 +1,105 @@ + + + + + Dashboard Complexe - Smart Building + + + + +<%- include('partials/header') %> + +
+

Gestion des objets

+ +
+

Ajouter un objet

+ +
+ + + + + + + + + + + + + + + <% objets.forEach(objet => { %> + + + + + + + + + + + + + <% }) %> + +
NomAdresse IPTypeNiveauÉtatModifierSupprimer
<%= objet.denomination %><%= objet.adresse_ip %><%= objet.type %> + + + + + + +
+ + +
+
+
+ +<%- include('partials/footer') %> + + diff --git a/z2/webapp/views/description.ejs b/z2/webapp/views/description.ejs new file mode 100644 index 0000000..2cc0288 --- /dev/null +++ b/z2/webapp/views/description.ejs @@ -0,0 +1,42 @@ + + + + + + Présentation de la maison - Smart Building + + + + + + + + <%- include('partials/header') %> + +
+
+ Image de la maison connectée +
+ +
+
+

Présentation de la maison connectée

+
+
+

+ Notre maison connectée est conçue pour offrir confort, sécurité et efficacité énergétique. Équipée de capteurs intelligents, d’un système de gestion centralisée et d’une connectivité en temps réel, elle permet une surveillance continue des équipements et une optimisation automatique des ressources. +

+

+ Avec une surface habitable estimée à environ 300 m² répartie sur 4 à 5 niveaux, la maison connectée propose de vastes espaces ouverts et lumineux. Chaque étage est conçu pour maximiser l’apport en lumière naturelle grâce aux nombreuses baies vitrées. Les terrasses végétalisées apportent un lien direct avec la nature, tout en favorisant l’isolation thermique. +

+

+ Le bâtiment comprend de nombreux équipements connectés tels que l’éclairage intelligent, le chauffage programmable, des outils ménager et une surveillance vidéo connectée. Toutes les données sont centralisées via une interface web qui permet un contrôle à distance via son smartphone ou son ordinateur. De plus en fonction des droits que possèdes votre compte vous avez accés à différentes fonctionnalités. +

+
+
+
+ + <%- include('partials/footer') %> + + + \ No newline at end of file diff --git a/z2/webapp/views/inscription.ejs b/z2/webapp/views/inscription.ejs new file mode 100644 index 0000000..180263e --- /dev/null +++ b/z2/webapp/views/inscription.ejs @@ -0,0 +1,273 @@ + + + + + Page d'accueil - Smart Building + + + + <%- include('partials/header') %> + +
+
+
+ +
+ +
+

Inscription

+
+
+ + + + + + +
+ +
+ + + + + + +
+ +
+ +
+ + + + + +
+
+
+ +
+ + + + + + + +
+ +
+ +
+ + + + + + +
+
+ +
+ + + + + + +
+ +
+ + + + + + +
+ +
+ + + + + + +
+ +
+ + + + + + +
+ +
+ + + + + +
+
+
+
+
+
+ + <%- include('partials/footer') %> + + \ No newline at end of file diff --git a/z2/webapp/views/membre.ejs b/z2/webapp/views/membre.ejs new file mode 100644 index 0000000..464ada9 --- /dev/null +++ b/z2/webapp/views/membre.ejs @@ -0,0 +1,31 @@ + + + + + Page d'accueil - Smart Building + + + + + + + + <%- include('partials/header') %> + +
+

Member Details

+

Member ID: <%= id %>

+ + + +
+ + <%- include('partials/footer') %> + + diff --git a/z2/webapp/views/membres.ejs b/z2/webapp/views/membres.ejs new file mode 100644 index 0000000..c26e1d9 --- /dev/null +++ b/z2/webapp/views/membres.ejs @@ -0,0 +1,119 @@ + + + + + Page d'accueil - Smart Building + + + + + + <%- include('partials/header') %> + +
+
+ Bâtiment +
+ +
+
+

Membres

+
+ +
+
+ +
+
+
+ + <%- include('partials/footer') %> + + + + \ No newline at end of file diff --git a/z2/webapp/views/objets.ejs b/z2/webapp/views/objets.ejs new file mode 100644 index 0000000..82517a9 --- /dev/null +++ b/z2/webapp/views/objets.ejs @@ -0,0 +1,200 @@ + + + + + Page d'accueil - Smart Building + + + + + + + + + + <%- include('partials/header') %> + +
+
+ Bâtiment +
+ +
+
+

Objets Connectés

+
+ +
+
+
+
+ + +
+ + <%- include('partials/footer') %> + + + + \ No newline at end of file diff --git a/z2/webapp/views/partials/footer.ejs b/z2/webapp/views/partials/footer.ejs new file mode 100644 index 0000000..108ebc6 --- /dev/null +++ b/z2/webapp/views/partials/footer.ejs @@ -0,0 +1,9 @@ + diff --git a/z2/webapp/views/partials/header.ejs b/z2/webapp/views/partials/header.ejs new file mode 100644 index 0000000..7dc2994 --- /dev/null +++ b/z2/webapp/views/partials/header.ejs @@ -0,0 +1,39 @@ +
+ + smart_building + + + + + <% if (!session.utilisateur) { %> + Connexion + <% } else { %> + Déconnexion + <% } %> +
diff --git a/z2/webapp/views/profil.ejs b/z2/webapp/views/profil.ejs new file mode 100644 index 0000000..68b4616 --- /dev/null +++ b/z2/webapp/views/profil.ejs @@ -0,0 +1,70 @@ + + + + + + Mon Profil + + + + + <%- include("partials/header") %> + +
+

Mon Profil

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ +
+
+ + +
+ + +
+
+ + + + <%- include("partials/footer") %> + + diff --git a/z2/webapp/views/ressources.ejs b/z2/webapp/views/ressources.ejs new file mode 100644 index 0000000..fb22576 --- /dev/null +++ b/z2/webapp/views/ressources.ejs @@ -0,0 +1,162 @@ + + + + + + Page d'accueil - Smart Building + + + + + + + + + <%- include('partials/header') %> + +
+
+ Bâtiment +
+ +
+
+

Gestion des ressources

+
+
+
+ + +
+ <%- include('partials/footer') %> + + + + + \ No newline at end of file