Interface Web de Gestion MkDocs
Ce guide décrit l'installation et le fonctionnement d'une interface web locale permettant d’éditer les pages MkDocs sans passer par la ligne de commande.
🎯 Objectif
- Modifier les fichiers
.md
de MkDocs directement depuis un navigateur. - Gérer les pages (édition, création, suppression).
- Possibilité future : lancer
mkdocs serve
oumkdocs build
depuis l'interface.
🧰 Prérequis
Avoir un mkdocs, ce tutoriel sera appuyer sur le tuto [[installation_mkdocs_linux]]
🖥️ Étapes d'installation
1. Installer Python
apt install python3-venv -y
2. Créer un dossier pour l'interface
mkdir -p /home/mkdox/interface
cd /home/mkdox/interface
3. Créer un environnement Python
python3 -m venv venv
source venv/bin/activate
4. Installer Flask
pip install flask
5. Créer le fichier app.py
Créer un fichier app.py
avec ce contenue
nano app.py
XXXX
docs
6. Lancer l'application
Toujours dans le dossier interface
, lance :
source venv/bin/activate
python app.py
erreur
WARNING: This is a development server. Do not use it in a production deployment.
🔒 Ce message est normal : Flask te prévient juste que son serveur n’est pas prévu pour la production web. Mais pour un usage local ou interne à ton réseau, comme dans ton cas, aucun souci
Ouvre ensuite ton navigateur sur :
http://localhost:5000
Automatisé le démarrage
créer un scrip dans le dossier interface
nano mkdocs-interface-setup.sh
#!/bin/bash
SERVICE_NAME="mkdocs-interface"
WORKDIR="/.../interface" # remplacer par le chemin
PYTHON=/.../interface/venv/bin/python # remplacer par le chemin
APP="$WORKDIR/app.py"
SERVICE_PATH="/etc/systemd/system/${SERVICE_NAME}.service"
echo "🔧 Création du service ${SERVICE_NAME}..."
# Créer le fichier systemd
bash -c "cat > $SERVICE_PATH" <<EOF
[Unit]
Description=Interface Web MkDocs
After=network.target
[Service]
User=root
WorkingDirectory=/.../interface # remplacer par le chemin
ExecStart=/.../interface/venv/bin/python -m flask run --host=0.0.0.0 --port=5000 # remplacer par le chemin
Environment=FLASK_APP=app.py
Environment=FLASK_ENV=production
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
echo "✅ Fichier de service créé à $SERVICE_PATH"
# Recharger systemd
systemctl daemon-reexec # ajouter sudo si user
systemctl daemon-reload # ajouter sudo si user
# Activer et démarrer le service
systemctl enable $SERVICE_NAME # ajouter sudo si user
systemctl start $SERVICE_NAME # ajouter sudo si user
# Afficher le statut
echo "🚀 Lancement du service..."
sleep 1
systemctl status $SERVICE_NAME --no-pager # ajouter sudo si user
il faut ensuite lui donner les droits :
chmod +x mkdocs-interface-setup.sh
le lancer :
./mkdocs-interface-setup.sh
Pour mettre a jour l'interface après changement
systemctl restart mkdocs-interface
Désinstaller (à ne pas faire)
systemctl disable mkdocs-interface
rm /etc/systemd/system/mkdocs-interface.service
rm /home/CHEMIN_VERS/.../interface
PREMIUM
nano /.../interface/app.py
from flask import Flask, render_template, request, jsonify, redirect, url_for
from markupsafe import Markup
import os
import yaml
import subprocess
app = Flask(__name__)
BASE_DIR = "/home/mkdox/spider"
DOCS_PATH = os.path.join(BASE_DIR, "docs")
MKDOCS_YML = os.path.join(BASE_DIR, "mkdocs.yml")
UPDATE_SCRIPT = "/usr/local/bin/mkdocs-update"
# ------------------------------
# UTILS
# ------------------------------
def parse_nav(nav, indent=0):
result = []
for item in nav:
if isinstance(item, dict):
for key, value in item.items():
if isinstance(value, str):
result.append(" " * indent + value)
elif isinstance(value, list):
result.append({key: parse_nav(value, indent + 1)})
return result
# ------------------------------
# ROUTES
# ------------------------------
@app.route("/")
def index():
with open(MKDOCS_YML, "r", encoding="utf-8") as f:
config = yaml.safe_load(f)
nav = config.get("nav", [])
structure = parse_nav(nav)
return render_template("index.html", structure=structure)
@app.route("/edit/<path:filename>", methods=["GET", "POST"])
def edit(filename):
full_path = os.path.join(DOCS_PATH, filename)
if request.method == "POST":
new_name = request.form.get("new_filename")
if new_name and new_name != filename:
new_path = os.path.join(DOCS_PATH, new_name)
os.makedirs(os.path.dirname(new_path), exist_ok=True)
os.rename(full_path, new_path)
full_path = new_path
filename = new_name
with open(full_path, "w", encoding="utf-8") as f:
f.write(request.form["content"])
return redirect(url_for("index"))
with open(full_path, "r", encoding="utf-8") as f:
content = f.read()
return render_template("edit.html", filename=filename, content=content)
@app.route("/deploy", methods=["POST"])
def deploy():
try:
output = subprocess.check_output(["/bin/bash", "-c", f"cd {BASE_DIR} && {UPDATE_SCRIPT}"], stderr=subprocess.STDOUT)
return jsonify({"status": "ok", "output": output.decode("utf-8")})
except subprocess.CalledProcessError as e:
return jsonify({"status": "error", "output": e.output.decode("utf-8")}), 500
@app.route("/new_file", methods=["POST"])
def new_file():
filename = request.form["filename"]
full_path = os.path.join(DOCS_PATH, filename)
os.makedirs(os.path.dirname(full_path), exist_ok=True)
with open(full_path, "w", encoding="utf-8") as f:
f.write("# Nouveau fichier\n")
return redirect(url_for("index"))
@app.route("/new_folder", methods=["POST"])
def new_folder():
folder = request.form["folder"]
full_path = os.path.join(DOCS_PATH, folder)
os.makedirs(full_path, exist_ok=True)
return redirect(url_for("index"))
nano /.../interface/templates/index.html
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Structure MkDocs</title>
<style>
body {
font-family: sans-serif;
padding: 20px;
}
ul {
list-style-type: none;
padding-left: 0;
}
.folder, .file {
margin: 4px 0;
padding: 6px 10px;
border: 1px solid #ccc;
background: #f9f9f9;
display: flex;
justify-content: space-between;
align-items: center;
}
.folder {
font-weight: bold;
background: #e0e0ff;
}
.indent-0 { margin-left: 0px; }
.indent-1 { margin-left: 20px; }
.indent-2 { margin-left: 40px; }
.indent-3 { margin-left: 60px; }
.indent-4 { margin-left: 80px; }
button {
margin-left: 10px;
}
</style>
</head>
<body>
<h1>✨ Gestion de la structure MkDocs</h1>
<ul>
{% macro render_item(item, indent=0) %}
{% if item is string %}
<li class="file indent-{{ indent }}">
📄 {{ item }}
<span>
<a href="/edit/{{ item }}"><button>✏️ Modifier</button></a>
</span>
</li>
{% elif item is mapping %}
{% for key, children in item.items() %}
<li class="folder indent-{{ indent }}">📁 {{ key }}</li>
<ul>
{% for child in children %}
{{ render_item(child, indent + 1) }}
{% endfor %}
</ul>
{% endfor %}
{% endif %}
{% endmacro %}
{% for item in structure %}
{{ render_item(item, 0) }}
{% endfor %}
</ul>
<br>
<form action="/new_file" method="post">
<input type="text" name="filename" placeholder="Nouveau fichier .md">
<button type="submit">📄 Créer un fichier</button>
</form>
<form action="/new_folder" method="post">
<input type="text" name="folder" placeholder="Nouveau dossier">
<button type="submit">📁 Créer un dossier</button>
</form>
<br>
<form id="deployForm" method="post" action="/deploy">
<button type="submit">🚀 Déployer</button>
</form>
<script>
document.getElementById("deployForm").addEventListener("submit", async function(e) {
e.preventDefault();
const res = await fetch("/deploy", { method: "POST" });
const data = await res.json();
if (data.status === "ok") {
alert("✅ Déploiement réussi !");
} else {
alert("❌ Erreur :\n" + data.output);
}
});
</script>
</body>
</html>
nano /.../interface/templates/edit.html
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Modifier {{ filename }}</title>
<style>
body {
font-family: sans-serif;
padding: 20px;
}
textarea {
width: 100%;
height: 80vh;
font-family: monospace;
font-size: 14px;
}
input[type="text"] {
width: 100%;
padding: 6px;
margin-bottom: 10px;
}
</style>
</head>
<body>
<h1>✏️ Modifier {{ filename }}</h1>
<form method="POST">
<label>Nom du fichier (relatif à /docs) :</label>
<input type="text" name="new_filename" value="{{ filename }}">
<textarea name="content">{{ content }}</textarea><br><br>
<button type="submit">💾 Enregistrer</button>
<a href="{{ url_for('index') }}">⬅ Retour</a>
</form>
</body>
</html>
mettre à jour
systemctl restart mkdocs-interface
Créé pour le projet Spider Web — Corentin VARACHAUD