mirror of
https://github.com/YunoHost/apps.git
synced 2024-09-03 20:06:07 +02:00
Create YunohostAppGenerator.py
This commit is contained in:
parent
d52a3adf7e
commit
a5b373b8ee
1 changed files with 577 additions and 0 deletions
577
tools/yunopackage/YunohostAppGenerator.py
Normal file
577
tools/yunopackage/YunohostAppGenerator.py
Normal file
|
@ -0,0 +1,577 @@
|
|||
#### Imports
|
||||
import jinja2 as j2
|
||||
from flask import Flask, render_template, render_template_string, request, redirect, flash, send_file
|
||||
from markupsafe import Markup # No longer imported from Flask
|
||||
# Form libraries
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import StringField, RadioField, SelectField, SubmitField, TextAreaField, BooleanField, SelectMultipleField
|
||||
from wtforms.validators import DataRequired, InputRequired, Optional, Regexp, URL, Length
|
||||
# Markdown to HTML - for debugging purposes
|
||||
from misaka import Markdown, HtmlRenderer
|
||||
# Managing zipfiles
|
||||
import zipfile
|
||||
from flask_cors import CORS
|
||||
from urllib import parse
|
||||
from secrets import token_urlsafe
|
||||
|
||||
#### GLOBAL VARIABLES
|
||||
YOLOGEN_VERSION = '0.7.5'
|
||||
GENERATOR_DICT = {'GENERATOR_VERSION': YOLOGEN_VERSION}
|
||||
|
||||
#### Create FLASK and Jinja Environments
|
||||
url_prefix = ''
|
||||
# url_prefix = '/yunohost-app-generator'
|
||||
|
||||
# app = Flask(__name__)
|
||||
app = Flask(__name__) # Blueprint('main', __name__, url_prefix=url_prefix)
|
||||
app.config['SECRET_KEY'] = token_urlsafe(16) # Necessary for the form CORS
|
||||
cors = CORS(app)
|
||||
|
||||
environment = j2.Environment(loader=j2.FileSystemLoader("templates/"))
|
||||
|
||||
|
||||
#### Custom functions
|
||||
|
||||
# Define custom filter
|
||||
@app.template_filter('render_markdown')
|
||||
def render_markdown(text):
|
||||
renderer = HtmlRenderer()
|
||||
markdown = Markdown(renderer)
|
||||
return markdown(text)
|
||||
|
||||
|
||||
# Add custom filter
|
||||
j2.filters.FILTERS['render_markdown'] = render_markdown
|
||||
|
||||
|
||||
# Converting markdown to html
|
||||
def markdown_file_to_html_string(file):
|
||||
with open(file, 'r') as file:
|
||||
markdown_content = file.read()
|
||||
# Convert content from Markdown to HTML
|
||||
html_content = render_markdown(markdown_content)
|
||||
# Return Markdown and HTML contents
|
||||
return markdown_content, html_content
|
||||
|
||||
|
||||
### Forms
|
||||
|
||||
## PHP forms
|
||||
class Form_PHP_Config(FlaskForm):
|
||||
php_config_file = SelectField('Type de fichier PHP :', choices=[
|
||||
('php-fpm.conf', Markup('Fichier de configuration PHP complet <i class="grayed_hint">(php-fpm.conf)</i>')),
|
||||
('php_extra-fpm.conf',
|
||||
Markup('Fichier de configuration PHP particulier <i class ="grayed_hint">(extra_php-fpm.conf)</i>'))],
|
||||
default='php_extra-fpm.conf', validators=[DataRequired()])
|
||||
## TODO : figure out how to include these comments/title values
|
||||
# 'title': 'Remplace la configuration générée par défaut par un fichier de configuration complet. À éviter si possible.
|
||||
# 'title': "Choisir un fichier permettant un paramétrage d'options complémentaires. C'est généralement recommandé."
|
||||
|
||||
php_config_file_content = TextAreaField("Saisissez le contenu du fichier de configuration PHP :",
|
||||
validators=[Optional()],
|
||||
render_kw={"class": "form-control",
|
||||
"style": "width: 50%;height:11em;min-height: 5.5em; max-height: 55em;flex-grow: 1;box-sizing: border-box;",
|
||||
"title": "TODO",
|
||||
"placeholder": "; Additional php.ini defines, specific to this pool of workers. \n\nphp_admin_value[upload_max_filesize] = 100M \nphp_admin_value[post_max_size] = 100M"})
|
||||
|
||||
|
||||
class Form_PHP(Form_PHP_Config):
|
||||
use_php = BooleanField('Nécessite PHP', default=False)
|
||||
|
||||
|
||||
## NodeJS forms
|
||||
class Form_NodeJS(FlaskForm):
|
||||
use_nodejs = BooleanField('Nécessite NodeJS', default=False)
|
||||
use_nodejs_version = StringField("Version de NodeJS :",
|
||||
render_kw={"placeholder": "20", "class": "form-control",
|
||||
"title": "Saisissez la version de NodeJS à installer. Cela peut-être une version majeure (ex: 20) ou plus précise (ex: 20.1)."}) # TODO : this should be validated using a regex, should be only numbers and any (≥0) number of dots in between
|
||||
use_nodejs_needs_yarn = BooleanField('Nécessite Yarn', default=False, render_kw={
|
||||
"title": "Faut-il installer automatiquement Yarn ? Cela configurera les dépôts spécifiques à Yarn."})
|
||||
|
||||
|
||||
## Python forms
|
||||
|
||||
class Form_Python(FlaskForm):
|
||||
use_python = BooleanField('Nécessite Python',
|
||||
default=False) ## TODO -> python3, python3-pip, python3-ven dependencies by default
|
||||
python_dependencies_type = SelectField('Configuration des dépendances Python :', choices=[
|
||||
('requirements.txt', Markup('Fichier <i>requirements.txt</i>')),
|
||||
('manual_list', 'Liste manuelle')],
|
||||
default='requirements.txt', validators=[DataRequired(), Optional()])
|
||||
python_requirements = TextAreaField("La liste de dépendances inclue dans le fichier requirements.txt :",
|
||||
render_kw={"class": "form-control",
|
||||
"style": "width: 50%;height:5.5em;min-height: 5.5em; max-height: 55em;flex-grow: 1;box-sizing: border-box;",
|
||||
"title": "Lister les dépendances à installer, une par ligne, avec un numéro de version derrière.\nEx: 'dépendance==1.0'.",
|
||||
"placeholder": "tensorflow==2.3.1 \nuvicorn==0.12.2 \nfastapi==0.63.0"})
|
||||
python_dependencies_list = StringField("Liste de dépendances python :",
|
||||
render_kw={"placeholder": "tensorflow uvicorn fastapi",
|
||||
"class": "form-control",
|
||||
"title": "Lister les dépendances à installer, séparées d'un espace."})
|
||||
|
||||
|
||||
## Manifest form
|
||||
# Dependencies form
|
||||
class DependenciesForm(FlaskForm):
|
||||
auto_update = BooleanField("Activer le robot de mise à jour automatiques :", default=False,
|
||||
render_kw={
|
||||
"title": "Si le logiciel est disponible sur github et publie des releases ou des tags pour ses nouvelles versions, un robot proposera automatiquement des mises à jours."})
|
||||
|
||||
## TODO
|
||||
# These infos are used by https://github.com/YunoHost/apps/blob/master/tools/autoupdate_app_sources/autoupdate_app_sources.py
|
||||
# to auto-update the previous asset urls and sha256sum + manifest version
|
||||
# assuming the upstream's code repo is on github and relies on tags or releases
|
||||
# See the 'sources' resource documentation for more details
|
||||
|
||||
# autoupdate.strategy = "latest_github_tag"
|
||||
|
||||
dependencies = StringField("Dépendances de l'application (liste des paquets apt) à installer :",
|
||||
render_kw={"placeholder": "foo foo2.1-ext somerandomdep", "class": "form-control",
|
||||
"title": "Lister les paquets dont dépend l'application, séparés par un espace."})
|
||||
|
||||
use_db = SelectField("Configurer une base de données :", choices=[
|
||||
('false', "Non"),
|
||||
('mysql', "MySQL/MariaDB"),
|
||||
('postgresql', "PostgreSQL")],
|
||||
default='false',
|
||||
render_kw={"title": "L'application nécessite-t-elle une base de données ?"})
|
||||
|
||||
|
||||
# manifest
|
||||
class manifestForm(DependenciesForm):
|
||||
version = StringField('Version', validators=[Regexp('\d{1,4}.\d{1,4}(.\d{1,4})?(.\d{1,4})?~ynh\d+')],
|
||||
render_kw={"class": "form-control",
|
||||
"placeholder": "1.0~ynh1"})
|
||||
description_en = TextAreaField("Description en quelques lignes de l'application, en anglais :",
|
||||
validators=[DataRequired()],
|
||||
render_kw={"class": "form-control", "style": "resize: none;",
|
||||
"title": "Explain in *a few (10~15) words* the purpose of the app \\"
|
||||
"or what it actually does (it is meant to give a rough idea to users browsing a catalog of 100+ apps)"})
|
||||
description_fr = TextAreaField("Description en quelques lignes de l'application :", validators=[DataRequired()],
|
||||
render_kw={"class": "form-control", "style": "resize: none;",
|
||||
"title": "Expliquez en *quelques* (10~15) mots l'utilité de l'app \\"
|
||||
"ou ce qu'elle fait (l'objectif est de donner une idée grossière pour des utilisateurs qui naviguent dans un catalogue de 100+ apps)"})
|
||||
|
||||
# TODO : handle multiple names separated by commas (.split(',') ?
|
||||
maintainers = StringField('Mainteneurs et mainteneuses', render_kw={"class": "form-control",
|
||||
"placeholder": "Généralement vous mettez votre nom ici… Si vous êtes d'accord ;)"}) # TODO : Usually you put your name here… if you like ;)
|
||||
architectures = SelectMultipleField('Architectures supportées :', choices=[
|
||||
('all', 'Toutes les architectures'),
|
||||
('amd64', 'amd64'),
|
||||
('arm64', 'arm64'),
|
||||
('i386', 'i386'),
|
||||
('todo', 'TODO : list more architectures')],
|
||||
default=['all'], validators=[DataRequired()])
|
||||
yunohost_required_version = StringField('Mainteneurs et mainteneuses', render_kw={"class": "form-control",
|
||||
"placeholder": "11.1.21",
|
||||
"title": "Version minimale de Yunohost pour que l'application fonctionne."})
|
||||
|
||||
multi_instance = BooleanField("Application multi-instance", default=False,
|
||||
render_kw={"class": "",
|
||||
"title": "Peux-t-on installer simultannément plusieurs fois l'application sur un même serveur ?"})
|
||||
|
||||
ldap = SelectField('Integrate with LDAP (user can login using Yunohost credentials :', choices=[
|
||||
('false', 'False'),
|
||||
('true', 'True'),
|
||||
('not_relevant', 'Not relevant')], default='not_relevant', validators=[DataRequired()], render_kw={
|
||||
"title": """Not to confuse with the "sso" key: the "ldap" key corresponds to wether or not a user *can* login on the app using its YunoHost credentials."""})
|
||||
sso = SelectField('Integrate with Yunohost SingleSignOn (SSO) :', choices=[
|
||||
('false', 'False'),
|
||||
('true', 'True'),
|
||||
('not_relevant', 'Not relevant')], default='not_relevant', validators=[DataRequired()], render_kw={
|
||||
"title": """Not to confuse with the "ldap" key: the "sso" key corresponds to wether or not a user is *automatically logged-in* on the app when logged-in on the YunoHost portal."""})
|
||||
|
||||
license = StringField('Licence', validators=[DataRequired()], render_kw={"class": "form-control",
|
||||
"placeholder": "GPL"})
|
||||
|
||||
website = StringField('Site web', validators=[URL(), Optional()], render_kw={"class": "form-control",
|
||||
"placeholder": "https://awesome-app-website.com"})
|
||||
demo = StringField('Site de démonstration', validators=[URL(), Optional()], render_kw={"class": "form-control",
|
||||
"placeholder": "https://awesome-app-website.com/demo"})
|
||||
admindoc = StringField("Documentation d'aministration", validators=[URL(), Optional()],
|
||||
render_kw={"class": "form-control",
|
||||
"placeholder": "https://awesome-app-website.com/doc/admin"})
|
||||
userdoc = StringField("Documentation d'utilisation", validators=[URL(), Optional()],
|
||||
render_kw={"class": "form-control",
|
||||
"placeholder": "https://awesome-app-website.com/doc/user"})
|
||||
code = StringField('Dépôt de code', validators=[URL(), Optional()], render_kw={"class": "form-control",
|
||||
"placeholder": "https://awesome-app-website.com/get-the-code"})
|
||||
|
||||
data_dir = BooleanField("L'application nécessite un répertoire dédié pour ses données", default=False,
|
||||
render_kw={"title": "Faut-il créer un répertoire /home/yunohost.app/votreApplication ?"})
|
||||
data_subdirs = StringField('Si nécessaire, lister les sous-répertoires à configurer :',
|
||||
validators=[Optional()], render_kw={"class": "form-control",
|
||||
"placeholder": "data, uploads, themes"})
|
||||
use_whole_domain = BooleanField("L'application nécessite d'utiliser tout un domaine (installation à la racine) :",
|
||||
default=False,
|
||||
render_kw={
|
||||
"title": "Doit-on installer l'application à la racine du domaine ? Sinon, on pourra l'installer dans un sous-dossier, par exemple /mon_app."})
|
||||
supports_change_url = BooleanField(
|
||||
"L'application autorise le changement d'adresse (changement de domaine ou de chemin)", default=True,
|
||||
render_kw={"title": "Faut-il permettre le changement d'URL pour l'application ? (fichier change_url)"})
|
||||
|
||||
needs_admin = BooleanField("L'application nécessite de configurer un compte d'administration :", default=False,
|
||||
render_kw={"class": "",
|
||||
"title": "Faut-il configurer un compte admin à l'installation ?"})
|
||||
|
||||
# admin_password_help_message = BooleanField("TODO :", default=False,
|
||||
# render_kw={"class": "",
|
||||
# "title": "TODO"})
|
||||
|
||||
language = SelectMultipleField('Langues supportées :', choices=[
|
||||
('en', 'English'),
|
||||
('fr', 'Français'),
|
||||
('en', 'Spanish'),
|
||||
('it', 'Italian'),
|
||||
('de', 'German'),
|
||||
('zh', 'Chinese'),
|
||||
('jp', 'Japanese'),
|
||||
('da', 'Danish'),
|
||||
('pt', 'Portugese'),
|
||||
('nl', 'Dutch'),
|
||||
('ru', 'Russian')],
|
||||
default=['en'], validators=[DataRequired()])
|
||||
|
||||
default_language = SelectField('Langues par défaut :', choices=[
|
||||
('en', 'English'),
|
||||
('fr', 'Français'),
|
||||
('en', 'Spanish'),
|
||||
('it', 'Italian'),
|
||||
('zh', 'Chinese'),
|
||||
('jp', 'Japanese'),
|
||||
('da', 'Danish'),
|
||||
('pt', 'Portugese'),
|
||||
('nl', 'Dutch'),
|
||||
('ru', 'Russian')],
|
||||
default=['en'])
|
||||
|
||||
visibility = RadioField("Visibilité de l'application :", choices=[
|
||||
('admin', "Administrateur/administratrice uniquement"),
|
||||
('all_users', "Personnes connectées"),
|
||||
('visitors', "Publique")],
|
||||
default='all_users', validators=[DataRequired()])
|
||||
|
||||
source_url = StringField("Code source ou exécutable de l'application", validators=[DataRequired(), URL()],
|
||||
render_kw={"class": "form-control",
|
||||
"placeholder": "https://github.com/foo/bar/archive/refs/tags/v1.2.3.tar.gz"}) # Application source code URL
|
||||
sha256sum = StringField('Empreinte du code source (format sha256sum)',
|
||||
validators=[DataRequired(), Length(min=64, max=64)],
|
||||
render_kw={"class": "form-control",
|
||||
"placeholder": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
"title": "Sha256sum of the archive. Should be 64 characters-long."}) # Source code hash (sha256sum format)
|
||||
|
||||
|
||||
## Main form
|
||||
|
||||
class appGeneratorForm(manifestForm, DependenciesForm, Form_PHP, Form_NodeJS, Form_Python):
|
||||
app_name = StringField("Nom de l'application :", validators=[DataRequired()],
|
||||
render_kw={"placeholder": "My Great App", "class": "form-control",
|
||||
"title": "Définir le nom de l'application, affiché dans l'interface"})
|
||||
app_id = StringField(
|
||||
Markup(
|
||||
"""Identifiant (id) de l'application <i class="grayed_hint">(en minuscule et sans espaces)</i> :"""),
|
||||
validators=[DataRequired(), Regexp("[a-z_1-9]+.*(?<!_ynh)$")],
|
||||
render_kw={"placeholder": "yunohost_awesome_app", "class": "form-control",
|
||||
"title": "Définir l'identifiant de l'application, utilisé pour le nom du dépôt Github"})
|
||||
|
||||
tutorial = SelectField("Type d'application :", choices=[
|
||||
('false', "Version épurée"),
|
||||
('true', "Version tutoriel")],
|
||||
default='true', validators=[DataRequired()])
|
||||
|
||||
use_logrotate = BooleanField("Utiliser logrotate pour gérer les journaux :", default=True,
|
||||
render_kw={
|
||||
"title": "Si l'application genère des journaux (log), cette option permet d'en gérer l'archivage. Recommandé."})
|
||||
# TODO : specify custom log file
|
||||
# custom_log_file = "/var/log/$app/$app.log" "/var/log/nginx/${domain}-error.log"
|
||||
use_fail2ban = BooleanField("Protéger l'application des attaques par force brute (via fail2ban) :", default=True,
|
||||
render_kw={
|
||||
"title": "Si l'application genère des journaux (log) d'erreurs de connexion, cette option permet de bannir automatiquement les IP au bout d'un certain nombre d'essais de mot de passe. Recommandé."})
|
||||
use_cron = BooleanField("Ajouter une tâche CRON pour cette application :", default=False,
|
||||
render_kw={
|
||||
"title": "Créer une tâche cron pour gérer des opérations périodiques de l'application."})
|
||||
cron_config_file = TextAreaField("Saisissez le contenu du fichier CRON :",
|
||||
validators=[Optional()],
|
||||
render_kw={"class": "form-control",
|
||||
"style": "width: 50%;height:22em;min-height: 5.5em; max-height: 55em;flex-grow: 1;box-sizing: border-box;",
|
||||
"title": "Saisir le contenu du fichier de la tâche CRON."})
|
||||
|
||||
fail2ban_regex = StringField("Expression régulière pour fail2ban :",
|
||||
# Regex to match into the log for a failed login
|
||||
validators=[Optional()],
|
||||
render_kw={"placeholder": "A regular expression",
|
||||
"class": "form-control",
|
||||
"title": "Expression régulière à vérifier dans le journal pour que fail2ban s'active (cherchez une ligne qui indique une erreur d'identifiants deconnexion)."})
|
||||
|
||||
nginx_config_file = TextAreaField("Saisissez le contenu du fichier de configuration du serveur NGINX :",
|
||||
validators=[Optional()],
|
||||
render_kw={"class": "form-control",
|
||||
"style": "width: 50%;height:22em;min-height: 5.5em; max-height: 55em;flex-grow: 1;box-sizing: border-box;",
|
||||
"title": "Saisir le contenu du fichier de configuration du serveur NGINX.",
|
||||
"placeholder": "location __PATH__/ {\n \n proxy_pass http://127.0.0.1:__PORT__;\n proxy_set_header X-Real-IP $remote_addr;\n proxy_set_header Host $host;\n proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n \n # Include SSOWAT user panel.\n include conf.d/yunohost_panel.conf.inc;\n }"})
|
||||
|
||||
use_systemd_service = BooleanField(
|
||||
"Utiliser un service système (via systemd) pour gérer le fonctionnement de l'application :", default=False,
|
||||
render_kw={
|
||||
"title": "Un service systemd s'occupera de démarrer l'application avec le système, et permettra de l'arrêter ou la redémarrer. Recommandé."})
|
||||
systemd_config_file = TextAreaField("Saisissez le contenu du fichier de configuration du service SystemD :",
|
||||
validators=[Optional()],
|
||||
render_kw={"class": "form-control",
|
||||
"style": "width: 50%;height:22em;min-height: 5.5em; max-height: 55em;flex-grow: 1;box-sizing: border-box;",
|
||||
"title": "Saisir le contenu du fichier de configuration du service systemd."})
|
||||
systemd_service_description = StringField("Description du service de l'application :",
|
||||
validators=[Optional()],
|
||||
render_kw={"placeholder": "A short description of the app",
|
||||
"class": "form-control",
|
||||
"style": "width: 30%;",
|
||||
"title": "Décrire en une ligne ce que fait ce service. Ceci sera affiché dans l'interface d'administration."})
|
||||
|
||||
use_custom_config_file = BooleanField(
|
||||
"Utiliser un fichier de configuration personnalisé :", default=False,
|
||||
render_kw={
|
||||
"title": "Est-ce que l'application nécessite un fichier de configuration personnalisé ? (du type .env, config.json, parameters.yaml, …)"})
|
||||
custom_config_file = StringField("Nom du fichier à utiliser :",
|
||||
validators=[Optional()],
|
||||
render_kw={"placeholder": "config.json",
|
||||
"class": "form-control",
|
||||
"style": "width: 30%;",
|
||||
"title": "Décrire en une ligne ce que fait ce service. Ceci sera affiché dans l'interface d'administration."})
|
||||
|
||||
custom_config_file_content = TextAreaField("Saisissez le contenu du fichier de configuration personnalisé :",
|
||||
validators=[Optional()],
|
||||
render_kw={"class": "form-control",
|
||||
"style": "width: 50%;height:22em;min-height: 5.5em; max-height: 55em;flex-grow: 1;box-sizing: border-box;",
|
||||
"title": "Saisir le contenu du fichier de configuration personnalisé."})
|
||||
|
||||
needs_exposed_ports = StringField("Nom de l'application :", validators=[Optional(), Regexp('([0-9]+,)+([0-9]+)?')],
|
||||
# TODO : add this in the HTML
|
||||
render_kw={"placeholder": "5000, ", "class": "form-control",
|
||||
"title": "Liste de ports à ouvrir publiquement, séparés par des virgules. NE PAS ACTIVER si le port est uniquement utilisé en interne ou disponible via le reverse-proxy de Nginx."})
|
||||
|
||||
submit = SubmitField('Soumettre')
|
||||
|
||||
|
||||
#### Retriving templates
|
||||
|
||||
parameters = dict(GENERATOR_DICT)
|
||||
|
||||
template_manifest = environment.get_template("manifest.j2")
|
||||
manifest = dict(GENERATOR_DICT)
|
||||
|
||||
template_install = environment.get_template("install.j2")
|
||||
install = dict(GENERATOR_DICT)
|
||||
|
||||
|
||||
#### Initialising variables
|
||||
|
||||
|
||||
#### Web pages
|
||||
|
||||
@app.route(url_prefix + "/", methods=['GET', 'POST'])
|
||||
def main_form():
|
||||
if not 'appGeneratorForm' in locals():
|
||||
main_form = appGeneratorForm()
|
||||
|
||||
if request.method == "POST":
|
||||
result = request.form
|
||||
results = dict(result)
|
||||
# print("[DEBUG] This is a POST request")
|
||||
# print(results)
|
||||
for key, value in results.items():
|
||||
parameters[key] = value
|
||||
parameters["preview"] = True
|
||||
# print(install)
|
||||
if main_form.validate_on_submit():
|
||||
parameters["invalid_form"] = False
|
||||
print()
|
||||
print('formulaire valide')
|
||||
# print(main_form.data.items())
|
||||
|
||||
for key, value in main_form.data.items():
|
||||
parameters[key] = value # TODO change the name
|
||||
|
||||
templates = (
|
||||
'templates/manifest.j2', 'templates/install.j2', 'templates/remove.j2', 'templates/backup.j2',
|
||||
'templates/restore.j2', 'templates/upgrade.j2', 'templates/config.j2', 'templates/change_url.j2',
|
||||
'templates/_common.sh.j2')
|
||||
markdown_to_html = dict()
|
||||
for template in templates:
|
||||
markdown_content, html_content = markdown_file_to_html_string(template)
|
||||
template_key = template.split('templates/')[1].split('.j2')[
|
||||
0] # Let's retrieve what's the exact template used
|
||||
markdown_to_html[template_key] = {"markdown_content": markdown_content,
|
||||
"html_content": html_content}
|
||||
# print(markdown_to_html["markdown_content"])
|
||||
# print(markdown_to_html["html_content"])
|
||||
|
||||
## Prepare the file contents for the download button
|
||||
# print(markdown_to_html['manifest']['markdown_content'])
|
||||
|
||||
# Workaround so /download_zip can access the content - FIXME ?
|
||||
global template_manifest_content
|
||||
global template_install_content
|
||||
global template_remove_content
|
||||
global template_backup_content
|
||||
global template_restore_content
|
||||
global template_upgrade_content
|
||||
global template_config_content
|
||||
global template_change_url_content
|
||||
global template_common_sh_content
|
||||
global custom_config_file
|
||||
global nginx_config_file
|
||||
global systemd_config_file
|
||||
global cron_config_file
|
||||
|
||||
template_manifest_content = render_template_string(markdown_to_html['manifest']['markdown_content'],
|
||||
parameters=parameters,
|
||||
main_form=main_form)
|
||||
|
||||
template_install_content = render_template_string(markdown_to_html['install']['markdown_content'],
|
||||
parameters=parameters, main_form=main_form,
|
||||
markdown_to_html=markdown_to_html['install'])
|
||||
|
||||
template_remove_content = render_template_string(markdown_to_html['remove']['markdown_content'],
|
||||
parameters=parameters, main_form=main_form,
|
||||
markdown_to_html=markdown_to_html['remove'])
|
||||
|
||||
template_backup_content = render_template_string(markdown_to_html['backup']['markdown_content'],
|
||||
parameters=parameters, main_form=main_form,
|
||||
markdown_to_html=markdown_to_html['backup'])
|
||||
|
||||
template_restore_content = render_template_string(markdown_to_html['restore']['markdown_content'],
|
||||
parameters=parameters, main_form=main_form,
|
||||
markdown_to_html=markdown_to_html['restore'])
|
||||
|
||||
template_upgrade_content = render_template_string(markdown_to_html['upgrade']['markdown_content'],
|
||||
parameters=parameters, main_form=main_form,
|
||||
markdown_to_html=markdown_to_html['upgrade'])
|
||||
|
||||
template_config_content = render_template_string(markdown_to_html['config']['markdown_content'],
|
||||
parameters=parameters, main_form=main_form,
|
||||
markdown_to_html=markdown_to_html['config'])
|
||||
|
||||
template_common_sh_content = render_template_string(markdown_to_html['_common.sh']['markdown_content'],
|
||||
parameters=parameters, main_form=main_form,
|
||||
markdown_to_html=markdown_to_html['_common.sh'])
|
||||
|
||||
if parameters['supports_change_url']:
|
||||
template_change_url_content = render_template_string(markdown_to_html['change_url']['markdown_content'],
|
||||
parameters=parameters, main_form=main_form,
|
||||
markdown_to_html=markdown_to_html['change_url'])
|
||||
else:
|
||||
template_change_url_content = False
|
||||
|
||||
print(parameters['custom_config_file'])
|
||||
print(parameters['use_custom_config_file'])
|
||||
return render_template('index.html', parameters=parameters, main_form=main_form,
|
||||
markdown_to_html=markdown_to_html,
|
||||
template_manifest_content=template_manifest_content,
|
||||
template_install_content=template_install_content,
|
||||
template_remove_content=template_remove_content,
|
||||
template_backup_content=template_backup_content,
|
||||
template_restore_content=template_restore_content,
|
||||
template_upgrade_content=template_upgrade_content,
|
||||
template_config_content=template_config_content,
|
||||
template_change_url_content=template_change_url_content,
|
||||
template_common_sh_content=template_common_sh_content,
|
||||
nginx_config_file=parameters['nginx_config_file'],
|
||||
systemd_config_file=parameters['systemd_config_file'],
|
||||
custom_config_file=parameters['custom_config_file'],
|
||||
cron_config_file=parameters['cron_config_file'], url_prefix=url_prefix)
|
||||
else:
|
||||
print("[DEBUG] Formulaire invalide: ", main_form.errors)
|
||||
parameters["preview"] = False
|
||||
parameters["invalid_form"] = True
|
||||
|
||||
elif request.method == "GET":
|
||||
parameters["preview"] = False
|
||||
|
||||
return render_template('index.html', parameters=install, main_form=main_form, url_prefix=url_prefix)
|
||||
|
||||
|
||||
@app.route("/install")
|
||||
def install_page():
|
||||
return render_template(template_install, parameters=install)
|
||||
|
||||
|
||||
@app.route('/generator', methods=('GET', 'POST'))
|
||||
def create():
|
||||
if request.method == 'POST':
|
||||
title = request.form['title']
|
||||
content = request.form['content']
|
||||
|
||||
if not title:
|
||||
flash('Title is required!')
|
||||
elif not content:
|
||||
flash('Content is required!')
|
||||
else:
|
||||
install.append({'title': title, 'content': content})
|
||||
return redirect('/')
|
||||
return render_template('form.html')
|
||||
|
||||
|
||||
@app.route('/download_zip', methods=('GET', 'POST'))
|
||||
def telecharger_zip():
|
||||
# Retrieve arguments
|
||||
print("Génération du .zip")
|
||||
app_id = request.args.get('app_id')
|
||||
print("Génération du .zip pour " + app_id)
|
||||
|
||||
custom_config_file = parse.unquote(request.args.get('custom_config_file'))
|
||||
custom_config_file_content = parse.unquote(request.args.get('custom_config_file_content'))
|
||||
systemd_config_file = parse.unquote(request.args.get('systemd_config_file'))
|
||||
nginx_config_file = parse.unquote(request.args.get('nginx_config_file'))
|
||||
cron_config_file = parse.unquote(request.args.get('cron_config_file'))
|
||||
|
||||
global template_manifest_content
|
||||
global template_install_content
|
||||
global template_remove_content
|
||||
global template_backup_content
|
||||
global template_restore_content
|
||||
global template_upgrade_content
|
||||
global template_config_content
|
||||
global template_change_url_content
|
||||
global template_common_sh_content
|
||||
|
||||
# global custom_config_file
|
||||
|
||||
use_php = request.args.get('use_php')
|
||||
print("PHP")
|
||||
print(use_php)
|
||||
php_config_file = parse.unquote(request.args.get('php_config_file'))
|
||||
php_config_file_content = parse.unquote(request.args.get('php_config_file_content'))
|
||||
|
||||
archive_name = app_id + '.zip' # Actually it's the javascript that decide of the filename… this is only an internal name
|
||||
|
||||
# Generate the zip file (will be stored in the working directory)
|
||||
with zipfile.ZipFile(archive_name, 'w') as zf:
|
||||
# Add text in directly in the ZIP, as a file
|
||||
zf.writestr('manifest.toml', template_manifest_content)
|
||||
zf.writestr('scripts/install', template_install_content)
|
||||
zf.writestr('scripts/remove', template_remove_content)
|
||||
zf.writestr('scripts/backup', template_backup_content)
|
||||
zf.writestr('scripts/restore', template_restore_content)
|
||||
zf.writestr('scripts/upgrade', template_upgrade_content)
|
||||
zf.writestr('scripts/_common_sh', template_common_sh_content)
|
||||
|
||||
if template_config_content:
|
||||
zf.writestr('scripts/config', template_config_content)
|
||||
if template_change_url_content:
|
||||
zf.writestr('scripts/change_url', template_change_url_content)
|
||||
if custom_config_file:
|
||||
zf.writestr('conf/' + custom_config_file, custom_config_file_content)
|
||||
if systemd_config_file:
|
||||
zf.writestr('conf/systemd.service', systemd_config_file)
|
||||
if nginx_config_file:
|
||||
zf.writestr('conf/nginx.conf', nginx_config_file)
|
||||
if cron_config_file:
|
||||
zf.writestr('conf/task.conf', cron_config_file)
|
||||
if use_php == "True":
|
||||
zf.writestr('conf/' + php_config_file, php_config_file_content)
|
||||
|
||||
# Send the zip file to the user
|
||||
return send_file(archive_name, as_attachment=True)
|
||||
|
||||
|
||||
#### Running the web server
|
||||
if __name__ == "__main__":
|
||||
app.run(debug=True)
|
Loading…
Add table
Reference in a new issue