From 3b6f55e703bd18fc0522df616860b172a3e08d46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20MARQUET?= Date: Mon, 4 Mar 2024 12:34:36 +0100 Subject: [PATCH] Add web interface to manage repo --- Dockerfile | 12 ++++++++-- entrypoint.sh | 3 +++ index.html | 43 ++++++++++++++++++++++++++++++++++ nginx.conf | 32 ++++++++++++++++++++++++++ ntfy.py | 36 +++++++++++++++++++++-------- ntfy_api.py | 60 ++++++++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 4 +++- script.js | 45 ++++++++++++++++++++++++++++++++++++ 8 files changed, 222 insertions(+), 13 deletions(-) create mode 100644 index.html create mode 100644 nginx.conf create mode 100644 ntfy_api.py create mode 100644 script.js diff --git a/Dockerfile b/Dockerfile index b8a651c..ccad950 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,9 +3,12 @@ FROM python:3.11.8-alpine3.19 LABEL maintainer="BreizhHardware" ADD ntfy.py / +ADD ntfy_api.py / ADD requirements.txt / ADD entrypoint.sh / -RUN apk add --no-cache sqlite-dev sqlite-libs gcc musl-dev +ADD index.html /var/www/html/index.html +ADD script.js /var/www/html/script.js +RUN apk add --no-cache sqlite-dev sqlite-libs gcc musl-dev nginx RUN pip install -r requirements.txt # Définir les variables d'environnement pour username et password @@ -13,6 +16,11 @@ ENV USERNAME="" \ PASSWORD="" \ NTFY_URL="" \ GHNTFY_TIMEOUT="3600" \ - GHREPO="" + GHNTFY_TOKEN="" + +# Exposer le port 5000 pour l'API et le port 80 pour le serveur web +EXPOSE 5000 80 + +COPY nginx.conf /etc/nginx/nginx.conf ENTRYPOINT ["/entrypoint.sh"] diff --git a/entrypoint.sh b/entrypoint.sh index dfb8e20..3940da6 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -3,5 +3,8 @@ # Génère le contenu du fichier auth.txt à partir des variables d'environnement echo -n "$USERNAME:$PASSWORD" | base64 > /auth.txt +# Démarrer nginx en arrière-plan +nginx -g 'daemon off;' & + # Exécute le script Python exec python ./ntfy.py diff --git a/index.html b/index.html new file mode 100644 index 0000000..43de228 --- /dev/null +++ b/index.html @@ -0,0 +1,43 @@ + + + + + + Github-Ntfy Add a repo + + + + +
+

Github-Ntfy

+

Add a repo

+
+
+
+

Name of the github repo

+
+
+
+
+ github.com/ + +
+
+
+
+
+
+
+ + +
+
+
+

Watched Repositories

+
    + +
+
+
+ + \ No newline at end of file diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..60b9b1b --- /dev/null +++ b/nginx.conf @@ -0,0 +1,32 @@ +events { + worker_connections 1024; +} + +http { + include mime.types; + default_type application/octet-stream; + + server { + listen 80; + + location / { + root /var/www/html; + index index.html; + } + + location /app_repo { + proxy_pass http://127.0.0.1:5000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + location /watched_repos { + proxy_pass http://127.0.0.1:5000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + } +} diff --git a/ntfy.py b/ntfy.py index 3b126e0..91400e6 100644 --- a/ntfy.py +++ b/ntfy.py @@ -2,8 +2,8 @@ import requests import time import os import logging -import json import sqlite3 +import subprocess # Configurer le logger logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') @@ -14,16 +14,9 @@ github_headers = {} if github_token: github_headers['Authorization'] = f"token {github_token}" -repo_list_env = os.environ.get('GHREPO') -watched_repos_list = json.loads(repo_list_env) if repo_list_env else [] - -if not watched_repos_list: - logger.error("Aucun dépôt n'a été spécifié. Veuillez spécifier les dépôts à surveiller dans l'environnement GHREPO") - exit(1) # Connexion à la base de données pour stocker les versions précédentes -db_path = '/github-ntfy/ghntfy_versions.db' -conn = sqlite3.connect(db_path) +conn = sqlite3.connect('/github-ntfy/ghntfy_versions.db', check_same_thread=False) cursor = conn.cursor() # Création de la table si elle n'existe pas @@ -33,6 +26,26 @@ conn.commit() logger.info("Démarrage de la surveillance des versions...") +conn2 = sqlite3.connect('/github-ntfy/watched_repos.db', check_same_thread=False) +cursor2 = conn2.cursor() + +cursor2.execute('''CREATE TABLE IF NOT EXISTS watched_repos + (id INTEGER PRIMARY KEY, repo TEXT)''') +conn2.commit() + + +def get_watched_repos(): + cursor2.execute("SELECT * FROM watched_repos") + watched_repos = cursor2.fetchall() + watched_repos = [] + for repo in watched_repos: + watched_repos.append(repo[1]) + return watched_repos + + +def start_api(): + subprocess.Popen(["python", "ntfy_api.py"]) + def get_latest_releases(watched_repos): releases = [] @@ -101,6 +114,7 @@ def send_to_ntfy(releases, auth, url): if __name__ == "__main__": + start_api() with open('/auth.txt', 'r') as f: auth = f.read().strip() ntfy_url = os.environ.get('NTFY_URL') @@ -108,11 +122,13 @@ if __name__ == "__main__": if auth and ntfy_url: while True: + watched_repos_list = get_watched_repos() latest_release = get_latest_releases(watched_repos_list) if latest_release: send_to_ntfy(latest_release, auth, ntfy_url) time.sleep(timeout) # Attendre une heure avant de vérifier à nouveau else: logger.error("Usage: python ntfy.py") - logger.error("auth: can be generataed by the folowing command: echo -n 'username:password' | base64 and need to be stored in a file named auth.txt") + logger.error( + "auth: can be generataed by the folowing command: echo -n 'username:password' | base64 and need to be stored in a file named auth.txt") logger.error("NTFY_URL: the url of the ntfy server need to be stored in an environment variable named NTFY_URL") diff --git a/ntfy_api.py b/ntfy_api.py new file mode 100644 index 0000000..91fc906 --- /dev/null +++ b/ntfy_api.py @@ -0,0 +1,60 @@ +from flask import Flask, request, jsonify +from flask_cors import CORS +import sqlite3 + +app = Flask(__name__) +CORS(app) + + +def get_db_connection(): + conn = sqlite3.connect('/github-ntfy/watched_repos.db') + conn.row_factory = sqlite3.Row + return conn + + +def close_db_connection(conn): + conn.close() + + +@app.route('/app_repo', methods=['POST']) +def app_repo(): + data = request.json + repo = data.get('repo') + + # Vérifier si le champ 'repo' est présent dans les données JSON + if not repo: + return jsonify({"error": "Le champ 'repo' est requis."}), 400 + + # Établir une connexion à la base de données + conn = get_db_connection() + cursor = conn.cursor() + + try: + # Vérifier si le dépôt existe déjà dans la base de données + cursor.execute("SELECT * FROM watched_repos WHERE repo=?", (repo,)) + existing_repo = cursor.fetchone() + if existing_repo: + return jsonify({"error": f"Le dépôt {repo} existe déjà."}), 409 + + # Ajouter le dépôt à la base de données + cursor.execute("INSERT INTO watched_repos (repo) VALUES (?)", (repo,)) + conn.commit() + return jsonify({"message": f"Le dépôt {repo} a été ajouté à la liste des dépôts surveillés."}) + finally: + # Fermer la connexion à la base de données + close_db_connection(conn) + + +@app.route('/watched_repos', methods=['GET']) +def get_watched_repos(): + db = get_db_connection() + cursor = db.cursor() + cursor.execute("SELECT repo FROM watched_repos") + watched_repos = [repo[0] for repo in cursor.fetchall()] + cursor.close() + db.close() + return jsonify(watched_repos) + + +if __name__ == "__main__": + app.run(debug=True) diff --git a/requirements.txt b/requirements.txt index e8dedf8..322bf8e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,4 @@ requests==2.31.0 -pysqlite3==0.5.2 \ No newline at end of file +pysqlite3==0.5.2 +flask==3.0.2 +flask-cors==4.0.0 \ No newline at end of file diff --git a/script.js b/script.js new file mode 100644 index 0000000..f106fc1 --- /dev/null +++ b/script.js @@ -0,0 +1,45 @@ +document.getElementById('addRepoForm').addEventListener('submit', function(event) { + event.preventDefault(); + let repoName = document.getElementById('repo').value; + fetch('/app_repo', { + method: 'POST', + headers: { + 'Access-Control-Allow-Origin': '*', + 'Content-Type': 'application/json' + }, + body: JSON.stringify({repo: repoName}) + }) + .then(response => { + if (response.ok) { + // Si la requête s'est bien déroulée, actualiser la liste des dépôts surveillés + refreshWatchedRepos(); + } else { + throw new Error('Erreur lors de l\'ajout du dépôt'); + } + }) + .catch(error => { + console.error('Error:', error); + }); +}); + +function refreshWatchedRepos() { + fetch('/watched_repos') + .then(response => response.json()) + .then(data => { + const watchedReposList = document.getElementById('watchedReposList'); + // Vider la liste actuelle + watchedReposList.innerHTML = ''; + // Ajouter chaque dépôt surveillé à la liste + data.forEach(repo => { + const listItem = document.createElement('li'); + listItem.textContent = repo; + watchedReposList.appendChild(listItem); + }); + }) + .catch(error => { + console.error('Error:', error); + }); +} + +// Appeler la fonction pour charger les dépôts surveillés au chargement de la page +refreshWatchedRepos(); \ No newline at end of file