From 27dd1b50fe14fe1c61e06778322585e71b0c83d6 Mon Sep 17 00:00:00 2001 From: antoine Date: Sun, 12 Jan 2020 16:13:50 +0100 Subject: [PATCH] permier commit --- README.md | 82 +++++- README_fr.md | 89 ++++++ check_process.default | 40 +++ conf/app.src | 6 + conf/nginx.conf | 33 +++ conf/php-fpm.conf | 430 +++++++++++++++++++++++++++++ conf/systemd.service | 13 + doc/demo.png | Bin 0 -> 77366 bytes manifest.json | 57 ++++ pull_request_template.md | 18 ++ scripts/_common.sh | 20 ++ scripts/backup | 113 ++++++++ scripts/change_url | 134 +++++++++ scripts/install | 370 +++++++++++++++++++++++++ scripts/remove | 141 ++++++++++ scripts/restore | 158 +++++++++++ scripts/upgrade | 225 +++++++++++++++ sources/extra_files/app/.gitignore | 2 + sources/patches/.gitignore | 2 + 19 files changed, 1931 insertions(+), 2 deletions(-) create mode 100644 README_fr.md create mode 100644 check_process.default create mode 100644 conf/app.src create mode 100644 conf/nginx.conf create mode 100644 conf/php-fpm.conf create mode 100644 conf/systemd.service create mode 100644 doc/demo.png create mode 100644 manifest.json create mode 100644 pull_request_template.md create mode 100644 scripts/_common.sh create mode 100755 scripts/backup create mode 100644 scripts/change_url create mode 100755 scripts/install create mode 100755 scripts/remove create mode 100755 scripts/restore create mode 100755 scripts/upgrade create mode 100644 sources/extra_files/app/.gitignore create mode 100644 sources/patches/.gitignore diff --git a/README.md b/README.md index 0565c5d..073a82d 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,80 @@ -# petitesannonces_ynh -Yunohost package for PetitesAnnonces +# PetitesAnnonces for YunoHost + +[![Integration level](https://dash.yunohost.org/integration/petitesannonces.svg)](https://dash.yunohost.org/appci/app/petitesannonces) +[![Install petitesannonces with YunoHost](https://install-app.yunohost.org/install-with-yunohost.png)](https://install-app.yunohost.org/?app=petitesannonces) + +*[Lire ce readme en français.](./README_fr.md)* + +> *This package allow you to install PetitesAnnonces quickly and simply on a YunoHost server. +If you don't have YunoHost, please see [here](https://yunohost.org/#/install) to know how to install and enjoy it.* + +## Overview + +Make your own classified advertising website easily. + +**Shipped version:** 0.1.0 + +## Screenshots + +![demo](doc/demo.png) + + + +## Documentation + + * Official documentation: https://framagit.org/toitoinebzh/petitesannonces + * YunoHost documentation: If specific documentation is needed, feel free to contribute. + +## YunoHost specific features + +#### Multi-users support + +Are LDAP and HTTP auth supported? : No +Can the app be used by multiple users? : No + +#### Supported architectures + +* x86-64b - [![Build Status](https://ci-apps.yunohost.org/ci/logs/petitesannonces%20%28Apps%29.svg)](https://ci-apps.yunohost.org/ci/apps/petitesannonces/) +* ARMv8-A - [![Build Status](https://ci-apps-arm.yunohost.org/ci/logs/petitesannonces%20%28Apps%29.svg)](https://ci-apps-arm.yunohost.org/ci/apps/petitesannonces/) + +## Limitations + +* Any known limitations : young application, still in developpment. + +## Additional information + +* Other information you would add about this application + +**More information on the documentation page:** +https://yunohost.org/packaging_apps + +## Links + + * Report a bug: https://github.com/YunoHost-Apps/petitesannonces_ynh/issues + * App website: https://framagit.org/toitoinebzh/petitesannonces + * Upstream app repository: https://framagit.org/toitoinebzh/petitesannonces + * YunoHost website: https://yunohost.org/ + +--- + +Developers info +---------------- + +**Only if you want to use a testing branch for coding, instead of merging directly into master.** +Please do your pull request to the [testing branch](https://github.com/YunoHost-Apps/petitesannonces_ynh/tree/testing). + +To try the testing branch, please proceed like that. +``` +sudo yunohost app install https://github.com/YunoHost-Apps/petitesannonces_ynh/tree/testing --debug +or +sudo yunohost app upgrade petitesannonces -u https://github.com/YunoHost-Apps/petitesannonces_ynh/tree/testing --debug +``` diff --git a/README_fr.md b/README_fr.md new file mode 100644 index 0000000..77417fb --- /dev/null +++ b/README_fr.md @@ -0,0 +1,89 @@ +# PetitesAnnonces pour YunoHost + +[![Niveau d'intégration](https://dash.yunohost.org/integration/petitesannonces.svg)](https://dash.yunohost.org/appci/app/petitesannonces) +[![Installer petitesannonces avec YunoHost](https://install-app.yunohost.org/install-with-yunohost.png)](https://install-app.yunohost.org/?app=petitesannonces) + +*[Read this readme in english.](./README.md)* + +> *Ce package vous permet d'installer PetitesAnnonces 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 + +Grâce à cet outil, créer simplement et rapidement votre site de Petites Annonces. + +Les fonctionnalités de cette outils sont : + * Affichage des annonces mises en ligne + * Tri selon la région, la catégorie et mot-clés + * Ajout/suppression d'annonces + * Possibilité d'envoyer un message à celui qui a déposé l'annonce + + +**Version incluse:** 0.1.0 + +## Captures d'écran + +![demo](doc/demo.png) + + + +## Documentation + + * Documentation officielle: https://framagit.org/toitoinebzh/petitesannonces + * Documentation YunoHost: Si une documentation spécifique est nécessaire, n'hésitez pas à contribuer. + +## Caractéristiques spécifiques YunoHost + +#### Support multi-utilisateurs + +L'authentification LDAP et HTTP est-elle prise en charge? : Non +L'application peut-elle être utilisée par plusieurs utilisateurs? : Non + +#### Architectures supportées + +* x86-64b - [![Build Status](https://ci-apps.yunohost.org/ci/logs/petitesannonces%20%28Apps%29.svg)](https://ci-apps.yunohost.org/ci/apps/petitesannonces/) +* ARMv8-A - [![Build Status](https://ci-apps-arm.yunohost.org/ci/logs/petitesannonces%20%28Apps%29.svg)](https://ci-apps-arm.yunohost.org/ci/apps/petitesannonces/) +* Jessie x86-64b - [![Build Status](https://ci-stretch.nohost.me/ci/logs/petitesannonces%20%28Apps%29.svg)](https://ci-stretch.nohost.me/ci/apps/petitesannonces/) + +## Limitations + +* Limitations connues : jeune application en cours de développement. + +## Informations additionnelles + +* Autres informations à ajouter sur cette application : RAS + +**Plus d'informations sur la page de documentation:** +https://yunohost.org/packaging_apps + +## Liens + + * Signaler un bug: https://github.com/YunoHost-Apps/petitesannonces_ynh/issues + * Site de l'application: https://framagit.org/toitoinebzh/petitesannonces + * Dépôt de l'application principale: https://framagit.org/toitoinebzh/petitesannonces + * Site web YunoHost: https://yunohost.org/ + +--- + +Informations pour les développeurs +---------------- + +**Seulement si vous voulez utiliser une branche de test pour le codage, au lieu de fusionner directement dans la banche principale.** +Merci de faire vos pull request sur la [branche testing](https://github.com/YunoHost-Apps/petitesannonces_ynh/tree/testing). + +Pour essayer la branche testing, procédez comme suit. +``` +sudo yunohost app install https://github.com/YunoHost-Apps/petitesannonces_ynh/tree/testing --debug +ou +sudo yunohost app upgrade petitesannonces -u https://github.com/YunoHost-Apps/petitesannonces_ynh/tree/testing --debug +``` diff --git a/check_process.default b/check_process.default new file mode 100644 index 0000000..97a2bc5 --- /dev/null +++ b/check_process.default @@ -0,0 +1,40 @@ +# See here for more information +# https://github.com/YunoHost/package_check#syntax-check_process-file + +# Move this file from check_process.default to check_process when you have filled it. + +;; Test complet + ; Manifest + domain="domain.tld" (DOMAIN) + path="/path" (PATH) + admin="john" (USER) + language="fr" + is_public=1 (PUBLIC|public=1|private=0) + password="pass" + port="666" (PORT) + ; Checks + pkg_linter=1 + setup_sub_dir=1 + setup_root=1 + setup_nourl=0 + setup_private=1 + setup_public=1 + upgrade=1 + upgrade=1 from_commit=CommitHash + backup_restore=1 + multi_instance=1 + # This test is no longer necessary since the version 2.7 (PR: https://github.com/YunoHost/yunohost/pull/304), you can still do it if your app could be installed with this version. + # incorrect_path=1 + port_already_use=0 + change_url=1 +;;; Levels + # If the level 5 (Package linter) is forced to 1. Please add justifications here. + Level 5=auto +;;; Options +Email= +Notification=none +;;; Upgrade options + ; commit=CommitHash + name=Name and date of the commit. + manifest_arg=domain=DOMAIN&path=PATH&admin=USER&language=fr&is_public=1&password=pass&port=666& + diff --git a/conf/app.src b/conf/app.src new file mode 100644 index 0000000..7cdd1e3 --- /dev/null +++ b/conf/app.src @@ -0,0 +1,6 @@ +SOURCE_URL=https://framagit.org/toitoinebzh/petitesannonces/-/archive/master/petitesannonces-master.tar.gz +SOURCE_SUM=247895eb74422f36e6b42640d51a6654ddaf57ffa0ea94841e86b717ef337f79 +SOURCE_SUM_PRG=sha256sum +SOURCE_FORMAT=tar.gz +SOURCE_IN_SUBDIR=true +SOURCE_FILENAME= diff --git a/conf/nginx.conf b/conf/nginx.conf new file mode 100644 index 0000000..f2277ea --- /dev/null +++ b/conf/nginx.conf @@ -0,0 +1,33 @@ +#sub_path_only rewrite ^__PATH__$ __PATH__/ permanent; +location __PATH__/ { + + # Path to source + alias __FINALPATH__/ ; + + # Force usage of https + if ($scheme = http) { + rewrite ^ https://$server_name$request_uri? permanent; + } + +### Example PHP configuration (remove it if not used) + index index.php; + + # Common parameter to increase upload size limit in conjunction with dedicated php-fpm file + #client_max_body_size 50M; + + try_files $uri $uri/ index.php; + location ~ [^/]\.php(/|$) { + fastcgi_split_path_info ^(.+?\.php)(/.*)$; + fastcgi_pass unix:/var/run/php/php7.0-fpm-__NAME__.sock; + + fastcgi_index index.php; + include fastcgi_params; + fastcgi_param REMOTE_USER $remote_user; + fastcgi_param PATH_INFO $fastcgi_path_info; + fastcgi_param SCRIPT_FILENAME $request_filename; + } +### End of PHP configuration part + + # Include SSOWAT user panel. + include conf.d/yunohost_panel.conf.inc; +} diff --git a/conf/php-fpm.conf b/conf/php-fpm.conf new file mode 100644 index 0000000..ab5dca9 --- /dev/null +++ b/conf/php-fpm.conf @@ -0,0 +1,430 @@ +; Start a new pool named 'www'. +; the variable $pool can be used in any directive and will be replaced by the +; pool name ('www' here) +[__NAMETOCHANGE__] + +; Per pool prefix +; It only applies on the following directives: +; - 'access.log' +; - 'slowlog' +; - 'listen' (unixsocket) +; - 'chroot' +; - 'chdir' +; - 'php_values' +; - 'php_admin_values' +; When not set, the global prefix (or /usr) applies instead. +; Note: This directive can also be relative to the global prefix. +; Default Value: none +;prefix = /path/to/pools/$pool + +; Unix user/group of processes +; Note: The user is mandatory. If the group is not set, the default user's group +; will be used. +user = __USER__ +group = __USER__ + +; The address on which to accept FastCGI requests. +; Valid syntaxes are: +; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific IPv4 address on +; a specific port; +; '[ip:6:addr:ess]:port' - to listen on a TCP socket to a specific IPv6 address on +; a specific port; +; 'port' - to listen on a TCP socket to all addresses +; (IPv6 and IPv4-mapped) on a specific port; +; '/path/to/unix/socket' - to listen on a unix socket. +; Note: This value is mandatory. +listen = /var/run/php/php7.0-fpm-__NAMETOCHANGE__.sock + +; Set listen(2) backlog. +; Default Value: 511 (-1 on FreeBSD and OpenBSD) +;listen.backlog = 511 + +; Set permissions for unix socket, if one is used. In Linux, read/write +; permissions must be set in order to allow connections from a web server. Many +; BSD-derived systems allow connections regardless of permissions. +; Default Values: user and group are set as the running user +; mode is set to 0660 +listen.owner = www-data +listen.group = www-data +;listen.mode = 0660 +; When POSIX Access Control Lists are supported you can set them using +; these options, value is a comma separated list of user/group names. +; When set, listen.owner and listen.group are ignored +;listen.acl_users = +;listen.acl_groups = + +; List of addresses (IPv4/IPv6) of FastCGI clients which are allowed to connect. +; Equivalent to the FCGI_WEB_SERVER_ADDRS environment variable in the original +; PHP FCGI (5.2.2+). Makes sense only with a tcp listening socket. Each address +; must be separated by a comma. If this value is left blank, connections will be +; accepted from any ip address. +; Default Value: any +;listen.allowed_clients = 127.0.0.1 + +; Specify the nice(2) priority to apply to the pool processes (only if set) +; The value can vary from -19 (highest priority) to 20 (lower priority) +; Note: - It will only work if the FPM master process is launched as root +; - The pool processes will inherit the master process priority +; unless it specified otherwise +; Default Value: no set +; process.priority = -19 + +; Set the process dumpable flag (PR_SET_DUMPABLE prctl) even if the process user +; or group is differrent than the master process user. It allows to create process +; core dump and ptrace the process for the pool user. +; Default Value: no +; process.dumpable = yes + +; Choose how the process manager will control the number of child processes. +; Possible Values: +; static - a fixed number (pm.max_children) of child processes; +; dynamic - the number of child processes are set dynamically based on the +; following directives. With this process management, there will be +; always at least 1 children. +; pm.max_children - the maximum number of children that can +; be alive at the same time. +; pm.start_servers - the number of children created on startup. +; pm.min_spare_servers - the minimum number of children in 'idle' +; state (waiting to process). If the number +; of 'idle' processes is less than this +; number then some children will be created. +; pm.max_spare_servers - the maximum number of children in 'idle' +; state (waiting to process). If the number +; of 'idle' processes is greater than this +; number then some children will be killed. +; ondemand - no children are created at startup. Children will be forked when +; new requests will connect. The following parameter are used: +; pm.max_children - the maximum number of children that +; can be alive at the same time. +; pm.process_idle_timeout - The number of seconds after which +; an idle process will be killed. +; Note: This value is mandatory. +pm = dynamic + +; The number of child processes to be created when pm is set to 'static' and the +; maximum number of child processes when pm is set to 'dynamic' or 'ondemand'. +; This value sets the limit on the number of simultaneous requests that will be +; served. Equivalent to the ApacheMaxClients directive with mpm_prefork. +; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP +; CGI. The below defaults are based on a server without much resources. Don't +; forget to tweak pm.* to fit your needs. +; Note: Used when pm is set to 'static', 'dynamic' or 'ondemand' +; Note: This value is mandatory. +pm.max_children = 5 + +; The number of child processes created on startup. +; Note: Used only when pm is set to 'dynamic' +; Default Value: min_spare_servers + (max_spare_servers - min_spare_servers) / 2 +pm.start_servers = 2 + +; The desired minimum number of idle server processes. +; Note: Used only when pm is set to 'dynamic' +; Note: Mandatory when pm is set to 'dynamic' +pm.min_spare_servers = 1 + +; The desired maximum number of idle server processes. +; Note: Used only when pm is set to 'dynamic' +; Note: Mandatory when pm is set to 'dynamic' +pm.max_spare_servers = 3 + +; The number of seconds after which an idle process will be killed. +; Note: Used only when pm is set to 'ondemand' +; Default Value: 10s +;pm.process_idle_timeout = 10s; + +; The number of requests each child process should execute before respawning. +; This can be useful to work around memory leaks in 3rd party libraries. For +; endless request processing specify '0'. Equivalent to PHP_FCGI_MAX_REQUESTS. +; Default Value: 0 +;pm.max_requests = 500 + +; The URI to view the FPM status page. If this value is not set, no URI will be +; recognized as a status page. It shows the following informations: +; pool - the name of the pool; +; process manager - static, dynamic or ondemand; +; start time - the date and time FPM has started; +; start since - number of seconds since FPM has started; +; accepted conn - the number of request accepted by the pool; +; listen queue - the number of request in the queue of pending +; connections (see backlog in listen(2)); +; max listen queue - the maximum number of requests in the queue +; of pending connections since FPM has started; +; listen queue len - the size of the socket queue of pending connections; +; idle processes - the number of idle processes; +; active processes - the number of active processes; +; total processes - the number of idle + active processes; +; max active processes - the maximum number of active processes since FPM +; has started; +; max children reached - number of times, the process limit has been reached, +; when pm tries to start more children (works only for +; pm 'dynamic' and 'ondemand'); +; Value are updated in real time. +; Example output: +; pool: www +; process manager: static +; start time: 01/Jul/2011:17:53:49 +0200 +; start since: 62636 +; accepted conn: 190460 +; listen queue: 0 +; max listen queue: 1 +; listen queue len: 42 +; idle processes: 4 +; active processes: 11 +; total processes: 15 +; max active processes: 12 +; max children reached: 0 +; +; By default the status page output is formatted as text/plain. Passing either +; 'html', 'xml' or 'json' in the query string will return the corresponding +; output syntax. Example: +; http://www.foo.bar/status +; http://www.foo.bar/status?json +; http://www.foo.bar/status?html +; http://www.foo.bar/status?xml +; +; By default the status page only outputs short status. Passing 'full' in the +; query string will also return status for each pool process. +; Example: +; http://www.foo.bar/status?full +; http://www.foo.bar/status?json&full +; http://www.foo.bar/status?html&full +; http://www.foo.bar/status?xml&full +; The Full status returns for each process: +; pid - the PID of the process; +; state - the state of the process (Idle, Running, ...); +; start time - the date and time the process has started; +; start since - the number of seconds since the process has started; +; requests - the number of requests the process has served; +; request duration - the duration in µs of the requests; +; request method - the request method (GET, POST, ...); +; request URI - the request URI with the query string; +; content length - the content length of the request (only with POST); +; user - the user (PHP_AUTH_USER) (or '-' if not set); +; script - the main script called (or '-' if not set); +; last request cpu - the %cpu the last request consumed +; it's always 0 if the process is not in Idle state +; because CPU calculation is done when the request +; processing has terminated; +; last request memory - the max amount of memory the last request consumed +; it's always 0 if the process is not in Idle state +; because memory calculation is done when the request +; processing has terminated; +; If the process is in Idle state, then informations are related to the +; last request the process has served. Otherwise informations are related to +; the current request being served. +; Example output: +; ************************ +; pid: 31330 +; state: Running +; start time: 01/Jul/2011:17:53:49 +0200 +; start since: 63087 +; requests: 12808 +; request duration: 1250261 +; request method: GET +; request URI: /test_mem.php?N=10000 +; content length: 0 +; user: - +; script: /home/fat/web/docs/php/test_mem.php +; last request cpu: 0.00 +; last request memory: 0 +; +; Note: There is a real-time FPM status monitoring sample web page available +; It's available in: /usr/share/php/7.0/fpm/status.html +; +; Note: The value must start with a leading slash (/). The value can be +; anything, but it may not be a good idea to use the .php extension or it +; may conflict with a real PHP file. +; Default Value: not set +;pm.status_path = /status + +; The ping URI to call the monitoring page of FPM. If this value is not set, no +; URI will be recognized as a ping page. This could be used to test from outside +; that FPM is alive and responding, or to +; - create a graph of FPM availability (rrd or such); +; - remove a server from a group if it is not responding (load balancing); +; - trigger alerts for the operating team (24/7). +; Note: The value must start with a leading slash (/). The value can be +; anything, but it may not be a good idea to use the .php extension or it +; may conflict with a real PHP file. +; Default Value: not set +;ping.path = /ping + +; This directive may be used to customize the response of a ping request. The +; response is formatted as text/plain with a 200 response code. +; Default Value: pong +;ping.response = pong + +; The access log file +; Default: not set +;access.log = log/$pool.access.log + +; The access log format. +; The following syntax is allowed +; %%: the '%' character +; %C: %CPU used by the request +; it can accept the following format: +; - %{user}C for user CPU only +; - %{system}C for system CPU only +; - %{total}C for user + system CPU (default) +; %d: time taken to serve the request +; it can accept the following format: +; - %{seconds}d (default) +; - %{miliseconds}d +; - %{mili}d +; - %{microseconds}d +; - %{micro}d +; %e: an environment variable (same as $_ENV or $_SERVER) +; it must be associated with embraces to specify the name of the env +; variable. Some exemples: +; - server specifics like: %{REQUEST_METHOD}e or %{SERVER_PROTOCOL}e +; - HTTP headers like: %{HTTP_HOST}e or %{HTTP_USER_AGENT}e +; %f: script filename +; %l: content-length of the request (for POST request only) +; %m: request method +; %M: peak of memory allocated by PHP +; it can accept the following format: +; - %{bytes}M (default) +; - %{kilobytes}M +; - %{kilo}M +; - %{megabytes}M +; - %{mega}M +; %n: pool name +; %o: output header +; it must be associated with embraces to specify the name of the header: +; - %{Content-Type}o +; - %{X-Powered-By}o +; - %{Transfert-Encoding}o +; - .... +; %p: PID of the child that serviced the request +; %P: PID of the parent of the child that serviced the request +; %q: the query string +; %Q: the '?' character if query string exists +; %r: the request URI (without the query string, see %q and %Q) +; %R: remote IP address +; %s: status (response code) +; %t: server time the request was received +; it can accept a strftime(3) format: +; %d/%b/%Y:%H:%M:%S %z (default) +; The strftime(3) format must be encapsuled in a %{}t tag +; e.g. for a ISO8601 formatted timestring, use: %{%Y-%m-%dT%H:%M:%S%z}t +; %T: time the log has been written (the request has finished) +; it can accept a strftime(3) format: +; %d/%b/%Y:%H:%M:%S %z (default) +; The strftime(3) format must be encapsuled in a %{}t tag +; e.g. for a ISO8601 formatted timestring, use: %{%Y-%m-%dT%H:%M:%S%z}t +; %u: remote user +; +; Default: "%R - %u %t \"%m %r\" %s" +;access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{mili}d %{kilo}M %C%%" + +; The log file for slow requests +; Default Value: not set +; Note: slowlog is mandatory if request_slowlog_timeout is set +;slowlog = log/$pool.log.slow + +; The timeout for serving a single request after which a PHP backtrace will be +; dumped to the 'slowlog' file. A value of '0s' means 'off'. +; Available units: s(econds)(default), m(inutes), h(ours), or d(ays) +; Default Value: 0 +;request_slowlog_timeout = 0 + +; The timeout for serving a single request after which the worker process will +; be killed. This option should be used when the 'max_execution_time' ini option +; does not stop script execution for some reason. A value of '0' means 'off'. +; Available units: s(econds)(default), m(inutes), h(ours), or d(ays) +; Default Value: 0 +request_terminate_timeout = 1d + +; Set open file descriptor rlimit. +; Default Value: system defined value +;rlimit_files = 1024 + +; Set max core size rlimit. +; Possible Values: 'unlimited' or an integer greater or equal to 0 +; Default Value: system defined value +;rlimit_core = 0 + +; Chroot to this directory at the start. This value must be defined as an +; absolute path. When this value is not set, chroot is not used. +; Note: you can prefix with '$prefix' to chroot to the pool prefix or one +; of its subdirectories. If the pool prefix is not set, the global prefix +; will be used instead. +; Note: chrooting is a great security feature and should be used whenever +; possible. However, all PHP paths will be relative to the chroot +; (error_log, sessions.save_path, ...). +; Default Value: not set +;chroot = + +; Chdir to this directory at the start. +; Note: relative path can be used. +; Default Value: current directory or / when chroot +chdir = __FINALPATH__ + +; Redirect worker stdout and stderr into main error log. If not set, stdout and +; stderr will be redirected to /dev/null according to FastCGI specs. +; Note: on highloaded environement, this can cause some delay in the page +; process time (several ms). +; Default Value: no +;catch_workers_output = yes + +; Clear environment in FPM workers +; Prevents arbitrary environment variables from reaching FPM worker processes +; by clearing the environment in workers before env vars specified in this +; pool configuration are added. +; Setting to "no" will make all environment variables available to PHP code +; via getenv(), $_ENV and $_SERVER. +; Default Value: yes +;clear_env = no + +; Limits the extensions of the main script FPM will allow to parse. This can +; prevent configuration mistakes on the web server side. You should only limit +; FPM to .php extensions to prevent malicious users to use other extensions to +; execute php code. +; Note: set an empty value to allow all extensions. +; Default Value: .php +;security.limit_extensions = .php .php3 .php4 .php5 .php7 + +; Pass environment variables like LD_LIBRARY_PATH. All $VARIABLEs are taken from +; the current environment. +; Default Value: clean env +;env[HOSTNAME] = $HOSTNAME +;env[PATH] = /usr/local/bin:/usr/bin:/bin +;env[TMP] = /tmp +;env[TMPDIR] = /tmp +;env[TEMP] = /tmp + +; Additional php.ini defines, specific to this pool of workers. These settings +; overwrite the values previously defined in the php.ini. The directives are the +; same as the PHP SAPI: +; php_value/php_flag - you can set classic ini defines which can +; be overwritten from PHP call 'ini_set'. +; php_admin_value/php_admin_flag - these directives won't be overwritten by +; PHP call 'ini_set' +; For php_*flag, valid values are on, off, 1, 0, true, false, yes or no. + +; Defining 'extension' will load the corresponding shared extension from +; extension_dir. Defining 'disable_functions' or 'disable_classes' will not +; overwrite previously defined php.ini values, but will append the new value +; instead. + +; Note: path INI options can be relative and will be expanded with the prefix +; (pool, global or /usr) + +; Default Value: nothing is defined by default except the values in php.ini and +; specified at startup with the -d argument +;php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f www@my.domain.com +;php_flag[display_errors] = off +;php_admin_value[error_log] = /var/log/fpm-php.www.log +;php_admin_flag[log_errors] = on +;php_admin_value[memory_limit] = 32M + +; Common values to change to increase file upload limit +; php_admin_value[upload_max_filesize] = 50M +; php_admin_value[post_max_size] = 50M +; php_admin_flag[mail.add_x_header] = Off + +; Other common parameters +; php_admin_value[max_execution_time] = 600 +; php_admin_value[max_input_time] = 300 +; php_admin_value[memory_limit] = 256M +; php_admin_flag[short_open_tag] = On diff --git a/conf/systemd.service b/conf/systemd.service new file mode 100644 index 0000000..76cdf64 --- /dev/null +++ b/conf/systemd.service @@ -0,0 +1,13 @@ +[Unit] +Description=Small description of the service +After=network.target + +[Service] +Type=simple +User=__APP__ +Group=__APP__ +WorkingDirectory=__FINALPATH__/ +ExecStart=__FINALPATH__/script >> /var/log/__APP__/__APP__.log 2>&1 + +[Install] +WantedBy=multi-user.target diff --git a/doc/demo.png b/doc/demo.png new file mode 100644 index 0000000000000000000000000000000000000000..31544b24778b5c8091fa41dbeb120f670c0fc64b GIT binary patch literal 77366 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®zqQf2 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 3.5" + }, + "multi_instance": true, + "services": [ + "nginx", + "php7.0-fpm", + "mysql" + ], + "arguments": { + "install" : [ + { + "name": "domain", + "type": "domain", + "ask": { + "en": "Choose a domain name for PetitesAnnonces", + "fr": "Choisissez un nom de domaine pour PetitesAnnonces" + }, + "example": "example.com" + }, + { + "name": "path", + "type": "path", + "ask": { + "en": "Choose a path for PetitesAnnonces", + "fr": "Choisissez un chemin pour PetitesAnnonces" + }, + "example": "/annonces", + "default": "/annonces" + }, + { + "name": "is_public", + "type": "boolean", + "ask": { + "en": "Is it a public application?", + "fr": "Est-ce une application publique ?" + }, + "default": true + } + ] + } +} diff --git a/pull_request_template.md b/pull_request_template.md new file mode 100644 index 0000000..e393d3e --- /dev/null +++ b/pull_request_template.md @@ -0,0 +1,18 @@ +## Problem +- *Description of why you made this PR* + +## Solution +- *And how do you fix that problem* + +## PR Status +- [ ] Code finished. +- [ ] Tested with Package_check. +- [ ] Fix or enhancement tested. +- [ ] Upgrade from last version tested. +- [ ] Can be reviewed and tested. + +## Package_check results +--- +*If you have access to [App Continuous Integration for packagers](https://yunohost.org/#/packaging_apps_ci) you can provide a link to the package_check results like below, replacing '-NUM-' in this link by the PR number and USERNAME by your username on the ci-apps-dev. Or you provide a screenshot or a pastebin of the results* + +[![Build Status](https://ci-apps-dev.yunohost.org/jenkins/job/petitesannonces_ynh%20PR-NUM-%20(USERNAME)/badge/icon)](https://ci-apps-dev.yunohost.org/jenkins/job/petitesannonces_ynh%20PR-NUM-%20(USERNAME)/) diff --git a/scripts/_common.sh b/scripts/_common.sh new file mode 100644 index 0000000..8bb05b4 --- /dev/null +++ b/scripts/_common.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +#================================================= +# COMMON VARIABLES +#================================================= + +# dependencies used by the app +pkg_dependencies="deb1 deb2" + +#================================================= +# PERSONAL HELPERS +#================================================= + +#================================================= +# EXPERIMENTAL HELPERS +#================================================= + +#================================================= +# FUTURE OFFICIAL HELPERS +#================================================= diff --git a/scripts/backup b/scripts/backup new file mode 100755 index 0000000..ab85f23 --- /dev/null +++ b/scripts/backup @@ -0,0 +1,113 @@ +#!/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 () { + ### Remove this function if there's nothing to clean before calling the remove script. + 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..." --time --weight=1 + +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) + +#================================================= +# STANDARD BACKUP STEPS +#================================================= +# STOP SYSTEMD SERVICE +#================================================= +ynh_script_progression --message="Stopping a systemd service..." --time --weight=1 + +#ynh_systemd_action --service_name=$app --action="stop" --log_path="/var/log/$app/$app.log" + +#================================================= +# BACKUP THE APP MAIN DIR +#================================================= +ynh_script_progression --message="Backing up the main app directory..." --time --weight=1 + +ynh_backup --src_path="$final_path" + +#================================================= +# BACKUP THE NGINX CONFIGURATION +#================================================= +ynh_script_progression --message="Backing up nginx web server configuration..." --time --weight=1 + +ynh_backup --src_path="/etc/nginx/conf.d/$domain.d/$app.conf" + +#================================================= +# BACKUP THE PHP-FPM CONFIGURATION +#================================================= +ynh_script_progression --message="Backing up php-fpm configuration..." --time --weight=1 + +ynh_backup --src_path="/etc/php/7.0/fpm/pool.d/$app.conf" + +#================================================= +# BACKUP THE MYSQL DATABASE +#================================================= +ynh_script_progression --message="Backing up the MySQL database..." --time --weight=1 + +ynh_mysql_dump_db --database="$db_name" > db.sql + +#================================================= +# BACKUP FAIL2BAN CONFIGURATION +#================================================= +ynh_script_progression --message="Backing up fail2ban configuration..." --time --weight=1 + +ynh_backup --src_path="/etc/fail2ban/jail.d/$app.conf" +ynh_backup --src_path="/etc/fail2ban/filter.d/$app.conf" + +#================================================= +# SPECIFIC BACKUP +#================================================= +# BACKUP LOGROTATE +#================================================= +ynh_script_progression --message="Backing up logrotate configuration..." --time --weight=1 + +#ynh_backup --src_path="/etc/logrotate.d/$app" + +#================================================= +# BACKUP SYSTEMD +#================================================= +ynh_script_progression --message="Backing up systemd configuration..." --time --weight=1 + +#ynh_backup --src_path="/etc/systemd/system/$app.service" + +#================================================= +# BACKUP A CRON FILE +#================================================= + +#ynh_backup --src_path="/etc/cron.d/$app" + +#================================================= +# START SYSTEMD SERVICE +#================================================= +ynh_script_progression --message="Starting a systemd service..." --time --weight=1 + +#ynh_systemd_action --service_name=$app --action="start" --log_path="/var/log/$app/$app.log" + +#================================================= +# END OF SCRIPT +#================================================= + +ynh_script_progression --message="Backup script completed for $app. (YunoHost will then actually copy those files to the archive)." --time --last diff --git a/scripts/change_url b/scripts/change_url new file mode 100644 index 0000000..6708db0 --- /dev/null +++ b/scripts/change_url @@ -0,0 +1,134 @@ +#!/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..." --time --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 UPGRADE THEN ACTIVE TRAP +#================================================= +ynh_script_progression --message="Backing up the app before changing its url (may take a while)..." --time --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..." --time --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..." --time --weight=1 + +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..." --time --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..." --time --weight=1 + +ynh_systemd_action --service_name=nginx --action=reload + +#================================================= +# END OF SCRIPT +#================================================= + +ynh_script_progression --message="Change of URL completed for $app" --time --last diff --git a/scripts/install b/scripts/install new file mode 100755 index 0000000..f88ef3c --- /dev/null +++ b/scripts/install @@ -0,0 +1,370 @@ +#!/bin/bash + +#================================================= +# GENERIC START +#================================================= +# IMPORT GENERIC HELPERS +#================================================= + +source _common.sh +source /usr/share/yunohost/helpers + +#================================================= +# MANAGE SCRIPT FAILURE +#================================================= + +ynh_clean_setup () { + ### Remove this function if there's nothing to clean before calling the remove script. + true +} +# Exit if an error occurs during the execution of the script +ynh_abort_if_errors + +#================================================= +# RETRIEVE ARGUMENTS FROM THE MANIFEST +#================================================= + +domain=$YNH_APP_ARG_DOMAIN +path_url=$YNH_APP_ARG_PATH +#admin=$YNH_APP_ARG_ADMIN # l'administration se fait via phpmyadmin user et mot de passe créés plus bas +is_public=$YNH_APP_ARG_IS_PUBLIC +#language=$YNH_APP_ARG_LANGUAGE +#password=$YNH_APP_ARG_PASSWORD + +### If it's a multi-instance app, meaning it can be installed several times independently +### The id of the app as stated in the manifest is available as $YNH_APP_ID +### The instance number is available as $YNH_APP_INSTANCE_NUMBER (equals "1", "2", ...) +### The app instance name is available as $YNH_APP_INSTANCE_NAME +### - the first time the app is installed, YNH_APP_INSTANCE_NAME = ynhexample +### - the second time the app is installed, YNH_APP_INSTANCE_NAME = ynhexample__2 +### - ynhexample__{N} for the subsequent installations, with N=3,4, ... +### The app instance name is probably what interests you most, since this is +### guaranteed to be unique. This is a good unique identifier to define installation path, +### db names, ... +app=$YNH_APP_INSTANCE_NAME + +#================================================= +# CHECK IF THE APP CAN BE INSTALLED WITH THESE ARGS +#================================================= +### About --weight and --time +### ynh_script_progression will show to your final users the progression of each scripts. +### In order to do that, --weight will represent the relative time of execution compared to the other steps in the script. +### --time is a packager option, it will show you the execution time since the previous call. +### This option should be removed before releasing your app. +### Use the execution time, given by --time, to estimate the weight of a step. +### A common way to do it is to set a weight equal to the execution time in second +1. +### The execution time is given for the duration since the previous call. So the weight should be applied to this previous call. +ynh_script_progression --message="Validating installation parameters..." --time --weight=1 + +### If the app uses nginx as web server (written in HTML/PHP in most cases), the final path should be "/var/www/$app". +### If the app provides an internal web server (or uses another application server such as uwsgi), the final path should be "/opt/yunohost/$app" +final_path=/var/www/$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..." --time --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=is_public --value=$is_public +#ynh_app_setting_set --app=$app --key=language --value=$language + +#================================================= +# STANDARD MODIFICATIONS +#================================================= +# FIND AND OPEN A PORT +#================================================= +ynh_script_progression --message="Configuring firewall..." --time --weight=1 + +### Use these lines if you have to open a port for the application +### `ynh_find_port` will find the first available port starting from the given port. +### If you're not using these lines: +### - Remove the section "CLOSE A PORT" in the remove script + +# Find an available port # pas nécessaire +#port=$(ynh_find_port --port=8095) +#ynh_app_setting_set --app=$app --key=port --value=$port + +# Optional: Expose this port publicly +# (N.B. : you only need to do this if the app actually needs to expose the port publicly. +# If you do this and the app doesn't actually need you are CREATING SECURITY HOLES IN THE SERVER !) + +# Open the port +# ynh_exec_warn_less yunohost firewall allow --no-upnp TCP $port + +#================================================= +# INSTALL DEPENDENCIES +#================================================= +ynh_script_progression --message="Installing dependencies..." --time --weight=1 + +### `ynh_install_app_dependencies` allows you to add any "apt" dependencies to the package. +### Those deb packages will be installed as dependencies of this package. +### If you're not using this helper: +### - Remove the section "REMOVE DEPENDENCIES" in the remove script +### - Remove the variable "pkg_dependencies" in _common.sh +### - As well as the section "REINSTALL DEPENDENCIES" in the restore script +### - And the section "UPGRADE DEPENDENCIES" in the upgrade script + +#ynh_install_app_dependencies $pkg_dependencies + +#================================================= +# CREATE A MYSQL DATABASE +#================================================= +ynh_script_progression --message="Creating a MySQL database..." --time --weight=1 + +### Use these lines if you need a database for the application. +### `ynh_mysql_setup_db` will create a database, an associated user and a ramdom password. +### The password will be stored as 'mysqlpwd' into the app settings, +### and will be available as $db_pwd +### If you're not using these lines: +### - Remove the section "BACKUP THE MYSQL DATABASE" in the backup script +### - Remove also the section "REMOVE THE MYSQL DATABASE" in the remove script +### - As well as the section "RESTORE THE MYSQL DATABASE" in the restore script + +db_name=$(ynh_sanitize_dbid --db_name=$app) +db_user=$db_name +ynh_app_setting_set --app=$app --key=db_name --value=$db_name +ynh_mysql_setup_db --db_user=$db_user --db_name=$db_name + +#================================================= +# DOWNLOAD, CHECK AND UNPACK SOURCE +#================================================= +ynh_script_progression --message="Setting up source files..." --time --weight=1 + +### `ynh_setup_source` is used to install an app from a zip or tar.gz file, +### downloaded from an upstream source, like a git repository. +### `ynh_setup_source` use the file conf/app.src + +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" + +#================================================= +# NGINX CONFIGURATION +#================================================= +ynh_script_progression --message="Configuring nginx web server..." --time --weight=1 + +### `ynh_add_nginx_config` will use the file conf/nginx.conf + +# Create a dedicated nginx config +ynh_add_nginx_config + +#================================================= +# CREATE DEDICATED USER +#================================================= +ynh_script_progression --message="Configuring system user..." --time --weight=1 + +# Create a system user +ynh_system_user_create --username=$app + +#================================================= +# PHP-FPM CONFIGURATION +#================================================= +ynh_script_progression --message="Configuring php-fpm..." --time --weight=1 + +### `ynh_add_fpm_config` is used to set up a PHP config. +### You can remove it if your app doesn't use PHP. +### `ynh_add_fpm_config` will use the files conf/php-fpm.conf +### If you're not using these lines: +### - You can remove these files in conf/. +### - Remove the section "BACKUP THE PHP-FPM CONFIGURATION" in the backup script +### - Remove also the section "REMOVE PHP-FPM CONFIGURATION" in the remove script +### - As well as the section "RESTORE THE PHP-FPM CONFIGURATION" in the restore script +### With the reload at the end of the script. +### - And the section "PHP-FPM CONFIGURATION" in the upgrade script + +# Create a dedicated php-fpm config +ynh_add_fpm_config + +#================================================= +# SPECIFIC SETUP +#================================================= +# ... +#================================================= + +#================================================= +# SETUP SYSTEMD +#================================================= +ynh_script_progression --message="Configuring a systemd service..." --time --weight=1 + +### `ynh_systemd_config` is used to configure a systemd script for an app. +### It can be used for apps that use sysvinit (with adaptation) or systemd. +### Have a look at the app to be sure this app needs a systemd script. +### `ynh_systemd_config` will use the file conf/systemd.service +### If you're not using these lines: +### - You can remove those files in conf/. +### - Remove the section "BACKUP SYSTEMD" in the backup script +### - Remove also the section "STOP AND REMOVE SERVICE" in the remove script +### - As well as the section "RESTORE SYSTEMD" in the restore script +### - And the section "SETUP SYSTEMD" in the upgrade script + +# Create a dedicated systemd config +#ynh_add_systemd_config + +#================================================= +# SETUP APPLICATION WITH CURL +#================================================= + +### Use these lines only if the app installation needs to be finalized through +### web forms. We generally don't want to ask the final user, +### so we're going to use curl to automatically fill the fields and submit the +### forms. + +# Set right permissions for curl install +chown -R $app: $final_path + +# Set the app as temporarily public for curl call +ynh_script_progression --message="Configuring SSOwat..." --time --weight=1 +ynh_app_setting_set --app=$app --key=skipped_uris --value="/" +# Reload SSOwat config +yunohost app ssowatconf + +# Reload Nginx +ynh_systemd_action --service_name=nginx --action=reload + +# Installation with curl +ynh_script_progression --message="Finalizing installation..." --time --weight=1 +#ynh_local_curl "/INSTALL_PATH" "key1=value1" "key2=value2" "key3=value3" +#ynh_local_curl "/install.php" + +# Remove the public access +if [ $is_public -eq 0 ] +then + ynh_app_setting_delete --app=$app --key=skipped_uris +fi + +#================================================= +# MODIFY A CONFIG FILE +#================================================= + +### `ynh_replace_string` is used to replace a string in a file. +### (It's compatible with sed regular expressions syntax) + +#ynh_replace_string --match_string="match_string" --replace_string="replace_string" --target_file="$final_path/CONFIG_FILE" +ynh_replace_string --match_string="votre_nom_de_bdd" --replace_string=$db_name --target_file="$final_path/conf/config.php" +ynh_replace_string --match_string="votre_utilisateur_de_bdd" --replace_string=$db_user --target_file="$final_path/conf/config.php" +ynh_replace_string --match_string="votre_mdp_de_bdd" --replace_string=$db_pwd --target_file="$final_path/conf/config.php" + +ynh_local_curl "/install.php" + +#================================================= +# STORE THE CONFIG FILE CHECKSUM +#================================================= + +### `ynh_store_file_checksum` is used to store the checksum of a file. +### That way, during the upgrade script, by using `ynh_backup_if_checksum_is_different`, +### you can make a backup of this file before modifying it again if the admin had modified it. + +# Calculate and store the config file checksum into the app settings +#ynh_store_file_checksum --file="$final_path/CONFIG_FILE" + +#================================================= +# GENERIC FINALIZATION +#================================================= + +# Supprimer install.php +rm $final_path/install.php + +#================================================= +# SECURE FILES AND DIRECTORIES +#================================================= + +### For security reason, any app should set the permissions to root: before anything else. +### Then, if write authorization is needed, any access should be given only to directories +### that really need such authorization. + +# Set permissions to app files +chown -R root: $final_path + +#================================================= +# SETUP LOGROTATE +#================================================= +ynh_script_progression --message="Configuring log rotation..." --time --weight=1 + +### `ynh_use_logrotate` is used to configure a logrotate configuration for the logs of this app. +### Use this helper only if there is effectively a log file for this app. +### If you're not using this helper: +### - Remove the section "BACKUP LOGROTATE" in the backup script +### - Remove also the section "REMOVE LOGROTATE CONFIGURATION" in the remove script +### - As well as the section "RESTORE THE LOGROTATE CONFIGURATION" in the restore script +### - And the section "SETUP LOGROTATE" in the upgrade script + +# Use logrotate to manage application logfile(s) +#ynh_use_logrotate + +#================================================= +# INTEGRATE SERVICE IN YUNOHOST +#================================================= + +### `yunohost service add` integrates a service in YunoHost. It then gets +### displayed in the admin interface and through the others `yunohost service` commands. +### (N.B. : this line only makes sense if the app adds a service to the system!) +### If you're not using these lines: +### - You can remove these files in conf/. +### - Remove the section "REMOVE SERVICE FROM ADMIN PANEL" in the remove script +### - As well as the section "ADVERTISE SERVICE IN ADMIN PANEL" in the restore script + +#yunohost service add $app --description "A short description of the app" --log "/var/log/$app/$app.log" + +### With YunoHost 3.8 you will then be able to: +### - specify a list of ports that needs to be publicly exposed (c.f. --needs_exposed_ports) +### which will then be checked by YunoHost's diagnosis system +### - specify a custom command to check the status of the service (c.f. --test_status) +### though it's only needed for weird cases where 'systemctl status' doesn't do a good job +### - specify a custom command to check / validate the configuration of the service (c.f. --test_conf) +### for example, the command to check the configuration of nginx is "nginx -t" + +#================================================= +# START SYSTEMD SERVICE +#================================================= +ynh_script_progression --message="Starting a systemd service..." --time --weight=1 + +### `ynh_systemd_action` is used to start a systemd service for an app. +### Only needed if you have configure a systemd service +### If you're not using these lines: +### - Remove the section "STOP SYSTEMD SERVICE" and "START SYSTEMD SERVICE" in the backup script +### - As well as the section "START SYSTEMD SERVICE" in the restore script +### - As well as the section"STOP SYSTEMD SERVICE" and "START SYSTEMD SERVICE" in the upgrade script +### - And the section "STOP SYSTEMD SERVICE" and "START SYSTEMD SERVICE" in the change_url script + +# Start a systemd service +#ynh_systemd_action --service_name=$app --action="start" --log_path="/var/log/$app/$app.log" + +#================================================= +# SETUP FAIL2BAN +#================================================= +ynh_script_progression --message="Configuring fail2ban..." --time --weight=1 + +# Create a dedicated fail2ban config +ynh_add_fail2ban_config --logpath="/var/log/nginx/${domain}-error.log" --failregex="Regex to match into the log for a failed login" + +#================================================= +# SETUP SSOWAT +#================================================= +ynh_script_progression --message="Configuring SSOwat..." --time --weight=1 + +# Make app public if necessary +if [ $is_public -eq 1 ] +then + # unprotected_uris allows SSO credentials to be passed anyway. + ynh_app_setting_set --app=$app --key=unprotected_uris --value="/" +fi + +#================================================= +# RELOAD NGINX +#================================================= +ynh_script_progression --message="Reloading nginx web server..." --time --weight=1 + +ynh_systemd_action --service_name=nginx --action=reload + +#================================================= +# END OF SCRIPT +#================================================= + +ynh_script_progression --message="Installation of $app completed" --time --last diff --git a/scripts/remove b/scripts/remove new file mode 100755 index 0000000..9e3494b --- /dev/null +++ b/scripts/remove @@ -0,0 +1,141 @@ +#!/bin/bash + +#================================================= +# GENERIC START +#================================================= +# IMPORT GENERIC HELPERS +#================================================= + +source _common.sh +source /usr/share/yunohost/helpers + +#================================================= +# LOAD SETTINGS +#================================================= +ynh_script_progression --message="Loading installation settings..." --time --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 +#================================================= + +# 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..." --time --weight=1 + yunohost service remove $app +fi + +#================================================= +# STOP AND REMOVE SERVICE +#================================================= +ynh_script_progression --message="Stopping and removing the systemd service..." --time --weight=1 + +# Remove the dedicated systemd config +#ynh_remove_systemd_config + +#================================================= +# REMOVE THE MYSQL DATABASE +#================================================= +ynh_script_progression --message="Removing the MySQL database..." --time --weight=1 + +# Remove a database if it exists, along with the associated user +ynh_mysql_remove_db --db_user=$db_user --db_name=$db_name + +#================================================= +# REMOVE DEPENDENCIES +#================================================= +ynh_script_progression --message="Removing dependencies..." --time --weight=1 + +# Remove metapackage and its dependencies +#ynh_remove_app_dependencies + +#================================================= +# REMOVE APP MAIN DIR +#================================================= +ynh_script_progression --message="Removing app main directory..." --time --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..." --time --weight=1 + +# Remove the dedicated nginx config +ynh_remove_nginx_config + +#================================================= +# REMOVE PHP-FPM CONFIGURATION +#================================================= +ynh_script_progression --message="Removing php-fpm configuration..." --time --weight=1 + +# Remove the dedicated php-fpm config +ynh_remove_fpm_config + +#================================================= +# REMOVE LOGROTATE CONFIGURATION +#================================================= +ynh_script_progression --message="Removing logrotate configuration..." --time --weight=1 + +# Remove the app-specific logrotate config +ynh_remove_logrotate + +#================================================= +# CLOSE A PORT +#================================================= + +if yunohost firewall list | grep -q "\- $port$" +then + ynh_script_progression --message="Closing port $port..." + ynh_exec_warn_less yunohost firewall disallow TCP $port +fi + +#================================================= +# REMOVE FAIL2BAN CONFIGURATION +#================================================= +ynh_script_progression --message="Removing fail2ban configuration..." --time --weight=1 + +# Remove the dedicated fail2ban config +ynh_remove_fail2ban_config + +#================================================= +# SPECIFIC REMOVE +#================================================= +# REMOVE THE CRON FILE +#================================================= + +# Remove a cron file +#ynh_secure_remove --file="/etc/cron.d/$app" + +# Remove a directory securely +#ynh_secure_remove --file="/etc/$app/" + +# 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..." --time --weight=1 + +# Delete a system user +ynh_system_user_delete --username=$app + +#================================================= +# END OF SCRIPT +#================================================= + +ynh_script_progression --message="Removal of $app completed" --time --last diff --git a/scripts/restore b/scripts/restore new file mode 100755 index 0000000..8faa2a9 --- /dev/null +++ b/scripts/restore @@ -0,0 +1,158 @@ +#!/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 () { + #### Remove this function if there's nothing to clean before calling the remove script. + true +} +# Exit if an error occurs during the execution of the script +ynh_abort_if_errors + +#================================================= +# LOAD SETTINGS +#================================================= +ynh_script_progression --message="Loading settings..." --time --weight=1 + +app=$YNH_APP_INSTANCE_NAME + +domain=$(ynh_app_setting_get --app=$app --key=domain) +path_url=$(ynh_app_setting_get --app=$app --key=path) +final_path=$(ynh_app_setting_get --app=$app --key=final_path) +db_name=$(ynh_app_setting_get --app=$app --key=db_name) +db_user=$db_name + +#================================================= +# CHECK IF THE APP CAN BE RESTORED +#================================================= +ynh_script_progression --message="Validating restoration parameters..." --time --weight=1 + +ynh_webpath_available --domain=$domain --path_url=$path_url \ + || ynh_die --message="Path not available: ${domain}${path_url}" +test ! -d $final_path \ + || ynh_die --message="There is already a directory: $final_path " + +#================================================= +# STANDARD RESTORATION STEPS +#================================================= +# RESTORE THE NGINX CONFIGURATION +#================================================= + +ynh_restore_file --origin_path="/etc/nginx/conf.d/$domain.d/$app.conf" + +#================================================= +# RESTORE THE APP MAIN DIR +#================================================= +ynh_script_progression --message="Restoring the app main directory..." --time --weight=1 + +ynh_restore_file --origin_path="$final_path" + +#================================================= +# RECREATE THE DEDICATED USER +#================================================= +ynh_script_progression --message="Recreating the dedicated system user..." --time --weight=1 + +# Create the dedicated user (if not existing) +ynh_system_user_create --username=$app + +#================================================= +# RESTORE USER RIGHTS +#================================================= + +# Restore permissions on app files +chown -R root: $final_path + +#================================================= +# RESTORE THE PHP-FPM CONFIGURATION +#================================================= + +ynh_restore_file --origin_path="/etc/php/7.0/fpm/pool.d/$app.conf" + +#================================================= +# RESTORE FAIL2BAN CONFIGURATION +#================================================= +ynh_script_progression --message="Restoring the fail2ban configuration..." --time --weight=1 + +ynh_restore_file "/etc/fail2ban/jail.d/$app.conf" +ynh_restore_file "/etc/fail2ban/filter.d/$app.conf" +ynh_systemd_action --action=restart --service_name=fail2ban + +#================================================= +# SPECIFIC RESTORATION +#================================================= +# REINSTALL DEPENDENCIES +#================================================= +ynh_script_progression --message="Reinstalling dependencies..." --time --weight=1 + +# Define and install dependencies +#ynh_install_app_dependencies $pkg_dependencies + +#================================================= +# RESTORE THE MYSQL DATABASE +#================================================= +ynh_script_progression --message="Restoring the MySQL database..." --time --weight=1 + +db_pwd=$(ynh_app_setting_get --app=$app --key=mysqlpwd) +ynh_mysql_setup_db --db_user=$db_user --db_name=$db_name --db_pwd=$db_pwd +ynh_mysql_connect_as --user=$db_user --password=$db_pwd --database=$db_name < ./db.sql + +#================================================= +# RESTORE SYSTEMD +#================================================= +ynh_script_progression --message="Restoring the systemd configuration..." --time --weight=1 + +#ynh_restore_file --origin_path="/etc/systemd/system/$app.service" +#systemctl enable $app.service + +#================================================= +# INTEGRATE SERVICE IN YUNOHOST +#================================================= + +#yunohost service add $app --description "A short description of the app" --log "/var/log/$app/$app.log" + +#================================================= +# START SYSTEMD SERVICE +#================================================= +ynh_script_progression --message="Starting a systemd service..." --time --weight=1 + +#ynh_systemd_action --service_name=$app --action="start" --log_path="/var/log/$app/$app.log" + +#================================================= +# RESTORE THE CRON FILE +#================================================= + +#ynh_restore_file --origin_path="/etc/cron.d/$app" + +#================================================= +# RESTORE THE LOGROTATE CONFIGURATION +#================================================= + +#ynh_restore_file --origin_path="/etc/logrotate.d/$app" + +#================================================= +# GENERIC FINALIZATION +#================================================= +# RELOAD NGINX AND PHP-FPM +#================================================= +ynh_script_progression --message="Reloading nginx web server and php-fpm..." --time --weight=1 + +ynh_systemd_action --service_name=php7.0-fpm --action=reload +ynh_systemd_action --service_name=nginx --action=reload + +#================================================= +# END OF SCRIPT +#================================================= + +ynh_script_progression --message="Restoration completed for $app" --time --last diff --git a/scripts/upgrade b/scripts/upgrade new file mode 100755 index 0000000..b30a0f2 --- /dev/null +++ b/scripts/upgrade @@ -0,0 +1,225 @@ +#!/bin/bash + +#================================================= +# GENERIC START +#================================================= +# IMPORT GENERIC HELPERS +#================================================= + +source _common.sh +source /usr/share/yunohost/helpers + +#================================================= +# LOAD SETTINGS +#================================================= +ynh_script_progression --message="Loading installation settings..." --time --weight=1 + +app=$YNH_APP_INSTANCE_NAME + +domain=$(ynh_app_setting_get --app=$app --key=domain) +path_url=$(ynh_app_setting_get --app=$app --key=path) +#admin=$(ynh_app_setting_get --app=$app --key=admin) +is_public=$(ynh_app_setting_get --app=$app --key=is_public) +final_path=$(ynh_app_setting_get --app=$app --key=final_path) +#language=$(ynh_app_setting_get --app=$app --key=language) +db_name=$(ynh_app_setting_get --app=$app --key=db_name) + +#================================================= +# CHECK VERSION +#================================================= + +### This helper will compare the version of the currently installed app and the version of the upstream package. +### $upgrade_type can have 2 different values +### - UPGRADE_APP if the upstream app version has changed +### - UPGRADE_PACKAGE if only the YunoHost package has changed +### ynh_check_app_version_changed will stop the upgrade if the app is up to date. +### UPGRADE_APP should be used to upgrade the core app only if there's an upgrade to do. +upgrade_type=$(ynh_check_app_version_changed) + +#================================================= +# ENSURE DOWNWARD COMPATIBILITY +#================================================= +ynh_script_progression --message="Ensuring downward compatibility..." --time --weight=1 + +# Fix is_public as a boolean value +if [ "$is_public" = "Yes" ]; then + ynh_app_setting_set --app=$app --key=is_public --value=1 + is_public=1 +elif [ "$is_public" = "No" ]; then + ynh_app_setting_set --app=$app --key=is_public --value=0 + is_public=0 +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=/var/www/$app + ynh_app_setting_set --app=$app --key=final_path --value=$final_path +fi + +#================================================= +# BACKUP BEFORE UPGRADE THEN ACTIVE TRAP +#================================================= +ynh_script_progression --message="Backing up the app before upgrading (may take a while)..." --time --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 + +#================================================= +# CHECK THE PATH +#================================================= + +# Normalize the URL path syntax +# N.B. : this is for app installations before YunoHost 2.7 +# where this value might be something like /foo/ or foo/ +# instead of /foo .... +# If nobody installed your app before 2.7, then you may +# safely remove this line +path_url=$(ynh_normalize_url_path --path_url=$path_url) + +#================================================= +# STANDARD UPGRADE STEPS +#================================================= +# STOP SYSTEMD SERVICE +#================================================= +ynh_script_progression --message="Stopping a systemd service..." --time --weight=1 + +#ynh_systemd_action --service_name=$app --action="stop" --log_path="/var/log/$app/$app.log" + +#================================================= +# DOWNLOAD, CHECK AND UNPACK SOURCE +#================================================= + +if [ "$upgrade_type" == "UPGRADE_APP" ] +then + ynh_script_progression --message="Upgrading source files..." --time --weight=1 + + # Download, check integrity, uncompress and patch the source from app.src + ynh_setup_source --dest_dir="$final_path" +fi + +#================================================= +# NGINX CONFIGURATION +#================================================= +ynh_script_progression --message="Upgrading nginx web server configuration..." --time --weight=1 + +# Create a dedicated nginx config +ynh_add_nginx_config + +#================================================= +# UPGRADE DEPENDENCIES +#================================================= +ynh_script_progression --message="Upgrading dependencies..." --time --weight=1 + +#ynh_install_app_dependencies $pkg_dependencies + +#================================================= +# CREATE DEDICATED USER +#================================================= +ynh_script_progression --message="Making sure dedicated system user exists..." --time --weight=1 + +# Create a dedicated user (if not existing) +ynh_system_user_create --username=$app + +#================================================= +# PHP-FPM CONFIGURATION +#================================================= +ynh_script_progression --message="Upgrading php-fpm configuration..." --time --weight=1 + +# Create a dedicated php-fpm config +ynh_add_fpm_config + +#================================================= +# SPECIFIC UPGRADE +#================================================= +# ... +#================================================= + +# Supprimer install.php +rm $final_path/install.php + +#================================================= +# STORE THE CONFIG FILE CHECKSUM +#================================================= + +### Verify the checksum of a file, stored by `ynh_store_file_checksum` in the install script. +### And create a backup of this file if the checksum is different. So the file will be backed up if the admin had modified it. +ynh_backup_if_checksum_is_different --file="$final_path/CONFIG_FILE" +# Recalculate and store the checksum of the file for the next upgrade. +#ynh_store_file_checksum --file="$final_path/CONFIG_FILE" + +#================================================= +# SETUP LOGROTATE +#================================================= +ynh_script_progression --message="Upgrading logrotate configuration..." --time --weight=1 + +# Use logrotate to manage app-specific logfile(s) +#ynh_use_logrotate --non-append + +#================================================= +# SETUP SYSTEMD +#================================================= +ynh_script_progression --message="Upgrading systemd configuration..." --time --weight=1 + +# Create a dedicated systemd config +#ynh_add_systemd_config + +#================================================= +# GENERIC FINALIZATION +#================================================= +# UPGRADE FAIL2BAN +#================================================= +ynh_script_progression --message="Reconfiguring fail2ban..." --time --weight=1 + +# Create a dedicated fail2ban config +ynh_add_fail2ban_config --logpath="/var/log/nginx/${domain}-error.log" --failregex="Regex to match into the log for a failed login" + +#================================================= +# SECURE FILES AND DIRECTORIES +#================================================= + +# Set permissions on app files +chown -R root: $final_path + +#================================================= +# SETUP SSOWAT +#================================================= +ynh_script_progression --message="Upgrading SSOwat configuration..." --time --weight=1 + +# Make app public if necessary +if [ $is_public -eq 1 ] +then + # unprotected_uris allows SSO credentials to be passed anyway + ynh_app_setting_set --app=$app --key=unprotected_uris --value="/" +fi + +#================================================= +# START SYSTEMD SERVICE +#================================================= +ynh_script_progression --message="Starting a systemd service..." --time --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..." --time --weight=1 + +ynh_systemd_action --service_name=nginx --action=reload + +#================================================= +# END OF SCRIPT +#================================================= + +ynh_script_progression --message="Upgrade of $app completed" --time --last diff --git a/sources/extra_files/app/.gitignore b/sources/extra_files/app/.gitignore new file mode 100644 index 0000000..783a4ae --- /dev/null +++ b/sources/extra_files/app/.gitignore @@ -0,0 +1,2 @@ +*~ +*.sw[op] diff --git a/sources/patches/.gitignore b/sources/patches/.gitignore new file mode 100644 index 0000000..783a4ae --- /dev/null +++ b/sources/patches/.gitignore @@ -0,0 +1,2 @@ +*~ +*.sw[op]