diff --git a/web/annonces.json b/web/annonces.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/web/annonces.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/web/app.py b/web/app.py index aba6ea0..61cde0b 100644 --- a/web/app.py +++ b/web/app.py @@ -1,10 +1,12 @@ import os import uuid -from flask import Flask, redirect, url_for, session, render_template +import json +from flask import Flask, redirect, url_for, jsonify, session, render_template from flask_sqlalchemy import SQLAlchemy from authlib.integrations.flask_client import OAuth app = Flask(__name__) +ANNOUNCE_FILE = os.path.join(os.path.dirname(__file__), "annonces.json") app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://flaskuser:flaskpass@mariadb/flaskdb' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False app.secret_key = os.environ.get("SECRET_KEY", "dev-key") @@ -45,13 +47,89 @@ def auth(): nonce = session.pop('nonce', None) userinfo = keycloak.parse_id_token(token, nonce=nonce) session['user'] = userinfo + session["id_token"] = token.get("id_token") app.logger.debug(f"User info: {userinfo}") return redirect('/') -@app.route('/logout') +@app.route("/logout") def logout(): - session.pop('user', None) - return redirect('/') + id_token = session.get("id_token") + print("ID Token Hint:", id_token) + session.clear() + return redirect( + f"https://keycloak.ninolbt.com/realms/gesthub/protocol/openid-connect/logout" + f"?post_logout_redirect_uri=https://dashboard.ninolbt.com" + f"&id_token_hint={id_token}" + ) +def load_announces(): + try: + with open(ANNOUNCE_FILE, "r", encoding="utf-8") as f: + return json.load(f) + except (FileNotFoundError, json.JSONDecodeError): + return [] + +def save_announces(announces): + with open(ANNOUNCE_FILE, "w", encoding="utf-8") as f: + json.dump(announces, f, ensure_ascii=False, indent=2) + +def get_next_id(announces): + return max((a.get("id", 0) for a in announces), default=0) + 1 + +@app.route("/api/annonces", methods=["GET"]) +def get_announces(): + return jsonify(load_announces()) + +@app.route("/api/annonces", methods=["POST"]) +def create_annonce(): + user = session.get("user") + if not user or "/admin" not in user.get("groups", []): + return jsonify({"error": "unauthorized"}), 403 + data = request.json + if not data.get("text"): + return jsonify({"error": "missing text"}), 400 + + announces = load_announces() + new_announce = { + "id": get_next_id(announces), + "text": data["text"], + "author": user.get("preferred_username", "admin") + } + announces.append(new_announce) + save_announces(announces) + return jsonify({"status": "ok", "announce": new_announce}) + +@app.route("/api/annonces/", methods=["DELETE"]) +def delete_annonce(annonce_id): + user = session.get("user") + if not user or "/admin" not in user.get("groups", []): + return jsonify({"error": "unauthorized"}), 403 + announces = load_announces() + announces = [a for a in announces if a["id"] != annonce_id] + save_announces(announces) + return jsonify({"status": "deleted"}) + +@app.route("/api/annonces/", methods=["PUT"]) +def edit_annonce(annonce_id): + user = session.get("user") + if not user or "/admin" not in user.get("groups", []): + return jsonify({"error": "unauthorized"}), 403 + data = request.json + announces = load_announces() + found = False + for a in announces: + if a["id"] == annonce_id: + a["text"] = data.get("text", a["text"]) + found = True + if not found: + return jsonify({"error": "not found"}), 404 + save_announces(announces) + return jsonify({"status": "updated"}) + +@app.route("/api/is_admin") +def is_admin(): + user = session.get("user") + return jsonify({"admin": "/admin" in user.get("groups", [])}) + if __name__ == '__main__': app.run(host='0.0.0.0', debug=True) diff --git a/web/static/assets/css/index.css b/web/static/assets/css/index.css index c8aa241..d74d640 100644 --- a/web/static/assets/css/index.css +++ b/web/static/assets/css/index.css @@ -388,4 +388,15 @@ .dark .right-column { background-color: #2d2d2d; - } \ No newline at end of file + } + + .postit { + background-color: #fff475; + color: #333; + padding: 0.8rem; + margin-bottom: 0.8rem; + border-radius: 8px; + box-shadow: 2px 2px 6px rgba(0,0,0,0.15); + font-family: 'Comic Sans MS', sans-serif; + font-size: 0.95rem; +} \ No newline at end of file diff --git a/web/static/assets/js/index.js b/web/static/assets/js/index.js index eb1913c..0327ed5 100644 --- a/web/static/assets/js/index.js +++ b/web/static/assets/js/index.js @@ -80,4 +80,74 @@ if (e.target === contactModal) { contactModal.style.display = 'none'; } - }); \ No newline at end of file + }); + +// Gestion des annonces + + let isAdmin = false; + + fetch("/api/is_admin") + .then(res => res.json()) + .then(data => { + isAdmin = data.admin; + if (isAdmin) { + document.getElementById("admin-tools").style.display = "block"; + } + }); + + fetch("/api/annonces") + .then(res => res.json()) + .then(data => { + const container = document.getElementById("annonces-container"); + container.innerHTML = ""; + + data.forEach(item => { + const div = document.createElement("div"); + div.className = "postit"; + div.dataset.id = item.id; + div.innerHTML = ` + ${item.text} +
${item.author} + `; + + if (isAdmin) { + const editBtn = document.createElement("button"); + editBtn.textContent = "✏️"; + editBtn.onclick = () => editAnnonce(item.id, item.text); + div.appendChild(editBtn); + + const delBtn = document.createElement("button"); + delBtn.textContent = "🗑️"; + delBtn.onclick = () => deleteAnnonce(item.id); + div.appendChild(delBtn); + } + + container.appendChild(div); + }); + }); + + function submitAnnonce() { + const txt = document.getElementById("annonce-text").value; + fetch("/api/annonces", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ text: txt }) + }).then(() => location.reload()); + } + + function deleteAnnonce(id) { + fetch(`/api/annonces/${id}`, { + method: "DELETE" + }).then(() => location.reload()); + } + + function editAnnonce(id, oldText) { + const newText = prompt("Modifier l'annonce :", oldText); + if (newText && newText !== oldText) { + fetch(`/api/annonces/${id}`, { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ text: newText }) + }).then(() => location.reload()); + } + } \ No newline at end of file diff --git a/web/templates/view/index.html b/web/templates/view/index.html index 0897289..ce108cf 100644 --- a/web/templates/view/index.html +++ b/web/templates/view/index.html @@ -22,6 +22,10 @@ + Chat
{{ user['preferred_username'] }}
-

Mon profil

+ Déconnexion
@@ -65,14 +70,20 @@

Gesthub

-
Zone Trello - +
+
@@ -80,15 +91,28 @@ je vais metrre des boutons et autres features comme des doc etc ou jsp je m en occupe --> - +