Compare commits

...

6 Commits

Author SHA1 Message Date
59b7f8bef2 modif gitea url + fix lien ticket dans mail 2024-05-28 10:13:32 +02:00
b1f907aaaa Diverses corrections mineures 2024-05-26 22:54:24 +02:00
297c0a8308 Merge pull request 'Nouvelle route /report pour la création de ticket' (#1) from lyanis/installmgr:report into master
Reviewed-on: https://scodoc.org/git/ScoDoc/installmgr/pulls/1
2024-05-23 13:55:12 +02:00
b07f597553 Ajout d'une route pour la création de tickets
- Ajout de la route /report qui permet de créer un ticket git depuis un formulaire dans ScoDoc
- Modification de la route /upload-dump pour qu'elle retourne du json + ajout d'un champ dump_id dans la réponse
2024-05-23 00:08:46 +02:00
9b67f77be7 Mise à jour du README 2024-05-13 15:48:40 +02:00
f0d5e29439 Ajout PyCharm dans .gitignore 2024-05-13 15:46:20 +02:00
4 changed files with 232 additions and 50 deletions

8
.gitignore vendored
View File

@ -169,5 +169,13 @@ Thumbs.db
.vscode/ .vscode/
*.code-workspace *.code-workspace
# PyCharm
.idea/
copy copy
incoming_dumps/
upload-dump-*
counter
# Le .env contient le token et ne DOIT PAS être sous git
.env

View File

@ -17,55 +17,57 @@ Mini-app Flask remplaçant les CGI scripts de `scodoc.iutv`.
## Installation ## Installation
1. Créer un utilisateur 1. Créer un utilisateur
```bash
adduser installmgr adduser installmgr
```
2. Cloner le dépot 2. Cloner le dépot
```bash
cd /opt git clone https://scodoc.org/git/ScoDoc/installmgr.git /opt/installmgr
git clone https://scodoc.org/git/viennet/installmgr.git chown -R installmgr:installmgr /opt/installmgr
chown -R installmgr installmgr ```
3. Créer l'environnement 3. Créer l'environnement
```bash
su - installmgr su - installmgr
cd /opt/installmgr cd /opt/installmgr
python3 -m venv venv python3 -m venv venv
source venv/bin/activate source venv/bin/activate
pip install wheel pip install wheel
pip install -r requirements.txt pip install -r requirements.txt
```
4. Créer les répertoires 4. Créer les répertoires
```bash
mkdir incoming_dumps mkdir incoming_dumps
# et éventuellement: # et éventuellement:
echo 1000 > counter echo 1000 > counter
```
5. Configurer nginx 5. Configurer nginx
```nginx
location /scodoc-installmgr { location /scodoc-installmgr {
# forward application requests to the gunicorn server # forward application requests to the gunicorn server
proxy_pass http://localhost:8010; proxy_pass http://localhost:8010;
proxy_redirect off; proxy_redirect off;
proxy_set_header Host $host; proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https; proxy_set_header X-Forwarded-Proto https;
proxy_connect_timeout 120; proxy_connect_timeout 120;
client_max_body_size 100m; client_max_body_size 100m;
} }
```
6. Installer le service 6. Installer le service
```bash
cp etc/installmgr.service /etc/systemd/system/ cp etc/installmgr.service /etc/systemd/system/
systemctl daemon-reload systemctl daemon-reload
systemctl start installmgr systemctl start installmgr
```
7. Tester 7. Tester
Depuis un client extérieur, Depuis un client extérieur,
```bash
http https://scodoc.org/scodoc-installmgr/last_stable_version http https://scodoc.org/scodoc-installmgr/last_stable_version
```

View File

@ -1,27 +1,35 @@
import json, datetime, fcntl, glob, os, re, socket, subprocess, time import json, datetime, fcntl, glob, os, re, socket, subprocess, time, requests
from flask import jsonify, request, abort from flask import jsonify, request, abort
from flask import Blueprint from flask import Blueprint
from app import email from app import email
from dotenv import load_dotenv
load_dotenv()
bp = Blueprint("routes", __name__) bp = Blueprint("routes", __name__)
# -------------------------------------------------------------- # --------------------------------------------------------------
DIR = "/opt/installmgr/" DIR = "/opt/installmgr/"
REPOSIT_DIR = "/opt/installmgr/incoming_dumps" REPOSIT_DIR = "/opt/installmgr/incoming_dumps"
MAX_REPOSIT_SIZE = 300 * 20 * 1024 # kB (here, max 300 dumps of 20MB) MAX_REPOSIT_SIZE = 2000 * 20 * 1024 # kB (here, max 2000 dumps of 20MB)
ALERT_MAIL_FROM = "root@scodoc.org" ALERT_MAIL_FROM = "root@scodoc.org"
ALERT_MAIL_TO = "emmanuel.viennet@gmail.com" ALERT_MAIL_TO = "viennet"
LOG_FILENAME = os.path.join(DIR, "upload-dump-errors.log") LOG_FILENAME = os.path.join(DIR, "upload-dump-errors.log")
UPLOAD_LOG_FILENAME = os.path.join(DIR, "upload-dump-log.json") UPLOAD_LOG_FILENAME = os.path.join(DIR, "upload-dump-log.json")
DEBUG = False # if false, don't publish error messages DEBUG = False # if false, don't publish error messages
# Les paquets publiés: # Les paquets publiés:
DEBIAN_PACKAGES_EXP = "/srv/packages/pool/main/s/scodoc9/scodoc9_*.deb" #DEBIAN_PACKAGES_EXP = "/srv/packages/pool/main/s/scodoc9/scodoc9_*.deb"
DEBIAN_PACKAGES_EXP = "/srv/bookworm/pool/main/s/scodoc9/scodoc9_*.deb"
RELEASE_LOG_FILE = "/home/viennet/scodoc-releases.log" RELEASE_LOG_FILE = "/home/viennet/scodoc-releases.log"
GITEA_URL = "https://git.scodoc.org/"
GITEA_REPO = "ScoDoc/ScoDoc"
GITEA_LABEL_ID = None
@bp.route("/scodoc-installmgr/check_version/<client_version>") @bp.route("/scodoc-installmgr/check_version/<client_version>")
def check_version(client_version: str): def check_version(client_version: str):
@ -107,25 +115,173 @@ def last_stable_version():
) )
@bp.route("/scodoc-installmgr/report", methods=["POST"])
def report():
"""Création d'un ticket Gitea depuis ScoDoc
=> json
"""
log = open(LOG_FILENAME, "a")
log.write("report\n")
now = datetime.datetime.now()
fulltime = now.isoformat()
remote_addr = request.environ.get("HTTP_X_REAL_IP", request.remote_addr)
log.write(fulltime + " request from " + remote_addr + "\n")
log.flush()
request_data = request.get_json(silent=True)
if request_data is None:
log.write("json proccessing error\n")
log.close()
return (
jsonify(
{
"message": "Une erreur est survenue lors du traitement de la requête. Veuillez réessayer."
}
),
400,
)
ticket = request_data.get("ticket", "")
user = request_data.get("user", "")
dump = request_data.get("dump", "")
scodoc = request_data.get("scodoc", "")
if (
not ticket
or not user
or not dump
or not scodoc
or not ticket.get("title")
or not ticket.get("message")
):
log.write("missing json fields\n")
log.close()
return (
jsonify(
{
"message": "Une erreur est survenue lors du traitement de la requête (données requises manquantes). Veuillez réessayer."
}
),
400,
)
meta = "Informations complémentaires :"
meta += "\n- Version de ScoDoc : " + scodoc.get("version", "inconnue")
meta += "\n- Dump : " + (
("inclus (id : " + dump.get("id", "inconnu") + ")")
if dump.get("included", False)
else "non inclus"
)
meta += "\n- Établissement : " + ticket.get("etab") if ticket.get("etab") else ""
meta += "\n- Département : " + ticket.get("dept") if ticket.get("dept") else ""
ticket_body = ticket.get("message") + "\n\n---\n\n" + meta
response = requests.post(
GITEA_URL + "/api/v1/repos/" + GITEA_REPO + "/issues",
json={
"title": ticket.get("title"),
"body": ticket_body,
"labels": [GITEA_LABEL_ID],
},
headers={"Authorization": "token " + os.getenv("GITEA_TOKEN")},
)
if response.status_code != 201:
log.write(f"gitea error: status code={response.status_code}\n")
try:
log.write("sending notification to {}\n".format(ALERT_MAIL_TO))
email.send_email(
"[report] Gitea error !",
ALERT_MAIL_FROM,
[ALERT_MAIL_TO],
f"""Error {response.status_code}
while creating the gitea ticket :
{response.text}
Ticket info : {ticket.get("title")}
{ticket_body}
Utilisateur : {user.get("name", "Nom inconnu")} <{user.get("email", "Adresse email inconnue")}>
"""
)
except Exception as exc:
log.write("exception while sending email (1) !\n")
log.write(f"{type(exc).__name__}, Exception message: {exc}\n")
log.close()
return (
jsonify(
{
"message": "Une erreur est survenue lors de la création du ticket. Veuillez réessayer."
}
),
500,
)
try:
log.write(f"sending notification to {ALERT_MAIL_TO}\n")
email.send_email(
f"""[report] Ticket #{response.json()["id"]} créé: {ticket.get("title")}""",
ALERT_MAIL_FROM,
[ALERT_MAIL_TO],
f"""Nouveau ticket utilisateur :
{meta}
Lien du ticket : {response.json()["html_url"]}
Utilisateur : {user.get("name", "Nom inconnu")} <{user.get("email", "Adresse email inconnue")}>
"""
)
except Exception as exc:
log.write("exception while sending email (2) !\n")
log.write(f"{type(exc).__name__}, Exception message: {exc}\n")
log.close()
return (
jsonify(
{
"message": "Votre demande a été enregistrée. Vous pouvez suivre son avancement sur <a target='_blank' href='"
+ response.json()["html_url"]
+ "'>"
+ response.json()["html_url"]
+ "</a>. Vous êtes susceptible d'être contacté(e) par email à l'adresse liée à votre compte ScoDoc si des informations supplémentaires sont nécessaires."
}
),
201,
)
@bp.route("/scodoc-installmgr/upload-dump", methods=["POST"]) @bp.route("/scodoc-installmgr/upload-dump", methods=["POST"])
def upload_scodoc9(): def upload_scodoc9():
"""Réception d'un fichier de dump""" """Réception d'un fichier de dump"""
log = open(LOG_FILENAME, "a") log = open(LOG_FILENAME, "a")
log.write("haallo\n") log.write("upload_scodoc9\n")
now = datetime.datetime.now() now = datetime.datetime.now()
fulltime = now.isoformat() fulltime = now.isoformat()
# client addr: # client addr:
remote_addr = request.environ.get("HTTP_X_REAL_IP", request.remote_addr) remote_addr = request.environ.get("HTTP_X_REAL_IP", request.remote_addr)
log.write(f"{fulltime} request from {remote_addr}\n") log.write(f"{fulltime} request from {remote_addr}\n")
log.write(f"{request.form}")
log.flush() log.flush()
# Avec seulement alphanum et tiret: # Avec seulement alphanum et tiret:
clean_deptname = re.sub(r"[^A-Za-z-]", "", request.form["dept_name"]) clean_deptname = re.sub(r"[^A-Za-z-]", "", request.form["dept_name"])
if not clean_deptname: if not clean_deptname:
abort(400, "missing argument: dept_name") return (
jsonify(
{
"message": 'Erreur: champ dept_name manquant.\n Merci de contacter <a href="mailto:'
+ ALERT_MAIL_TO
+ '">'
+ ALERT_MAIL_TO
+ "</a></p>"
}
),
400,
)
try: try:
remote_host = socket.gethostbyaddr(remote_addr)[0] remote_host = socket.gethostbyaddr(remote_addr)[0]
except: except:
log.write("reverse DNS lookup failed for {}".format(remote_addr)) log.write(f"reverse DNS lookup failed for {remote_addr}")
remote_host = "" remote_host = ""
the_file = request.files["file"] the_file = request.files["file"]
@ -138,6 +294,7 @@ def upload_scodoc9():
"sent_by": request.form["sent_by"], "sent_by": request.form["sent_by"],
"sco_version": request.form.get("sco_version", ""), # release "sco_version": request.form.get("sco_version", ""), # release
"sco_subversion": request.form.get("sco_subversion", ""), "sco_subversion": request.form.get("sco_subversion", ""),
"traceback_str": request.form.get("traceback_str", ""),
"dump_filename": fulltime + "_" + clean_deptname + ".gz", "dump_filename": fulltime + "_" + clean_deptname + ".gz",
"dump_size": len(data), "dump_size": len(data),
"message": request.form.get("message", ""), "message": request.form.get("message", ""),
@ -171,7 +328,18 @@ def upload_scodoc9():
except: except:
log.write("exception while sending email !\n") log.write("exception while sending email !\n")
log.close() log.close()
abort(507, "Insufficient Storage") return (
jsonify(
{
"message": 'Erreur: espace de stockage insuffisant.\n Merci de contacter <a href="mailto:'
+ ALERT_MAIL_TO
+ '">'
+ ALERT_MAIL_TO
+ "</a></p>"
}
),
507,
)
else: else:
log.write("writing dump to {}\n".format(D["dump_filename"])) log.write("writing dump to {}\n".format(D["dump_filename"]))
# dump: # dump:
@ -196,7 +364,12 @@ def upload_scodoc9():
log.write("exception while sending email !\n") log.write("exception while sending email !\n")
log.close() log.close()
return "Données envoyées." return jsonify(
{
"message": "Données envoyées",
"dump_id": fulltime + "_" + clean_deptname,
}
)
@bp.route("/scodoc-installmgr/scodoc9") @bp.route("/scodoc-installmgr/scodoc9")

View File

@ -10,12 +10,11 @@ Jinja2==3.0.1
MarkupSafe==2.0.1 MarkupSafe==2.0.1
mypy-extensions==0.4.3 mypy-extensions==0.4.3
pathspec==0.9.0 pathspec==0.9.0
pkg-resources==0.0.0
platformdirs==2.3.0 platformdirs==2.3.0
python-dotenv==0.19.0 python-dotenv==0.19.0
regex==2021.8.28 regex==2021.8.28
tomli==1.2.1 tomli==1.2.1
typed-ast==1.4.3
typing-extensions==3.10.0.2 typing-extensions==3.10.0.2
Werkzeug==2.0.1 Werkzeug==2.0.1
zipp==3.6.0 zipp==3.6.0
requests==2.32.2