From 6ffc651b7448a942e49bd07340ee19f4a52a57e9 Mon Sep 17 00:00:00 2001 From: yunohost-bot Date: Mon, 16 Jan 2023 21:30:26 +0000 Subject: [PATCH 01/23] Auto-update README --- README_fr.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README_fr.md b/README_fr.md index 80b9be1..37f63cc 100644 --- a/README_fr.md +++ b/README_fr.md @@ -5,29 +5,29 @@ It shall NOT be edited by hand. # Coin pour YunoHost -[![Niveau d'intégration](https://dash.yunohost.org/integration/coin.svg)](https://dash.yunohost.org/appci/app/coin) ![Statut du fonctionnement](https://ci-apps.yunohost.org/ci/badges/coin.status.svg) ![Statut de maintenance](https://ci-apps.yunohost.org/ci/badges/coin.maintain.svg) +[![Niveau d’intégration](https://dash.yunohost.org/integration/coin.svg)](https://dash.yunohost.org/appci/app/coin) ![Statut du fonctionnement](https://ci-apps.yunohost.org/ci/badges/coin.status.svg) ![Statut de maintenance](https://ci-apps.yunohost.org/ci/badges/coin.maintain.svg) [![Installer Coin avec YunoHost](https://install-app.yunohost.org/install-with-yunohost.svg)](https://install-app.yunohost.org/?app=coin) *[Read this readme in english.](./README.md)* -> *Ce package vous permet d'installer Coin rapidement et simplement sur un serveur YunoHost. -Si vous n'avez pas YunoHost, regardez [ici](https://yunohost.org/#/install) pour savoir comment l'installer et en profiter.* +> *Ce package vous permet d’installer Coin rapidement et simplement sur un serveur YunoHost. +Si vous n’avez pas YunoHost, regardez [ici](https://yunohost.org/#/install) pour savoir comment l’installer et en profiter.* -## Vue d'ensemble +## Vue d’ensemble COIN is an Information System designed for associative ISPs in the FFDN. **Version incluse :** 20220401~ynh1 -## Captures d'écran +## Captures d’écran -![Capture d'écran de Coin](./doc/screenshots/user-subscriptions.png) +![Capture d’écran de Coin](./doc/screenshots/user-subscriptions.png) ## Documentations et ressources -* Site officiel de l'app : -* Dépôt de code officiel de l'app : +* Site officiel de l’app : +* Dépôt de code officiel de l’app : * Documentation YunoHost pour cette app : * Signaler un bug : @@ -43,4 +43,4 @@ ou sudo yunohost app upgrade coin -u https://github.com/YunoHost-Apps/coin_ynh/tree/testing --debug ``` -**Plus d'infos sur le packaging d'applications :** +**Plus d’infos sur le packaging d’applications :** \ No newline at end of file From e9006664c8ce5cf0c30b2c231da5bad2947dcab5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Gaspar?= <46165813+ericgaspar@users.noreply.github.com> Date: Mon, 16 Jan 2023 22:30:45 +0100 Subject: [PATCH 02/23] homogenize the description --- manifest.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/manifest.json b/manifest.json index a5154dc..32bb293 100644 --- a/manifest.json +++ b/manifest.json @@ -3,8 +3,8 @@ "id": "coin", "packaging_format": 1, "description": { - "en": "Member dashboard for non profit isp.", - "fr": "Coin est un Outil pour un Internet Neutre." + "en": "Member dashboard for non profit isp", + "fr": "Coin est un Outil pour un Internet Neutre" }, "version": "20220401~ynh1", "url": "https://code.ffdn.org/FFDN/coin/", From e61f2715363584aeb855b7683c4dfeb8d81e8eba Mon Sep 17 00:00:00 2001 From: yunohost-bot Date: Thu, 16 Nov 2023 16:00:32 +0000 Subject: [PATCH 03/23] Auto-update README --- README_fr.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README_fr.md b/README_fr.md index 37f63cc..c5cb7cc 100644 --- a/README_fr.md +++ b/README_fr.md @@ -5,7 +5,8 @@ It shall NOT be edited by hand. # Coin pour YunoHost -[![Niveau d’intégration](https://dash.yunohost.org/integration/coin.svg)](https://dash.yunohost.org/appci/app/coin) ![Statut du fonctionnement](https://ci-apps.yunohost.org/ci/badges/coin.status.svg) ![Statut de maintenance](https://ci-apps.yunohost.org/ci/badges/coin.maintain.svg) +[![Niveau d’intégration](https://dash.yunohost.org/integration/coin.svg)](https://dash.yunohost.org/appci/app/coin) ![Statut du fonctionnement](https://ci-apps.yunohost.org/ci/badges/coin.status.svg) ![Statut de maintenance](https://ci-apps.yunohost.org/ci/badges/coin.maintain.svg) + [![Installer Coin avec YunoHost](https://install-app.yunohost.org/install-with-yunohost.svg)](https://install-app.yunohost.org/?app=coin) *[Read this readme in english.](./README.md)* @@ -28,7 +29,7 @@ COIN is an Information System designed for associative ISPs in the FFDN. * Site officiel de l’app : * Dépôt de code officiel de l’app : -* Documentation YunoHost pour cette app : +* YunoHost Store: * Signaler un bug : ## Informations pour les développeurs From 86d4bf33615f2d4297380a6de38a6d4e79e08823 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 16 Dec 2023 15:11:09 +0100 Subject: [PATCH 04/23] Packaging v2 --- check_process | 25 ---- conf/app.src | 7 - conf/local.py.j2 | 4 +- conf/nginx.conf | 10 +- conf/systemd.service | 32 ++--- manifest.json | 68 ---------- manifest.toml | 73 ++++++++++ scripts/_common.sh | 20 --- scripts/backup | 48 +------ scripts/change_url | 125 +---------------- scripts/install | 191 +++++--------------------- scripts/remove | 79 +---------- scripts/restore | 115 +++------------- scripts/upgrade | 169 ++++------------------- scripts/ynh_install_python | 267 ------------------------------------- tests.toml | 3 + 16 files changed, 179 insertions(+), 1057 deletions(-) delete mode 100644 check_process delete mode 100644 conf/app.src delete mode 100644 manifest.json create mode 100644 manifest.toml delete mode 100644 scripts/ynh_install_python create mode 100644 tests.toml diff --git a/check_process b/check_process deleted file mode 100644 index 894a568..0000000 --- a/check_process +++ /dev/null @@ -1,25 +0,0 @@ -;; Test complet sans multisite - auto_remove=1 - ; Manifest - domain="domain.tld" - admin="john" - email="john@example.com" - isp_name="ARN" - isp_site="https://arn-fai.net" - ; Checks - pkg_linter=1 - setup_sub_dir=0 - setup_root=1 - setup_nourl=0 - setup_private=0 - setup_public=0 - upgrade=1 - # 20200630~ynh1 - #upgrade=1 from_commit=41916dc245ca37d999782cc2bc46a3a6ea9fa1c8 - backup_restore=1 - multi_instance=0 - port_already_use=0 - change_url=0 -;;; Options -Email= -Notification=none diff --git a/conf/app.src b/conf/app.src deleted file mode 100644 index be1a359..0000000 --- a/conf/app.src +++ /dev/null @@ -1,7 +0,0 @@ -SOURCE_URL=https://code.ffdn.org/ffdn/coin/-/archive/afcf0846f939e80f34956d5f24f7bdd6a5058282/coin-afcf0846f939e80f34956d5f24f7bdd6a5058282.tar.gz -SOURCE_SUM=71a2570a7f145ae826042853f7af2dff46350ef70f83698871b14fa7f9884219 -SOURCE_SUM_PRG=sha256sum -SOURCE_FORMAT=tar.gz -SOURCE_IN_SUBDIR=true -SOURCE_FILENAME= -SOURCE_EXTRACT=true diff --git a/conf/local.py.j2 b/conf/local.py.j2 index b260fee..d42a4be 100644 --- a/conf/local.py.j2 +++ b/conf/local.py.j2 @@ -8,10 +8,10 @@ DEBUG = TEMPLATE_DEBUG = False ALLOWED_HOSTS = ['{{ domain }}'] URL_PREFIX = '{{ prefix }}' -STATIC_ROOT = '{{ final_path }}/static' +STATIC_ROOT = '{{ install_dir }}/static' NOTIFICATION_EMAILS = ['{{ email }}'] DEFAULT_FROM_EMAIL = 'notifier@{{ domain }}' -SITE_URL = "https://{{ domain }}{{ path_url }}" +SITE_URL = "https://{{ domain }}{{ path }}" SECRET_KEY = '{{ secret }}' ISP = { 'NAME': '{{ isp_name }}', diff --git a/conf/nginx.conf b/conf/nginx.conf index 6f929db..2ee1541 100644 --- a/conf/nginx.conf +++ b/conf/nginx.conf @@ -5,19 +5,19 @@ location __PATH__/ { location __PATH__/protected/ { internal; - alias __FINALPATH__/__NAME__/smedia/; + alias __INSTALL_DIR__/__NAME__/smedia/; } location __PATH__/media/ { - alias __FINALPATH__/media/; + alias __INSTALL_DIR__/media/; } location __PATH__/static/ { - alias __FINALPATH__/static/; + alias __INSTALL_DIR__/static/; } location __PATH__/assets/ { - alias __FINALPATH__/static/; + alias __INSTALL_DIR__/static/; } location @__NAME__ { @@ -25,5 +25,5 @@ location @__NAME__ { 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; - proxy_pass http://unix:__FINALPATH__/sock; + proxy_pass http://unix:__INSTALL_DIR__/sock; } diff --git a/conf/systemd.service b/conf/systemd.service index 3e1d373..1531ef2 100644 --- a/conf/systemd.service +++ b/conf/systemd.service @@ -1,16 +1,16 @@ -[Unit] -Description=__APP__ gunicorn daemon -After=network.target - -[Service] -PIDFile=/run/gunicorn/__APP__-pid -User=__APP__ -Group=__APP__ -WorkingDirectory=__FINALPATH__/ -ExecStart=__YNH_PYTHON_PATH__/gunicorn -c __FINALPATH__/gunicorn_config.py __APP__.wsgi -ExecReload=/bin/kill -s HUP $MAINPID -ExecStop=/bin/kill -s TERM $MAINPID -PrivateTmp=true - -[Install] -WantedBy=multi-user.target +[Unit] +Description=__APP__ gunicorn daemon +After=network.target + +[Service] +PIDFile=/run/gunicorn/__APP__-pid +User=__APP__ +Group=__APP__ +WorkingDirectory=__INSTALL_DIR__/ +ExecStart=__YNH_PYTHON_PATH__/gunicorn -c __INSTALL_DIR__/gunicorn_config.py __APP__.wsgi +ExecReload=/bin/kill -s HUP $MAINPID +ExecStop=/bin/kill -s TERM $MAINPID +PrivateTmp=true + +[Install] +WantedBy=multi-user.target diff --git a/manifest.json b/manifest.json deleted file mode 100644 index a5154dc..0000000 --- a/manifest.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "name": "Coin", - "id": "coin", - "packaging_format": 1, - "description": { - "en": "Member dashboard for non profit isp.", - "fr": "Coin est un Outil pour un Internet Neutre." - }, - "version": "20220401~ynh1", - "url": "https://code.ffdn.org/FFDN/coin/", - "upstream": { - "license": "AGPL-3.0-or-later", - "website": "https://code.ffdn.org/FFDN/coin", - "code": "https://code.ffdn.org/ffdn/coin" - }, - "license": "AGPL-3.0-or-later", - "maintainer": { - "name": "ljf", - "email": "ljf+coin_ynh@grimaud.me", - "url": "https://www.arn-fai.net" - }, - "requirements": { - "yunohost": ">= 11.0.0" - }, - "multi_instance": true, - "services": [ - "nginx" - ], - "arguments": { - "install": [ - { - "name": "domain", - "type": "domain" - }, - { - "name": "admin", - "type": "user" - }, - { - "name": "email", - "type": "string", - "ask": { - "en": "Choose email were send notification", - "fr": "Choisissez l'email vers lequel envoyer les notifications" - }, - "example": "admin@example.tld" - }, - { - "name": "isp_name", - "type": "string", - "ask": { - "en": "Enter your ISP name", - "fr": "Indiquez le nom de votre FAI" - }, - "example": "My ISP" - }, - { - "name": "isp_site", - "type": "string", - "ask": { - "en": "Enter the address of your ISP website", - "fr": "Saisissez l'adresse du siteweb de votre FAI" - }, - "example": "//www.exemple.tld" - } - ] - } -} diff --git a/manifest.toml b/manifest.toml new file mode 100644 index 0000000..0ac0849 --- /dev/null +++ b/manifest.toml @@ -0,0 +1,73 @@ +#:schema https://raw.githubusercontent.com/YunoHost/apps/master/schemas/manifest.v2.schema.json + +packaging_format = 2 + +id = "coin" +name = "Coin" +description.en = "Member dashboard for non profit isp." +description.fr = "Coin est un Outil pour un Internet Neutre." + +version = "20220401~ynh1" + +maintainers = ["ljf"] + +[upstream] +license = "AGPL-3.0-or-later" +website = "https://code.ffdn.org/FFDN/coin" +code = "https://code.ffdn.org/ffdn/coin" + +[integration] +yunohost = ">= 11.0.0" +architectures = "all" +multi_instance = true +ldap = false +sso = false +disk = "50M" +ram.build = "50M" +ram.runtime = "50M" + +[install] + [install.domain] + # this is a generic question - ask strings are automatically handled by Yunohost's core + type = "domain" + + [install.admin] + # this is a generic question - ask strings are automatically handled by Yunohost's core + type = "user" + + [install.email] + ask.en = "Choose email were send notification" + ask.fr = "Choisissez l'email vers lequel envoyer les notifications" + type = "string" + example = "admin@example.tld" + + [install.isp_name] + ask.en = "Enter your ISP name" + ask.fr = "Indiquez le nom de votre FAI" + type = "string" + example = "My ISP" + + [install.isp_site] + ask.en = "Enter the address of your ISP website" + ask.fr = "Saisissez l'adresse du siteweb de votre FAI" + type = "string" + example = "https://www.exemple.tld" + +[resources] + [resources.sources.main] + url = "https://code.ffdn.org/ffdn/coin/-/archive/afcf0846f939e80f34956d5f24f7bdd6a5058282/coin-afcf0846f939e80f34956d5f24f7bdd6a5058282.tar.gz" + sha256 = "71a2570a7f145ae826042853f7af2dff46350ef70f83698871b14fa7f9884219" + + + [resources.system_user] + + [resources.install_dir] + + [resources.permissions] + main.url = "/" + + [resources.apt] + packages = "gunicorn, libldap2-dev, libpq-dev, libsasl2-dev, libjpeg-dev, libxml2-dev, libxslt1-dev, libffi-dev, libpango1.0-0, postgresql, postgresql-contrib, postgresql-server-dev-13" + + [resources.database] + type = "postgresql" diff --git a/scripts/_common.sh b/scripts/_common.sh index ba3926a..05a7907 100644 --- a/scripts/_common.sh +++ b/scripts/_common.sh @@ -1,22 +1,2 @@ #!/bin/bash -#================================================= -# COMMON VARIABLES -#================================================= - -python_version=2.7.18 - -# dependencies used by the app -pkg_dependencies="gunicorn libldap2-dev libpq-dev libsasl2-dev libjpeg-dev libxml2-dev libxslt1-dev libffi-dev libpango1.0-0 postgresql postgresql-contrib postgresql-server-dev-13" - -#================================================= -# PERSONAL HELPERS -#================================================= - -#================================================= -# EXPERIMENTAL HELPERS -#================================================= - -#================================================= -# FUTURE OFFICIAL HELPERS -#================================================= diff --git a/scripts/backup b/scripts/backup index 19f6fe4..a79e87e 100644 --- a/scripts/backup +++ b/scripts/backup @@ -1,68 +1,22 @@ #!/bin/bash -#================================================= -# GENERIC START -#================================================= -# IMPORT GENERIC HELPERS -#================================================= - # Keep this path for calling _common.sh inside the execution's context of backup and restore scripts source ../settings/scripts/_common.sh source /usr/share/yunohost/helpers -#================================================= -# MANAGE SCRIPT FAILURE -#================================================= - -ynh_clean_setup () { - true -} -# Exit if an error occurs during the execution of the script -ynh_abort_if_errors - -#================================================= -# LOAD SETTINGS -#================================================= -ynh_print_info --message="Loading installation settings..." - -app=$YNH_APP_INSTANCE_NAME - -final_path=$(ynh_app_setting_get --app=$app --key=final_path) -domain=$(ynh_app_setting_get --app=$app --key=domain) -db_name=$(ynh_app_setting_get --app=$app --key=db_name) - #================================================= # DECLARE DATA AND CONF FILES TO BACKUP #================================================= ynh_print_info --message="Declaring files to be backed up..." -#================================================= -# BACKUP THE APP MAIN DIR -#================================================= - -ynh_backup --src_path="$final_path" - -#================================================= -# BACKUP THE NGINX CONFIGURATION -#================================================= - +ynh_backup --src_path="$install_dir" ynh_backup --src_path="/etc/nginx/conf.d/$domain.d/$app.conf" - -#================================================= -# BACKUP SYSTEMD -#================================================= - ynh_backup --src_path="/etc/systemd/system/$app.service" -#================================================= -# BACKUP THE POSTGRESQL DATABASE -#================================================= ynh_print_info --message="Backing up the PostgreSQL database..." - ynh_psql_dump_db --database="$db_name" > db.sql #================================================= # END OF SCRIPT #================================================= - ynh_print_info --message="Backup script completed for $app. (YunoHost will then actually copy those files to the archive)." diff --git a/scripts/change_url b/scripts/change_url index 94b1b70..e65f14b 100644 --- a/scripts/change_url +++ b/scripts/change_url @@ -1,135 +1,12 @@ #!/bin/bash -#================================================= -# GENERIC STARTING -#================================================= -# IMPORT GENERIC HELPERS -#================================================= - source _common.sh source /usr/share/yunohost/helpers -#================================================= -# RETRIEVE ARGUMENTS -#================================================= - -old_domain=$YNH_APP_OLD_DOMAIN -old_path=$YNH_APP_OLD_PATH - -new_domain=$YNH_APP_NEW_DOMAIN -new_path=$YNH_APP_NEW_PATH - -app=$YNH_APP_INSTANCE_NAME - -#================================================= -# LOAD SETTINGS -#================================================= -ynh_script_progression --message="Loading installation settings..." --weight=1 - -# Needed for helper "ynh_add_nginx_config" -final_path=$(ynh_app_setting_get --app=$app --key=final_path) - -# Add settings here as needed by your application -#db_name=$(ynh_app_setting_get --app=$app --key=db_name) -#db_user=$db_name -#db_pwd=$(ynh_app_setting_get --app=$app --key=db_pwd) - -#================================================= -# BACKUP BEFORE CHANGE URL THEN ACTIVE TRAP -#================================================= -ynh_script_progression --message="Backing up the app before changing its URL (may take a while)..." --weight=1 - -# Backup the current version of the app -ynh_backup_before_upgrade -ynh_clean_setup () { - # Remove the new domain config file, the remove script won't do it as it doesn't know yet its location. - ynh_secure_remove --file="/etc/nginx/conf.d/$new_domain.d/$app.conf" - - # Restore it if the upgrade fails - ynh_restore_upgradebackup -} -# Exit if an error occurs during the execution of the script -ynh_abort_if_errors - -#================================================= -# CHECK WHICH PARTS SHOULD BE CHANGED -#================================================= - -change_domain=0 -if [ "$old_domain" != "$new_domain" ] -then - change_domain=1 -fi - -change_path=0 -if [ "$old_path" != "$new_path" ] -then - change_path=1 -fi - -#================================================= -# STANDARD MODIFICATIONS -#================================================= -# STOP SYSTEMD SERVICE -#================================================= -ynh_script_progression --message="Stopping a systemd service..." --weight=1 - ynh_systemd_action --service_name=$app --action="stop" --log_path="/var/log/$app/$app.log" -#================================================= -# MODIFY URL IN NGINX CONF -#================================================= -ynh_script_progression --message="Updating NGINX web server configuration..." --weight=1 +ynh_change_url_nginx_config -nginx_conf_path=/etc/nginx/conf.d/$old_domain.d/$app.conf - -# Change the path in the NGINX config file -if [ $change_path -eq 1 ] -then - # Make a backup of the original NGINX config file if modified - ynh_backup_if_checksum_is_different --file="$nginx_conf_path" - # Set global variables for NGINX helper - domain="$old_domain" - path_url="$new_path" - # Create a dedicated NGINX config - ynh_add_nginx_config -fi - -# Change the domain for NGINX -if [ $change_domain -eq 1 ] -then - # Delete file checksum for the old conf file location - ynh_delete_file_checksum --file="$nginx_conf_path" - mv $nginx_conf_path /etc/nginx/conf.d/$new_domain.d/$app.conf - # Store file checksum for the new config file location - ynh_store_file_checksum --file="/etc/nginx/conf.d/$new_domain.d/$app.conf" -fi - -#================================================= -# SPECIFIC MODIFICATIONS -#================================================= -# ... -#================================================= - -#================================================= -# GENERIC FINALISATION -#================================================= -# START SYSTEMD SERVICE -#================================================= -ynh_script_progression --message="Starting a systemd service..." --weight=1 - -# Start a systemd service ynh_systemd_action --service_name=$app --action="start" --log_path="/var/log/$app/$app.log" -#================================================= -# RELOAD NGINX -#================================================= -ynh_script_progression --message="Reloading NGINX web server..." --weight=1 - -ynh_systemd_action --service_name=nginx --action=reload - -#================================================= -# END OF SCRIPT -#================================================= - ynh_script_progression --message="Change of URL completed for $app" --last diff --git a/scripts/install b/scripts/install index 30a17b5..e499deb 100644 --- a/scripts/install +++ b/scripts/install @@ -1,166 +1,60 @@ #!/bin/bash -#================================================= -# GENERIC START -#================================================= -# IMPORT GENERIC HELPERS -#================================================= - source _common.sh -source ynh_install_python source /usr/share/yunohost/helpers -#================================================= -# MANAGE SCRIPT FAILURE -#================================================= - -ynh_clean_setup () { - true -} -# Exit if an error occurs during the execution of the script -ynh_abort_if_errors - -#================================================= -# RETRIEVE ARGUMENTS FROM THE MANIFEST -#================================================= - -export domain=$YNH_APP_ARG_DOMAIN -export path_url=/ -export admin=$YNH_APP_ARG_ADMIN -export email=$YNH_APP_ARG_EMAIL -export isp_name=$YNH_APP_ARG_ISP_NAME -export isp_site=$YNH_APP_ARG_ISP_SITE -is_public=1 export secret=$(ynh_string_random 24) -export app=$YNH_APP_INSTANCE_NAME - -#================================================= -# CHECK IF THE APP CAN BE INSTALLED WITH THESE ARGS -#================================================= -ynh_script_progression --message="Validating installation parameters..." --weight=1 - -export final_path=/opt/$app -test ! -e "$final_path" || ynh_die --message="This path already contains a folder" - -# Register (book) web path -ynh_webpath_register --app=$app --domain=$domain --path_url=$path_url - -#================================================= -# STORE SETTINGS FROM MANIFEST -#================================================= -ynh_script_progression --message="Storing installation settings..." --weight=1 - -ynh_app_setting_set --app=$app --key=domain --value=$domain -ynh_app_setting_set --app=$app --key=path --value=$path_url -ynh_app_setting_set --app=$app --key=admin --value=$admin -ynh_app_setting_set --app=$app --key=email --value=$email -ynh_app_setting_set --app=$app --key=isp_name --value=$isp_name -ynh_app_setting_set --app=$app --key=isp_site --value=$isp_site - -#================================================= -# STANDARD MODIFICATIONS -#================================================= -# INSTALL DEPENDENCIES -#================================================= -ynh_script_progression --message="Installing dependencies..." --weight=1 - -ynh_install_app_dependencies $pkg_dependencies -ynh_install_python --python_version=$python_version - -#================================================= -# CREATE DEDICATED USER -#================================================= -ynh_script_progression --message="Configuring system user..." --weight=1 - -# Create a system user -ynh_system_user_create --username=$app - -#================================================= -# CREATE A POSTGRESQL DATABASE -#================================================= -ynh_script_progression --message="Creating a PostgreSQL database..." --weight=1 - -export db_name=$(ynh_sanitize_dbid --db_name=$app) -export db_user=$db_name -ynh_app_setting_set --app=$app --key=db_name --value=$db_name -ynh_psql_test_if_first_run -ynh_psql_setup_db --db_user=$db_user --db_name=$db_name -export db_pwd=$(ynh_app_setting_get --app=$app --key=psqlpwd) - #================================================= # DOWNLOAD, CHECK AND UNPACK SOURCE #================================================= -ynh_script_progression --message="Setting up source files..." --weight=1 +ynh_script_progression --message="Setting up source files..." -ynh_app_setting_set --app=$app --key=final_path --value=$final_path -# Download, check integrity, uncompress and patch the source from app.src -ynh_setup_source --dest_dir="$final_path" +ynh_setup_source --dest_dir="$install_dir" -chmod 750 "$final_path" -chmod -R o-rwx "$final_path" -chown -R $app:www-data "$final_path" - -#================================================= -# NGINX CONFIGURATION -#================================================= -ynh_script_progression --message="Configuring NGINX web server..." --weight=1 - -# Create a dedicated NGINX config -ynh_add_nginx_config +chmod 750 "$install_dir" +chmod -R o-rwx "$install_dir" +chown -R $app:www-data "$install_dir" #================================================= # SPECIFIC SETUP #================================================= # PYTHON DEPENDENCIES #================================================= -ynh_script_progression --message="Installing more dependencies..." --weight=1 +ynh_script_progression --message="Installing more dependencies..." -pushd "$final_path" - ynh_use_python - ynh_exec_warn_less $ynh_pip install --upgrade pip - ynh_exec_warn_less $ynh_pip install gunicorn - echo "django-auth-ldap<1.4" >> $final_path/requirements.txt - ynh_exec_warn_less $ynh_pip install -r $final_path/requirements.txt +pushd "$install_dir" + python3 -m venv venv + venv/bin/pip install --upgrade pip + venv/bin/pip install gunicorn + #echo "django-auth-ldap<1.4" >> $install_dir/requirements.txt + venv/bin/pip install -r requirements.txt popd #================================================= # CONFIGURATION DJANGO #================================================= -ynh_script_progression --message="Configuring application..." --weight=1 +ynh_script_progression --message="Configuring application..." -export prefix="${path_url#"/"}/" +export prefix="${path#"/"}/" prefix=${prefix%"/"} -ynh_render_template ../conf/local.py.j2 "$final_path/coin/settings_local.py" -ynh_store_file_checksum --file="$final_path/coin/settings_local.py" +ynh_render_template --template="local.py.j2" --destination="$install_dir/coin/settings_local.py" +ynh_store_file_checksum --file="$install_dir/coin/settings_local.py" -#================================================= # SERVE STATIC FILES IN PRODUCTION MODE -#================================================= +ln -s $install_dir/$app/static $install_dir/static -ln -s $final_path/$app/static $final_path/static - -#================================================= -# SETUP DATABASE -#================================================= - -# Set permissions -chown -R $app:www-data $final_path - -pushd $final_path +pushd $install_dir ynh_exec_warn_less $ynh_python manage.py migrate --noinput ynh_exec_warn_less $ynh_python manage.py collectstatic --noinput popd # Set permissions to directory -chmod 750 "$final_path" -chmod -R o-rwx "$final_path" -chown -R $app:www-data "$final_path" - -#================================================= -# CONFIGURE LOG DIR -#================================================= +chmod 750 "$install_dir" +chmod -R o-rwx "$install_dir" +chown -R $app:www-data "$install_dir" +# Setup log permissions mkdir -p /var/log/$app chown -R $app /var/log/$app chown -R $app:www-data "/var/log/$app" @@ -168,57 +62,34 @@ chown -R $app:www-data "/var/log/$app" #================================================= # ADD A CONFIGURATION #================================================= -ynh_script_progression --message="Adding a configuration file..." --weight=1 +ynh_script_progression --message="Adding a configuration file..." -ynh_add_config --template="../conf/gunicorn_config.py" --destination="$final_path/gunicorn_config.py" +ynh_add_config --template="gunicorn_config.py" --destination="$install_dir/gunicorn_config.py" -chown $app:www-data "$final_path/gunicorn_config.py" +chown $app:www-data "$install_dir/gunicorn_config.py" #================================================= # SETUP SYSTEMD #================================================= -ynh_script_progression --message="Configuring a systemd service..." --weight=1 +ynh_script_progression --message="Configuring system..." + +# Create a dedicated NGINX config +ynh_add_nginx_config # Create a dedicated systemd config ynh_add_systemd_config -#================================================= -# GENERIC FINALIZATION -#================================================= -# INTEGRATE SERVICE IN YUNOHOST -#================================================= -ynh_script_progression --message="Integrating service in YunoHost..." --weight=1 - +# Integrating service in YunoHost yunohost service add $app --description "$app daemon" --log="/var/log/$app/$app.log" #================================================= # START SYSTEMD SERVICE #================================================= -ynh_script_progression --message="Starting a systemd service..." --weight=1 +ynh_script_progression --message="Starting systemd service..." # Start a systemd service ynh_systemd_action --service_name=$app --action="start" --log_path="/var/log/$app/$app.log" -#================================================= -# SETUP SSOWAT -#================================================= -ynh_script_progression --message="Configuring permissions..." --weight=1 - -# Make app public if necessary -if [ $is_public -eq 1 ] -then - # Everyone can access the app. - # The "main" permission is automatically created before the install script. - ynh_permission_update --permission="main" --add="visitors" -fi - -#================================================= -# RELOAD NGINX -#================================================= -ynh_script_progression --message="Reloading NGINX web server..." --weight=1 - -ynh_systemd_action --service_name=nginx --action=reload - #================================================= # END OF SCRIPT #================================================= diff --git a/scripts/remove b/scripts/remove index 86b1c6d..23b5bb5 100644 --- a/scripts/remove +++ b/scripts/remove @@ -1,102 +1,25 @@ #!/bin/bash -#================================================= -# GENERIC START -#================================================= -# IMPORT GENERIC HELPERS -#================================================= - source _common.sh -source ynh_install_python source /usr/share/yunohost/helpers -#================================================= -# LOAD SETTINGS -#================================================= -ynh_script_progression --message="Loading installation settings..." --weight=1 - -app=$YNH_APP_INSTANCE_NAME - -domain=$(ynh_app_setting_get --app=$app --key=domain) -port=$(ynh_app_setting_get --app=$app --key=port) -db_name=$(ynh_app_setting_get --app=$app --key=db_name) -db_user=$db_name -final_path=$(ynh_app_setting_get --app=$app --key=final_path) - -#================================================= -# STANDARD REMOVE -#================================================= -# REMOVE SERVICE INTEGRATION IN YUNOHOST -#================================================= +ynh_script_progression --message="Removing system configurations..." --weight=1 # Remove the service from the list of services known by YunoHost (added from `yunohost service add`) if ynh_exec_warn_less yunohost service status $app >/dev/null then - ynh_script_progression --message="Removing $app service integration..." --weight=1 yunohost service remove $app fi -#================================================= -# STOP AND REMOVE SERVICE -#================================================= -ynh_script_progression --message="Stopping and removing the systemd service..." --weight=1 - # Remove the dedicated systemd config ynh_remove_systemd_config -#================================================= -# REMOVE THE POSTGRESQL DATABASE -#================================================= -ynh_script_progression --message="Removing the PostgreSQL database..." --weight=1 - -# Remove a database if it exists, along with the associated user -ynh_psql_remove_db --db_user=$db_user --db_name=$db_name - -#================================================= -# REMOVE APP MAIN DIR -#================================================= -ynh_script_progression --message="Removing app main directory..." --weight=1 - -# Remove the app directory securely -ynh_secure_remove --file="$final_path" - -#================================================= -# REMOVE NGINX CONFIGURATION -#================================================= -ynh_script_progression --message="Removing NGINX web server configuration..." --weight=1 - # Remove the dedicated NGINX config ynh_remove_nginx_config -#================================================= -# REMOVE DEPENDENCIES -#================================================= -ynh_script_progression --message="Removing dependencies..." --weight=1 - -# Remove metapackage and its dependencies -ynh_remove_python -ynh_remove_app_dependencies - -#================================================= -# SPECIFIC REMOVE -#================================================= -# REMOVE VARIOUS FILES -#================================================= -ynh_script_progression --message="Removing various files..." --weight=1 - # Remove the log files ynh_secure_remove --file="/var/log/$app" -#================================================= -# GENERIC FINALIZATION -#================================================= -# REMOVE DEDICATED USER -#================================================= -ynh_script_progression --message="Removing the dedicated system user..." --weight=1 - -# Delete a system user -ynh_system_user_delete --username=$app - #================================================= # END OF SCRIPT #================================================= diff --git a/scripts/restore b/scripts/restore index 51e23ee..c8080d8 100644 --- a/scripts/restore +++ b/scripts/restore @@ -1,83 +1,19 @@ #!/bin/bash -#================================================= -# GENERIC START -#================================================= -# IMPORT GENERIC HELPERS -#================================================= - # Keep this path for calling _common.sh inside the execution's context of backup and restore scripts source ../settings/scripts/_common.sh -source ../settings/scripts/ynh_install_python source /usr/share/yunohost/helpers -#================================================= -# MANAGE SCRIPT FAILURE -#================================================= - -ynh_clean_setup () { - true -} -# Exit if an error occurs during the execution of the script -ynh_abort_if_errors - -#================================================= -# LOAD SETTINGS -#================================================= -ynh_script_progression --message="Loading installation settings..." --weight=1 - -export app=$YNH_APP_INSTANCE_NAME - -export domain=$(ynh_app_setting_get --app=$app --key=domain) -export path_url=$(ynh_app_setting_get --app=$app --key=path) -export final_path=$(ynh_app_setting_get --app=$app --key=final_path) -export db_name=$(ynh_app_setting_get --app=$app --key=db_name) -export db_user=$db_name -export admin=$(ynh_app_setting_get --app=$app --key=admin) -export email=$(ynh_app_setting_get --app=$app --key=email) -export isp_name=$(ynh_app_setting_get --app=$app --key=isp_name) -export isp_site=$(ynh_app_setting_get --app=$app --key=isp_site) -export secret=$(ynh_app_setting_get --app=$app --key=secret) - -#================================================= -# CHECK IF THE APP CAN BE RESTORED -#================================================= -ynh_script_progression --message="Validating restoration parameters..." --weight=1 - -test ! -d $final_path \ - || ynh_die --message="There is already a directory: $final_path " - -#================================================= -# STANDARD RESTORATION STEPS -#================================================= -# RECREATE THE DEDICATED USER -#================================================= -ynh_script_progression --message="Recreating the dedicated system user..." --weight=1 - -# Create the dedicated user (if not existing) -ynh_system_user_create --username=$app - #================================================= # RESTORE THE APP MAIN DIR #================================================= ynh_script_progression --message="Restoring the app main directory..." --weight=1 -ynh_restore_file --origin_path="$final_path" +ynh_restore_file --origin_path="$install_dir" -chmod 750 "$final_path" -chmod -R o-rwx "$final_path" -chown -R $app:www-data "$final_path" - -#================================================= -# SPECIFIC RESTORATION -#================================================= -# REINSTALL DEPENDENCIES -#================================================= -ynh_script_progression --message="Reinstalling dependencies..." --weight=1 - -# Define and install dependencies -ynh_install_app_dependencies $pkg_dependencies -ynh_install_python --python_version=$python_version +chmod 750 "$install_dir" +chmod -R o-rwx "$install_dir" +chown -R $app:www-data "$install_dir" #================================================= # RESTORE THE NGINX CONFIGURATION @@ -86,29 +22,29 @@ ynh_script_progression --message="Restoring the NGINX web server configuration.. ynh_restore_file --origin_path="/etc/nginx/conf.d/$domain.d/$app.conf" -#================================================= -# RESTORE THE POSTGRESQL DATABASE -#================================================= -ynh_script_progression --message="Restoring the PostgreSQL database..." --weight=1 - -export db_pwd=$(ynh_app_setting_get --app=$app --key=psqlpwd) -ynh_psql_test_if_first_run -ynh_psql_setup_db --db_user=$db_user --db_name=$db_name --db_pwd=$db_pwd ynh_psql_execute_file_as_root --file="./db.sql" --database=$db_name #================================================= # PYTHON DEPENDENCIES #================================================= -ynh_script_progression --message="Installing more dependencies..." --weight=1 +ynh_script_progression --message="Reinstalling venv..." --weight=1 -pushd "$final_path" - ynh_use_python - ynh_exec_warn_less $ynh_pip install --upgrade pip - ynh_exec_warn_less $ynh_pip install gunicorn - echo "django-auth-ldap<1.4" >> $final_path/requirements.txt - ynh_exec_warn_less $ynh_pip install -r $final_path/requirements.txt +ynh_secure_remove --file="${install_dir}/venv" +pushd "$install_dir" + python3 -m venv venv + venv/bin/pip install --upgrade pip + venv/bin/pip install gunicorn + #echo "django-auth-ldap<1.4" >> $install_dir/requirements.txt + venv/bin/pip install -r requirements.txt popd +#================================================= +# CONFIGURE LOG DIR +#================================================= + +mkdir -p /var/log/$app +chown -R $app:www-data /var/log/$app + #================================================= # RESTORE SYSTEMD #================================================= @@ -117,19 +53,6 @@ ynh_script_progression --message="Restoring the systemd configuration..." --weig ynh_restore_file --origin_path="/etc/systemd/system/$app.service" systemctl enable $app.service --quiet -#================================================= -# CONFIGURE LOG DIR -#================================================= - -mkdir -p /var/log/$app -chown -R $app /var/log/$app -chgrp -R www-data /var/log/$app - -#================================================= -# INTEGRATE SERVICE IN YUNOHOST -#================================================= -ynh_script_progression --message="Integrating service in YunoHost..." --weight=1 - yunohost service add $app --description "$app daemon" --log="/var/log/$app/$app.log" #================================================= diff --git a/scripts/upgrade b/scripts/upgrade index 16045d0..e5e1b36 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -1,55 +1,10 @@ #!/bin/bash -#================================================= -# GENERIC START -#================================================= -# IMPORT GENERIC HELPERS -#================================================= - source _common.sh -source ynh_install_python source /usr/share/yunohost/helpers -#================================================= -# LOAD SETTINGS -#================================================= -ynh_script_progression --message="Loading installation settings..." --weight=1 - -export app=$YNH_APP_INSTANCE_NAME - -export domain=$(ynh_app_setting_get --app=$app --key=domain) -export path_url=$(ynh_app_setting_get --app=$app --key=path) -export admin=$(ynh_app_setting_get --app=$app --key=admin) -export email=$(ynh_app_setting_get --app=$app --key=email) -export isp_name=$(ynh_app_setting_get --app=$app --key=isp_name) -export isp_site=$(ynh_app_setting_get --app=$app --key=isp_site) -export secret=$(ynh_app_setting_get --app=$app --key=secret) -export final_path=$(ynh_app_setting_get --app=$app --key=final_path) -export db_name=$(ynh_app_setting_get --app=$app --key=db_name) -export db_user=$db_name -export db_pwd=$(ynh_app_setting_get --app=$app --key=psqlpwd) - -#================================================= -# CHECK VERSION -#================================================= -ynh_script_progression --message="Checking version..." --weight=1 - upgrade_type=$(ynh_check_app_version_changed) -#================================================= -# BACKUP BEFORE UPGRADE THEN ACTIVE TRAP -#================================================= -ynh_script_progression --message="Backing up the app before upgrading (may take a while)..." --weight=1 - -# Backup the current version of the app -ynh_backup_before_upgrade -ynh_clean_setup () { - # Restore it if the upgrade fails - ynh_restore_upgradebackup -} -# Exit if an error occurs during the execution of the script -ynh_abort_if_errors - #================================================= # STANDARD UPGRADE STEPS #================================================= @@ -59,40 +14,6 @@ ynh_script_progression --message="Stopping a systemd service..." --weight=1 ynh_systemd_action --service_name=$app --action="stop" --log_path="/var/log/$app/$app.log" -#================================================= -# ENSURE DOWNWARD COMPATIBILITY -#================================================= -ynh_script_progression --message="Ensuring downward compatibility..." --weight=1 - -# Cleaning legacy permissions -if ynh_legacy_permissions_exists; then - ynh_legacy_permissions_delete_all - - ynh_app_setting_delete --app=$app --key=is_public -fi - -# If db_name doesn't exist, create it -if [ -z "$db_name" ]; then - db_name=$(ynh_sanitize_dbid --db_name=$app) - ynh_app_setting_set --app=$app --key=db_name --value=$db_name -fi - -# If final_path doesn't exist, create it -if [ -z "$final_path" ]; then - final_path=/opt/$app - ynh_app_setting_set --app=$app --key=final_path --value=$final_path -fi - -ynh_secure_remove --file="${final_path}/venv" - -#================================================= -# CREATE DEDICATED USER -#================================================= -ynh_script_progression --message="Making sure dedicated system user exists..." --weight=1 - -# Create a dedicated user (if not existing) -ynh_system_user_create --username=$app - #================================================= # DOWNLOAD, CHECK AND UNPACK SOURCE #================================================= @@ -102,28 +23,22 @@ then ynh_script_progression --message="Upgrading source files..." --weight=1 # Download, check integrity, uncompress and patch the source from app.src - ynh_setup_source --dest_dir="$final_path" + ynh_setup_source --dest_dir="$install_dir" fi -chmod 750 "$final_path" -chmod -R o-rwx "$final_path" -chown -R $app:www-data "$final_path" -#================================================= -# UPGRADE DEPENDENCIES -#================================================= -ynh_script_progression --message="Upgrading dependencies..." --weight=1 +ynh_secure_remove --file="${install_dir}/venv" +pushd "$install_dir" + python3 -m venv venv + venv/bin/pip install --upgrade pip + venv/bin/pip install gunicorn + #echo "django-auth-ldap<1.4" >> $install_dir/requirements.txt + venv/bin/pip install -r requirements.txt +popd -ynh_install_app_dependencies $pkg_dependencies -ynh_install_python --python_version=$python_version - -#================================================= -# NGINX CONFIGURATION -#================================================= -ynh_script_progression --message="Upgrading NGINX web server configuration..." --weight=1 - -# Create a dedicated NGINX config -ynh_add_nginx_config +chmod 750 "$install_dir" +chmod -R o-rwx "$install_dir" +chown -R $app:www-data "$install_dir" #================================================= # SPECIFIC UPGRADE @@ -132,31 +47,21 @@ ynh_add_nginx_config #================================================= ynh_script_progression --message="Configuring Django..." --weight=1 -export prefix="${path_url#"/"}/" +export prefix="${path#"/"}/" prefix=${prefix%"/"} -ynh_backup_if_checksum_is_different --file="$final_path/coin/settings_local.py" -ynh_render_template ../conf/local.py.j2 "$final_path/coin/setings_local.py" -ynh_store_file_checksum --file="$final_path/coin/settings_local.py" +ynh_backup_if_checksum_is_different --file="$install_dir/coin/settings_local.py" +ynh_render_template ../conf/local.py.j2 "$install_dir/coin/setings_local.py" +ynh_store_file_checksum --file="$install_dir/coin/settings_local.py" -#================================================= -# PYTHON DEPENDENCIES -#================================================= -ynh_script_progression --message="Installing more dependencies..." --weight=1 - -pushd "$final_path" - ynh_use_python - ynh_exec_warn_less $ynh_pip install --upgrade pip - ynh_exec_warn_less $ynh_pip install gunicorn - echo "django-auth-ldap<1.4" >> $final_path/requirements.txt - ynh_exec_warn_less $ynh_pip install -r $final_path/requirements.txt -popd +ynh_add_config --template="../conf/gunicorn_config.py" --destination="$install_dir/gunicorn_config.py" +chown $app:www-data "$install_dir/gunicorn_config.py" #================================================= # MIGRATE DB #================================================= ynh_script_progression --message="Migrating database..." --weight=1 -pushd "$final_path" +pushd "$install_dir" ynh_exec_warn_less $ynh_python manage.py migrate --noinput popd @@ -165,38 +70,25 @@ popd #================================================= ynh_script_progression --message="Collecting files..." --weight=1 -pushd "$final_path" +pushd "$install_dir" ynh_exec_warn_less $ynh_python manage.py collectstatic --noinput popd -chmod 750 "$final_path" -chmod -R o-rwx "$final_path" -chown -R $app:www-data "$final_path" - -#================================================ -# UPDATE A CONFIG FILE -#================================================= -ynh_script_progression --message="Updating a configuration file..." --weight=1 - -ynh_add_config --template="../conf/gunicorn_config.py" --destination="$final_path/gunicorn_config.py" - -chown $app:www-data "$final_path/gunicorn_config.py" +chmod 750 "$install_dir" +chmod -R o-rwx "$install_dir" +chown -R $app:www-data "$install_dir" #================================================= # SETUP SYSTEMD #================================================= -ynh_script_progression --message="Upgrading systemd configuration..." --weight=1 +ynh_script_progression --message="Upgrading system configurations..." --weight=1 + +# Create a dedicated NGINX config +ynh_add_nginx_config # Create a dedicated systemd config ynh_add_systemd_config -#================================================= -# GENERIC FINALIZATION -#================================================= -# INTEGRATE SERVICE IN YUNOHOST -#================================================= -ynh_script_progression --message="Integrating service in YunoHost..." --weight=1 - yunohost service add $app --description "$app daemon" --log="/var/log/$app/$app.log" #================================================= @@ -206,13 +98,6 @@ ynh_script_progression --message="Starting a systemd service..." --weight=1 ynh_systemd_action --service_name=$app --action="start" --log_path="/var/log/$app/$app.log" -#================================================= -# RELOAD NGINX -#================================================= -ynh_script_progression --message="Reloading NGINX web server..." --weight=1 - -ynh_systemd_action --service_name=nginx --action=reload - #================================================= # END OF SCRIPT #================================================= diff --git a/scripts/ynh_install_python b/scripts/ynh_install_python deleted file mode 100644 index 5747fd5..0000000 --- a/scripts/ynh_install_python +++ /dev/null @@ -1,267 +0,0 @@ -#!/bin/bash - -ynh_python_try_bash_extension() { - if [ -x src/configure ]; then - src/configure && make -C src || { - ynh_print_info --message="Optional bash extension failed to build, but things will still work normally." - } - fi -} - -pyenv_install_dir="/opt/pyenv" -python_version_path="$pyenv_install_dir/versions" -# PYENV_ROOT is the directory of pyenv, it needs to be loaded as a environment variable. -export PYENV_ROOT="$pyenv_install_dir" -export pyenv_root="$pyenv_install_dir" - -# Required dependencies -pyenv_dependencies="make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev git" - -# Load the version of Python for an app, and set variables. -# -# ynh_use_python has to be used in any app scripts before using Python for the first time. -# This helper will provide alias and variables to use in your scripts. -# -# To use pip or Python, use the alias `ynh_pip` and `ynh_python` -# Those alias will use the correct version installed for the app -# For example: use `ynh_pip install` instead of `pip install` -# -# With `sudo` or `ynh_exec_as`, use instead the fallback variables `$ynh_pip` and `$ynh_python` -# And propagate $PATH to sudo with $ynh_python_load_path -# Exemple: `ynh_exec_as $app $ynh_python_load_path $ynh_pip install` -# -# $PATH contains the path of the requested version of Python. -# However, $PATH is duplicated into $ynh_python_path to outlast any manipulation of $PATH -# You can use the variable `$ynh_python_load_path` to quickly load your Python version -# in $PATH for an usage into a separate script. -# Exemple: $ynh_python_load_path $final_path/script_that_use_pip.sh` -# -# -# Finally, to start a Python service with the correct version, 2 solutions -# Either the app is dependent of python or pip, but does not called it directly. -# In such situation, you need to load PATH -# `Environment="__YNH_PYTHON_LOAD_PATH__"` -# `ExecStart=__FINALPATH__/my_app` -# You will replace __YNH_PYTHON_LOAD_PATH__ with $ynh_python_load_path -# -# Or Python start the app directly, then you don't need to load the PATH variable -# `ExecStart=__YNH_PYTHON__ my_app run` -# You will replace __YNH_PYTHON__ with $ynh_python -# -# one other variable is also available -# - $ynh_python_path: The absolute path to Python binaries for the chosen version. -# -# usage: ynh_use_python -# -# Requires YunoHost version 3.2.2 or higher. -ynh_use_python () { - python_version=$(ynh_app_setting_get --app=$app --key=python_version) - - # Get the absolute path of this version of Python - ynh_python_path="$python_version_path/$YNH_APP_INSTANCE_NAME/bin" - - # Allow alias to be used into bash script - shopt -s expand_aliases - - # Create an alias for the specific version of Python and a variable as fallback - ynh_python="$ynh_python_path/python" - alias ynh_python="$ynh_python" - # And pip - ynh_pip="$ynh_python_path/pip" - alias ynh_pip="$ynh_pip" - - # Load the path of this version of Python in $PATH - if [[ :$PATH: != *":$ynh_python_path"* ]]; then - PATH="$ynh_python_path:$PATH" - fi - # Create an alias to easily load the PATH - ynh_python_load_path="PATH=$PATH" - - # Sets the local application-specific Python version - pushd $final_path - $pyenv_install_dir/bin/pyenv local $python_version - popd -} - -# Install a specific version of Python -# -# ynh_install_python will install the version of Python provided as argument by using pyenv. -# -# This helper creates a /etc/profile.d/pyenv.sh that configures PATH environment for pyenv -# for every LOGIN user, hence your user must have a defined shell (as opposed to /usr/sbin/nologin) -# -# Don't forget to execute python-dependent command in a login environment -# (e.g. sudo --login option) -# When not possible (e.g. in systemd service definition), please use direct path -# to pyenv shims (e.g. $PYENV_ROOT/shims/bundle) -# -# usage: ynh_install_python --python_version=python_version -# | arg: -v, --python_version= - Version of Python to install. -# -# Requires YunoHost version 3.2.2 or higher. -ynh_install_python () { - # Declare an array to define the options of this helper. - local legacy_args=v - local -A args_array=( [v]=python_version= ) - local python_version - # Manage arguments with getopts - ynh_handle_getopts_args "$@" - - # Install required dependencies - ynh_add_app_dependencies --package="$pyenv_dependencies" - - # Load pyenv path in PATH - local CLEAR_PATH="$pyenv_install_dir/bin:$PATH" - - # Remove /usr/local/bin in PATH in case of Python prior installation - PATH=$(echo $CLEAR_PATH | sed 's@/usr/local/bin:@@') - - # Move an existing Python binary, to avoid to block pyenv - #test -x /usr/bin/python && mv /usr/bin/python /usr/bin/python_pyenv - - # Install or update pyenv - pyenv="$(command -v pyenv $pyenv_install_dir/bin/pyenv | head -1)" - if [ -n "$pyenv" ]; then - ynh_print_info --message="pyenv already seems installed in \`$pyenv'." - pushd "${pyenv%/*/*}" - if git remote -v 2>/dev/null | grep "https://github.com/pyenv/pyenv.git"; then - ynh_print_info --message="Trying to update with git..." - git pull -q --tags origin master - cd .. - ynh_python_try_bash_extension - fi - popd - else - ynh_print_info --message="Installing pyenv with git..." - mkdir -p $pyenv_install_dir - pushd $pyenv_install_dir - git init -q - git remote add -f -t master origin https://github.com/pyenv/pyenv.git > /dev/null 2>&1 - git checkout -q -b master origin/master - ynh_python_try_bash_extension - pyenv=$pyenv_install_dir/bin/pyenv - popd - fi - - pyenv_virtualenv="$(command -v "$pyenv_install_dir"/plugins/*/bin/pyenv-virtualenv pyenv-virtualenv | head -1)" - if [ -n "$pyenv_virtualenv" ]; then - ynh_print_info --message="\`pyenv virtualenv' command already available in \`$pyenv_virtualenv'." - pushd "${pyenv_virtualenv%/*/*}" - if git remote -v 2>/dev/null | grep "https://github.com/pyenv/pyenv-virtualenv.git"; then - ynh_print_info --message="Trying to update pyenv-virtualenv with git..." - git pull -q origin master - fi - popd - else - ynh_print_info --message="Installing pyenv-virtualenv with git..." - mkdir -p "${pyenv_install_dir}/plugins" - git clone -q https://github.com/pyenv/pyenv-virtualenv.git "${pyenv_install_dir}/plugins/pyenv-virtualenv" - fi - - # Enable caching - mkdir -p "${pyenv_install_dir}/cache" - - # Create shims directory if needed - mkdir -p "${pyenv_install_dir}/shims" - chmod -R a+w "${pyenv_install_dir}/shims" - - # Restore /usr/local/bin in PATH - PATH=$CLEAR_PATH - - # And replace the old Python binary - # test -x /usr/bin/python_pyenv && mv /usr/bin/python_pyenv /usr/bin/python - - # Install the requested version of Python - ynh_print_info --message="Installation of Python-$python_version" - pyenv install --skip-existing $python_version > /dev/null 2>&1 - - # Store python_version into the config of this app - ynh_app_setting_set --app=$YNH_APP_INSTANCE_NAME --key=python_version --value=$python_version - - # Remove app virtualenv - if `pyenv virtualenvs | grep --quiet "$YNH_APP_INSTANCE_NAME " 1>/dev/null 2>&1` - then - pyenv virtualenv-delete --force $YNH_APP_INSTANCE_NAME - fi - - # Create app virtualenv - pyenv virtualenv --force $python_version $YNH_APP_INSTANCE_NAME - - # Cleanup Python versions - ynh_cleanup_python - - # Set environment for Python users - echo "#pyenv -export PYENV_ROOT=$pyenv_install_dir -export PATH=\"$pyenv_install_dir/bin:$PATH\" -eval \"\$(pyenv init -)\" -#pyenv" > /etc/profile.d/pyenv.sh - - # Load the environment - eval "$(pyenv init -)" -} - -# Remove the version of Python used by the app. -# -# This helper will also cleanup Python versions -# -# usage: ynh_remove_python -ynh_remove_python () { - local python_version=$(ynh_app_setting_get --app=$YNH_APP_INSTANCE_NAME --key=python_version) - - # Load pyenv path in PATH - local CLEAR_PATH="$pyenv_install_dir/bin:$PATH" - - # Remove /usr/local/bin in PATH in case of Python prior installation - PATH=$(echo $CLEAR_PATH | sed 's@/usr/local/bin:@@') - - pyenv virtualenv-delete --force $YNH_APP_INSTANCE_NAME - - # Remove the line for this app - ynh_app_setting_delete --app=$YNH_APP_INSTANCE_NAME --key=python_version - - # Cleanup Python versions - ynh_cleanup_python -} - -# Remove no more needed versions of Python used by the app. -# -# This helper will check what Python version are no more required, -# and uninstall them -# If no app uses Python, pyenv will be also removed. -# -# usage: ynh_cleanup_python -ynh_cleanup_python () { - - # List required Python versions - local installed_apps=$(yunohost app list | grep -oP 'id: \K.*$') - local required_python_versions="" - for installed_app in $installed_apps - do - local installed_app_python_version=$(ynh_app_setting_get --app=$installed_app --key="python_version") - if [[ $installed_app_python_version ]] - then - required_python_versions="${installed_app_python_version}\n${required_python_versions}" - fi - done - - # Remove no more needed Python versions - local installed_python_versions=$(pyenv versions --bare --skip-aliases | grep -Ev '/') - for installed_python_version in $installed_python_versions - do - if ! `echo ${required_python_versions} | grep "${installed_python_version}" 1>/dev/null 2>&1` - then - ynh_print_info --message="Removing of Python-$installed_python_version" - $pyenv_install_dir/bin/pyenv uninstall --force $installed_python_version - fi - done - - # If none Python version is required - if [[ ! $required_python_versions ]] - then - # Remove pyenv environment configuration - ynh_print_info --message="Removing of pyenv-$pyenv_version" - ynh_secure_remove --file="$pyenv_install_dir" - ynh_secure_remove --file="/etc/profile.d/pyenv.sh" - fi -} diff --git a/tests.toml b/tests.toml new file mode 100644 index 0000000..cbbeef1 --- /dev/null +++ b/tests.toml @@ -0,0 +1,3 @@ +test_format = 1.0 + +[default] From 05c73a036646002089cc2400a656f13b23417136 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 16 Dec 2023 15:37:12 +0100 Subject: [PATCH 05/23] Fixes, update screenshot and description --- doc/DESCRIPTION.md | 11 +++++++++++ doc/DESCRIPTION_fr.md | 12 ++++++++++++ doc/DISCLAIMER.md | 0 doc/screenshots/screenshot.png | Bin 0 -> 75554 bytes doc/screenshots/user-subscriptions.png | Bin 44201 -> 0 bytes manifest.toml | 1 + tests.toml | 4 ++++ 7 files changed, 28 insertions(+) create mode 100644 doc/DESCRIPTION_fr.md delete mode 100644 doc/DISCLAIMER.md create mode 100644 doc/screenshots/screenshot.png delete mode 100644 doc/screenshots/user-subscriptions.png diff --git a/doc/DESCRIPTION.md b/doc/DESCRIPTION.md index cf72c40..b3c9380 100644 --- a/doc/DESCRIPTION.md +++ b/doc/DESCRIPTION.md @@ -1 +1,12 @@ COIN is an Information System designed for associative ISPs in the FFDN. + +### Features + +- Provide a nice, clean UI for members +- Manage memberships +- Handle service requests for VPN, VPS, Housing, external accounts, etc, + - including IP pools management + - custom hooks can be added to interface with the actual infrastructure and provision services from the admin + - members can get status info regarding their services + - handle invoices, send reminders, import payment from bank, derive a member balance +- Optional features: mailing list, hardware provisionning diff --git a/doc/DESCRIPTION_fr.md b/doc/DESCRIPTION_fr.md new file mode 100644 index 0000000..61ef12f --- /dev/null +++ b/doc/DESCRIPTION_fr.md @@ -0,0 +1,12 @@ +COIN est un système d'information pour FAI associatif dans la FFDN + +### Fonctionnalités + +- Fourni une interface pour les membres +- Gestion des adhésions +- Gestion des demandes de services tels que VPN, VPS, Housing, compte externe, etc ... + - gestion des pools d'IP + - des hooks peuvent être ajoutés à l'interface pour s'interfacer avec la "vraie" infra et provisionner les services depuis l'admin + - les membres peuvent voir le status et infos de leurs services depuis leur espace + - gestion des factures, des rappels, import des paiements de la banque, calcul de solde +- Optionnel : gestion de mailing liste, prêt de matériel diff --git a/doc/DISCLAIMER.md b/doc/DISCLAIMER.md deleted file mode 100644 index e69de29..0000000 diff --git a/doc/screenshots/screenshot.png b/doc/screenshots/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..2be03eb5e8feca3b6ffe04d72339ad0f1d5fbfda GIT binary patch literal 75554 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%0A-rSakt1!TeJ&XHU2v>M$7?T1C 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 diff --git a/manifest.toml b/manifest.toml index 0ac0849..f6f5e5a 100644 --- a/manifest.toml +++ b/manifest.toml @@ -65,6 +65,7 @@ ram.runtime = "50M" [resources.permissions] main.url = "/" + main.allowed = "visitors" [resources.apt] packages = "gunicorn, libldap2-dev, libpq-dev, libsasl2-dev, libjpeg-dev, libxml2-dev, libxslt1-dev, libffi-dev, libpango1.0-0, postgresql, postgresql-contrib, postgresql-server-dev-13" diff --git a/tests.toml b/tests.toml index cbbeef1..8546ea3 100644 --- a/tests.toml +++ b/tests.toml @@ -1,3 +1,7 @@ test_format = 1.0 [default] + + args.email = "john@example.com" + args.isp_name = "SuperFAI" + args.isp_site = "https://super.fai.net" From a413024f173e048f532f655725186bb24d438751 Mon Sep 17 00:00:00 2001 From: yunohost-bot Date: Sat, 16 Dec 2023 14:37:23 +0000 Subject: [PATCH 06/23] Auto-update README --- README.md | 13 ++++++++++++- README_fr.md | 15 +++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 87da3c1..8dd49af 100644 --- a/README.md +++ b/README.md @@ -18,12 +18,23 @@ If you don't have YunoHost, please consult [the guide](https://yunohost.org/#/in COIN is an Information System designed for associative ISPs in the FFDN. +### Features + +- Provide a nice, clean UI for members +- Manage memberships +- Handle service requests for VPN, VPS, Housing, external accounts, etc, + - including IP pools management + - custom hooks can be added to interface with the actual infrastructure and provision services from the admin + - members can get status info regarding their services + - handle invoices, send reminders, import payment from bank, derive a member balance +- Optional features: mailing list, hardware provisionning + **Shipped version:** 20220401~ynh1 ## Screenshots -![Screenshot of Coin](./doc/screenshots/user-subscriptions.png) +![Screenshot of Coin](./doc/screenshots/screenshot.png) ## Documentation and resources diff --git a/README_fr.md b/README_fr.md index c5cb7cc..adf2fc4 100644 --- a/README_fr.md +++ b/README_fr.md @@ -16,14 +16,25 @@ Si vous n’avez pas YunoHost, regardez [ici](https://yunohost.org/#/install) po ## Vue d’ensemble -COIN is an Information System designed for associative ISPs in the FFDN. +COIN est un système d'information pour FAI associatif dans la FFDN + +### Fonctionnalités + +- Fourni une interface pour les membres +- Gestion des adhésions +- Gestion des demandes de services tels que VPN, VPS, Housing, compte externe, etc ... + - gestion des pools d'IP + - des hooks peuvent être ajoutés à l'interface pour s'interfacer avec la "vraie" infra et provisionner les services depuis l'admin + - les membres peuvent voir le status et infos de leurs services depuis leur espace + - gestion des factures, des rappels, import des paiements de la banque, calcul de solde +- Optionnel : gestion de mailing liste, prêt de matériel **Version incluse :** 20220401~ynh1 ## Captures d’écran -![Capture d’écran de Coin](./doc/screenshots/user-subscriptions.png) +![Capture d’écran de Coin](./doc/screenshots/screenshot.png) ## Documentations et ressources From 41ca12bd1302f39e308d32ed06867734acf9db8f Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 16 Dec 2023 15:43:50 +0100 Subject: [PATCH 07/23] Add python3-venv dependency --- manifest.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/manifest.toml b/manifest.toml index f6f5e5a..79003e4 100644 --- a/manifest.toml +++ b/manifest.toml @@ -68,7 +68,8 @@ ram.runtime = "50M" main.allowed = "visitors" [resources.apt] - packages = "gunicorn, libldap2-dev, libpq-dev, libsasl2-dev, libjpeg-dev, libxml2-dev, libxslt1-dev, libffi-dev, libpango1.0-0, postgresql, postgresql-contrib, postgresql-server-dev-13" + packages = "gunicorn, python3, python3-venv, libldap2-dev, libpq-dev, libsasl2-dev, libjpeg-dev, libxml2-dev, libxslt1-dev, libffi-dev, libpango1.0-0, postgresql, postgresql-contrib, postgresql-server-dev-13" + [resources.database] type = "postgresql" From 41764fcbe005b54ca11953437fbacdb84fbb6352 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 16 Dec 2023 16:07:24 +0100 Subject: [PATCH 08/23] Bump version to upcoming python3 / django 2.2 version --- manifest.toml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/manifest.toml b/manifest.toml index 79003e4..f7fb428 100644 --- a/manifest.toml +++ b/manifest.toml @@ -7,9 +7,9 @@ name = "Coin" description.en = "Member dashboard for non profit isp." description.fr = "Coin est un Outil pour un Internet Neutre." -version = "20220401~ynh1" +version = "20231216~ynh1" -maintainers = ["ljf"] +maintainers = ["ljf", "aleks"] [upstream] license = "AGPL-3.0-or-later" @@ -55,9 +55,8 @@ ram.runtime = "50M" [resources] [resources.sources.main] - url = "https://code.ffdn.org/ffdn/coin/-/archive/afcf0846f939e80f34956d5f24f7bdd6a5058282/coin-afcf0846f939e80f34956d5f24f7bdd6a5058282.tar.gz" - sha256 = "71a2570a7f145ae826042853f7af2dff46350ef70f83698871b14fa7f9884219" - + url = "https://code.ffdn.org/ffdn/coin/-/archive/a7149e7.tar.gz" + sha256 = "212e242f376095767cbcc682f33f9566f40212b55c6c58bdc442ac714d4098c2" [resources.system_user] From 4843b7aa3bf1bcbb9c3df1657bab70f72bd648c6 Mon Sep 17 00:00:00 2001 From: yunohost-bot Date: Sat, 16 Dec 2023 15:07:35 +0000 Subject: [PATCH 09/23] Auto-update README --- README.md | 2 +- README_fr.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8dd49af..514991b 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ COIN is an Information System designed for associative ISPs in the FFDN. - Optional features: mailing list, hardware provisionning -**Shipped version:** 20220401~ynh1 +**Shipped version:** 20231216~ynh1 ## Screenshots diff --git a/README_fr.md b/README_fr.md index adf2fc4..5030b68 100644 --- a/README_fr.md +++ b/README_fr.md @@ -30,7 +30,7 @@ COIN est un système d'information pour FAI associatif dans la FFDN - Optionnel : gestion de mailing liste, prêt de matériel -**Version incluse :** 20220401~ynh1 +**Version incluse :** 20231216~ynh1 ## Captures d’écran From 688bedd8e17d619616b2f6c13c4a3374f661a2a6 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 16 Dec 2023 16:15:15 +0100 Subject: [PATCH 10/23] Fix syntax --- scripts/install | 2 +- scripts/upgrade | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/install b/scripts/install index e499deb..68483b9 100644 --- a/scripts/install +++ b/scripts/install @@ -38,7 +38,7 @@ ynh_script_progression --message="Configuring application..." export prefix="${path#"/"}/" prefix=${prefix%"/"} -ynh_render_template --template="local.py.j2" --destination="$install_dir/coin/settings_local.py" +ynh_render_template ../conf/local.py.j2 "$install_dir/coin/settings_local.py" ynh_store_file_checksum --file="$install_dir/coin/settings_local.py" # SERVE STATIC FILES IN PRODUCTION MODE diff --git a/scripts/upgrade b/scripts/upgrade index e5e1b36..33a1e7d 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -50,7 +50,7 @@ ynh_script_progression --message="Configuring Django..." --weight=1 export prefix="${path#"/"}/" prefix=${prefix%"/"} ynh_backup_if_checksum_is_different --file="$install_dir/coin/settings_local.py" -ynh_render_template ../conf/local.py.j2 "$install_dir/coin/setings_local.py" +ynh_render_template ../conf/local.py.j2 "$install_dir/coin/settings_local.py" ynh_store_file_checksum --file="$install_dir/coin/settings_local.py" ynh_add_config --template="../conf/gunicorn_config.py" --destination="$install_dir/gunicorn_config.py" From cf185ac0f5552e0c95a19639c8d54bb78d346ffd Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 16 Dec 2023 16:21:28 +0100 Subject: [PATCH 11/23] ynh_python -> venv/bin/python3 --- scripts/install | 4 ++-- scripts/upgrade | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/install b/scripts/install index 68483b9..626f1f1 100644 --- a/scripts/install +++ b/scripts/install @@ -45,8 +45,8 @@ ynh_store_file_checksum --file="$install_dir/coin/settings_local.py" ln -s $install_dir/$app/static $install_dir/static pushd $install_dir - ynh_exec_warn_less $ynh_python manage.py migrate --noinput - ynh_exec_warn_less $ynh_python manage.py collectstatic --noinput + ynh_exec_warn_less venv/bin/python3 manage.py migrate --noinput + ynh_exec_warn_less venv/bin/python3 manage.py collectstatic --noinput popd # Set permissions to directory diff --git a/scripts/upgrade b/scripts/upgrade index 33a1e7d..d5cf032 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -62,7 +62,7 @@ chown $app:www-data "$install_dir/gunicorn_config.py" ynh_script_progression --message="Migrating database..." --weight=1 pushd "$install_dir" - ynh_exec_warn_less $ynh_python manage.py migrate --noinput + ynh_exec_warn_less venv/bin/python3 manage.py migrate --noinput popd #================================================= @@ -71,7 +71,7 @@ popd ynh_script_progression --message="Collecting files..." --weight=1 pushd "$install_dir" - ynh_exec_warn_less $ynh_python manage.py collectstatic --noinput + ynh_exec_warn_less venv/bin/python3 manage.py collectstatic --noinput popd chmod 750 "$install_dir" From bcb644d40b1f23f572e32c493647b5cd8ae57d3e Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 16 Dec 2023 16:29:02 +0100 Subject: [PATCH 12/23] Obsolete local.py, replaced by local.py.j2 --- conf/local.py | 94 --------------------------------------------------- 1 file changed, 94 deletions(-) delete mode 100644 conf/local.py diff --git a/conf/local.py b/conf/local.py deleted file mode 100644 index c129bc4..0000000 --- a/conf/local.py +++ /dev/null @@ -1,94 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals -import os -from settings_base import * - -DEBUG = TEMPLATE_DEBUG = False - -ALLOWED_HOSTS = ['YNH_APP_ARG_DOMAIN'] - -URL_PREFIX = 'YNH_APP_PREFIX' -STATIC_ROOT = 'YNH_APP_STATIC_ROOT' -NOTIFICATION_EMAILS = ['YNH_APP_ARG_EMAIL'] -DEFAULT_FROM_EMAIL = 'notifier@YNH_APP_ARG_DOMAIN' -SITE_URL = "https://YNH_APP_ARG_DOMAINYNH_APP_ARG_PATH" -SECRET_KEY = 'YNH_APP_SECRET_KEY' -ISP = { - 'NAME' : 'YNH_APP_ARG_ISP_NAME', - 'SITE' : 'YNH_APP_ARG_ISP_SITE', - 'EMAIL' : 'YNH_APP_ARG_EMAIL', -} -SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') - -PROJECT_DIR = os.path.abspath(os.path.dirname(__file__)) - -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.postgresql_psycopg2', - 'NAME': 'YNH_DB_NAME', - 'USER': 'YNH_DB_USER', - 'PASSWORD': 'YNH_DB_PASSWORD', - 'HOST': '', # Empty for localhost through domain sockets - 'PORT': '', # Empty for default - }, -} - -FEEDS = (('isp', 'http://www.illyse.net/feed/', 3), - ('ffdn', 'http://www.ffdn.org/fr/rss.xml', 3)) - - - - - - - - - - - - - - - - - - - - - -# Tous accés -# paramétrer SSO en protect_uris -# OU -# Pas d'accès -# hook -# paramétrer SSO en protect_uris -import ldap -from django_auth_ldap.config import LDAPSearch, PosixGroupType -AUTHENTICATION_BACKENDS = ( - 'django_auth_ldap.backend.LDAPBackend', - 'django.contrib.auth.backends.ModelBackend', -) -AUTH_LDAP_SERVER_URI = "ldap://localhost:389" -AUTH_LDAP_USER_SEARCH = LDAPSearch("uid=YNH_APP_ARG_ADMIN,ou=users,dc=yunohost,dc=org", ldap.SCOPE_SUBTREE, "(uid=%(user)s)") -AUTH_LDAP_USER_ATTR_MAP = { - "username": "uid", - "first_name": "givenName", - "last_name": "sn", - "email": "mail", -} -AUTH_LDAP_USER_FLAGS_BY_GROUP = { - "is_active": "cn=sftpusers,ou=groups,dc=yunohost,dc=org", - "is_staff": "cn=sftpusers,ou=groups,dc=yunohost,dc=org", - "is_superuser": "cn=sftpusers,ou=groups,dc=yunohost,dc=org" -} -AUTH_LDAP_GROUP_SEARCH = LDAPSearch("ou=groups,dc=yunohost,dc=org", ldap.SCOPE_SUBTREE, "(objectClass=posixGroup)") -AUTH_LDAP_GROUP_TYPE = PosixGroupType() -AUTH_LDAP_ALWAYS_UPDATE_USER = True -AUTH_LDAP_AUTHORIZE_ALL_USERS = True -AUTH_LDAP_FIND_GROUP_PERMS = True -#AUTH_LDAP_CACHE_GROUPS = True -#AUTH_LDAP_GROUP_CACHE_TIMEOUT = 300 -#import logging -#logger = logging.getLogger('django_auth_ldap') -#logger.addHandler(logging.StreamHandler()) -#logger.setLevel(logging.DEBUG) From 30c4ab94e27953ec4dbc304aeb74373fc3ec834f Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 16 Dec 2023 16:35:11 +0100 Subject: [PATCH 13/23] Disable ldap stuff that may be causing issue --- conf/local.py.j2 | 95 +++++++++++++++++++++--------------------------- 1 file changed, 41 insertions(+), 54 deletions(-) diff --git a/conf/local.py.j2 b/conf/local.py.j2 index d42a4be..692eacc 100644 --- a/conf/local.py.j2 +++ b/conf/local.py.j2 @@ -37,57 +37,44 @@ FEEDS = (('ffdn', 'http://www.ffdn.org/fr/rss.xml', 3),) - - - - - - - - - - - - - - - - - - -# Tous acces -# parametrer SSO en protect_uris -# OU -# Pas d'acces -# hook -# parametrer SSO en protect_uris -import ldap -from django_auth_ldap.config import LDAPSearch, PosixGroupType -AUTHENTICATION_BACKENDS = ( - 'django_auth_ldap.backend.LDAPBackend', - 'django.contrib.auth.backends.ModelBackend', -) -AUTH_LDAP_SERVER_URI = "ldap://localhost:389" -AUTH_LDAP_USER_SEARCH = LDAPSearch("uid={{ admin }},ou=users,dc=yunohost,dc=org", ldap.SCOPE_SUBTREE, "(uid=%(user)s)") -AUTH_LDAP_USER_ATTR_MAP = { - "username": "uid", - "first_name": "givenName", - "last_name": "sn", - "email": "mail", -} -AUTH_LDAP_USER_FLAGS_BY_GROUP = { - "is_active": "cn=sftpusers,ou=groups,dc=yunohost,dc=org", - "is_staff": "cn=sftpusers,ou=groups,dc=yunohost,dc=org", - "is_superuser": "cn=sftpusers,ou=groups,dc=yunohost,dc=org" -} -AUTH_LDAP_GROUP_SEARCH = LDAPSearch("ou=groups,dc=yunohost,dc=org", ldap.SCOPE_SUBTREE, "(objectClass=posixGroup)") -AUTH_LDAP_GROUP_TYPE = PosixGroupType() -AUTH_LDAP_ALWAYS_UPDATE_USER = True -AUTH_LDAP_AUTHORIZE_ALL_USERS = True -AUTH_LDAP_FIND_GROUP_PERMS = True -#AUTH_LDAP_CACHE_GROUPS = True -#AUTH_LDAP_GROUP_CACHE_TIMEOUT = 300 -#import logging -#logger = logging.getLogger('django_auth_ldap') -#logger.addHandler(logging.StreamHandler()) -#logger.setLevel(logging.DEBUG) +# +# +# +# +# +# # Tous acces +# # parametrer SSO en protect_uris +# # OU +# # Pas d'acces +# # hook +# # parametrer SSO en protect_uris +# import ldap +# from django_auth_ldap.config import LDAPSearch, PosixGroupType +# AUTHENTICATION_BACKENDS = ( +# 'django_auth_ldap.backend.LDAPBackend', +# 'django.contrib.auth.backends.ModelBackend', +# ) +# AUTH_LDAP_SERVER_URI = "ldap://localhost:389" +# AUTH_LDAP_USER_SEARCH = LDAPSearch("uid={{ admin }},ou=users,dc=yunohost,dc=org", ldap.SCOPE_SUBTREE, "(uid=%(user)s)") +# AUTH_LDAP_USER_ATTR_MAP = { +# "username": "uid", +# "first_name": "givenName", +# "last_name": "sn", +# "email": "mail", +# } +# AUTH_LDAP_USER_FLAGS_BY_GROUP = { +# "is_active": "cn=sftpusers,ou=groups,dc=yunohost,dc=org", +# "is_staff": "cn=sftpusers,ou=groups,dc=yunohost,dc=org", +# "is_superuser": "cn=sftpusers,ou=groups,dc=yunohost,dc=org" +# } +# AUTH_LDAP_GROUP_SEARCH = LDAPSearch("ou=groups,dc=yunohost,dc=org", ldap.SCOPE_SUBTREE, "(objectClass=posixGroup)") +# AUTH_LDAP_GROUP_TYPE = PosixGroupType() +# AUTH_LDAP_ALWAYS_UPDATE_USER = True +# AUTH_LDAP_AUTHORIZE_ALL_USERS = True +# AUTH_LDAP_FIND_GROUP_PERMS = True +# #AUTH_LDAP_CACHE_GROUPS = True +# #AUTH_LDAP_GROUP_CACHE_TIMEOUT = 300 +# #import logging +# #logger = logging.getLogger('django_auth_ldap') +# #logger.addHandler(logging.StreamHandler()) +# #logger.setLevel(logging.DEBUG) From bc2caf68f8a97883867144a4b8fd95d05dc67988 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 16 Dec 2023 16:42:45 +0100 Subject: [PATCH 14/23] Bump commit --- manifest.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/manifest.toml b/manifest.toml index f7fb428..2445633 100644 --- a/manifest.toml +++ b/manifest.toml @@ -55,8 +55,8 @@ ram.runtime = "50M" [resources] [resources.sources.main] - url = "https://code.ffdn.org/ffdn/coin/-/archive/a7149e7.tar.gz" - sha256 = "212e242f376095767cbcc682f33f9566f40212b55c6c58bdc442ac714d4098c2" + url = "https://code.ffdn.org/ffdn/coin/-/archive/c891075.tar.gz" + sha256 = "104448a644ed20248ea3d47a63ee2685f0fb3f82e7411f06b96af24addfc100c" [resources.system_user] From 428c5293ee964daba9839271fa5901d9767427e0 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 16 Dec 2023 17:02:21 +0100 Subject: [PATCH 15/23] Zgrmlb --- scripts/install | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/install b/scripts/install index 626f1f1..8760d72 100644 --- a/scripts/install +++ b/scripts/install @@ -44,6 +44,9 @@ ynh_store_file_checksum --file="$install_dir/coin/settings_local.py" # SERVE STATIC FILES IN PRODUCTION MODE ln -s $install_dir/$app/static $install_dir/static +ls -l $install_dir +ls -l $install_dir/coin + pushd $install_dir ynh_exec_warn_less venv/bin/python3 manage.py migrate --noinput ynh_exec_warn_less venv/bin/python3 manage.py collectstatic --noinput From ce85a23a39cf5ee5385e6cf01a1ba3a0c59b043e Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 16 Dec 2023 17:09:50 +0100 Subject: [PATCH 16/23] Zgrlbml --- manifest.toml | 1 - scripts/install | 5 ++--- scripts/upgrade | 3 +++ 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/manifest.toml b/manifest.toml index 2445633..61350c3 100644 --- a/manifest.toml +++ b/manifest.toml @@ -69,6 +69,5 @@ ram.runtime = "50M" [resources.apt] packages = "gunicorn, python3, python3-venv, libldap2-dev, libpq-dev, libsasl2-dev, libjpeg-dev, libxml2-dev, libxslt1-dev, libffi-dev, libpango1.0-0, postgresql, postgresql-contrib, postgresql-server-dev-13" - [resources.database] type = "postgresql" diff --git a/scripts/install b/scripts/install index 8760d72..4037416 100644 --- a/scripts/install +++ b/scripts/install @@ -40,13 +40,12 @@ export prefix="${path#"/"}/" prefix=${prefix%"/"} ynh_render_template ../conf/local.py.j2 "$install_dir/coin/settings_local.py" ynh_store_file_checksum --file="$install_dir/coin/settings_local.py" +chmod 400 "$install_dir/coin/settings_local.py" +chown $app "$install_dir/coin/settings_local.py" # SERVE STATIC FILES IN PRODUCTION MODE ln -s $install_dir/$app/static $install_dir/static -ls -l $install_dir -ls -l $install_dir/coin - pushd $install_dir ynh_exec_warn_less venv/bin/python3 manage.py migrate --noinput ynh_exec_warn_less venv/bin/python3 manage.py collectstatic --noinput diff --git a/scripts/upgrade b/scripts/upgrade index d5cf032..06bbca8 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -53,6 +53,9 @@ ynh_backup_if_checksum_is_different --file="$install_dir/coin/settings_local.py" ynh_render_template ../conf/local.py.j2 "$install_dir/coin/settings_local.py" ynh_store_file_checksum --file="$install_dir/coin/settings_local.py" +chmod 400 "$install_dir/coin/settings_local.py" +chown $app "$install_dir/coin/settings_local.py" + ynh_add_config --template="../conf/gunicorn_config.py" --destination="$install_dir/gunicorn_config.py" chown $app:www-data "$install_dir/gunicorn_config.py" From affabb064a1eb92f122433f9cef89feaabeb0d4c Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 16 Dec 2023 17:15:48 +0100 Subject: [PATCH 17/23] Zelrmvlf --- conf/gunicorn.service | 16 ---------------- conf/gunicorn_config.py | 22 +++++++++++----------- conf/local.py.j2 | 2 +- conf/systemd.service | 2 +- 4 files changed, 13 insertions(+), 29 deletions(-) delete mode 100644 conf/gunicorn.service diff --git a/conf/gunicorn.service b/conf/gunicorn.service deleted file mode 100644 index 87ba5fa..0000000 --- a/conf/gunicorn.service +++ /dev/null @@ -1,16 +0,0 @@ -[Unit] -Description=YNH_APP_INSTANCE_NAME gunicorn daemon -After=network.target - -[Service] -PIDFile=/run/gunicorn/YNH_APP_INSTANCE_NAME-pid -User=YNH_APP_INSTANCE_NAME -Group=www-data -WorkingDirectory=/opt/YNH_APP_INSTANCE_NAME -ExecStart=__YNH_PYTHON_PATH__/gunicorn -c /opt/YNH_APP_INSTANCE_NAME/gunicorn_config.py YNH_APP_INSTANCE_NAME.wsgi -ExecReload=/bin/kill -s HUP $MAINPID -ExecStop=/bin/kill -s TERM $MAINPID -PrivateTmp=true - -[Install] -WantedBy=multi-user.target diff --git a/conf/gunicorn_config.py b/conf/gunicorn_config.py index d1c2a5d..bb99fbc 100644 --- a/conf/gunicorn_config.py +++ b/conf/gunicorn_config.py @@ -1,11 +1,11 @@ -command = '__YNH_PYTHON_PATH__/gunicorn' -pythonpath = '/opt/__APP__' -workers = 4 -user = '__APP__' -bind = 'unix:/opt/__APP__/sock' -pid = '/run/gunicorn/__APP__-pid' -errorlog = '/var/log/__APP__/error.log' -accesslog = '/var/log/__APP__/access.log' -access_log_format = '%({X-Real-IP}i)s %({X-Forwarded-For}i)s %(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"' -loglevel = 'warning' -capture_output = True +command = '__INSTALL_DIR/venv/bin/gunicorn' +pythonpath = '__INSTALL_DIR__' +workers = 4 +user = '__APP__' +bind = 'unix:__INSTALL_DIR__/sock' +pid = '/run/gunicorn/__APP__-pid' +errorlog = '/var/log/__APP__/error.log' +accesslog = '/var/log/__APP__/access.log' +access_log_format = '%({X-Real-IP}i)s %({X-Forwarded-For}i)s %(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"' +loglevel = 'warning' +capture_output = True diff --git a/conf/local.py.j2 b/conf/local.py.j2 index 692eacc..da936d0 100644 --- a/conf/local.py.j2 +++ b/conf/local.py.j2 @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals import os -from settings_base import * +from .settings_base import * DEBUG = TEMPLATE_DEBUG = False diff --git a/conf/systemd.service b/conf/systemd.service index 1531ef2..9179d0e 100644 --- a/conf/systemd.service +++ b/conf/systemd.service @@ -7,7 +7,7 @@ PIDFile=/run/gunicorn/__APP__-pid User=__APP__ Group=__APP__ WorkingDirectory=__INSTALL_DIR__/ -ExecStart=__YNH_PYTHON_PATH__/gunicorn -c __INSTALL_DIR__/gunicorn_config.py __APP__.wsgi +ExecStart=__INSTALL_DIR__/venv/bin/gunicorn -c __INSTALL_DIR__/gunicorn_config.py __APP__.wsgi ExecReload=/bin/kill -s HUP $MAINPID ExecStop=/bin/kill -s TERM $MAINPID PrivateTmp=true From 08ebc4c4c40275b351880c309cb668d71af090ba Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 16 Dec 2023 18:13:48 +0100 Subject: [PATCH 18/23] Disable multi_instance because meh --- manifest.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifest.toml b/manifest.toml index 61350c3..ba324d6 100644 --- a/manifest.toml +++ b/manifest.toml @@ -19,7 +19,7 @@ code = "https://code.ffdn.org/ffdn/coin" [integration] yunohost = ">= 11.0.0" architectures = "all" -multi_instance = true +multi_instance = false ldap = false sso = false disk = "50M" From 1e1c0aee7277250df6353987fb4de2232005745c Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 16 Dec 2023 18:14:00 +0100 Subject: [PATCH 19/23] Try to remove some probably unecessary dependencies --- manifest.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifest.toml b/manifest.toml index ba324d6..e5a4ba9 100644 --- a/manifest.toml +++ b/manifest.toml @@ -67,7 +67,7 @@ ram.runtime = "50M" main.allowed = "visitors" [resources.apt] - packages = "gunicorn, python3, python3-venv, libldap2-dev, libpq-dev, libsasl2-dev, libjpeg-dev, libxml2-dev, libxslt1-dev, libffi-dev, libpango1.0-0, postgresql, postgresql-contrib, postgresql-server-dev-13" + packages = "gunicorn, python3, python3-venv, libpq-dev, libsasl2-dev, libjpeg-dev, libxml2-dev, libxslt1-dev, libpango1.0-0, postgresql, postgresql-contrib, postgresql-server-dev-13" [resources.database] type = "postgresql" From d873482487400b88719e5de7ebb66cd4fa7514b2 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 16 Dec 2023 18:16:10 +0100 Subject: [PATCH 20/23] Save the secret as a setting --- scripts/install | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/install b/scripts/install index 4037416..005049a 100644 --- a/scripts/install +++ b/scripts/install @@ -4,6 +4,7 @@ source _common.sh source /usr/share/yunohost/helpers export secret=$(ynh_string_random 24) +ynh_app_setting_set --app=$app --key=secret --value=$secret #================================================= # DOWNLOAD, CHECK AND UNPACK SOURCE From b4460a3894e38022ead78d6c4de36a78db390dfc Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 16 Dec 2023 19:13:01 +0100 Subject: [PATCH 21/23] Bump sources --- conf/local.py.j2 | 5 ----- manifest.toml | 4 ++-- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/conf/local.py.j2 b/conf/local.py.j2 index da936d0..477fd56 100644 --- a/conf/local.py.j2 +++ b/conf/local.py.j2 @@ -37,11 +37,6 @@ FEEDS = (('ffdn', 'http://www.ffdn.org/fr/rss.xml', 3),) -# -# -# -# -# # # Tous acces # # parametrer SSO en protect_uris # # OU diff --git a/manifest.toml b/manifest.toml index e5a4ba9..17aa768 100644 --- a/manifest.toml +++ b/manifest.toml @@ -55,8 +55,8 @@ ram.runtime = "50M" [resources] [resources.sources.main] - url = "https://code.ffdn.org/ffdn/coin/-/archive/c891075.tar.gz" - sha256 = "104448a644ed20248ea3d47a63ee2685f0fb3f82e7411f06b96af24addfc100c" + url = "https://code.ffdn.org/ffdn/coin/-/archive/4336141.tar.gz" + sha256 = "d2c98d52eedbd74cfbec46144ae0a46036322916321b6bac519e98717965fcb1" [resources.system_user] From 1607fb24075afd258fab916754e1f9780a3718a3 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 16 Dec 2023 19:31:32 +0100 Subject: [PATCH 22/23] Need to regen the app conf during changeurl --- scripts/change_url | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scripts/change_url b/scripts/change_url index e65f14b..2eb3b36 100644 --- a/scripts/change_url +++ b/scripts/change_url @@ -7,6 +7,13 @@ ynh_systemd_action --service_name=$app --action="stop" --log_path="/var/log/$app ynh_change_url_nginx_config +ynh_backup_if_checksum_is_different --file="$install_dir/coin/settings_local.py" +ynh_render_template ../conf/local.py.j2 "$install_dir/coin/settings_local.py" +ynh_store_file_checksum --file="$install_dir/coin/settings_local.py" + +chmod 400 "$install_dir/coin/settings_local.py" +chown $app "$install_dir/coin/settings_local.py" + ynh_systemd_action --service_name=$app --action="start" --log_path="/var/log/$app/$app.log" ynh_script_progression --message="Change of URL completed for $app" --last From ac7d88e3bce5ebddc87126e71da60b0ec5dac931 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 16 Dec 2023 21:33:17 +0100 Subject: [PATCH 23/23] Bump sources --- manifest.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/manifest.toml b/manifest.toml index 17aa768..3853642 100644 --- a/manifest.toml +++ b/manifest.toml @@ -55,8 +55,8 @@ ram.runtime = "50M" [resources] [resources.sources.main] - url = "https://code.ffdn.org/ffdn/coin/-/archive/4336141.tar.gz" - sha256 = "d2c98d52eedbd74cfbec46144ae0a46036322916321b6bac519e98717965fcb1" + url = "https://code.ffdn.org/ffdn/coin/-/archive/bcaad5f.tar.gz" + sha256 = "241d527c7bab5410bcc225d5beeff8286aa8ae649b9711c421624ae96136cdd7" [resources.system_user]