This commit is contained in:
Antoine Lima 2024-07-03 18:10:06 +02:00 committed by GitHub
commit d199bd7df6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 321 additions and 3 deletions

14
conf/nginx-nodejs.conf Normal file
View file

@ -0,0 +1,14 @@
#sub_path_only rewrite ^__PATH__$ __PATH__/ permanent;
location / {
proxy_pass http://127.0.0.1:__PORT__/;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Ssl on;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Scheme https;
proxy_buffering off;
# Include SSOWAT user panel.
include conf.d/yunohost_panel.conf.inc;
}

8
conf/nodejs-watcher.path Normal file
View file

@ -0,0 +1,8 @@
[Path]
Unit=__APP__-nodejs-watcher.service
# Trigger on creation, deletion or change to a file
PathChanged=__INSTALL_DIR__
[Install]
WantedBy=multi-user.target

View file

@ -0,0 +1,13 @@
[Unit]
Description=__APP__ NodeJS restarter
After=network.target
StartLimitIntervalSec=10
StartLimitBurst=5
[Service]
Type=oneshot
ExecStart=/usr/bin/systemctl restart __APP__-nodejs.service
Restart=on-failure
[Install]
WantedBy=multi-user.target

54
conf/nodejs.service Normal file
View file

@ -0,0 +1,54 @@
[Unit]
Description=__APP__ NodeJS Server
After=network.target
[Service]
Type=simple
User=__APP__
Group=__APP__
WorkingDirectory=__INSTALL_DIR__/www
StandardOutput=append:/var/log/__APP__-nodejs.log
StandardError=inherit
Environment=__YNH_NODE_LOAD_PATH__
Environment=PORT=__PORT__
Environment=NODE_ENV=production
ExecStartPre=__YNH_NPM__ install
ExecStartPre=__YNH_NPM__ run build
ExecStart=__YNH_NPM__ run start
# Sandboxing options to harden security
# Depending on specificities of your service/app, you may need to tweak these
# .. but this should be a good baseline
# Details for these options: https://www.freedesktop.org/software/systemd/man/systemd.exec.html
NoNewPrivileges=yes
PrivateTmp=yes
PrivateDevices=yes
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 AF_NETLINK
RestrictNamespaces=yes
RestrictRealtime=yes
DevicePolicy=closed
ProtectClock=yes
ProtectHostname=yes
ProtectProc=invisible
ProtectSystem=full
ProtectControlGroups=yes
ProtectKernelModules=yes
ProtectKernelTunables=yes
LockPersonality=yes
SystemCallArchitectures=native
SystemCallFilter=~@clock @debug @module @mount @obsolete @reboot @setuid @swap @cpu-emulation @privileged
# Denying access to capabilities that should not be relevant for webapps
# Doc: https://man7.org/linux/man-pages/man7/capabilities.7.html
CapabilityBoundingSet=~CAP_RAWIO CAP_MKNOD
CapabilityBoundingSet=~CAP_AUDIT_CONTROL CAP_AUDIT_READ CAP_AUDIT_WRITE
CapabilityBoundingSet=~CAP_SYS_BOOT CAP_SYS_TIME CAP_SYS_MODULE CAP_SYS_PACCT
CapabilityBoundingSet=~CAP_LEASE CAP_LINUX_IMMUTABLE CAP_IPC_LOCK
CapabilityBoundingSet=~CAP_BLOCK_SUSPEND CAP_WAKE_ALARM
CapabilityBoundingSet=~CAP_SYS_TTY_CONFIG
CapabilityBoundingSet=~CAP_MAC_ADMIN CAP_MAC_OVERRIDE
CapabilityBoundingSet=~CAP_NET_ADMIN CAP_NET_BROADCAST CAP_NET_RAW
CapabilityBoundingSet=~CAP_SYS_ADMIN CAP_SYS_PTRACE CAP_SYSLOG
[Install]
WantedBy=multi-user.target

View file

@ -52,6 +52,15 @@ name = "My Webapp configuration"
default = "low"
help = "low: Personal usage, behind the sso. No RAM footprint when not used, but the impact on the processor can be high if many users are using the service.<br>medium: Low usage, few people or/and publicly accessible. Low RAM footprint, medium processor footprint when used.<br>high: High usage, frequently visited website. High RAM footprint, but lower on processor usage and quickly responding."
[main.nodejs]
name = "NodeJS configuration"
[main.nodejs.nodeversion]
ask = "NodeJS version"
type = "select"
choices = ["none", "18", "20", "21"]
default = "none"
# TODO: Add protected_path as tags, which are created as permission "label (path)", so admin can protect a specific path
# [main.permissions]
# [main.permissions.proteced_path]

View file

@ -29,3 +29,16 @@ Once logged in, under the Web directory you will see a `www` folder which contai
### Customizing the nginx configuration
If you want to add tweak the nginx configuration for this app, it is recommended to edit `/etc/nginx/conf.d/__DOMAIN__.d/__ID__.d/WHATEVER_NAME.conf` (ensure that the file has the `.conf` extension) and reload the nginx after making sure that the configuration is valid using `nginx -t`.
{% if nodeversion != 'none' %}
### Interfacing with NodeJS
A `package.json` should be available within the `/var/www/__APP__/www`. It is used to `npm install`, `npm run build` then `npm run start`. As such, it should at least define the dependencies and provide the `build` and `install` scripts.
You should then start a server in `/var/www/__APP__/www/index.js`.
It should listen on the port provided through the `PORT` environment with `process.env.PORT` or statically with __PORT__.
The server should reload its files after they change, but due to systemd's limitations, it only works for top level folders/files.
If your server does not display the right things, restart the `__APP__-nodejs` service.
{% endif %}

View file

@ -29,3 +29,7 @@ Après vous être connecté, sous le répertoire Web vous verrez un dossier `www
### Personnaliser la configuration nginx
Si vous souhaitez ajuster la configuration nginx pour cette app, il est recommandé d'éditer `/etc/nginx/conf.d/__DOMAIN__.d/__ID__.d/WHATEVER_NAME.conf` (assurez-vous que le fichier a l'extension `.conf`) puis rechargez nginx après vous être assuré que la configuration est valide à l'aide de `nginx -t`.
### Écouter le bon port dans NodeJS
Le port d'écoute est accessible par le processus node au travers de la variable d'environment `PORT`. Veillez à ce que votre fichier `.js` principal le récupère bien avec `process.env.PORT` car sa valeur n'est pas prédictible.

View file

@ -4,4 +4,6 @@ It can also create a MySQL database - which will be backed up and restored with
PHP-FPM version can also be selected among `none`, `7.4`, `8.0`, `8.1` and `8.2`.
Finally, NodeJS can alternatively be used instead of PHP, with versions `18`, `20` or `21`.
**Once installed, go to the chosen URL to know the user, domain and port you will have to use for the SFTP access.** The password is one you chosen during the installation. Under the Web directory, you will see a `www` folder which contains the public files served by this app. You can put all the files of your custom Web application inside.

View file

@ -4,4 +4,6 @@ Elle peut également créer une base de données MySQL - qui sera sauvegardée e
La version de PHP-FPM peut aussi être choisie, parmi `none`, `7.4`, `8.0`, `8.1` et `8.2`.
Un serveur NodeJS peut finalement être utilisé à la place de PHP, avec les versions `18`, `20` ou `21`.
**Une fois installé, rendez-vous sur l'URL choisie pour connaître l'utilisateur, le domaine et le port que vous devrez utiliser pour l'accès SFTP.** Le mot de passe est celui que vous avez choisi lors de l'installation. Sous le répertoire Web, vous verrez un dossier `www` qui contient les fichiers publics servis par cette application. Vous pouvez mettre tous les fichiers de votre application Web personnalisée à l'intérieur.

View file

@ -48,10 +48,21 @@ ram.runtime = "50M"
[install.phpversion]
ask.en = "Choose a PHP version you want to use for your app"
ask.fr = "Choisissez une version PHP que vous souhaitez utiliser pour votre application"
help.en = "You can only choose NodeJS or PHP, not both"
help.fr = "Vous ne pouvez avoir que NodeJS ou PHP, pas les deux"
type = "select"
choices = ["none", "7.4", "8.0", "8.1", "8.2"]
default = "8.0"
[install.nodeversion]
ask.en = "Choose a NodeJS version you want to use for your app"
ask.fr = "Choisissez une version NodeJS que vous souhaitez utiliser pour votre application"
help.en = "You can only choose NodeJS or PHP, not both"
help.fr = "Vous ne pouvez avoir que NodeJS ou PHP, pas les deux"
type = "select"
choices = ["none", "18", "20", "21"]
default = "none"
[install.database]
ask.en = "Do you need a database?"
ask.fr = "Avez-vous besoin d'une base de données ?"
@ -64,6 +75,9 @@ ram.runtime = "50M"
[resources.install_dir]
[resources.ports]
main.default = 3000
[resources.permissions]
main.url = "/"

View file

@ -122,3 +122,53 @@ ynh_system_user_del_group() {
gpasswd -d "$username" "$group"
done
}
ynh_setup_my_nodeapp() {
# Declare an array to define the options of this helper.
local legacy_args=ai
local -A args_array=([a]=app= [i]=install_dir=)
local app
local install_dir
ynh_handle_getopts_args "$@"
ynh_add_systemd_config --service="${app}-nodejs" --template="nodejs.service"
ynh_add_systemd_config --service="${app}-nodejs-watcher" --template="nodejs-watcher.service"
ynh_add_config --template="nodejs-watcher.path" --destination="/etc/systemd/system/${app}-nodejs-watcher.path"
systemctl enable "${app}-nodejs-watcher.path" --quiet
systemctl daemon-reload
yunohost service add "${app}-nodejs" --description="$app NodeJS Server" --log="/var/log/$app-nodejs.log"
ynh_systemd_action --service_name="${app}-nodejs"
ynh_systemd_action --service_name="${app}-nodejs-watcher"
ynh_systemd_action --service_name="${app}-nodejs-watcher.path"
# Add the config manually because yunohost does not support custom nginx confs
ynh_add_config --template="nginx-nodejs.conf" --destination="/etc/nginx/conf.d/$domain.d/$app.conf"
ynh_store_file_checksum --file="/etc/nginx/conf.d/$domain.d/$app.conf"
ynh_systemd_action --service_name=nginx --action=reload
# Subsequent npm install will write to this folder (as it is within $app's home)
# As such we prepare it with fitting rights
mkdir -p "$install_dir/.npm"
chown $app:$app "$install_dir/.npm"
}
ynh_remove_my_nodeapp() {
# Declare an array to define the options of this helper.
local legacy_args=a
local -A args_array=([a]=app=)
local app
ynh_handle_getopts_args "$@"
yunohost service remove "${app}-nodejs"
ynh_remove_systemd_config --service="${app}-nodejs"
ynh_remove_systemd_config --service="${app}-nodejs-watcher"
ynh_secure_remove --file="/etc/systemd/system/${app}-nodejs-watcher.path"
ynh_remove_nodejs
}

View file

@ -36,6 +36,17 @@ then
ynh_backup --src_path="/etc/php/${phpversion}/fpm/pool.d/$app.conf"
fi
#=================================================
# BACKUP THE NodeJS CONFIGURATION
#=================================================
if [ $nodeversion != "none" ]
then
ynh_backup --src_path="/etc/systemd/system/${app}-nodejs.service"
ynh_backup --src_path="/etc/systemd/system/${app}-nodejs-watcher.service"
ynh_backup --src_path="/etc/systemd/system/${app}-nodejs-watcher.path"
fi
#=================================================
# BACKUP THE MYSQL DATABASE
#=================================================

View file

@ -105,6 +105,11 @@ ynh_app_config_validate() {
exit 0
fi
fi
if [ "${changed[nodeversion]}" == "true" ] && [ $nodeversion != "none" ] && [ $phpversion != "none" ]
then
ynh_die --message="You cannot have both PHP and NodeJS, choose only one."
fi
}
ynh_app_config_apply() {
@ -152,6 +157,19 @@ ynh_app_config_apply() {
then
ynh_add_fpm_config --phpversion=$phpversion --usage=$fpm_usage --footprint=$fpm_footprint
fi
if [ "${changed[nodeversion]}" == "true" ]
then
if [ "$nodeversion" != "none" ]
then
ynh_install_nodejs --nodejs_version=$nodeversion
ynh_use_nodejs
export port=$(ynh_app_setting_get $app port)
ynh_setup_my_nodeapp --app=$app --install_dir=$install_dir
else
ynh_remove_my_nodeapp --app=$app
fi
fi
}
ynh_app_config_run $1

View file

@ -23,6 +23,7 @@ ssh_port=$(grep "^Port" /etc/ssh/sshd_config | awk '{print $2}')
ynh_script_progression --message="Validating installation parameters..." --weight=2
[ $with_sftp -eq 0 ] || [ "$password" != "" ] || ynh_die --message="You need to set a password to enable SFTP"
[ $phpversion != "none" ] || [ $nodeversion != "none" ] || ynh_die --message="Either PHP or NodeJS can be used, not both"
#=================================================
# STORE SETTINGS FROM MANIFEST
@ -134,6 +135,22 @@ then
ynh_add_fpm_config --usage=$fpm_usage --footprint=$fpm_footprint --phpversion=$phpversion
fi
#=================================================
# NodeJS CONFIGURATION
#=================================================
if [ $nodeversion != "none" ]
then
ynh_script_progression --message="Configuring NodeJS..." --weight=3
ynh_install_nodejs --nodejs_version=$nodeversion
ynh_use_nodejs
ynh_add_config --template="../sources/www/package.json" --destination="$install_dir/www/package.json"
ynh_add_config --template="../sources/www/index.js" --destination="$install_dir/www/index.js"
ynh_setup_my_nodeapp --app=$app --install_dir=$install_dir
fi
#=================================================
# END OF SCRIPT
#=================================================

View file

@ -46,6 +46,16 @@ ynh_script_progression --message="Removing PHP-FPM configuration..."
# Remove the dedicated PHP-FPM config
ynh_remove_fpm_config
#=================================================
# REMOVE NodeJS CONFIGURATION
#=================================================
ynh_script_progression --message="Removing NodeJS configuration..."
if [ $nodeversion != "none" ]
then
ynh_remove_my_nodeapp --app=$app
fi
#=================================================
# END OF SCRIPT
#=================================================

View file

@ -81,18 +81,36 @@ then
ynh_restore_file --origin_path="/etc/php/${phpversion}/fpm/pool.d/$app.conf"
fi
#=================================================
# RESTORE THE NodeJS CONFIGURATION
#=================================================
if [ $nodeversion != "none" ]
then
ynh_restore_file --origin_path="/etc/systemd/system/${app}-nodejs.service"
ynh_restore_file --origin_path="/etc/systemd/system/${app}-nodejs-watcher.service"
ynh_restore_file --origin_path="/etc/systemd/system/${app}-nodejs-watcher.path"
mkdir -p "$install_dir"/.npm
chown -R $app:$app "$install_dir"/.npm
fi
#=================================================
# GENERIC FINALIZATION
#=================================================
# RELOAD NGINX AND PHP-FPM
#=================================================
ynh_script_progression --message="Reloading NGINX web server and PHP-FPM..."
ynh_script_progression --message="Reloading NGINX and the server..."
if [ $phpversion != "none" ]
then
ynh_systemd_action --service_name=php${phpversion}-fpm --action=reload
fi
if [ $nodeversion != "none" ]
then
ynh_systemd_action --service_name=$app-nodejs --action=reload
fi
ynh_systemd_action --service_name=nginx --action=reload
#=================================================

View file

@ -68,6 +68,12 @@ if [ -z "$phpversion" ]; then
ynh_app_setting_set --app=$app --key=phpversion --value=$phpversion
fi
# If phpversion doesn't exist, create it. We assume it is the default system one.
if [ -z "$nodeversion" ]; then
nodeversion="none"
ynh_app_setting_set --app=$app --key=nodeversion --value=$nodeversion
fi
# Delete old user
if [ -n "$(ynh_app_setting_get --app=$app --key=user)" ]
then
@ -103,8 +109,17 @@ then
fi
# Create a dedicated NGINX config
ynh_add_nginx_config
ynh_add_config --template="example-custom-nginx-config.conf" --destination="$nginx_extra_conf_dir/sample.conf"
# Use a custom nginx config when using nodejs as it is incompatible with the html/php one
if [ $nodeversion == "none" ]
then
ynh_add_nginx_config
ynh_add_config --template="example-custom-nginx-config.conf" --destination="$nginx_extra_conf_dir/sample.conf"
else
# Add the config manually because yunohost does not support custom nginx confs
ynh_add_config --template="nginx-nodejs.conf" --destination="/etc/nginx/conf.d/$domain.d/$app.conf"
ynh_store_file_checksum --file="/etc/nginx/conf.d/$domain.d/$app.conf"
ynh_systemd_action --service_name=nginx --action=reload
fi
#=================================================
# CREATE DEDICATED USER
@ -156,6 +171,22 @@ setfacl -m g:$app:r-x "$install_dir"
setfacl -m g:www-data:r-x "$install_dir"
chmod 750 "$install_dir"
#=================================================
# NodeJS CONFIGURATION
#=================================================
if [ $nodeversion != "none" ]
then
ynh_script_progression --message="Updating NodeJS..." --weight=3
ynh_install_nodejs --nodejs_version=$nodeversion
ynh_use_nodejs
ynh_add_config --template="../sources/www/package.json" --destination="$install_dir/www/package.json"
ynh_add_config --template="../sources/www/index.js" --destination="$install_dir/www/index.js"
ynh_setup_my_nodeapp --app=$app --install_dir=$install_dir
fi
#=================================================
# DEACTIVE MAINTENANCE MODE
#=================================================

19
sources/www/index.js Normal file
View file

@ -0,0 +1,19 @@
const http = require('node:http');
const fs = require('fs');
const index = fs.readFileSync('index.html').toString();
const host = '127.0.0.1';
var port = process.env.PORT;
port = (typeof port !== 'undefined') ? port : 3000;
const file = index.replace("<hr/>", `<hr/> <h2> Port configuration </h2> <p>Your node application have to listen on port ${port}. Alternatively, you can get port var from envirronment with the following: </p> <pre> process.env.PORT; </pre>`);
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/html');
res.end(file);
});
server.listen(port, host, () => {
console.log('Web server running at http://%s:%s', host, port);
});

11
sources/www/package.json Normal file
View file

@ -0,0 +1,11 @@
{
"name": "www",
"version": "1.0.0",
"description": "dummy app",
"author": "",
"scripts": {
"start": "node index.js",
"build": "exit 0"
},
"license": "ISC"
}