瀏覽代碼

Merge branch 'stretch' of https://github.com/bashrc/freedombone

Bob Mottram 7 年之前
父節點
當前提交
8d4dd741ed
共有 5 個文件被更改,包括 333 次插入528 次删除
  1. 45
    220
      doc/EN/devguide.org
  2. 二進制
      man/freedombone-template.1.gz
  3. 2
    2
      src/freedombone-app-nextcloud
  4. 221
    75
      src/freedombone-template
  5. 65
    231
      website/EN/devguide.html

+ 45
- 220
doc/EN/devguide.org 查看文件

15
 #+end_export
15
 #+end_export
16
 
16
 
17
 * Introduction
17
 * Introduction
18
-Freedombone consists of a set of bash scripts. There are a lot of them, but they're not very complicated. If you're familiar with the GNU/Linux commandline and can hack a bash script then you can probably add a new app or fix a bug in the system. There are no trendy development frameworks to learn or to get in your way. You might also want to consult the [[./codeofconduct.html][Code of Conduct]].
18
+Freedombone consists of a set of bash scripts. There are a lot of them, but they're not very complicated. If you're familiar with the GNU/Linux commandline and can hack a bash script then you can probably add a new app or fix a bug in the system. There are no trendy development frameworks to learn or to get in your way. You might also want to consult the [[./codeofconduct.html][Code of Conduct]], and there is a Matrix room at *#fbone:matrix.freedombone.net*
19
 * Adding extra apps
19
 * Adding extra apps
20
 Suppose you have some internet application which you want to add to the system. To do this you need to create an app script which tells the system how to install/remove and also backup/restore. The script should be designed to work with the current stable version of Debian.
20
 Suppose you have some internet application which you want to add to the system. To do this you need to create an app script which tells the system how to install/remove and also backup/restore. The script should be designed to work with the current stable version of Debian.
21
 
21
 
22
-On an installed system the app scripts go into the directory:
22
+There's a command which you can use to generate scripts for new apps. Some examples are as follows:
23
+
24
+To create a script for a generic PHP plus MySql/MariaDB web app:
23
 
25
 
24
 #+begin_src bash
26
 #+begin_src bash
25
-/usr/share/freedombone/apps
27
+freedombone-template --app [name] -e [email] -r [repo url] \
28
+                     -c [commit] --php yes -d mariadb > \
29
+                     src/freedombone-app-myappname
26
 #+end_src
30
 #+end_src
27
 
31
 
28
-and within the project repo they appear within the /src/ directory. Your new app script should have the name:
32
+For a Nodejs app with MySql/MariaDB database:
29
 
33
 
30
 #+begin_src bash
34
 #+begin_src bash
31
-freedombone-app-[myappname]
35
+freedombone-template --app [name] -e [email] -r [repo url] \
36
+                     -c [commit] --node yes -d mariadb \
37
+                     --dir /etc/myappname --daemon yes > \
38
+                     src/freedombone-app-myappname
32
 #+end_src
39
 #+end_src
33
 
40
 
34
-The /myappname/ value should not contain any spaces and will appear in the list of available apps.
35
-
36
-An example template for an app script is shown below. Copy this and add whatever variables and configuration you need. Search and replace /myappname/ with your own.
41
+For a Python app with Postgresql database:
37
 
42
 
38
 #+begin_src bash
43
 #+begin_src bash
39
-#!/bin/bash
40
-# Copyright (C) Year YourName <YourEmail>
41
-#
42
-# This program is free software: you can redistribute it
43
-# and/or modify it under the terms of the GNU Affero General
44
-# Public License as published by the Free Software Foundation,
45
-# either version 3 of the License, or (at your option) any
46
-# later version.
47
-#
48
-# This program is distributed in the hope that it will be useful,
49
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
50
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
51
-# GNU Affero General Public License for more details.
52
-
53
-# 'full' includes your app in the full installation and you
54
-# can also add other variants, separated by spaces. The
55
-# available variants will be detected automatically from the
56
-# app scripts. In most cases don't change this.
57
-VARIANTS='full'
58
-
59
-# If you want this to appear on the control panel About screen
60
-SHOW_ON_ABOUT=1
61
-
62
-# If you want this app to be in the default installation,
63
-# otherwise it will be available but not selected by default
64
-IN_DEFAULT_INSTALL=1
65
-
66
-SOME_IMPORTANT_CONFIG_VARIABLE='some important value'
67
-ANOTHER_IMPORTANT_CONFIG_VARIABLE='foo'
68
-MY_FUNKY_AVATAR=https://some-domain-or-other/fro.png
69
-MYAPPNAME_ONION_PORT=[port number]
70
-MYAPPNAME_DB_PASSWORD=
71
-
72
-# A directory where the data for this app exists
73
-MYAPP_DATA_DIR=/var/lib/somedirectory
74
-
75
-# List of configuration variables used by the app
76
-myappname_variables=(ONION_ONLY
77
-                     MY_USERNAME
78
-                     SOME_IMPORTANT_CONFIG_VARIABLE
79
-                     ANOTHER_IMPORTANT_CONFIG_VARIABLE
80
-                     MY_FUNKY_AVATAR
81
-                     MYAPPNAME_ONION_PORT
82
-                     MYAPPNAME_DB_PASSWORD)
83
-
84
-function logging_on_myappname {
85
-    echo -n ''
86
-    # Commands to turn on logging go here
87
-}
88
-
89
-function logging_off_myappname {
90
-    echo -n ''
91
-    # Commands to turn off logging go here
92
-}
93
-
94
-function change_password_myappname {
95
-    PASSWORD_USERNAME="$1"
96
-    PASSWORD_NEW="$2"
97
-    # Do something to change the password
98
-}
99
-
100
-function reconfigure_myappname {
101
-    echo -n ''
102
-    # Do something to delete existing keys/identity and
103
-    # generate new ones
104
-}
105
-
106
-function upgrade_myappname {
107
-    echo -n ''
108
-    # Do something to upgrade this app.
109
-    # If it's a debian package then it will be maintained by the
110
-    # operating system and you don't need anything here
111
-}
112
-
113
-function backup_local_myappname {
114
-    # If your app has a MariaDB/MySQL database
115
-    backup_database_to_usb myappname
116
-
117
-    # To backup a directory
118
-    backup_directory_to_usb $MYAPP_DATA_DIR myappname
119
-
120
-    # if you need to backup data within individual user
121
-    # home directories
122
-    for d in /home/*/ ; do
123
-        USERNAME=$(echo "$d" | awk -F '/' '{print $3}')
124
-        if [[ $(is_valid_user "$USERNAME") == "1" ]]; then
125
-            echo $"Backing up myappname config for $USERNAME"
126
-            if [ -d /home/$USERNAME/.config/myappname ]; then
127
-                backup_directory_to_usb \
128
-                    /home/$USERNAME/.config/myappname \
129
-                    myappname_users/$USERNAME
130
-            fi
131
-        fi
132
-    done
133
-}
134
-
135
-function restore_local_myappname {
136
-    temp_restore_dir=/root/tempmyappname
137
-
138
-    # If your app has a MariaDB/MySQL database
139
-    restore_database myappname
140
-
141
-    # Restore some data from a directory
142
-    # Note that we don't restore directly but to a temporary
143
-    # directory and then copy the files. This ensures that if
144
-    # there is a restore failure you don't end up with
145
-    # half-copied or corrupted files
146
-    restore_directory_from_usb $MYAPP_DATA_DIR myappname
147
-    cp -r $temp_restore_dir/$MYAPP_DATA_DIR $MYAPP_DATA_DIR
148
-    rm -rf $temp_restore_dir
149
-
150
-    # If you need to restore a configuration directory for each user
151
-    if [ -d $USB_MOUNT/backup/myappname_users ]; then
152
-        for d in $USB_MOUNT/backup/myappname_users/*/ ; do
153
-            USERNAME=$(echo "$d" | awk -F '/' '{print $6}')
154
-            if [[ $(is_valid_user "$USERNAME") == "1" ]]; then
155
-                if [ ! -d /home/$USERNAME ]; then
156
-                    ${PROJECT_NAME}-adduser $USERNAME
157
-                fi
158
-                echo $"Restoring Vim config for $USERNAME"
159
-                function_check restore_directory_from_usb
160
-                restore_directory_from_usb $temp_restore_dir \
161
-                                           myappname_users/$USERNAME
162
-                cp -r $temp_restore_dir/home/$USERNAME/.config \
163
-                      /home/$USERNAME/
164
-                if [ ! "$?" = "0" ]; then
165
-                    rm -rf $temp_restore_dir
166
-                    set_user_permissions
167
-                    backup_unmount_drive
168
-                    exit 664
169
-                fi
170
-                rm -rf $temp_restore_dir
171
-            fi
172
-        done
173
-    fi
174
-}
175
-
176
-function backup_remote_myappname {
177
-    # this should be the same as backup_local_myappname,
178
-    # but call the backup functions backup_directory_to_friend
179
-    # and backup_database_to_friend
180
-}
181
-
182
-function restore_remote_vim {
183
-    # this should be the same as restore_local_myappname,
184
-    # but call the restore function restore_directory_from_friend
185
-    # and restore_database_from_friend
186
-}
187
-
188
-function remove_myappname {
189
-    # if it's a debian package then:
190
-    apt-get -y remove --purge [my-app-package-name]
191
-
192
-    # If your app has a MariaDB/MySQL database
193
-    drop_database myappname
194
-
195
-    # If your app uses an onion address
196
-    remove_onion_service myappname ${MYAPPNAME_ONION_PORT}
197
-}
198
-
199
-function install_myappname {
200
-    # if it's a debian package then:
201
-    apt-get -y install [my-app-package-name]
202
-
203
-    # If you need to create a MariaDB/MySQL database for the app
204
-    MYAPPNAME_DB_PASSWORD="$(create_password 20)"
205
-    create_database myappname "$MYAPPNAME_DB_PASSWORD" $MY_USERNAME
206
-
207
-    # If you need to create an onion address for the app
208
-    MYAPPNAME_ONION_HOSTNAME=$(add_onion_service myappname \
209
-                               80 ${MYAPPNAME_ONION_PORT})
210
-
211
-    # Do any other configuration
212
-    # Here you might use $ONION_ONLY or
213
-    # $SOME_IMPORTANT_CONFIG_VARIABLE
44
+freedombone-template --app [name] -e [email] -r [repo url] \
45
+                     -c [commit] -d postgresql \
46
+                     --dir /etc/myappname --daemon yes > \
47
+                     src/freedombone-app-myappname
48
+#+end_src
214
 
49
 
215
-    # Mark the app as having installed successfully
216
-    # If this variable isn't set then it will be assumed that
217
-    # the install has failed
218
-    APP_INSTALLED=1
219
-}
50
+For a Python app without any database, communicating between the daemon and the web server on port 1234:
220
 
51
 
221
-function install_interactive_myappname {
222
-    # Interactively obtain some values using dialog, such as
223
-    # domain names. An avatar changing example is:
224
-    data=$(tempfile 2>/dev/null)
225
-    trap "rm -f $data" 0 1 2 5 15
226
-    dialog --title $"Change your avatar" \
227
-           --backtitle $"Freedombone Control Panel" \
228
-           --inputbox $"Enter a URL for an image. It should be " \
229
-                      $"approximately a square image." 8 75 2>$data
230
-    sel=$?
231
-    case $sel in
232
-        0)
233
-            MY_FUNKY_AVATAR=$(<$data)
234
-            if [ ${#MY_FUNKY_AVATAR} -gt 3 ]; then
235
-                clear
52
+#+begin_src bash
53
+freedombone-template --app [name] -e [email] -r [repo url] \
54
+                     -c [commit] --dir /etc/myappname \
55
+                     --daemon yes --portinternal 1234 > \
56
+                     src/freedombone-app-myappname
57
+#+end_src
236
 
58
 
237
-                # do whatever is needed to change the avatar
238
-                # in your app
59
+For an app without any database which communicates directly on a particular port through the firewall:
239
 
60
 
240
-                dialog --title $"Change your avatar" \
241
-                       --msgbox $"Your avatar has been changed" 6 40
242
-            fi
243
-            ;;
244
-    esac
61
+#+begin_src bash
62
+freedombone-template --app [name] -e [email] -r [repo url] \
63
+                     -c [commit] --dir /etc/myappname \
64
+                     --daemon yes --port 5000 > \
65
+                     src/freedombone-app-myappname
66
+#+end_src
245
 
67
 
246
-    # install_myappname will be called automatically after this function
247
-}
68
+A generic PHP plus MySql/MariaDB web app which is only available on an onion address:
248
 
69
 
249
-# NOTE: deliberately no exit 0
70
+#+begin_src bash
71
+freedombone-template --app [name] -e [email] -r [repo url] \
72
+                     -c [commit] --php yes -d mariadb \
73
+                     --onion yes > \
74
+                     src/freedombone-app-myappname
250
 #+end_src
75
 #+end_src
251
 
76
 
252
-To test your app log into your system, select *Exit to command line* then gain root powers with:
77
+For more details see the manpage:
253
 
78
 
254
 #+begin_src bash
79
 #+begin_src bash
255
-sudo su
80
+man freedombone-template
256
 #+end_src
81
 #+end_src
257
 
82
 
258
-Copy your app script to */usr/share/freedombone/apps/freedombone-app-myappname*.
83
+The template command won't give you a fully working app, but it will give you a big head start and avoid a lot of potential mistakes. It's highly likely that you'll still need to add extra configuration for your particular app, especially within the *install_app* function.
259
 
84
 
260
-And run the admin control panel:
85
+When your new script is ready for testing you can install it with:
261
 
86
 
262
 #+begin_src bash
87
 #+begin_src bash
263
-control
88
+make install
264
 #+end_src
89
 #+end_src
265
 
90
 
266
-Select *Add/Remove Apps* and if all is well then you should see your app listed as installable. Test that installing and removing it works as expected.
91
+Then run the administrator control panel and you should see the new app within *Add/Remove apps*.
267
 
92
 
268
-Submit your working app to *https://github.com/bashrc/freedombone/issues*
93
+Submit your working app to *https://github.com/bashrc/freedombone/issues* or create a pull request.
269
 
94
 
270
 * Customising mesh images
95
 * Customising mesh images
271
 If you want to make your own specially branded version of the mesh images, such as for a particular event, then to change the default desktop backgrounds edit the images within *img/backgrounds* and to change the available avatars and desktop icons edit the images within *img/avatars*. Re-create disk images using the instructions shown previously.
96
 If you want to make your own specially branded version of the mesh images, such as for a particular event, then to change the default desktop backgrounds edit the images within *img/backgrounds* and to change the available avatars and desktop icons edit the images within *img/avatars*. Re-create disk images using the instructions shown previously.

二進制
man/freedombone-template.1.gz 查看文件


+ 2
- 2
src/freedombone-app-nextcloud 查看文件

39
 NEXTCLOUD_CODE=
39
 NEXTCLOUD_CODE=
40
 NEXTCLOUD_ONION_PORT=8112
40
 NEXTCLOUD_ONION_PORT=8112
41
 NEXTCLOUD_REPO="https://github.com/nextcloud/server"
41
 NEXTCLOUD_REPO="https://github.com/nextcloud/server"
42
-# Stable 12 branch
43
-NEXTCLOUD_COMMIT='cd095bb0b85eed6a9a9f6f0f7d10f2366c4667a7'
42
+# Stable 13 branch
43
+NEXTCLOUD_COMMIT='b16824db31cd00e26e72216bf995d52389b9c93c'
44
 NEXTCLOUD_ADMIN_PASSWORD=
44
 NEXTCLOUD_ADMIN_PASSWORD=
45
 
45
 
46
 nextcloud_variables=(ONION_ONLY
46
 nextcloud_variables=(ONION_ONLY

+ 221
- 75
src/freedombone-template 查看文件

31
 PROJECT_NAME='freedombone'
31
 PROJECT_NAME='freedombone'
32
 
32
 
33
 app_name='noapp'
33
 app_name='noapp'
34
-app_name_lower="$(tr '[:upper:]' '[:lower:]' <<< ${app_name:0:1})${app_name:1}"
34
+app_name_lower=$(echo ${app_name} | tr '[:upper:]' '[:lower:]')
35
 app_name=$app_name_lower
35
 app_name=$app_name_lower
36
-app_name_upper="$(tr '[:lower:]' '[:upper:]' <<< ${app_name:0:1})${app_name:1}"
36
+app_name_upper=$(echo ${app_name} | tr '[:lower:]' '[:upper:]')
37
+echo "test: $app_name_upper"
37
 app_repo="TODO"
38
 app_repo="TODO"
38
 app_repo_commit='TODO'
39
 app_repo_commit='TODO'
39
 app_php=
40
 app_php=
40
 app_node=
41
 app_node=
42
+app_onion_only=
43
+app_port=
44
+app_port_internal=
45
+app_daemon=
46
+app_dir=
41
 your_name=''
47
 your_name=''
42
 your_email=''
48
 your_email=''
43
 SHOW_ON_ABOUT=1
49
 SHOW_ON_ABOUT=1
56
     echo $'  -e --email [address]                 Your email address'
62
     echo $'  -e --email [address]                 Your email address'
57
     echo $'  -r --repo [url]                      Git repo url for the app'
63
     echo $'  -r --repo [url]                      Git repo url for the app'
58
     echo $'  -c --commit [hash]                   Git commit'
64
     echo $'  -c --commit [hash]                   Git commit'
65
+    echo $'     --port [number]                   Port number for the app'
66
+    echo $'     --portinternal [number]           Internal port between a daemon and the web server'
59
     echo $'     --node [yes|no]                   Is this a nodejs app?'
67
     echo $'     --node [yes|no]                   Is this a nodejs app?'
68
+    echo $'  -o --onion [yes|no]                  Is this app only available on an onion address?'
60
     echo $'  -p --php [yes|no]                    Is this a PHP app?'
69
     echo $'  -p --php [yes|no]                    Is this a PHP app?'
70
+    echo $'  -s --daemon [yes|no]                 Add a daemon'
61
     echo $'  -d --database [mariadb|postgresql]   Type of database'
71
     echo $'  -d --database [mariadb|postgresql]   Type of database'
72
+    echo $'     --dir [directory]                 Where to install to'
62
     echo ''
73
     echo ''
63
     exit 0
74
     exit 0
64
 }
75
 }
74
         -a|--app|--appname)
85
         -a|--app|--appname)
75
             shift
86
             shift
76
             app_name="$1"
87
             app_name="$1"
77
-            app_name_lower="$(tr '[:upper:]' '[:lower:]' <<< ${app_name:0:1})${app_name:1}"
88
+            app_name_lower=$(echo ${app_name} | tr '[:upper:]' '[:lower:]')
78
             app_name=$app_name_lower
89
             app_name=$app_name_lower
79
-            app_name_upper="$(tr '[:lower:]' '[:upper:]' <<< ${app_name:0:1})${app_name:1}"
90
+            app_name_upper=$(echo ${app_name} | tr '[:lower:]' '[:upper:]')
80
             ;;
91
             ;;
81
         -r|--repo)
92
         -r|--repo)
82
             shift
93
             shift
106
             shift
117
             shift
107
             app_node="$1"
118
             app_node="$1"
108
             ;;
119
             ;;
120
+        -s|--daemon|--systemd)
121
+            shift
122
+            if [[ "$1" == 'yes' ]]; then
123
+                app_daemon=1
124
+            fi
125
+            ;;
126
+        -o|--onion)
127
+            shift
128
+            if [[ "$1" == 'yes' ]]; then
129
+                app_onion_only=1
130
+            fi
131
+            ;;
132
+        --port)
133
+            shift
134
+            app_port="$1"
135
+            ;;
136
+        --portinternal|--portint)
137
+            shift
138
+            app_port_internal="$1"
139
+            ;;
140
+        --dir)
141
+            shift
142
+            app_dir="$1"
143
+            ;;
109
         *)
144
         *)
110
             # unknown option
145
             # unknown option
111
             ;;
146
             ;;
185
 echo ''
220
 echo ''
186
 echo 'IN_DEFAULT_INSTALL=0'
221
 echo 'IN_DEFAULT_INSTALL=0'
187
 echo "SHOW_ON_ABOUT=${SHOW_ON_ABOUT}"
222
 echo "SHOW_ON_ABOUT=${SHOW_ON_ABOUT}"
223
+if [ $app_onion_only ]; then
224
+    echo 'SHOW_ICANN_ADDRESS_ON_ABOUT=0'
225
+fi
188
 echo ''
226
 echo ''
189
 echo "${app_name_upper}_DOMAIN_NAME="
227
 echo "${app_name_upper}_DOMAIN_NAME="
190
 echo "${app_name_upper}_CODE="
228
 echo "${app_name_upper}_CODE="
229
+if [ $app_port ]; then
230
+    echo "${app_name_upper}_PORT=$app_port"
231
+fi
191
 echo "${app_name_upper}_ONION_PORT=$(( ( RANDOM % 1000 )  + 9010 ))"
232
 echo "${app_name_upper}_ONION_PORT=$(( ( RANDOM % 1000 )  + 9010 ))"
192
 echo "${app_name_upper}_REPO=\"${app_repo}\""
233
 echo "${app_name_upper}_REPO=\"${app_repo}\""
193
 echo "${app_name_upper}_COMMIT='${app_repo_commit}'"
234
 echo "${app_name_upper}_COMMIT='${app_repo_commit}'"
235
+if [ $app_daemon ]; then
236
+    if [ ! $app_port_internal ]; then
237
+        echo "${app_name_upper}_PORT_INTERNAL=TODO"
238
+    else
239
+        echo "${app_name_upper}_PORT_INTERNAL=$app_port_internal"
240
+    fi
241
+fi
194
 echo ''
242
 echo ''
195
 echo "${app_name}=(ONION_ONLY"
243
 echo "${app_name}=(ONION_ONLY"
196
 echo "             ${app_name_upper}_DOMAIN_NAME"
244
 echo "             ${app_name_upper}_DOMAIN_NAME"
221
 echo '}'
269
 echo '}'
222
 echo ''
270
 echo ''
223
 echo "function install_interactive_${app_name} {"
271
 echo "function install_interactive_${app_name} {"
224
-echo '    if [ ! $ONION_ONLY ]; then'
225
-echo "        ONION_ONLY='no'"
226
-echo '    fi'
227
-echo ''
228
-echo '    if [[ $ONION_ONLY != "no" ]]; then'
229
-echo "        ${app_name_upper}_DOMAIN_NAME='${app_name}.local'"
230
-echo "        write_config_param \"${app_name_upper}_DOMAIN_NAME\" \"\$${app_name_upper}_DOMAIN_NAME\""
231
-echo '    else'
232
-echo "        interactive_site_details \"${app_name}\" \"${app_name_upper}_DOMAIN_NAME\" \"${app_name}_CODE\""
233
-echo '    fi'
272
+if [ ! $app_onion_only ]; then
273
+    echo '    if [ ! $ONION_ONLY ]; then'
274
+    echo "        ONION_ONLY='no'"
275
+    echo '    fi'
276
+    echo ''
277
+    echo '    if [[ $ONION_ONLY != "no" ]]; then'
278
+    echo "        ${app_name_upper}_DOMAIN_NAME='${app_name}.local'"
279
+    echo "        write_config_param \"${app_name_upper}_DOMAIN_NAME\" \"\$${app_name_upper}_DOMAIN_NAME\""
280
+    echo '    else'
281
+    echo "        interactive_site_details \"${app_name}\" \"${app_name_upper}_DOMAIN_NAME\" \"${app_name}_CODE\""
282
+    echo '    fi'
283
+else
284
+    echo "    echo -n ''"
285
+fi
234
 echo '    APP_INSTALLED=1'
286
 echo '    APP_INSTALLED=1'
235
 echo '}'
287
 echo '}'
236
 echo ''
288
 echo ''
311
 echo '    fi'
363
 echo '    fi'
312
 echo ''
364
 echo ''
313
 echo '    # update to the next commit'
365
 echo '    # update to the next commit'
314
-echo "    set_repo_commit /var/www/\$${app_name_upper}_DOMAIN_NAME/htdocs \"${app_name} commit\" \"\$${app_name_upper}_COMMIT\" \$${app_name_upper}_REPO"
315
-echo "    chown -R www-data:www-data /var/www/\${${app_name_upper}_DOMAIN_NAME}/htdocs"
366
+if [ ! $app_dir ]; then
367
+    echo "    set_repo_commit /var/www/\$${app_name_upper}_DOMAIN_NAME/htdocs \"${app_name} commit\" \"\$${app_name_upper}_COMMIT\" \$${app_name_upper}_REPO"
368
+    echo "    chown -R www-data:www-data /var/www/\${${app_name_upper}_DOMAIN_NAME}/htdocs"
369
+else
370
+    echo "    set_repo_commit ${app_dir} \"${app_name} commit\" \"\$${app_name_upper}_COMMIT\" \$${app_name_upper}_REPO"
371
+    echo "    chown -R ${app_name}:${app_name} ${app_dir}"
372
+fi
316
 echo '}'
373
 echo '}'
317
 echo ''
374
 echo ''
318
 echo "function backup_local_${app_name} {"
375
 echo "function backup_local_${app_name} {"
321
 echo "        ${app_name_upper}_DOMAIN_NAME=\$(get_completion_param \"${app_name} domain\")"
378
 echo "        ${app_name_upper}_DOMAIN_NAME=\$(get_completion_param \"${app_name} domain\")"
322
 echo '    fi'
379
 echo '    fi'
323
 echo ''
380
 echo ''
324
-echo "    source_directory=/var/www/\${${app_name_upper}_DOMAIN_NAME}/htdocs"
381
+if [ ! $app_dir ]; then
382
+    echo "    source_directory=/var/www/\${${app_name_upper}_DOMAIN_NAME}/htdocs"
383
+else
384
+    echo "    source_directory=${app_dir}"
385
+fi
325
 echo ''
386
 echo ''
326
 echo "    suspend_site \${${app_name_upper}_DOMAIN_NAME}"
387
 echo "    suspend_site \${${app_name_upper}_DOMAIN_NAME}"
327
 echo ''
388
 echo ''
347
 echo "    ${app_name_upper}_DOMAIN_NAME=\$(get_completion_param \"${app_name} domain\")"
408
 echo "    ${app_name_upper}_DOMAIN_NAME=\$(get_completion_param \"${app_name} domain\")"
348
 echo "    if [ \$${app_name_upper}_DOMAIN_NAME ]; then"
409
 echo "    if [ \$${app_name_upper}_DOMAIN_NAME ]; then"
349
 echo "        temp_restore_dir=/root/temp${app_name}"
410
 echo "        temp_restore_dir=/root/temp${app_name}"
350
-echo "        ${app_name}_dir=/var/www/\${${app_name_upper}_DOMAIN_NAME}/htdocs"
411
+if [ ! $app_dir ]; then
412
+    echo "        ${app_name}_dir=/var/www/\${${app_name_upper}_DOMAIN_NAME}/htdocs"
413
+else
414
+    echo "        ${app_name}_dir=${app_dir}"
415
+fi
351
 echo ''
416
 echo ''
352
 if [[ "$database_type" == "mariadb" || "$database_type" == "mysql" ]]; then
417
 if [[ "$database_type" == "mariadb" || "$database_type" == "mysql" ]]; then
353
     echo "        ${app_name}_create_database"
418
     echo "        ${app_name}_create_database"
391
 echo "        ${app_name_upper}_DOMAIN_NAME=\$(get_completion_param \"${app_name} domain\")"
456
 echo "        ${app_name_upper}_DOMAIN_NAME=\$(get_completion_param \"${app_name} domain\")"
392
 echo '    fi'
457
 echo '    fi'
393
 echo ''
458
 echo ''
394
-echo "    source_directory=/var/www/\${${app_name_upper}_DOMAIN_NAME}/htdocs"
459
+if [ ! $app_dir ]; then
460
+    echo "    source_directory=/var/www/\${${app_name_upper}_DOMAIN_NAME}/htdocs"
461
+else
462
+    echo "    source_directory=${app_dir}"
463
+fi
395
 echo ''
464
 echo ''
396
 echo "    suspend_site \${${app_name_upper}_DOMAIN_NAME}"
465
 echo "    suspend_site \${${app_name_upper}_DOMAIN_NAME}"
397
 echo ''
466
 echo ''
417
 echo "    ${app_name_upper}_DOMAIN_NAME=\$(get_completion_param \"${app_name} domain\")"
486
 echo "    ${app_name_upper}_DOMAIN_NAME=\$(get_completion_param \"${app_name} domain\")"
418
 echo "    if [ \$${app_name_upper}_DOMAIN_NAME ]; then"
487
 echo "    if [ \$${app_name_upper}_DOMAIN_NAME ]; then"
419
 echo "        temp_restore_dir=/root/temp${app_name}"
488
 echo "        temp_restore_dir=/root/temp${app_name}"
420
-echo "        ${app_name}_dir=/var/www/\${${app_name_upper}_DOMAIN_NAME}/htdocs"
489
+if [ ! $app_dir ]; then
490
+    echo "        ${app_name}_dir=/var/www/\${${app_name_upper}_DOMAIN_NAME}/htdocs"
491
+else
492
+    echo "        ${app_name}_dir=${app_dir}"
493
+fi
421
 echo ''
494
 echo ''
422
 if [[ "$database_type" == "mariadb" || "$database_type" == "mysql" ]]; then
495
 if [[ "$database_type" == "mariadb" || "$database_type" == "mysql" ]]; then
423
     echo "        ${app_name}_create_database"
496
     echo "        ${app_name}_create_database"
463
 echo "    nginx_dissite \$${app_name_upper}_DOMAIN_NAME"
536
 echo "    nginx_dissite \$${app_name_upper}_DOMAIN_NAME"
464
 echo "    remove_certs \$${app_name_upper}_DOMAIN_NAME"
537
 echo "    remove_certs \$${app_name_upper}_DOMAIN_NAME"
465
 echo ''
538
 echo ''
539
+if [ $app_daemon ]; then
540
+    echo "    if [ -f /etc/systemd/system/${app_name}.service ]; then"
541
+    echo "        systemctl stop ${app_name}"
542
+    echo "        systemctl disable ${app_name}"
543
+    echo "        rm /etc/systemd/system/${app_name}.service"
544
+    echo '    fi'
545
+    echo "    userdel -r ${app_name}"
546
+fi
547
+echo ''
466
 echo "    if [ -d /var/www/\$${app_name_upper}_DOMAIN_NAME ]; then"
548
 echo "    if [ -d /var/www/\$${app_name_upper}_DOMAIN_NAME ]; then"
467
 echo "        rm -rf /var/www/\$${app_name_upper}_DOMAIN_NAME"
549
 echo "        rm -rf /var/www/\$${app_name_upper}_DOMAIN_NAME"
468
 echo '    fi'
550
 echo '    fi'
482
 echo "    remove_app ${app_name}"
564
 echo "    remove_app ${app_name}"
483
 echo "    remove_completion_param install_${app_name}"
565
 echo "    remove_completion_param install_${app_name}"
484
 echo "    sed -i '/${app_name}/d' \$COMPLETION_FILE"
566
 echo "    sed -i '/${app_name}/d' \$COMPLETION_FILE"
485
-echo ''
486
-echo "    if grep -q '${app_name}-firewall' /etc/crontab; then"
487
-echo "        sed -i '/${app_name}-firewall/d' /etc/crontab"
488
-echo '    fi'
567
+if [ $app_port ]; then
568
+    echo ''
569
+    echo "    firewall_remove ${app_port} tcp"
570
+fi
489
 echo ''
571
 echo ''
490
 echo "    remove_ddns_domain \$${app_name_upper}_DOMAIN_NAME"
572
 echo "    remove_ddns_domain \$${app_name_upper}_DOMAIN_NAME"
491
 echo '}'
573
 echo '}'
515
 echo "    if [ ! -d /var/www/\$${app_name_upper}_DOMAIN_NAME/htdocs ]; then"
597
 echo "    if [ ! -d /var/www/\$${app_name_upper}_DOMAIN_NAME/htdocs ]; then"
516
 echo "        if [ -d /repos/${app_name} ]; then"
598
 echo "        if [ -d /repos/${app_name} ]; then"
517
 echo "            mkdir /var/www/\$${app_name_upper}_DOMAIN_NAME/htdocs"
599
 echo "            mkdir /var/www/\$${app_name_upper}_DOMAIN_NAME/htdocs"
518
-echo "            cp -r -p /repos/${app_name}/. /var/www/\$${app_name_upper}_DOMAIN_NAME/htdocs"
519
-echo "            cd /var/www/\$${app_name_upper}_DOMAIN_NAME/htdocs"
600
+if [ ! $app_dir ]; then
601
+    echo "            cp -r -p /repos/${app_name}/. /var/www/\$${app_name_upper}_DOMAIN_NAME/htdocs"
602
+    echo "            cd /var/www/\$${app_name_upper}_DOMAIN_NAME/htdocs"
603
+else
604
+    echo "            cp -r -p /repos/${app_name}/. ${app_dir}"
605
+    echo "            cd ${app_dir}"
606
+fi
520
 echo '            git pull'
607
 echo '            git pull'
521
 echo '        else'
608
 echo '        else'
522
-echo "            git_clone \$${app_name_upper}_REPO /var/www/\$${app_name_upper}_DOMAIN_NAME/htdocs"
609
+if [ ! $app_dir ]; then
610
+    echo "            git_clone \$${app_name_upper}_REPO /var/www/\$${app_name_upper}_DOMAIN_NAME/htdocs"
611
+else
612
+    echo "            git_clone \$${app_name_upper}_REPO ${app_dir}"
613
+fi
523
 echo '        fi'
614
 echo '        fi'
524
 echo ''
615
 echo ''
525
-echo "        if [ ! -d /var/www/\$${app_name_upper}_DOMAIN_NAME/htdocs ]; then"
616
+if [ ! $app_dir ]; then
617
+    echo "        if [ ! -d /var/www/\$${app_name_upper}_DOMAIN_NAME/htdocs ]; then"
618
+else
619
+    echo "        if [ ! -d ${app_dir} ]; then"
620
+fi
526
 echo "            echo \$'Unable to clone ${app_name} repo'"
621
 echo "            echo \$'Unable to clone ${app_name} repo'"
527
 echo '            exit 87525'
622
 echo '            exit 87525'
528
 echo '        fi'
623
 echo '        fi'
529
 echo '    fi'
624
 echo '    fi'
530
 echo ''
625
 echo ''
531
-echo "    cd /var/www/\$${app_name_upper}_DOMAIN_NAME/htdocs"
626
+if [ ! $app_dir ]; then
627
+    echo "    cd /var/www/\$${app_name_upper}_DOMAIN_NAME/htdocs"
628
+else
629
+    echo "    cd ${app_dir}"
630
+fi
532
 echo "    git checkout \$${app_name_upper}_COMMIT -b \$${app_name_upper}_COMMIT"
631
 echo "    git checkout \$${app_name_upper}_COMMIT -b \$${app_name_upper}_COMMIT"
533
 echo "    set_completion_param \"${app_name} commit\" \"\$${app_name_upper}_COMMIT\""
632
 echo "    set_completion_param \"${app_name} commit\" \"\$${app_name_upper}_COMMIT\""
534
 echo ''
633
 echo ''
535
 echo "    chmod g+w /var/www/\$${app_name_upper}_DOMAIN_NAME/htdocs"
634
 echo "    chmod g+w /var/www/\$${app_name_upper}_DOMAIN_NAME/htdocs"
536
 echo "    chown -R www-data:www-data /var/www/\$${app_name_upper}_DOMAIN_NAME/htdocs"
635
 echo "    chown -R www-data:www-data /var/www/\$${app_name_upper}_DOMAIN_NAME/htdocs"
537
 
636
 
538
-if [[ "$database_type" == "mariadb" || "$database_type" == "mysql" ]]; then
637
+if [[ "$database_type" == "mariadb" || "$database_type" == "mysql" || "$database_type" == "postgres"*  ]]; then
539
     echo ''
638
     echo ''
540
     echo "    ${app_name}_create_database"
639
     echo "    ${app_name}_create_database"
541
 fi
640
 fi
545
 echo "    ${app_name_upper}_ONION_HOSTNAME=\$(add_onion_service ${app_name} 80 \${${app_name_upper}_ONION_PORT})"
644
 echo "    ${app_name_upper}_ONION_HOSTNAME=\$(add_onion_service ${app_name} 80 \${${app_name_upper}_ONION_PORT})"
546
 echo ''
645
 echo ''
547
 echo "    ${app_name}_nginx_site=/etc/nginx/sites-available/\$${app_name_upper}_DOMAIN_NAME"
646
 echo "    ${app_name}_nginx_site=/etc/nginx/sites-available/\$${app_name_upper}_DOMAIN_NAME"
548
-echo '    if [[ $ONION_ONLY == "no" ]]; then'
549
-if [[ "$app_php" == 'yes' ]]; then
550
-    echo "        nginx_http_redirect \$${app_name_upper}_DOMAIN_NAME \"index index.php\""
551
-else
552
-    echo "        nginx_http_redirect \$${app_name_upper}_DOMAIN_NAME \"index index.html\""
553
-fi
554
-echo "        echo 'server {' >> \$${app_name}_nginx_site"
555
-echo "        echo '  listen 443 ssl;' >> \$${app_name}_nginx_site"
556
-echo "        echo '  listen [::]:443 ssl;' >> \$${app_name}_nginx_site"
557
-echo "        echo \"  server_name \$${app_name_upper}_DOMAIN_NAME;\" >> \$${app_name}_nginx_site"
558
-echo "        echo '' >> \$${app_name}_nginx_site"
559
-echo "        nginx_compress \$${app_name_upper}_DOMAIN_NAME"
560
-echo "        echo '' >> \$${app_name}_nginx_site"
561
-echo "        echo '  # Security' >> \$${app_name}_nginx_site"
562
-echo "        nginx_ssl \$${app_name_upper}_DOMAIN_NAME"
563
-echo ''
564
-echo "        nginx_disable_sniffing \$${app_name_upper}_DOMAIN_NAME"
565
-echo ''
566
-echo "        echo '  add_header Strict-Transport-Security max-age=15768000;' >> \$${app_name}_nginx_site"
567
-echo "        echo '' >> \$${app_name}_nginx_site"
568
-echo "        echo '  # Logs' >> \$${app_name}_nginx_site"
569
-echo "        echo '  access_log /dev/null;' >> \$${app_name}_nginx_site"
570
-echo "        echo '  error_log /dev/null;' >> \$${app_name}_nginx_site"
571
-echo "        echo '' >> \$${app_name}_nginx_site"
572
-echo "        echo '  # Root' >> \$${app_name}_nginx_site"
573
-echo "        echo \"  root /var/www/\$${app_name_upper}_DOMAIN_NAME/htdocs;\" >> \$${app_name}_nginx_site"
574
-echo "        echo '' >> \$${app_name}_nginx_site"
575
-if [[ "$app_php" == 'yes' ]]; then
576
-    echo "        echo '  index index.php;' >> \$${app_name}_nginx_site"
577
-    echo "        echo '  location ~ \.php {' >> \$${app_name}_nginx_site"
578
-    echo "        echo '    include snippets/fastcgi-php.conf;' >> \$${app_name}_nginx_site"
579
-    echo "        echo '    fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;' >> \$${app_name}_nginx_site"
580
-    echo "        echo '    fastcgi_read_timeout 30;' >> \$${app_name}_nginx_site"
581
-    echo "        echo '  }' >> \$${app_name}_nginx_site"
647
+
648
+if [ $app_onion_only ]; then
649
+    echo '    if [[ $ONION_ONLY == "no" ]]; then'
650
+    if [[ "$app_php" == 'yes' ]]; then
651
+        echo "        nginx_http_redirect \$${app_name_upper}_DOMAIN_NAME \"index index.php\""
652
+    else
653
+        echo "        nginx_http_redirect \$${app_name_upper}_DOMAIN_NAME \"index index.html\""
654
+    fi
655
+    echo "        echo 'server {' >> \$${app_name}_nginx_site"
656
+    echo "        echo '  listen 443 ssl;' >> \$${app_name}_nginx_site"
657
+    echo "        echo '  listen [::]:443 ssl;' >> \$${app_name}_nginx_site"
658
+    echo "        echo \"  server_name \$${app_name_upper}_DOMAIN_NAME;\" >> \$${app_name}_nginx_site"
659
+    echo "        echo '' >> \$${app_name}_nginx_site"
660
+    echo "        nginx_compress \$${app_name_upper}_DOMAIN_NAME"
661
+    echo "        echo '' >> \$${app_name}_nginx_site"
662
+    echo "        echo '  # Security' >> \$${app_name}_nginx_site"
663
+    echo "        nginx_ssl \$${app_name_upper}_DOMAIN_NAME"
664
+    echo ''
665
+    echo "        nginx_disable_sniffing \$${app_name_upper}_DOMAIN_NAME"
666
+    echo ''
667
+    echo "        echo '  add_header Strict-Transport-Security max-age=15768000;' >> \$${app_name}_nginx_site"
668
+    echo "        echo '' >> \$${app_name}_nginx_site"
669
+    echo "        echo '  # Logs' >> \$${app_name}_nginx_site"
670
+    echo "        echo '  access_log /dev/null;' >> \$${app_name}_nginx_site"
671
+    echo "        echo '  error_log /dev/null;' >> \$${app_name}_nginx_site"
582
     echo "        echo '' >> \$${app_name}_nginx_site"
672
     echo "        echo '' >> \$${app_name}_nginx_site"
673
+    echo "        echo '  # Root' >> \$${app_name}_nginx_site"
674
+    echo "        echo \"  root /var/www/\$${app_name_upper}_DOMAIN_NAME/htdocs;\" >> \$${app_name}_nginx_site"
675
+    echo "        echo '' >> \$${app_name}_nginx_site"
676
+    if [[ "$app_php" == 'yes' ]]; then
677
+        echo "        echo '  index index.php;' >> \$${app_name}_nginx_site"
678
+        echo "        echo '  location ~ \.php {' >> \$${app_name}_nginx_site"
679
+        echo "        echo '    include snippets/fastcgi-php.conf;' >> \$${app_name}_nginx_site"
680
+        echo "        echo '    fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;' >> \$${app_name}_nginx_site"
681
+        echo "        echo '    fastcgi_read_timeout 30;' >> \$${app_name}_nginx_site"
682
+        echo "        echo '  }' >> \$${app_name}_nginx_site"
683
+        echo "        echo '' >> \$${app_name}_nginx_site"
684
+    else
685
+        echo "        echo '  index index.html;' >> \$${app_name}_nginx_site"
686
+    fi
687
+    echo "        echo '  # Location' >> \$${app_name}_nginx_site"
688
+    echo "        echo '  location / {' >> \$${app_name}_nginx_site"
689
+    echo "        nginx_limits \$${app_name_upper}_DOMAIN_NAME '15m'"
690
+    if [ ! $app_daemon ]; then
691
+        echo "        echo '    try_files \$uri \$uri/ /index.html;' >> \$${app_name}_nginx_site"
692
+    else
693
+        echo "        echo \"    proxy_pass http://localhost:\$${app_name_upper}_PORT_INTERNAL;\" >> \$${app_name}_nginx_site"
694
+    fi
695
+    echo "        echo '  }' >> \$${app_name}_nginx_site"
696
+    echo "        echo '}' >> \$${app_name}_nginx_site"
697
+    echo '    else'
698
+    echo "        echo -n '' > \$${app_name}_nginx_site"
699
+    echo '    fi'
583
 else
700
 else
584
-    echo "        echo '  index index.html;' >> \$${app_name}_nginx_site"
701
+    echo "    echo -n '' > \$${app_name}_nginx_site"
585
 fi
702
 fi
586
-echo "        echo '  # Location' >> \$${app_name}_nginx_site"
587
-echo "        echo '  location / {' >> \$${app_name}_nginx_site"
588
-echo "        nginx_limits \$${app_name_upper}_DOMAIN_NAME '15m'"
589
-echo "        echo '    try_files \$uri \$uri/ @${app_name};' >> \$${app_name}_nginx_site"
590
-echo "        echo '  }' >> \$${app_name}_nginx_site"
591
-echo "        echo '}' >> \$${app_name}_nginx_site"
592
-echo '    else'
593
-echo "        echo -n '' > \$${app_name}_nginx_site"
594
-echo '    fi'
595
 echo "    echo 'server {' >> \$${app_name}_nginx_site"
703
 echo "    echo 'server {' >> \$${app_name}_nginx_site"
596
 echo "    echo \"    listen 127.0.0.1:\$${app_name_upper}_ONION_PORT default_server;\" >> \$${app_name}_nginx_site"
704
 echo "    echo \"    listen 127.0.0.1:\$${app_name_upper}_ONION_PORT default_server;\" >> \$${app_name}_nginx_site"
597
 echo "    echo \"    server_name \$${app_name_upper}_ONION_HOSTNAME;\" >> \$${app_name}_nginx_site"
705
 echo "    echo \"    server_name \$${app_name_upper}_ONION_HOSTNAME;\" >> \$${app_name}_nginx_site"
621
 echo "    echo '  # Location' >> \$${app_name}_nginx_site"
729
 echo "    echo '  # Location' >> \$${app_name}_nginx_site"
622
 echo "    echo '  location / {' >> \$${app_name}_nginx_site"
730
 echo "    echo '  location / {' >> \$${app_name}_nginx_site"
623
 echo "    nginx_limits \$${app_name_upper}_DOMAIN_NAME '15m'"
731
 echo "    nginx_limits \$${app_name_upper}_DOMAIN_NAME '15m'"
624
-echo "    echo '    try_files \$uri \$uri/ @${app_name};' >> \$${app_name}_nginx_site"
732
+if [ ! $app_daemon ]; then
733
+    echo "    echo '    try_files \$uri \$uri/ index.html;' >> \$${app_name}_nginx_site"
734
+else
735
+    echo "    echo \"    proxy_pass http://localhost:\$${app_name_upper}_PORT_INTERNAL;\" >> \$${app_name}_nginx_site"
736
+fi
625
 echo "    echo '  }' >> \$${app_name}_nginx_site"
737
 echo "    echo '  }' >> \$${app_name}_nginx_site"
626
 echo "    echo '}' >> \$${app_name}_nginx_site"
738
 echo "    echo '}' >> \$${app_name}_nginx_site"
627
 if [[ "$app_php" == 'yes' ]]; then
739
 if [[ "$app_php" == 'yes' ]]; then
628
     echo ''
740
     echo ''
629
     echo '    configure_php'
741
     echo '    configure_php'
630
 fi
742
 fi
743
+if [ $app_daemon ]; then
744
+    echo ''
745
+    echo "    useradd -d TODO_PATH_TO_INSTALL -s /bin/false ${app_name}"
746
+    echo ''
747
+    echo "    echo '[Unit]' > /etc/systemd/system/${app_name}.service"
748
+    echo "    echo 'Description=${app_name}' >> /etc/systemd/system/${app_name}.service"
749
+    echo "    echo 'After=syslog.target' >> /etc/systemd/system/${app_name}.service"
750
+    echo "    echo 'After=network.target' >> /etc/systemd/system/${app_name}.service"
751
+    echo "    echo '' >> /etc/systemd/system/${app_name}.service"
752
+    echo "    echo '[Service]' >> /etc/systemd/system/${app_name}.service"
753
+    echo "    echo 'Type=simple' >> /etc/systemd/system/${app_name}.service"
754
+    echo "    echo 'User=${app_name}' >> /etc/systemd/system/${app_name}.service"
755
+    echo "    echo 'Group=${app_name}' >> /etc/systemd/system/${app_name}.service"
756
+    if [ ! $app_dir ]; then
757
+        echo "    echo 'WorkingDirectory=TODO' >> /etc/systemd/system/${app_name}.service"
758
+    else
759
+        echo "    echo 'WorkingDirectory=${app_dir}' >> /etc/systemd/system/${app_name}.service"
760
+    fi
761
+    echo "    echo 'ExecStart=TODO' >> /etc/systemd/system/${app_name}.service"
762
+    echo "    echo 'Restart=always' >> /etc/systemd/system/${app_name}.service"
763
+    echo "    echo 'Environment=\"USER=${app_name}\"' >> /etc/systemd/system/${app_name}.service"
764
+    echo "    echo '' >> /etc/systemd/system/${app_name}.service"
765
+    echo "    echo '[Install]' >> /etc/systemd/system/${app_name}.service"
766
+    echo "    echo 'WantedBy=multi-user.target' >> /etc/systemd/system/${app_name}.service"
767
+    echo "    systemctl enable ${app_name}"
768
+    if [ $app_dir ]; then
769
+        echo "    chown -R ${app_name}:${app_name} ${app_dir}"
770
+    fi
771
+    echo "    systemctl start ${app_name}"
772
+fi
631
 echo ''
773
 echo ''
632
 echo "    create_site_certificate \$${app_name_upper}_DOMAIN_NAME 'yes'"
774
 echo "    create_site_certificate \$${app_name_upper}_DOMAIN_NAME 'yes'"
633
 echo ''
775
 echo ''
643
 echo ''
785
 echo ''
644
 echo "    \${PROJECT_NAME}-pass -u \$MY_USERNAME -a ${app_name} -p \"\$${app_name_upper}_ADMIN_PASSWORD\""
786
 echo "    \${PROJECT_NAME}-pass -u \$MY_USERNAME -a ${app_name} -p \"\$${app_name_upper}_ADMIN_PASSWORD\""
645
 echo "    set_completion_param \"${app_name} domain\" \"\$${app_name_upper}_DOMAIN_NAME\""
787
 echo "    set_completion_param \"${app_name} domain\" \"\$${app_name_upper}_DOMAIN_NAME\""
788
+if [ $app_port ]; then
789
+    echo ''
790
+    echo "    firewall_add ${app_name} ${app_port} tcp"
791
+fi
646
 echo ''
792
 echo ''
647
 echo '    APP_INSTALLED=1'
793
 echo '    APP_INSTALLED=1'
648
 echo '}'
794
 echo '}'

+ 65
- 231
website/EN/devguide.html 查看文件

3
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
4
 <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
4
 <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
5
 <head>
5
 <head>
6
-<!-- 2017-12-20 Wed 12:42 -->
6
+<!-- 2018-02-07 Wed 10:46 -->
7
 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
7
 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
8
 <meta name="viewport" content="width=device-width, initial-scale=1" />
8
 <meta name="viewport" content="width=device-width, initial-scale=1" />
9
 <title>&lrm;</title>
9
 <title>&lrm;</title>
246
 
246
 
247
 <center><h1>Developers Guide</h1></center>
247
 <center><h1>Developers Guide</h1></center>
248
 
248
 
249
-<div id="outline-container-org6818d03" class="outline-2">
250
-<h2 id="org6818d03">Introduction</h2>
251
-<div class="outline-text-2" id="text-org6818d03">
249
+<div id="outline-container-orgcb772bb" class="outline-2">
250
+<h2 id="orgcb772bb">Introduction</h2>
251
+<div class="outline-text-2" id="text-orgcb772bb">
252
 <p>
252
 <p>
253
-Freedombone consists of a set of bash scripts. There are a lot of them, but they're not very complicated. If you're familiar with the GNU/Linux commandline and can hack a bash script then you can probably add a new app or fix a bug in the system. There are no trendy development frameworks to learn or to get in your way. You might also want to consult the <a href="./codeofconduct.html">Code of Conduct</a>.
253
+Freedombone consists of a set of bash scripts. There are a lot of them, but they're not very complicated. If you're familiar with the GNU/Linux commandline and can hack a bash script then you can probably add a new app or fix a bug in the system. There are no trendy development frameworks to learn or to get in your way. You might also want to consult the <a href="./codeofconduct.html">Code of Conduct</a>, and there is a Matrix room at <b>#fbone:matrix.freedombone.net</b>
254
 </p>
254
 </p>
255
 </div>
255
 </div>
256
 </div>
256
 </div>
257
-<div id="outline-container-org080672c" class="outline-2">
258
-<h2 id="org080672c">Adding extra apps</h2>
259
-<div class="outline-text-2" id="text-org080672c">
257
+<div id="outline-container-org8fd5474" class="outline-2">
258
+<h2 id="org8fd5474">Adding extra apps</h2>
259
+<div class="outline-text-2" id="text-org8fd5474">
260
 <p>
260
 <p>
261
 Suppose you have some internet application which you want to add to the system. To do this you need to create an app script which tells the system how to install/remove and also backup/restore. The script should be designed to work with the current stable version of Debian.
261
 Suppose you have some internet application which you want to add to the system. To do this you need to create an app script which tells the system how to install/remove and also backup/restore. The script should be designed to work with the current stable version of Debian.
262
 </p>
262
 </p>
263
 
263
 
264
 <p>
264
 <p>
265
-On an installed system the app scripts go into the directory:
265
+There's a command which you can use to generate scripts for new apps. Some examples are as follows:
266
+</p>
267
+
268
+<p>
269
+To create a script for a generic PHP plus MySql/MariaDB web app:
266
 </p>
270
 </p>
267
 
271
 
268
 <div class="org-src-container">
272
 <div class="org-src-container">
269
-<pre class="src src-bash">/usr/share/freedombone/apps
273
+<pre class="src src-bash">freedombone-template --app [name] -e [email] -r [repo url] <span class="org-sh-escaped-newline">\</span>
274
+                     -c [commit] --php yes -d mariadb &gt; <span class="org-sh-escaped-newline">\</span>
275
+                     src/freedombone-app-myappname
270
 </pre>
276
 </pre>
271
 </div>
277
 </div>
272
 
278
 
273
 <p>
279
 <p>
274
-and within the project repo they appear within the <i>src</i> directory. Your new app script should have the name:
280
+For a Nodejs app with MySql/MariaDB database:
275
 </p>
281
 </p>
276
 
282
 
277
 <div class="org-src-container">
283
 <div class="org-src-container">
278
-<pre class="src src-bash">freedombone-app-[myappname]
284
+<pre class="src src-bash">freedombone-template --app [name] -e [email] -r [repo url] <span class="org-sh-escaped-newline">\</span>
285
+                     -c [commit] --node yes -d mariadb <span class="org-sh-escaped-newline">\</span>
286
+                     --dir /etc/myappname --daemon yes &gt; <span class="org-sh-escaped-newline">\</span>
287
+                     src/freedombone-app-myappname
279
 </pre>
288
 </pre>
280
 </div>
289
 </div>
281
 
290
 
282
 <p>
291
 <p>
283
-The <i>myappname</i> value should not contain any spaces and will appear in the list of available apps.
292
+For a Python app with Postgresql database:
284
 </p>
293
 </p>
285
 
294
 
295
+<div class="org-src-container">
296
+<pre class="src src-bash">freedombone-template --app [name] -e [email] -r [repo url] <span class="org-sh-escaped-newline">\</span>
297
+                     -c [commit] -d postgresql <span class="org-sh-escaped-newline">\</span>
298
+                     --dir /etc/myappname --daemon yes &gt; <span class="org-sh-escaped-newline">\</span>
299
+                     src/freedombone-app-myappname
300
+</pre>
301
+</div>
302
+
286
 <p>
303
 <p>
287
-An example template for an app script is shown below. Copy this and add whatever variables and configuration you need. Search and replace <i>myappname</i> with your own.
304
+For a Python app without any database, communicating between the daemon and the web server on port 1234:
288
 </p>
305
 </p>
289
 
306
 
290
 <div class="org-src-container">
307
 <div class="org-src-container">
291
-<pre class="src src-bash"><span class="org-comment-delimiter">#</span><span class="org-comment">!/bin/</span><span class="org-keyword">bash</span>
292
-<span class="org-comment-delimiter"># </span><span class="org-comment">Copyright (C) Year YourName &lt;YourEmail&gt;</span>
293
-<span class="org-comment-delimiter">#</span>
294
-<span class="org-comment-delimiter"># </span><span class="org-comment">This program is free software: you can redistribute it</span>
295
-<span class="org-comment-delimiter"># </span><span class="org-comment">and/or modify it under the terms of the GNU Affero General</span>
296
-<span class="org-comment-delimiter"># </span><span class="org-comment">Public License as published by the Free Software Foundation,</span>
297
-<span class="org-comment-delimiter"># </span><span class="org-comment">either version 3 of the License, or (at your option) any</span>
298
-<span class="org-comment-delimiter"># </span><span class="org-comment">later version.</span>
299
-<span class="org-comment-delimiter">#</span>
300
-<span class="org-comment-delimiter"># </span><span class="org-comment">This program is distributed in the hope that it will be useful,</span>
301
-<span class="org-comment-delimiter"># </span><span class="org-comment">but WITHOUT ANY WARRANTY; without even the implied warranty of</span>
302
-<span class="org-comment-delimiter"># </span><span class="org-comment">MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span>
303
-<span class="org-comment-delimiter"># </span><span class="org-comment">GNU Affero General Public License for more details.</span>
304
-
305
-<span class="org-comment-delimiter"># </span><span class="org-comment">'full' includes your app in the full installation and you</span>
306
-<span class="org-comment-delimiter"># </span><span class="org-comment">can also add other variants, separated by spaces. The</span>
307
-<span class="org-comment-delimiter"># </span><span class="org-comment">available variants will be detected automatically from the</span>
308
-<span class="org-comment-delimiter"># </span><span class="org-comment">app scripts. In most cases don't change this.</span>
309
-<span class="org-variable-name">VARIANTS</span>=<span class="org-string">'full'</span>
310
-
311
-<span class="org-comment-delimiter"># </span><span class="org-comment">If you want this to appear on the control panel About screen</span>
312
-<span class="org-variable-name">SHOW_ON_ABOUT</span>=1
313
-
314
-<span class="org-comment-delimiter"># </span><span class="org-comment">If you want this app to be in the default installation,</span>
315
-<span class="org-comment-delimiter"># </span><span class="org-comment">otherwise it will be available but not selected by default</span>
316
-<span class="org-variable-name">IN_DEFAULT_INSTALL</span>=1
317
-
318
-<span class="org-variable-name">SOME_IMPORTANT_CONFIG_VARIABLE</span>=<span class="org-string">'some important value'</span>
319
-<span class="org-variable-name">ANOTHER_IMPORTANT_CONFIG_VARIABLE</span>=<span class="org-string">'foo'</span>
320
-<span class="org-variable-name">MY_FUNKY_AVATAR</span>=https://some-domain-or-other/fro.png
321
-<span class="org-variable-name">MYAPPNAME_ONION_PORT</span>=[port number]
322
-<span class="org-variable-name">MYAPPNAME_DB_PASSWORD</span>=
323
-
324
-<span class="org-comment-delimiter"># </span><span class="org-comment">A directory where the data for this app exists</span>
325
-<span class="org-variable-name">MYAPP_DATA_DIR</span>=/var/lib/somedirectory
326
-
327
-<span class="org-comment-delimiter"># </span><span class="org-comment">List of configuration variables used by the app</span>
328
-<span class="org-variable-name">myappname_variables</span>=(ONION_ONLY
329
-                     MY_USERNAME
330
-                     SOME_IMPORTANT_CONFIG_VARIABLE
331
-                     ANOTHER_IMPORTANT_CONFIG_VARIABLE
332
-                     MY_FUNKY_AVATAR
333
-                     MYAPPNAME_ONION_PORT
334
-                     MYAPPNAME_DB_PASSWORD)
335
-
336
-<span class="org-keyword">function</span> <span class="org-function-name">logging_on_myappname</span> {
337
-    <span class="org-builtin">echo</span> -n <span class="org-string">''</span>
338
-    <span class="org-comment-delimiter"># </span><span class="org-comment">Commands to turn on logging go here</span>
339
-}
340
-
341
-<span class="org-keyword">function</span> <span class="org-function-name">logging_off_myappname</span> {
342
-    <span class="org-builtin">echo</span> -n <span class="org-string">''</span>
343
-    <span class="org-comment-delimiter"># </span><span class="org-comment">Commands to turn off logging go here</span>
344
-}
345
-
346
-<span class="org-keyword">function</span> <span class="org-function-name">change_password_myappname</span> {
347
-    <span class="org-variable-name">PASSWORD_USERNAME</span>=<span class="org-string">"$1"</span>
348
-    <span class="org-variable-name">PASSWORD_NEW</span>=<span class="org-string">"$2"</span>
349
-    <span class="org-comment-delimiter"># </span><span class="org-comment">Do something to change the password</span>
350
-}
351
-
352
-<span class="org-keyword">function</span> <span class="org-function-name">reconfigure_myappname</span> {
353
-    <span class="org-builtin">echo</span> -n <span class="org-string">''</span>
354
-    <span class="org-comment-delimiter"># </span><span class="org-comment">Do something to delete existing keys/identity and</span>
355
-    <span class="org-comment-delimiter"># </span><span class="org-comment">generate new ones</span>
356
-}
357
-
358
-<span class="org-keyword">function</span> <span class="org-function-name">upgrade_myappname</span> {
359
-    <span class="org-builtin">echo</span> -n <span class="org-string">''</span>
360
-    <span class="org-comment-delimiter"># </span><span class="org-comment">Do something to upgrade this app.</span>
361
-    <span class="org-comment-delimiter"># </span><span class="org-comment">If it's a debian package then it will be maintained by the</span>
362
-    <span class="org-comment-delimiter"># </span><span class="org-comment">operating system and you don't need anything here</span>
363
-}
364
-
365
-<span class="org-keyword">function</span> <span class="org-function-name">backup_local_myappname</span> {
366
-    <span class="org-comment-delimiter"># </span><span class="org-comment">If your app has a MariaDB/MySQL database</span>
367
-    backup_database_to_usb myappname
368
-
369
-    <span class="org-comment-delimiter"># </span><span class="org-comment">To backup a directory</span>
370
-    backup_directory_to_usb $<span class="org-variable-name">MYAPP_DATA_DIR</span> myappname
371
-
372
-    <span class="org-comment-delimiter"># </span><span class="org-comment">if you need to backup data within individual user</span>
373
-    <span class="org-comment-delimiter"># </span><span class="org-comment">home directories</span>
374
-    <span class="org-keyword">for</span> d<span class="org-keyword"> in</span> /home/*/ ; <span class="org-keyword">do</span>
375
-        <span class="org-variable-name">USERNAME</span>=$(<span class="org-sh-quoted-exec">echo</span> <span class="org-string">"$d"</span> | awk -F <span class="org-string">'/'</span> <span class="org-string">'{print $3}'</span>)
376
-        <span class="org-keyword">if</span> [[ $(<span class="org-sh-quoted-exec">is_valid_user</span> <span class="org-string">"$USERNAME"</span>) == <span class="org-string">"1"</span> ]]; <span class="org-keyword">then</span>
377
-            <span class="org-builtin">echo</span> $<span class="org-string">"Backing up myappname config for $USERNAME"</span>
378
-            <span class="org-keyword">if</span> [ -d /home/$<span class="org-variable-name">USERNAME</span>/.config/myappname ]; <span class="org-keyword">then</span>
379
-                backup_directory_to_usb <span class="org-sh-escaped-newline">\</span>
380
-                    /home/$<span class="org-variable-name">USERNAME</span>/.config/myappname <span class="org-sh-escaped-newline">\</span>
381
-                    myappname_users/$<span class="org-variable-name">USERNAME</span>
382
-            <span class="org-keyword">fi</span>
383
-        <span class="org-keyword">fi</span>
384
-    <span class="org-keyword">done</span>
385
-}
386
-
387
-<span class="org-keyword">function</span> <span class="org-function-name">restore_local_myappname</span> {
388
-    <span class="org-variable-name">temp_restore_dir</span>=/root/tempmyappname
389
-
390
-    <span class="org-comment-delimiter"># </span><span class="org-comment">If your app has a MariaDB/MySQL database</span>
391
-    restore_database myappname
392
-
393
-    <span class="org-comment-delimiter"># </span><span class="org-comment">Restore some data from a directory</span>
394
-    <span class="org-comment-delimiter"># </span><span class="org-comment">Note that we don't restore directly but to a temporary</span>
395
-    <span class="org-comment-delimiter"># </span><span class="org-comment">directory and then copy the files. This ensures that if</span>
396
-    <span class="org-comment-delimiter"># </span><span class="org-comment">there is a restore failure you don't end up with</span>
397
-    <span class="org-comment-delimiter"># </span><span class="org-comment">half-copied or corrupted files</span>
398
-    restore_directory_from_usb $<span class="org-variable-name">MYAPP_DATA_DIR</span> myappname
399
-    cp -r $<span class="org-variable-name">temp_restore_dir</span>/$<span class="org-variable-name">MYAPP_DATA_DIR</span> $<span class="org-variable-name">MYAPP_DATA_DIR</span>
400
-    rm -rf $<span class="org-variable-name">temp_restore_dir</span>
401
-
402
-    <span class="org-comment-delimiter"># </span><span class="org-comment">If you need to restore a configuration directory for each user</span>
403
-    <span class="org-keyword">if</span> [ -d $<span class="org-variable-name">USB_MOUNT</span>/backup/myappname_users ]; <span class="org-keyword">then</span>
404
-        <span class="org-keyword">for</span> d<span class="org-keyword"> in</span> $<span class="org-variable-name">USB_MOUNT</span>/backup/myappname_users/*/ ; <span class="org-keyword">do</span>
405
-            <span class="org-variable-name">USERNAME</span>=$(<span class="org-sh-quoted-exec">echo</span> <span class="org-string">"$d"</span> | awk -F <span class="org-string">'/'</span> <span class="org-string">'{print $6}'</span>)
406
-            <span class="org-keyword">if</span> [[ $(<span class="org-sh-quoted-exec">is_valid_user</span> <span class="org-string">"$USERNAME"</span>) == <span class="org-string">"1"</span> ]]; <span class="org-keyword">then</span>
407
-                <span class="org-keyword">if</span> [ <span class="org-negation-char">!</span> -d /home/$<span class="org-variable-name">USERNAME</span> ]; <span class="org-keyword">then</span>
408
-                    ${<span class="org-variable-name">PROJECT_NAME</span>}-adduser $<span class="org-variable-name">USERNAME</span>
409
-                <span class="org-keyword">fi</span>
410
-                <span class="org-builtin">echo</span> $<span class="org-string">"Restoring Vim config for $USERNAME"</span>
411
-                function_check restore_directory_from_usb
412
-                restore_directory_from_usb $<span class="org-variable-name">temp_restore_dir</span> <span class="org-sh-escaped-newline">\</span>
413
-                                           myappname_users/$<span class="org-variable-name">USERNAME</span>
414
-                cp -r $<span class="org-variable-name">temp_restore_dir</span>/home/$<span class="org-variable-name">USERNAME</span>/.config <span class="org-sh-escaped-newline">\</span>
415
-                      /home/$<span class="org-variable-name">USERNAME</span>/
416
-                <span class="org-keyword">if</span> [ <span class="org-negation-char">!</span> <span class="org-string">"$?"</span> = <span class="org-string">"0"</span> ]; <span class="org-keyword">then</span>
417
-                    rm -rf $<span class="org-variable-name">temp_restore_dir</span>
418
-                    set_user_permissions
419
-                    backup_unmount_drive
420
-                    <span class="org-keyword">exit</span> 664
421
-                <span class="org-keyword">fi</span>
422
-                rm -rf $<span class="org-variable-name">temp_restore_dir</span>
423
-            <span class="org-keyword">fi</span>
424
-        <span class="org-keyword">done</span>
425
-    <span class="org-keyword">fi</span>
426
-}
427
-
428
-<span class="org-keyword">function</span> <span class="org-function-name">backup_remote_myappname</span> {
429
-    <span class="org-comment-delimiter"># </span><span class="org-comment">this should be the same as backup_local_myappname,</span>
430
-    <span class="org-comment-delimiter"># </span><span class="org-comment">but call the backup functions backup_directory_to_friend</span>
431
-    <span class="org-comment-delimiter"># </span><span class="org-comment">and backup_database_to_friend</span>
432
-}
433
-
434
-<span class="org-keyword">function</span> <span class="org-function-name">restore_remote_vim</span> {
435
-    <span class="org-comment-delimiter"># </span><span class="org-comment">this should be the same as restore_local_myappname,</span>
436
-    <span class="org-comment-delimiter"># </span><span class="org-comment">but call the restore function restore_directory_from_friend</span>
437
-    <span class="org-comment-delimiter"># </span><span class="org-comment">and restore_database_from_friend</span>
438
-}
439
-
440
-<span class="org-keyword">function</span> <span class="org-function-name">remove_myappname</span> {
441
-    <span class="org-comment-delimiter"># </span><span class="org-comment">if it's a debian package then:</span>
442
-    apt-get -y remove --purge [my-app-package-name]
443
-
444
-    <span class="org-comment-delimiter"># </span><span class="org-comment">If your app has a MariaDB/MySQL database</span>
445
-    drop_database myappname
446
-
447
-    <span class="org-comment-delimiter"># </span><span class="org-comment">If your app uses an onion address</span>
448
-    remove_onion_service myappname ${<span class="org-variable-name">MYAPPNAME_ONION_PORT</span>}
449
-}
450
-
451
-<span class="org-keyword">function</span> <span class="org-function-name">install_myappname</span> {
452
-    <span class="org-comment-delimiter"># </span><span class="org-comment">if it's a debian package then:</span>
453
-    apt-get -y install [my-app-package-name]
454
-
455
-    <span class="org-comment-delimiter"># </span><span class="org-comment">If you need to create a MariaDB/MySQL database for the app</span>
456
-    <span class="org-variable-name">MYAPPNAME_DB_PASSWORD</span>=<span class="org-string">"$(</span><span class="org-sh-quoted-exec">create_password</span><span class="org-string"> 20)"</span>
457
-    create_database myappname <span class="org-string">"$MYAPPNAME_DB_PASSWORD"</span> $<span class="org-variable-name">MY_USERNAME</span>
458
-
459
-    <span class="org-comment-delimiter"># </span><span class="org-comment">If you need to create an onion address for the app</span>
460
-    <span class="org-variable-name">MYAPPNAME_ONION_HOSTNAME</span>=$(<span class="org-sh-quoted-exec">add_onion_service</span> myappname <span class="org-sh-escaped-newline">\</span>
461
-                               80 ${<span class="org-variable-name">MYAPPNAME_ONION_PORT</span>})
308
+<pre class="src src-bash">freedombone-template --app [name] -e [email] -r [repo url] <span class="org-sh-escaped-newline">\</span>
309
+                     -c [commit] --dir /etc/myappname <span class="org-sh-escaped-newline">\</span>
310
+                     --daemon yes --portinternal 1234 &gt; <span class="org-sh-escaped-newline">\</span>
311
+                     src/freedombone-app-myappname
312
+</pre>
313
+</div>
462
 
314
 
463
-    <span class="org-comment-delimiter"># </span><span class="org-comment">Do any other configuration</span>
464
-    <span class="org-comment-delimiter"># </span><span class="org-comment">Here you might use $ONION_ONLY or</span>
465
-    <span class="org-comment-delimiter"># </span><span class="org-comment">$SOME_IMPORTANT_CONFIG_VARIABLE</span>
315
+<p>
316
+For an app without any database which communicates directly on a particular port through the firewall:
317
+</p>
466
 
318
 
467
-    <span class="org-comment-delimiter"># </span><span class="org-comment">Mark the app as having installed successfully</span>
468
-    <span class="org-comment-delimiter"># </span><span class="org-comment">If this variable isn't set then it will be assumed that</span>
469
-    <span class="org-comment-delimiter"># </span><span class="org-comment">the install has failed</span>
470
-    <span class="org-variable-name">APP_INSTALLED</span>=1
471
-}
319
+<div class="org-src-container">
320
+<pre class="src src-bash">freedombone-template --app [name] -e [email] -r [repo url] <span class="org-sh-escaped-newline">\</span>
321
+                     -c [commit] --dir /etc/myappname <span class="org-sh-escaped-newline">\</span>
322
+                     --daemon yes --port 5000 &gt; <span class="org-sh-escaped-newline">\</span>
323
+                     src/freedombone-app-myappname
324
+</pre>
325
+</div>
472
 
326
 
473
-<span class="org-keyword">function</span> <span class="org-function-name">install_interactive_myappname</span> {
474
-    <span class="org-comment-delimiter"># </span><span class="org-comment">Interactively obtain some values using dialog, such as</span>
475
-    <span class="org-comment-delimiter"># </span><span class="org-comment">domain names. An avatar changing example is:</span>
476
-    <span class="org-variable-name">data</span>=$(<span class="org-sh-quoted-exec">tempfile</span> 2&gt;/dev/null)
477
-    <span class="org-keyword">trap</span> <span class="org-string">"rm -f $data"</span> 0 1 2 5 15
478
-    dialog --title $<span class="org-string">"Change your avatar"</span> <span class="org-sh-escaped-newline">\</span>
479
-           --backtitle $<span class="org-string">"Freedombone Control Panel"</span> <span class="org-sh-escaped-newline">\</span>
480
-           --inputbox $<span class="org-string">"Enter a URL for an image. It should be "</span> <span class="org-sh-escaped-newline">\</span>
481
-                      $<span class="org-string">"approximately a square image."</span> 8 75 2&gt;$<span class="org-variable-name">data</span>
482
-    <span class="org-variable-name">sel</span>=$<span class="org-variable-name">?</span>
483
-    <span class="org-keyword">case</span> $<span class="org-variable-name">sel</span><span class="org-keyword"> in</span>
484
-        0)
485
-            <span class="org-variable-name">MY_FUNKY_AVATAR</span>=$(&lt;$<span class="org-variable-name">data</span>)
486
-            <span class="org-keyword">if</span> [ ${#<span class="org-variable-name">MY_FUNKY_AVATAR</span>} -gt 3 ]; <span class="org-keyword">then</span>
487
-                clear
488
-
489
-                <span class="org-comment-delimiter"># </span><span class="org-comment">do whatever is needed to change the avatar</span>
490
-                <span class="org-comment-delimiter"># </span><span class="org-comment">in your app</span>
491
-
492
-                dialog --title $<span class="org-string">"Change your avatar"</span> <span class="org-sh-escaped-newline">\</span>
493
-                       --msgbox $<span class="org-string">"Your avatar has been changed"</span> 6 40
494
-            <span class="org-keyword">fi</span>
495
-            ;;
496
-    <span class="org-keyword">esac</span>
497
-
498
-    <span class="org-comment-delimiter"># </span><span class="org-comment">install_myappname will be called automatically after this function</span>
499
-}
327
+<p>
328
+A generic PHP plus MySql/MariaDB web app which is only available on an onion address:
329
+</p>
500
 
330
 
501
-<span class="org-comment-delimiter"># </span><span class="org-comment">NOTE: deliberately no exit 0</span>
331
+<div class="org-src-container">
332
+<pre class="src src-bash">freedombone-template --app [name] -e [email] -r [repo url] <span class="org-sh-escaped-newline">\</span>
333
+                     -c [commit] --php yes -d mariadb <span class="org-sh-escaped-newline">\</span>
334
+                     --onion yes &gt; <span class="org-sh-escaped-newline">\</span>
335
+                     src/freedombone-app-myappname
502
 </pre>
336
 </pre>
503
 </div>
337
 </div>
504
 
338
 
505
 <p>
339
 <p>
506
-To test your app log into your system, select <b>Exit to command line</b> then gain root powers with:
340
+For more details see the manpage:
507
 </p>
341
 </p>
508
 
342
 
509
 <div class="org-src-container">
343
 <div class="org-src-container">
510
-<pre class="src src-bash">sudo su
344
+<pre class="src src-bash">man freedombone-template
511
 </pre>
345
 </pre>
512
 </div>
346
 </div>
513
 
347
 
514
 <p>
348
 <p>
515
-Copy your app script to <b>/usr/share/freedombone/apps/freedombone-app-myappname</b>.
349
+The template command won't give you a fully working app, but it will give you a big head start and avoid a lot of potential mistakes. It's highly likely that you'll still need to add extra configuration for your particular app, especially within the <b>install_app</b> function.
516
 </p>
350
 </p>
517
 
351
 
518
 <p>
352
 <p>
519
-And run the admin control panel:
353
+When your new script is ready for testing you can install it with:
520
 </p>
354
 </p>
521
 
355
 
522
 <div class="org-src-container">
356
 <div class="org-src-container">
523
-<pre class="src src-bash">control
357
+<pre class="src src-bash">make install
524
 </pre>
358
 </pre>
525
 </div>
359
 </div>
526
 
360
 
527
 <p>
361
 <p>
528
-Select <b>Add/Remove Apps</b> and if all is well then you should see your app listed as installable. Test that installing and removing it works as expected.
362
+Then run the administrator control panel and you should see the new app within <b>Add/Remove apps</b>.
529
 </p>
363
 </p>
530
 
364
 
531
 <p>
365
 <p>
532
-Submit your working app to <b><a href="https://github.com/bashrc/freedombone/issues">https://github.com/bashrc/freedombone/issues</a></b>
366
+Submit your working app to <b><a href="https://github.com/bashrc/freedombone/issues">https://github.com/bashrc/freedombone/issues</a></b> or create a pull request.
533
 </p>
367
 </p>
534
 </div>
368
 </div>
535
 </div>
369
 </div>
536
 
370
 
537
-<div id="outline-container-orge29cd52" class="outline-2">
538
-<h2 id="orge29cd52">Customising mesh images</h2>
539
-<div class="outline-text-2" id="text-orge29cd52">
371
+<div id="outline-container-orgaefd9da" class="outline-2">
372
+<h2 id="orgaefd9da">Customising mesh images</h2>
373
+<div class="outline-text-2" id="text-orgaefd9da">
540
 <p>
374
 <p>
541
 If you want to make your own specially branded version of the mesh images, such as for a particular event, then to change the default desktop backgrounds edit the images within <b>img/backgrounds</b> and to change the available avatars and desktop icons edit the images within <b>img/avatars</b>. Re-create disk images using the instructions shown previously.
375
 If you want to make your own specially branded version of the mesh images, such as for a particular event, then to change the default desktop backgrounds edit the images within <b>img/backgrounds</b> and to change the available avatars and desktop icons edit the images within <b>img/avatars</b>. Re-create disk images using the instructions shown previously.
542
 </p>
376
 </p>