diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml new file mode 100644 index 0000000..2a74b4d --- /dev/null +++ b/.github/workflows/python-app.yml @@ -0,0 +1,31 @@ +# This workflow will install Python dependencies, run tests and lint with a single version of Python +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python + +name: Deploy + +on: + push: + branches: [ "flask" ] + pull_request: + branches: [ "flask" ] + +permissions: + contents: read + +jobs: + deploy: + + runs-on: self-hosted + + steps: + - uses: actions/checkout@v4 + - name: LS command + run: ls + - name: PWD command + run: pwd + - name: cp command + run: cp -r * /home/alex/VDI-APP-INSA + - name: api-service restart + run: sudo systemctl restart app-vdi + - name: api-service status + run: sudo systemctl status app-vdi diff --git a/.gitignore b/.gitignore index e71bf17..8efc4f2 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -*.sec \ No newline at end of file +*.sec +*.key +*.pyc diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..b977275 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,18 @@ +FROM python:3.11.2-bullseye + +RUN apt update + +RUN pip install Flask==3.0.0 gunicorn==20.1.0 + +RUN mkdir /app +WORKDIR /app + +COPY . /app + +# Partie variable d'environnement URLs +ENV URL_API=https://api.insa-cvl.com + + +EXPOSE 5000 + +CMD ["/bin/bash", "entrypoint.sh"] \ No newline at end of file diff --git a/README.md b/README.md index 51ff15a..acbdcd1 100644 --- a/README.md +++ b/README.md @@ -1 +1,59 @@ -# VDI \ No newline at end of file +# APP VDI + +## Description +This the fronted of the APP VDI. You can find the API [here](https://github.com/AlexTheGeek/api-vdi). + +## Installation +### Requirements +1. Linux : Ubuntu 20.04, Debian 11,12 +2. Python 3.11.2 +3. Pip or Pipx : you can even create a virtual environment to run those scripts +4. Create a user vdi with the home directory `/home/vdi` +5. Create a folder `/home/vdi/VDI-APP` for the API and and clone this repository in it + 5.1. `mkdir -p /home/vdi/VDI-APP` + 5.2. `cd /home/vdi/VDI-APP` + 5.3. `git clone https://github.com/loimax/vdi.git` +6. Install the requirements : `pip install -r requirements.txt` +7. Create a folder /var/log/VDI/API for the logs of the API : `mkdir -p /var/log/VDI/APP` + 7.1. Set the rights of the user who runs the API (here vdi) on this folder : `chown -R vdi:vdi /var/log/VDI/APP` + +### Docker +1. build the image : `docker build -t vdi-app .` or `docker-compose build` with the docker-compose.yml file +2. run the container : `docker run -it -d -p 5000:5000 --name vdi-app vdi-app` or `docker-compose up -d` with the docker-compose.yml file +```yaml +version: "3.9" +services: + vdi-app: + #build: . + image: vdi-app + container_name: vdi-app + ports: + - "5000:5000" + environment: + - URL_API=https://api.insa-cvl.com + restart: always +``` +3. check the logs : `docker logs vdi-app` or `docker-compose logs -f` + + +### Systemd +1. Create a file `/etc/systemd/system/vdi-app.service` with the following content : + ``` + [Unit] + Description=Gunicorn instance to serve APP VDI + After=network.target + + [Service] + User=vdi + Group=vdi + WorkingDirectory=/home/vdi/VDI-APP + Environment="PATH=/home/vdi/.local/bin" + ExecStart=/home/vdi/.local/bin/gunicorn --access-logfile /var/log/VDI/APP/access.log --error-logfile /var/log/VDI/APP/error.log --workers 3 --bind 0.0.0.0:5000 app:app + + [Install] + WantedBy=multi-user.target + ``` +2. Reload the systemd daemon : `systemctl daemon-reload` +3. Enable the service : `systemctl enable vdi-app.service` +4. Start the service : `systemctl start vdi-app.service` +5. Check the status of the service : `systemctl status vdi-app.service` diff --git a/app-vdi.service b/app-vdi.service new file mode 100644 index 0000000..8ef515a --- /dev/null +++ b/app-vdi.service @@ -0,0 +1,13 @@ +[Unit] +Description=Gunicorn instance to serve APP VDI +After=network.target + +[Service] +User=alex +Group=alex +WorkingDirectory=/home/alex/VDI-APP-INSA +Environment="PATH=/home/alex/.local/bin" +ExecStart=/home/alex/.local/bin/gunicorn --access-logfile /var/log/VDI/APP/access.log --error-logfile /var/log/VDI/APP/error.log --workers 3 --bind 0.0.0.0:5000 app:app + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/app.py b/app.py new file mode 100644 index 0000000..40bfdf1 --- /dev/null +++ b/app.py @@ -0,0 +1,36 @@ +from flask import Flask, render_template, redirect, url_for, send_from_directory, request + +app = Flask(__name__, static_folder='static') + +app.config['URL_API']="https://api.insa-cvl.com" + +@app.route('/robots.txt') +def static_from_root(): + return send_from_directory(app.static_folder, request.path[1:]) + +@app.route('/') +def home(): + return redirect(url_for('login')) + +@app.route('/login', methods=['GET', 'POST']) +def login(): + return render_template('login.html', URL_API=app.config['URL_API']) + +@app.route('/admin', methods=['GET', 'POST']) +def admin(): + return render_template('admin.html', URL_API=app.config['URL_API']) + +@app.route('/professor', methods=['GET', 'POST']) +def professor(): + return render_template('professor.html', URL_API=app.config['URL_API']) + +@app.route('/dashboard', methods=['GET', 'POST']) +def dashboard(): + return render_template('dashboard.html', URL_API=app.config['URL_API']) + +@app.route('/viewer', methods=['GET']) +def viewer(): + return render_template('viewer.html', URL_API=app.config['URL_API']) + +if __name__ == '__main__': + app.run(debug=True, host="0.0.0.0", port=5001) diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..513877b --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,11 @@ +version: "3.9" +services: + vdi-app: + #build: . + image: vdi-app + container_name: vdi-app + ports: + - "5000:5000" + environment: + - URL_API=https://api.insa-cvl.com + restart: always \ No newline at end of file diff --git a/docker.py b/docker.py new file mode 100644 index 0000000..7264870 --- /dev/null +++ b/docker.py @@ -0,0 +1,37 @@ +from flask import Flask, render_template, redirect, url_for, send_from_directory, request +import os + +app = Flask(__name__, static_folder='static') + +app.config['URL_API']=os.getenv('URL_API') + +@app.route('/robots.txt') +def static_from_root(): + return send_from_directory(app.static_folder, request.path[1:]) + +@app.route('/') +def home(): + return redirect(url_for('login')) + +@app.route('/login', methods=['GET', 'POST']) +def login(): + return render_template('login.html', URL_API=app.config['URL_API']) + +@app.route('/admin', methods=['GET', 'POST']) +def admin(): + return render_template('admin.html', URL_API=app.config['URL_API']) + +@app.route('/professor', methods=['GET', 'POST']) +def professor(): + return render_template('professor.html', URL_API=app.config['URL_API']) + +@app.route('/dashboard', methods=['GET', 'POST']) +def dashboard(): + return render_template('dashboard.html', URL_API=app.config['URL_API']) + +@app.route('/viewer', methods=['GET']) +def viewer(): + return render_template('viewer.html', URL_API=app.config['URL_API']) + +if __name__ == '__main__': + app.run(debug=True, host="0.0.0.0", port=5001) diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100644 index 0000000..c7d50e4 --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +if [[ ! -d "/var/log/VDI/APP" ]]; then + mkdir -p /var/log/VDI/APP + touch /var/log/VDI/APP/access.log + touch /var/log/VDI/APP/error.log + touch /var/log/VDI/APP/app-flask.log +fi + + +gunicorn --access-logfile /var/log/VDI/APP/access.log --error-logfile /var/log/VDI/APP/error.log --workers 3 --bind 0.0.0.0:5000 docker:app \ No newline at end of file diff --git a/logo.colors b/logo.colors new file mode 100644 index 0000000..83d5fb0 --- /dev/null +++ b/logo.colors @@ -0,0 +1,6 @@ +#B61900 rouge foncé +#E52100 rouge moyen foncé +#E22700 rouge +#FFFFFF blanc +#001014 noir +#869CA2 gris \ No newline at end of file diff --git a/static/css/base.css b/static/css/base.css new file mode 100644 index 0000000..34d5fc7 --- /dev/null +++ b/static/css/base.css @@ -0,0 +1,87 @@ +footer { + font-size: 10px; + line-height: 0px; +} + +.border-bottom-nav { + border-bottom: 2px solid #FFF; +} + +.dropdown-menu-right { + right: 0; + left: auto; +} + +.lds-default-container { + display: flex; + justify-content: center; + align-items: center; +} + +.cards { + display: flex; + flex-wrap: wrap; + list-style: none; + padding: 0; + margin: 20px; +} + +.card { + margin-left: 20px; + margin-right: 20px; +} + +body { + background-color: #343A40; + color: white; + margin-bottom: 20px; + overflow-y: scroll; +} + +.blockquote-footer { + color: white; +} + +h1 { + margin: 20px; + font-size: 24px; + text-align: center; +} + +h2 { + font-size: 20px; + text-align: center; +} + +#addUserCard, #addVMOutCard, #addTemplateCard, a:hover { + cursor: pointer; +} + +.navbar { + background-color: #343A40; +} + +.navbar-dark .navbar-nav .nav-link { + color: white; +} + +.nav-tabs .nav-item { + transition: transform 0.3s ease-in-out; + margin-bottom: 1px; +} + +.nav-tabs .nav-item:hover { + transform: scale(1.1); +} + +.nav-tabs { + border-bottom: none; +} + +.nav-item { + padding: 0 20px; +} + +a, a:hover { + color: inherit; +} \ No newline at end of file diff --git a/static/css/card_spinner.css b/static/css/card_spinner.css new file mode 100644 index 0000000..628491e --- /dev/null +++ b/static/css/card_spinner.css @@ -0,0 +1,82 @@ +.lds-default { + display: inline-block; + position: relative; + width: 80px; + height: 80px; + } + .lds-default div { + position: absolute; + width: 6px; + height: 6px; + background: #fff; + border-radius: 50%; + animation: lds-default 1.2s linear infinite; + } + .lds-default div:nth-child(1) { + animation-delay: 0s; + top: 37px; + left: 66px; + } + .lds-default div:nth-child(2) { + animation-delay: -0.1s; + top: 22px; + left: 62px; + } + .lds-default div:nth-child(3) { + animation-delay: -0.2s; + top: 11px; + left: 52px; + } + .lds-default div:nth-child(4) { + animation-delay: -0.3s; + top: 7px; + left: 37px; + } + .lds-default div:nth-child(5) { + animation-delay: -0.4s; + top: 11px; + left: 22px; + } + .lds-default div:nth-child(6) { + animation-delay: -0.5s; + top: 22px; + left: 11px; + } + .lds-default div:nth-child(7) { + animation-delay: -0.6s; + top: 37px; + left: 7px; + } + .lds-default div:nth-child(8) { + animation-delay: -0.7s; + top: 52px; + left: 11px; + } + .lds-default div:nth-child(9) { + animation-delay: -0.8s; + top: 62px; + left: 22px; + } + .lds-default div:nth-child(10) { + animation-delay: -0.9s; + top: 66px; + left: 37px; + } + .lds-default div:nth-child(11) { + animation-delay: -1s; + top: 62px; + left: 52px; + } + .lds-default div:nth-child(12) { + animation-delay: -1.1s; + top: 52px; + left: 62px; + } + @keyframes lds-default { + 0%, 20%, 80%, 100% { + transform: scale(1); + } + 50% { + transform: scale(1.5); + } + } \ No newline at end of file diff --git a/static/css/login.css b/static/css/login.css new file mode 100644 index 0000000..984dc8c --- /dev/null +++ b/static/css/login.css @@ -0,0 +1,73 @@ +footer { + font-size: 10px; + line-height: 0px; +} + +.border-bottom-nav { + border-bottom: 2px solid #FFF; +} + +.cards { + display: flex; + flex-wrap: wrap; + list-style: none; + padding: 0; + margin: 20px; +} + +.card { + width: 18rem; + margin: 10px; +} + +body { + background-color: #343A40; + color: white; +} + +.blockquote-footer { + color: white; +} + +h1 { + margin: 20px; + font-size: 24px; + text-align: center; +} + +h2 { + font-size: 20px; + text-align: center; +} + +#addTemplateCard { + cursor: pointer; +} + +#addUserCard, #addVMOutCard { + cursor: pointer; +} + +.navbar { + background-color: #343A40; +} + +.navbar-dark .navbar-nav .nav-link { + color: white; +} + +.navbar-dark .navbar-nav .nav-link:hover { + color: #17A2B8; +} + +.nav-item { + padding: 0 20px; +} + +a { + color: inherit; +} + +a:hover { + cursor:hand; +} \ No newline at end of file diff --git a/static/css/spinner.css b/static/css/spinner.css new file mode 100644 index 0000000..af34d3f --- /dev/null +++ b/static/css/spinner.css @@ -0,0 +1,79 @@ +.lds-spinner { + color: official; + display: inline-block; + position: relative; + width: 80px; + height: 80px; + } + .lds-spinner div { + transform-origin: 40px 40px; + animation: lds-spinner 1.2s linear infinite; + } + .lds-spinner div:after { + content: " "; + display: block; + position: absolute; + top: 3px; + left: 37px; + width: 6px; + height: 18px; + border-radius: 20%; + background: #000000; + } + .lds-spinner div:nth-child(1) { + transform: rotate(0deg); + animation-delay: -1.1s; + } + .lds-spinner div:nth-child(2) { + transform: rotate(30deg); + animation-delay: -1s; + } + .lds-spinner div:nth-child(3) { + transform: rotate(60deg); + animation-delay: -0.9s; + } + .lds-spinner div:nth-child(4) { + transform: rotate(90deg); + animation-delay: -0.8s; + } + .lds-spinner div:nth-child(5) { + transform: rotate(120deg); + animation-delay: -0.7s; + } + .lds-spinner div:nth-child(6) { + transform: rotate(150deg); + animation-delay: -0.6s; + } + .lds-spinner div:nth-child(7) { + transform: rotate(180deg); + animation-delay: -0.5s; + } + .lds-spinner div:nth-child(8) { + transform: rotate(210deg); + animation-delay: -0.4s; + } + .lds-spinner div:nth-child(9) { + transform: rotate(240deg); + animation-delay: -0.3s; + } + .lds-spinner div:nth-child(10) { + transform: rotate(270deg); + animation-delay: -0.2s; + } + .lds-spinner div:nth-child(11) { + transform: rotate(300deg); + animation-delay: -0.1s; + } + .lds-spinner div:nth-child(12) { + transform: rotate(330deg); + animation-delay: 0s; + } + @keyframes lds-spinner { + 0% { + opacity: 1; + } + 100% { + opacity: 0; + } + } + \ No newline at end of file diff --git a/static/css/style.css b/static/css/style.css new file mode 100644 index 0000000..34d5fc7 --- /dev/null +++ b/static/css/style.css @@ -0,0 +1,87 @@ +footer { + font-size: 10px; + line-height: 0px; +} + +.border-bottom-nav { + border-bottom: 2px solid #FFF; +} + +.dropdown-menu-right { + right: 0; + left: auto; +} + +.lds-default-container { + display: flex; + justify-content: center; + align-items: center; +} + +.cards { + display: flex; + flex-wrap: wrap; + list-style: none; + padding: 0; + margin: 20px; +} + +.card { + margin-left: 20px; + margin-right: 20px; +} + +body { + background-color: #343A40; + color: white; + margin-bottom: 20px; + overflow-y: scroll; +} + +.blockquote-footer { + color: white; +} + +h1 { + margin: 20px; + font-size: 24px; + text-align: center; +} + +h2 { + font-size: 20px; + text-align: center; +} + +#addUserCard, #addVMOutCard, #addTemplateCard, a:hover { + cursor: pointer; +} + +.navbar { + background-color: #343A40; +} + +.navbar-dark .navbar-nav .nav-link { + color: white; +} + +.nav-tabs .nav-item { + transition: transform 0.3s ease-in-out; + margin-bottom: 1px; +} + +.nav-tabs .nav-item:hover { + transform: scale(1.1); +} + +.nav-tabs { + border-bottom: none; +} + +.nav-item { + padding: 0 20px; +} + +a, a:hover { + color: inherit; +} \ No newline at end of file diff --git a/static/icons/admin.png b/static/icons/admin.png new file mode 100644 index 0000000..918e84d Binary files /dev/null and b/static/icons/admin.png differ diff --git a/static/icons/insa.png b/static/icons/insa.png new file mode 100644 index 0000000..d3af712 Binary files /dev/null and b/static/icons/insa.png differ diff --git a/static/icons/prof.png b/static/icons/prof.png new file mode 100644 index 0000000..d626040 Binary files /dev/null and b/static/icons/prof.png differ diff --git a/static/icons/profil.png b/static/icons/profil.png new file mode 100644 index 0000000..05c3476 Binary files /dev/null and b/static/icons/profil.png differ diff --git a/static/icons/vdi.png b/static/icons/vdi.png new file mode 100644 index 0000000..94b8869 Binary files /dev/null and b/static/icons/vdi.png differ diff --git a/static/js/admin.js b/static/js/admin.js new file mode 100644 index 0000000..0b381a1 --- /dev/null +++ b/static/js/admin.js @@ -0,0 +1,286 @@ +async function deleteVmAdmin(id) { // showLoadingModal | checkIfVmAliveAdmin | hideLoadingModal + try { + var output = await checkIfVmAliveAdmin(id); + if (output == 1) { + showLoadingModal(); + $.ajax({ + type: 'DELETE', + url: URL_API + '/vm/delete_admin', + contentType: 'application/json;charset=UTF-8', + data: JSON.stringify({ vm_id: id }), + xhrFields: { + withCredentials: true + }, + success: function (response) { + hideLoadingModal(); + location.reload(); + }, + error: function (error) { + alert('Erreur de suppression de la VM'); + hideLoadingModal(); + } + }); + } + } catch (error) { + alert(error); + hideLoadingModal(); + } +} + +function changePassword(userId, newPassword, newPassword2) { + $.ajax({ + type: 'POST', + url: URL_API + '/updatepasswordpa', + contentType: 'application/json;charset=UTF-8', + data: JSON.stringify({ user_id: userId, new_password: newPassword, new_password2: newPassword2 }), + xhrFields: { + withCredentials: true + }, + success: function (response) { + alert('Mot de passe changé avec succès!'); + }, + error: function (error) { + alert('Erreur lors du changement de mot de passe. L\'utilisateur est peut-être du CAS.'); + } + }); +} + +function changeRole(userId, role) { + $.ajax({ + type: 'POST', + url: URL_API + '/updaterole', + contentType: 'application/json;charset=UTF-8', + data: JSON.stringify({ user_id: userId, role: role }), + xhrFields: { + withCredentials: true + }, + success: function (response) { + alert('Role mis à jour !'); + location.reload(); + }, + error: function (error) { + alert('Erreur lors de la mise à jour du rôle.'); + } + }) +} + +function createUser() { + var firstname = $('#firstname').val(); + var lastname = $('#lastname').val(); + var email = $('#email').val(); + + var userData = { + first_name: firstname, + last_name: lastname, + email: email + }; + + $.ajax({ + type: 'POST', + url: URL_API + '/createuser', + contentType: 'application/json;charset=UTF-8', + data: JSON.stringify(userData), + xhrFields: { + withCredentials: true + }, + success: function (response) { + alert('Utilisateur créé avec succès! Mot de passe: ' + response.password); + location.reload(); + }, + error: function (error) { + alert('Erreur lors de la création de l\'utilisateur.'); + } + }); +} + +function deleteUser(userId) { // + $.ajax({ + type: 'DELETE', + url: URL_API + '/deleteuser', + contentType: 'application/json;charset=UTF-8', + data: JSON.stringify({ user_id: userId }), + xhrFields: { + withCredentials: true + }, + success: function (response) { + alert('Utilisateur supprimé avec succès!'); + location.reload(); + }, + error: function (error) { + alert('Erreur lors de la suppression de l\'utilisateur.'); + } + }); +} + +function addUserToTableAdmin(user) { // deleteUser + var row = $(''); + row.append($('').attr('scope', 'row').text(user.id)); + row.append($('').text(user.first_name)); + row.append($('').text(user.last_name)); + row.append($('').text(user.email)); + row.append($('').text(user.role)); + + var changePasswordButton = $(''; + existingAlert.style.display = 'block'; +} + +function getUserProfile() { + $.ajax({ + type: 'GET', + url: URL_API + '/profile', + contentType: 'application/json;charset=UTF-8', + xhrFields: { + withCredentials: true + }, + success: function (response) { + window.location = "/dashboard"; + } + }); +} + +// DIVERS +function createBlockquoteWithIcon(title, text, iconClass) { + var blockquote = $('
').addClass('blockquote text-center'); + var h1 = $('

').addClass('mb-0').text(title); + var p = $('

').html('' + text + ''); + var icon = $('').addClass(iconClass); + blockquote.append(h1, p, icon); + return blockquote; +} \ No newline at end of file diff --git a/static/js/prof.js b/static/js/prof.js new file mode 100644 index 0000000..18612f3 --- /dev/null +++ b/static/js/prof.js @@ -0,0 +1,233 @@ +async function deleteVmProf(id) { // showLoadingModal | hideLoadingModal | checkIfVmAliveAdmin + try { + var output = await checkIfVmAliveAdmin(id); + if (output == 1) { + showLoadingModal(); + $.ajax({ + type: 'DELETE', + url: URL_API + '/vm/delete_admin', + contentType: 'application/json;charset=UTF-8', + data: JSON.stringify({ vm_id: id }), + xhrFields: { + withCredentials: true + }, + success: function (response) { + hideLoadingModal(); + location.reload(); + }, + error: function (error) { + alert('Erreur de suppression de la VM'); + hideLoadingModal(); + } + }); + } + } catch (error) { + alert(error); + hideLoadingModal(); + } +} + +function changePassword(userId, newPassword, newPassword2) { + $.ajax({ + type: 'POST', + url: URL_API + '/updatepasswordpa', + contentType: 'application/json;charset=UTF-8', + data: JSON.stringify({ user_id: userId, new_password: newPassword, new_password2: newPassword2 }), + xhrFields: { + withCredentials: true + }, + success: function (response) { + alert('Mot de passe changé avec succès!'); + }, + error: function (error) { + alert('Erreur lors du changement de mot de passe. L\'utilisateur est peut-être du CAS.'); + } + }); +} + +function createUser() { + var firstname = $('#firstname').val(); + var lastname = $('#lastname').val(); + var email = $('#email').val(); + + var userData = { + first_name: firstname, + last_name: lastname, + email: email + }; + + $.ajax({ + type: 'POST', + url: URL_API + '/createuser', + contentType: 'application/json;charset=UTF-8', + data: JSON.stringify(userData), + xhrFields: { + withCredentials: true + }, + success: function (response) { + alert('Utilisateur créé avec succès! Mot de passe: ' + response.password); + location.reload(); + }, + error: function (error) { + alert('Erreur lors de la création de l\'utilisateur.'); + } + }); +} + +function deleteUser(userId) { // + $.ajax({ + type: 'DELETE', + url: URL_API + '/deleteuser', + contentType: 'application/json;charset=UTF-8', + data: JSON.stringify({ user_id: userId }), + xhrFields: { + withCredentials: true + }, + success: function (response) { + alert('Utilisateur supprimé avec succès!'); + location.reload(); + }, + error: function (error) { + alert('Erreur lors de la suppression de l\'utilisateur.'); + } + }); +} + +function addUserToTableProf(user) { // deleteUser + var row = $(''); + row.append($('').attr('scope', 'row').text(user.id)); + row.append($('').text(user.first_name)); + row.append($('').text(user.last_name)); + row.append($('').text(user.email)); + + var changePasswordButton = $(' + + + +

+
+

Templates

+

Visualisation des templates de VMs créés avec OpenStack

+
+
+
+
+
+ + + + +{% endblock %} diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000..f09fdc6 --- /dev/null +++ b/templates/base.html @@ -0,0 +1,249 @@ + + + + + {% block title %}Bienvenue sur votre espace{% endblock %} + + + + + + + + + + + + + + + + +
+
+ © 2024 Copyright - INSA Centre Val de Loire +
+
+ + + + + + + + + + + + + + + + + + + + + + + {% block content %}{% endblock %} + + \ No newline at end of file diff --git a/templates/dashboard.html b/templates/dashboard.html new file mode 100644 index 0000000..96ff9e0 --- /dev/null +++ b/templates/dashboard.html @@ -0,0 +1,35 @@ +{% extends 'base.html' %} + +{% block title %}VDI - Dashboard{% endblock %} + +{% block navbar %} + + + +{% endblock %} + +{% block content %} +
+
+ +
+
+
+
+
+ + + +{% endblock %} \ No newline at end of file diff --git a/templates/login.html b/templates/login.html new file mode 100644 index 0000000..427b08c --- /dev/null +++ b/templates/login.html @@ -0,0 +1,113 @@ + + + + VDI - Connexion + + + + + + + + + + + + + + +
+
+ © 2024 Copyright - INSA Centre Val de Loire +
+
+ + + +
+ + +

+
+

OU

+ +
+

+
+
+
+
+ + +
+
+ + +
+ +
+
+
+ +
+ + + diff --git a/templates/professor.html b/templates/professor.html new file mode 100644 index 0000000..803bafd --- /dev/null +++ b/templates/professor.html @@ -0,0 +1,73 @@ +{% extends 'base.html' %} + +{% block title %}VDI - Professeur{% endblock %} + +{% block navbar %} + + + +{% endblock %} + +{% block content %} +
+ +
+ +
+
+
+
+ +
+ +
+ + + + + + + + + + + + +
IDPrénomNomEmail
+
+
+ +
+
+
+ + + +{% endblock %} diff --git a/templates/viewer.html b/templates/viewer.html new file mode 100644 index 0000000..dd6cfd0 --- /dev/null +++ b/templates/viewer.html @@ -0,0 +1,41 @@ +{% extends 'base.html' %} + +{% block title %}VDI - VNC Viewer{% endblock %} + +{% block navbar %} + +{% endblock %} + + +{% block content %} + + + +
+ + + + + +{% endblock %}