diff --git a/Dockerfile b/Dockerfile index 926ab62..f5f83a8 100755 --- a/Dockerfile +++ b/Dockerfile @@ -18,19 +18,24 @@ VOLUME $POSTGRES_DATADIR VOLUME $POSTGRES_CONFIG # Install mariadb mysql database server -RUN apt-get update \ - && apt-get install -y mariadb-server wget sqlite3 apache2-utils curl \ - postgresql \ +RUN apt-get update -y \ + && apt-get install -y mariadb-server wget sqlite3 apache2-utils curl wget gnupg ca-certificates \ # Cleanup && apt-get autoremove -y \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* +RUN sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt bookworm-pgdg main" > /etc/apt/sources.list.d/postgresql.list' \ + && curl https://www.postgresql.org/media/keys/ACCC4CF8.asc \ + | gpg --dearmor \ + | tee /etc/apt/trusted.gpg.d/apt.postgresql.org.gpg >/dev/null \ + && apt-get update -y && apt-get install postgresql-14 -y + RUN wget https://github.com/mikefarah/yq/releases/download/v4.6.3/yq_linux_${PLATFORM}.tar.gz -O - |\ tar xz && mv yq_linux_${PLATFORM} /usr/bin/yq -ADD ./example.env /app/.env -ADD ./reset-admin.sh /usr/local/bin/reset-admin.sh +ADD ./utils/example.env /app/.env +ADD ./utils/scripts/reset-admin.sh /usr/local/bin/reset-admin.sh ADD ./docker_entrypoint.sh /usr/local/bin/docker_entrypoint.sh -ADD ./migration-from-lt-2-3-9.sh /usr/local/bin/migration-from-lt-2-3-9.sh +ADD ./utils/scripts/migration-from-lt-2-3-9.sh /usr/local/bin/migration-from-lt-2-3-9.sh RUN chmod a+x /usr/local/bin/*.sh diff --git a/Makefile b/Makefile index 9cdd972..870bb2a 100755 --- a/Makefile +++ b/Makefile @@ -1,6 +1,8 @@ PKG_ID := $(shell yq e ".id" manifest.yaml) PKG_VERSION := $(shell yq e ".version" manifest.yaml) TS_FILES := $(shell find ./ -name \*.ts) +UTILS_SRC := $(shell find ./utils/**/*) +ASSET_PATHS := $(shell find ./assets/*) # delete the target of a rule if it has changed and its recipe exits with a nonzero exit status .DELETE_ON_ERROR: @@ -22,14 +24,14 @@ clean: scripts/embassy.js: $(TS_FILES) deno bundle scripts/embassy.ts scripts/embassy.js -docker-images/x86_64.tar: Dockerfile docker_entrypoint.sh example.env +docker-images/x86_64.tar: Dockerfile docker_entrypoint.sh $(UTILS_SRC) mkdir -p docker-images docker buildx build --tag start9/$(PKG_ID)/main:$(PKG_VERSION) --platform=linux/amd64 --build-arg PLATFORM=amd64 -o type=docker,dest=docker-images/x86_64.tar . -docker-images/aarch64.tar: Dockerfile docker_entrypoint.sh example.env +docker-images/aarch64.tar: Dockerfile docker_entrypoint.sh $(UTILS_SRC) mkdir -p docker-images docker buildx build --tag start9/$(PKG_ID)/main:$(PKG_VERSION) --platform=linux/arm64 --build-arg PLATFORM=arm64 -o type=docker,dest=docker-images/aarch64.tar . -$(PKG_ID).s9pk: manifest.yaml instructions.md LICENSE icon.png scripts/embassy.js docker-images/aarch64.tar docker-images/x86_64.tar +$(PKG_ID).s9pk: manifest.yaml instructions.md LICENSE icon.png scripts/embassy.js docker-images/aarch64.tar docker-images/x86_64.tar $(ASSET_PATHS) if ! [ -z "$(ARCH)" ]; then cp docker-images/$(ARCH).tar image.tar; fi embassy-sdk pack diff --git a/README.md b/README.md index b0085fc..0e36248 100755 --- a/README.md +++ b/README.md @@ -92,7 +92,7 @@ If you already have your `embassy-cli` config file setup with a default `host`, make install ``` -> **Tip:** You can also install the photoview.s9pk using **Sideload Service** under the **Embassy > Settings** section. +> **Tip:** You can also install the photoview.s9pk using **Sideload Service** under the **System > Manage** section. ### Verify Install Go to your Embassy Services page, select **Photoview**, and start the service. Then, verify its interfaces are accessible. diff --git a/assets/compat/backup-x.sh b/assets/compat/backup-x.sh new file mode 100755 index 0000000..17e8996 --- /dev/null +++ b/assets/compat/backup-x.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +set -ea + +case "$1" in + backup-create) + mkdir -p /mnt/backup/media + compat duplicity create /mnt/backup/media /media + + mkdir -p /mnt/backup/database + compat duplicity create /mnt/backup/database /var/lib/postgresql/14 + + mkdir -p /mnt/backup/dbconfig + compat duplicity create /mnt/backup/dbconfig /etc/postgresql/14 + ;; + backup-restore) + compat duplicity restore /mnt/backup/media /media + compat duplicity restore /mnt/backup/database /var/lib/postgresql/14 + compat duplicity restore /mnt/backup/dbconfig /etc/postgresql/14 + ;; + *) +esac + +exit 0 \ No newline at end of file diff --git a/docker_entrypoint.sh b/docker_entrypoint.sh index df4e282..3a0b370 100755 --- a/docker_entrypoint.sh +++ b/docker_entrypoint.sh @@ -7,18 +7,31 @@ _term() { kill -TERM "$photoview_child" 2>/dev/null } -# set permissions for postgres folders -chown -R postgres:postgres $POSTGRES_DATADIR -chown -R postgres:postgres $POSTGRES_CONFIG -chmod -R 700 $POSTGRES_DATADIR -chmod -R 700 $POSTGRES_CONFIG -mkdir -p /media/start9 -service postgresql start +if test -f /etc/postgresql/14/photoview/postgresql.conf +then + # restart + echo "postgresql already initialized" + echo "starting postgresql..." + service postgresql start +else + # fresh install + echo 'setting up postgresql...' + # set permissions for postgres folders + chown -R postgres:postgres $POSTGRES_DATADIR + chown -R postgres:postgres $POSTGRES_CONFIG + chmod -R 700 $POSTGRES_DATADIR + chmod -R 700 $POSTGRES_CONFIG + mkdir -p /media/start9 + su - postgres -c "pg_createcluster 14 photoview" + echo "starting postgresql..." + service postgresql start +fi + echo 'checking for existing admin user...' - export USERS=$(sqlite3 $PHOTOVIEW_SQLITE_PATH 'select * from users;') - export NEW_USERS=$(su - postgres -c 'psql -d '$POSTGRES_DB' -c "select * from users"') - sleep 1 +export USERS=$(sqlite3 $PHOTOVIEW_SQLITE_PATH 'select * from users;') +export NEW_USERS=$(su - postgres -c 'psql -d '$POSTGRES_DB' -c "select * from users"') +sleep 1 if [ -f /media/start9/config.yaml ] && ! [ -z "$NEW_USERS" ]; then echo 'loading existing admin credentials...' @@ -64,10 +77,6 @@ if [ -z "$NEW_USERS" ]; then fi echo 'applying database permissions...' - su - postgres -c "pg_createcluster 14 photoview" - service postgresql start - sleep 5 - su - postgres -c 'psql -c "UPDATE pg_database SET datistemplate = FALSE WHERE datname = '"'"template1"'"';"' su - postgres -c 'psql -c "DROP DATABASE template1;"' su - postgres -c 'psql -c "CREATE DATABASE template1 WITH TEMPLATE = template0 ENCODING = '"'"UTF8"'"';"' diff --git a/manifest.yaml b/manifest.yaml index 0b084bb..85af5c2 100644 --- a/manifest.yaml +++ b/manifest.yaml @@ -1,7 +1,10 @@ id: photoview title: Photoview version: 2.3.13.3 -release-notes: Update to run on x86 architecture +release-notes: | + * Update to run on x86 architecture + * Fix reset admin user action + * Fix backups license: gnu wrapper-repo: "https://github.com/Start9Labs/embassyos-photoview-wrapper" upstream-repo: "https://github.com/photoview/photoview/tree/master" @@ -74,6 +77,8 @@ volumes: type: data dbconfig: type: data + compat: + type: assets alerts: {} interfaces: main: @@ -95,34 +100,34 @@ backup: type: docker image: compat system: true - entrypoint: compat - args: - - duplicity - - create - - /mnt/backup - - /media + entrypoint: /mnt/assets/backup-x.sh + args: + - backup-create mounts: BACKUP: /mnt/backup main: /media + db: /var/lib/postgresql/14 + dbconfig: /etc/postgresql/14 + compat: /mnt/assets io-format: yaml restore: type: docker image: compat system: true - entrypoint: compat - args: - - duplicity - - restore - - /mnt/backup - - /media + entrypoint: /mnt/assets/backup-x.sh + args: + - backup-restore mounts: BACKUP: /mnt/backup main: /media + db: /var/lib/postgresql/14 + dbconfig: /etc/postgresql/14 + compat: /mnt/assets io-format: yaml actions: reset-admin: - name: Reset Root User - description: Resets your root user (the first user) to username "admin" and a random password; restores any lost admin privileges. + name: Reset Admin User + description: Restores lost admin privileges by resetting your root user with the username "admin" and a random password. warning: This will invalidate existing sessions and password managers if you have them set up. allowed-statuses: - stopped @@ -134,4 +139,6 @@ actions: args: [] mounts: main: /media + db: /var/lib/postgresql/14 + dbconfig: /etc/postgresql/14 io-format: json diff --git a/scripts/procedures/health.ts b/scripts/procedures/health.ts index 4810a45..ff4c684 100644 --- a/scripts/procedures/health.ts +++ b/scripts/procedures/health.ts @@ -1,4 +1,4 @@ -import { types as T, checkWebUrl, catchError } from "../deps.ts"; +import { types as T, checkWebUrl, catchError, ok, isKnownError, errorCode, error } from "../deps.ts"; export const health: T.ExpectedExports.health = { // deno-lint-ignore require-await @@ -13,15 +13,32 @@ export const health: T.ExpectedExports.health = { }, }; -// deno-lint-ignore require-await const healthWeb: T.ExpectedExports.health[""] = async (effects, duration) => { - return checkWebUrl("http://photoview.embassy")(effects, duration).catch(catchError(effects)) + const url = 'http://photoview.embassy' + let errorValue + if ( + // deno-lint-ignore no-cond-assign + errorValue = guardDurationAboveMinimum({ duration, minimumTime: 20000 }) + ) return errorValue + + return await effects.fetch(url) + .then((_) => ok) + .catch((e) => { + effects.warn(`Error while fetching URL: ${url}`); + effects.error(JSON.stringify(e)); + effects.error(e.toString()); + return error(`Error while fetching URL: ${url}`); + }); }; const healthApi: T.ExpectedExports.health[""] = async (effects, duration) => { - await guardDurationAboveMinimum({ duration, minimumTime: 15000 }); + let errorValue + if ( + // deno-lint-ignore no-cond-assign + errorValue = guardDurationAboveMinimum({ duration, minimumTime: 20000 }) + ) return errorValue - return effects.fetch("http://photoview.embassy:80/api/graphql", { + return await effects.fetch("http://photoview.embassy:80/api/graphql", { method: 'POST', headers: { 'Content-Type': 'application/json' @@ -37,16 +54,13 @@ const healthApi: T.ExpectedExports.health[""] = async (effects, duration) => { // *** HELPER FUNCTIONS *** // -// Ensure the starting duration is pass a minimum -const guardDurationAboveMinimum = ( +// Ensure the starting duration is past a minimum +export const guardDurationAboveMinimum = ( input: { duration: number; minimumTime: number }, -) => - (input.duration <= input.minimumTime) - ? Promise.reject(errorCode(60, "Starting")) - : null; - -const errorCode = (code: number, error: string) => ({ - "error-code": [code, error] as const, -}); -const error = (error: string) => ({ error }); -const ok = { result: null }; +) => (input.duration <= input.minimumTime) ? errorCode(60, "Starting") : null; + +export const catchError_ = (effects: T.Effects) => (e: unknown) => { + if (isKnownError(e)) return e; + effects.error(`Health check failed: ${e}`); + return error("Error while running health check"); +} diff --git a/select * from users; b/select * from users; deleted file mode 100644 index e69de29..0000000 diff --git a/example.env b/utils/example.env similarity index 100% rename from example.env rename to utils/example.env diff --git a/migration-from-lt-2-3-9.sh b/utils/scripts/migration-from-lt-2-3-9.sh similarity index 100% rename from migration-from-lt-2-3-9.sh rename to utils/scripts/migration-from-lt-2-3-9.sh diff --git a/reset-admin.sh b/utils/scripts/reset-admin.sh similarity index 62% rename from reset-admin.sh rename to utils/scripts/reset-admin.sh index 55ee9dc..1a7b128 100644 --- a/reset-admin.sh +++ b/utils/scripts/reset-admin.sh @@ -1,7 +1,7 @@ #!/bin/bash # ensure start9 directory exists if action is run before first run of service -mkdir /media/start9 +mkdir -p /media/start9 export PASS=$(cat /dev/urandom | base64 | head -c 16) echo 'version: 2' > /media/start9/stats.yaml echo 'data:' >> /media/start9/stats.yaml @@ -21,17 +21,21 @@ echo ' masked: true' >> /media/start9/stats.yaml echo ' qr: false' >> /media/start9/stats.yaml export PASS_HASH=$(htpasswd -bnBC 12 "" $PASS | tr -d ':\n' | sed 's/$2y/$2a/') -export PHOTOVIEW_SQLITE_PATH="/media/photoview.db" -USERS=$(sqlite3 $PHOTOVIEW_SQLITE_PATH "select * from users where id = 1;") +service postgresql start &>/dev/null +USERS=$(su - postgres -c 'psql -d '$POSTGRES_DB' -c "select * from users"') if [ -z $USERS ]; then - sqlite3 $PHOTOVIEW_SQLITE_PATH "insert into users (id, created_at, updated_at, username, password, admin) values (1, datetime('now'), datetime('now'), 'admin', '$PASS_HASH', true);" + PASS_HASH=$(htpasswd -bnBC 12 "" $PASS | tr -d ':\n' | sed 's/$2y/$2a/') PATH_MD5=$(echo -n /mnt/filebrowser | md5sum | head -c 32) - sqlite3 $PHOTOVIEW_SQLITE_PATH "insert or ignore into albums (id, created_at, updated_at, title, parent_album_id, path, path_hash) values (1, datetime('now'), datetime('now'), 'filebrowser', NULL, '/mnt/filebrowser', '$PATH_MD5');" - sqlite3 $PHOTOVIEW_SQLITE_PATH "insert or ignore into user_albums (album_id, user_id) values (1,1);" - sqlite3 $PHOTOVIEW_SQLITE_PATH "update site_info set initial_setup = false;" + USER_INSERT="INSERT INTO users (id, created_at, updated_at, username, password, admin) VALUES (21, current_timestamp, current_timestamp, 'admin', '$PASS_HASH', true);" + ALBUM_INSERT="INSERT INTO albums (id, created_at, updated_at, title, parent_album_id, path, path_hash) VALUES (21, current_timestamp, current_timestamp, 'filebrowser', NULL, '/mnt/filebrowser', '$PATH_MD5');" + JOIN_INSERT="INSERT INTO user_albums (album_id, user_id) VALUES (21, 21);" + INFO_UPDATE="update site_info set initial_setup = false;" + echo "begin; $USER_INSERT $ALBUM_INSERT $JOIN_INSERT $INFO_UPDATE commit;" | su - postgres -c "psql -d '$POSTGRES_DB'" &>/dev/null fi -sqlite3 $PHOTOVIEW_SQLITE_PATH "update users set password = '$PASS_HASH', username = 'admin' where id = 1;" +SET_PW="UPDATE users SET password = '$PASS_HASH', username = 'admin' WHERE id = 21;" +echo $SET_PW | su - postgres -c "psql -d '$POSTGRES_DB'" &>/dev/null +service postgresql stop &>/dev/null action_result=" { \"version\": \"0\", \"message\": \"Here is your new password. This will also be reflected in the Properties page for this service.\",