From 1eec46494c68a499da4bc8e7ecc7fc990d913042 Mon Sep 17 00:00:00 2001 From: Nicolas Meienberger Date: Tue, 17 May 2022 18:54:04 +0200 Subject: [PATCH] Provide host root folder to mount docker volumes --- apps/nextcloud/docker-compose.yml | 2 +- packages/system-api/src/config/config.ts | 4 +++- .../apps/__tests__/apps.service.test.ts | 12 ++++++------ .../src/modules/apps/apps.helpers.ts | 3 ++- scripts/app.sh | 18 +++++++++++++++--- scripts/start.sh | 10 ++++++++++ scripts/system-info.sh | 2 +- templates/env-sample | 1 + 8 files changed, 39 insertions(+), 13 deletions(-) diff --git a/apps/nextcloud/docker-compose.yml b/apps/nextcloud/docker-compose.yml index 77bef418..df53e160 100644 --- a/apps/nextcloud/docker-compose.yml +++ b/apps/nextcloud/docker-compose.yml @@ -52,7 +52,7 @@ services: - POSTGRES_DB=nextcloud - NEXTCLOUD_ADMIN_USER=${NEXTCLOUD_ADMIN_USER} - NEXTCLOUD_ADMIN_PASSWORD=${NEXTCLOUD_ADMIN_PASSWORD} - - NEXTCLOUD_TRUSTED_DOMAINS=${DEVICE_IP}:${APP_PORT} + - NEXTCLOUD_TRUSTED_DOMAINS=${INTERNAL_IP}:${APP_PORT} depends_on: - db-nextcloud - redis-nextcloud diff --git a/packages/system-api/src/config/config.ts b/packages/system-api/src/config/config.ts index 157916c7..8737c3e7 100644 --- a/packages/system-api/src/config/config.ts +++ b/packages/system-api/src/config/config.ts @@ -6,11 +6,12 @@ interface IConfig { JWT_SECRET: string; CLIENT_URLS: string[]; VERSION: string; + ROOT_FOLDER_HOST: string; } dotenv.config(); -const { NODE_ENV = 'development', JWT_SECRET = '', INTERNAL_IP = '', TIPI_VERSION = '' } = process.env; +const { NODE_ENV = 'development', JWT_SECRET = '', INTERNAL_IP = '', TIPI_VERSION = '', ROOT_FOLDER_HOST = '' } = process.env; const config: IConfig = { NODE_ENV, @@ -18,6 +19,7 @@ const config: IConfig = { JWT_SECRET, CLIENT_URLS: ['http://localhost:3000', `http://${INTERNAL_IP}`, `http://${INTERNAL_IP}:3000`], VERSION: TIPI_VERSION, + ROOT_FOLDER_HOST, }; export default config; diff --git a/packages/system-api/src/modules/apps/__tests__/apps.service.test.ts b/packages/system-api/src/modules/apps/__tests__/apps.service.test.ts index 811ad45f..3636e6d3 100644 --- a/packages/system-api/src/modules/apps/__tests__/apps.service.test.ts +++ b/packages/system-api/src/modules/apps/__tests__/apps.service.test.ts @@ -84,7 +84,7 @@ describe('Install app', () => { await AppsService.installApp('test-app', { test: 'test' }); - expect(spy.mock.lastCall).toEqual([`${config.ROOT_FOLDER}/scripts/app.sh`, ['install', 'test-app'], {}, expect.any(Function)]); + expect(spy.mock.lastCall).toEqual([`${config.ROOT_FOLDER}/scripts/app.sh`, ['install', 'test-app', '/tipi'], {}, expect.any(Function)]); spy.mockRestore(); }); @@ -96,8 +96,8 @@ describe('Install app', () => { await AppsService.installApp('test-app', { test: 'test' }); expect(spy.mock.calls.length).toBe(2); - expect(spy.mock.calls[0]).toEqual([`${config.ROOT_FOLDER}/scripts/app.sh`, ['install', 'test-app'], {}, expect.any(Function)]); - expect(spy.mock.calls[1]).toEqual([`${config.ROOT_FOLDER}/scripts/app.sh`, ['start', 'test-app'], {}, expect.any(Function)]); + expect(spy.mock.calls[0]).toEqual([`${config.ROOT_FOLDER}/scripts/app.sh`, ['install', 'test-app', '/tipi'], {}, expect.any(Function)]); + expect(spy.mock.calls[1]).toEqual([`${config.ROOT_FOLDER}/scripts/app.sh`, ['start', 'test-app', '/tipi'], {}, expect.any(Function)]); spy.mockRestore(); }); @@ -126,7 +126,7 @@ describe('Uninstall app', () => { await AppsService.uninstallApp('test-app'); - expect(spy.mock.lastCall).toEqual([`${config.ROOT_FOLDER}/scripts/app.sh`, ['uninstall', 'test-app'], {}, expect.any(Function)]); + expect(spy.mock.lastCall).toEqual([`${config.ROOT_FOLDER}/scripts/app.sh`, ['uninstall', 'test-app', '/tipi'], {}, expect.any(Function)]); spy.mockRestore(); }); @@ -147,7 +147,7 @@ describe('Start app', () => { await AppsService.startApp('test-app'); - expect(spy.mock.lastCall).toEqual([`${config.ROOT_FOLDER}/scripts/app.sh`, ['start', 'test-app'], {}, expect.any(Function)]); + expect(spy.mock.lastCall).toEqual([`${config.ROOT_FOLDER}/scripts/app.sh`, ['start', 'test-app', '/tipi'], {}, expect.any(Function)]); spy.mockRestore(); }); @@ -193,7 +193,7 @@ describe('Stop app', () => { await AppsService.stopApp('test-app'); - expect(spy.mock.lastCall).toEqual([`${config.ROOT_FOLDER}/scripts/app.sh`, ['stop', 'test-app'], {}, expect.any(Function)]); + expect(spy.mock.lastCall).toEqual([`${config.ROOT_FOLDER}/scripts/app.sh`, ['stop', 'test-app', '/tipi'], {}, expect.any(Function)]); }); it('Should throw if app is not installed', async () => { diff --git a/packages/system-api/src/modules/apps/apps.helpers.ts b/packages/system-api/src/modules/apps/apps.helpers.ts index 05da79d8..ff4420e8 100644 --- a/packages/system-api/src/modules/apps/apps.helpers.ts +++ b/packages/system-api/src/modules/apps/apps.helpers.ts @@ -3,6 +3,7 @@ import p from 'p-iteration'; import { AppConfig } from '../../config/types'; import { fileExists, readdirSync, readFile, readJsonFile, runScript, writeFile } from '../fs/fs.helpers'; import InternalIp from 'internal-ip'; +import config from '../../config'; type AppsState = { installed: string }; @@ -76,7 +77,7 @@ export const checkAppExists = (appName: string) => { export const runAppScript = (params: string[]): Promise => { return new Promise((resolve, reject) => { - runScript('/scripts/app.sh', params, (err: string) => { + runScript('/scripts/app.sh', [...params, config.ROOT_FOLDER_HOST], (err: string) => { if (err) { reject(err); } diff --git a/scripts/app.sh b/scripts/app.sh index ee284c4c..e10f4302 100755 --- a/scripts/app.sh +++ b/scripts/app.sh @@ -60,6 +60,7 @@ if [ -z ${2+x} ]; then exit 1 else app="$2" + root_folder_host="$3" app_dir="${ROOT_FOLDER}/apps/${app}" app_data_dir="${ROOT_FOLDER}/app-data/${app}" @@ -67,6 +68,11 @@ else echo "Error: \"${app}\" is not a valid app" exit 1 fi + + if [[ -z "${root_folder_host}" ]]; then + echo "Error: Root folder not provided" + exit 1 + fi fi if [ -z ${3+x} ]; then @@ -98,7 +104,7 @@ compose() { local app_dir="${ROOT_FOLDER}/apps/${app}" # Vars to use in compose file - export APP_DATA_DIR="${app_data_dir}" + export APP_DATA_DIR="${root_folder_host}/app-data/${app}" export APP_DIR="${app_dir}" export ROOT_FOLDER="${ROOT_FOLDER}" @@ -123,6 +129,8 @@ if [[ "$command" = "install" ]]; then cp -r "${ROOT_FOLDER}/apps/${app}/data" "${app_data_dir}/data" fi + chown -R "1000:1000" "${app_data_dir}" + compose "${app}" up -d exit fi @@ -130,11 +138,12 @@ fi # Removes images and destroys all data for an app if [[ "$command" = "uninstall" ]]; then echo "Removing images for app ${app}..." - compose "${app}" down --remove-orphans + + # compose "${app}" down --remove-orphans echo "Deleting app data for app ${app}..." if [[ -d "${app_data_dir}" ]]; then - sudo rm -rf "${app_data_dir}" + rm -rf "${app_data_dir}" fi echo "Successfully uninstalled app ${app}" @@ -145,6 +154,7 @@ fi if [[ "$command" = "stop" ]]; then echo "Stopping app ${app}..." + compose "${app}" down --remove-orphans --rmi all compose "${app}" rm --force --stop exit @@ -153,6 +163,8 @@ fi # Starts an installed app if [[ "$command" = "start" ]]; then echo "Starting app ${app}..." + compose "${app}" pull + compose "${app}" up --detach exit diff --git a/scripts/start.sh b/scripts/start.sh index aa3c196d..9015872c 100755 --- a/scripts/start.sh +++ b/scripts/start.sh @@ -14,6 +14,11 @@ SED_ROOT_FOLDER="$(echo $ROOT_FOLDER | sed 's/\//\\\//g')" INTERNAL_IP="$(hostname -I | awk '{print $1}')" DNS_IP=9.9.9.9 # Default to Quad9 DNS USERNAME="$(id -nu 1000)" +ARCHITECTURE="$(uname -m)" + +if [[ "$architecture" == "aarch64" ]]; then + ARCHITECTURE="arm64" +fi if [[ $UID != 0 ]]; then echo "Tipi must be started as root" @@ -106,6 +111,8 @@ for template in "${ENV_FILE}"; do sed -i "s//${JWT_SECRET}/g" "${template}" sed -i "s//${SED_ROOT_FOLDER}/g" "${template}" sed -i "s//$(cat "${ROOT_FOLDER}/VERSION")/g" "${template}" + sed -i "s//${ARCHITECTURE}/g" "${template}" + done mv -f "$ENV_FILE" "$ROOT_FOLDER/.env" @@ -129,6 +136,9 @@ apps_to_start=($str) # "${ROOT_FOLDER}/scripts/app.sh" start $app # done +# Give permissions 1000:1000 to app data +chown -R 1000:1000 "${ROOT_FOLDER}/app-data" + echo "Tipi is now running" echo "" cat << "EOF" diff --git a/scripts/system-info.sh b/scripts/system-info.sh index 1aac03a2..5d8737d4 100755 --- a/scripts/system-info.sh +++ b/scripts/system-info.sh @@ -19,7 +19,7 @@ MEM_USED_BYTES=$(($MEM_TOTAL_BYTES - $MEM_AVAILABLE_BYTES)) # Create temporary json file TEMP_JSON_FILE=$(mktemp) -echo '{ "cpu": { "load": '"${CPU_LOAD_PERCENTAGE}"' }, "memory": { "total": '"${MEM_TOTAL_BYTES}"' , "used": '"${MEM_USED_BYTES}"', "available": '"${MEM_FREE_BYTES}"' }, "disk": { "total": '"${TOTAL_DISK_SPACE_BYTES}"' , "used": '"${USED_DISK_SPACE_BYTES}"', "available": '"${AVAILABLE_DISK_SPACE_BYTES}"' } }' > "${TEMP_JSON_FILE}" +echo '{ "cpu": { "load": '"${CPU_LOAD_PERCENTAGE}"' }, "memory": { "total": '"${MEM_TOTAL_BYTES}"' , "used": '"${MEM_USED_BYTES}"', "available": '"${MEM_AVAILABLE_BYTES}"' }, "disk": { "total": '"${TOTAL_DISK_SPACE_BYTES}"' , "used": '"${USED_DISK_SPACE_BYTES}"', "available": '"${AVAILABLE_DISK_SPACE_BYTES}"' } }' > "${TEMP_JSON_FILE}" # Write to state file echo "$(cat "${TEMP_JSON_FILE}")" > "${STATE_FOLDER}/system-info.json" diff --git a/templates/env-sample b/templates/env-sample index a28e16d4..2441dd7c 100644 --- a/templates/env-sample +++ b/templates/env-sample @@ -9,3 +9,4 @@ DNS_IP= ARCHITECTURE= TIPI_VERSION= JWT_SECRET= +ROOT_FOLDER_HOST=