diff --git a/.gitignore b/.gitignore index 655ca753d..a10821349 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ *.log *.db *.db-journal +docker-compose.override.yml # Binaries for programs and plugins *.exe diff --git a/Dockerfile b/Dockerfile index af23050e2..2cb716f20 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM photoprism/development:20200825 +FROM photoprism/development:20201004 # Set up project directory WORKDIR "/go/src/github.com/photoprism/photoprism" diff --git a/docker-compose.yml b/docker-compose.yml index 8d485a27e..fc02884c7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -20,6 +20,8 @@ services: security_opt: - seccomp:unconfined - apparmor:unconfined + # Uncomment and edit the following line to set a specific user / group id: + # user: "1000:1000" depends_on: - photoprism-db ports: diff --git a/docker/development/Dockerfile b/docker/development/Dockerfile index 12cedb5b9..701869762 100644 --- a/docker/development/Dockerfile +++ b/docker/development/Dockerfile @@ -57,7 +57,8 @@ RUN apt-get update && apt-get upgrade && \ apache2-utils \ seccomp \ libseccomp-dev \ - libseccomp2 + libseccomp2 \ + fonts-roboto # Install Chromium Browser via .deb RUN add-apt-repository -y ppa:xalt7x/chromium-deb-vaapi @@ -91,18 +92,21 @@ RUN apt-get update && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* +# Set default https://en.wikipedia.org/wiki/Umask +RUN umask 0000 + # Install and configure NodeJS Package Manager (npm) ENV NODE_ENV production RUN npm install --unsafe-perm=true --allow-root -g npm testcafe chromedriver RUN npm config set cache ~/.cache/npm # Install Go -ENV GOLANG_VERSION 1.15 +ENV GOLANG_VERSION 1.15.2 RUN set -eux; \ \ url="https://golang.org/dl/go${GOLANG_VERSION}.linux-amd64.tar.gz"; \ wget -O go.tgz "$url"; \ - echo "2d75848ac606061efe52a8068d0e647b35ce487a15bb52272c427df485193602 *go.tgz" | sha256sum -c -; \ + echo "b49fda1ca29a1946d6bb2a5a6982cf07ccd2aba849289508ee0f9918f6bb4552 *go.tgz" | sha256sum -c -; \ tar -C /usr/local -xzf go.tgz; \ rm go.tgz; \ export PATH="/usr/local/go/bin:$PATH"; \ @@ -112,24 +116,36 @@ RUN set -eux; \ ENV GOPATH /go ENV GOBIN $GOPATH/bin ENV PATH $GOBIN:/usr/local/go/bin:/root/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin +ENV TMPDIR /tmp ENV GO111MODULE on RUN mkdir -p "$GOPATH/src" "$GOPATH/bin" && chmod -R 777 "$GOPATH" # Download TensorFlow model and test files -RUN rm -rf /tmp/* && mkdir -p /tmp/photoprism -RUN wget "https://dl.photoprism.org/tensorflow/nsfw.zip?${BUILD_TAG}" -O /tmp/photoprism/nsfw.zip -RUN wget "https://dl.photoprism.org/tensorflow/nasnet.zip?${BUILD_TAG}" -O /tmp/photoprism/nasnet.zip -RUN wget "https://dl.photoprism.org/fixtures/testdata.zip?${BUILD_TAG}" -O /tmp/photoprism/testdata.zip +RUN rm -rf /tmp/* && mkdir -p /tmp/photoprism && \ + wget "https://dl.photoprism.org/tensorflow/nsfw.zip?${BUILD_TAG}" -O /tmp/photoprism/nsfw.zip && \ + wget "https://dl.photoprism.org/tensorflow/nasnet.zip?${BUILD_TAG}" -O /tmp/photoprism/nasnet.zip && \ + wget "https://dl.photoprism.org/fixtures/testdata.zip?${BUILD_TAG}" -O /tmp/photoprism/testdata.zip # Install additional tools for development -RUN env GO111MODULE=off /usr/local/go/bin/go get -u github.com/psampaz/go-mod-outdated -RUN env GO111MODULE=off /usr/local/go/bin/go get -u golang.org/x/tools/cmd/goimports -RUN env GO111MODULE=off /usr/local/go/bin/go get -u github.com/tsliwowicz/go-wrk -RUN env GO111MODULE=off /usr/local/go/bin/go get -u github.com/kyoh86/richgo -RUN echo "alias go=richgo" > /root/.bash_aliases +RUN env GO111MODULE=off /usr/local/go/bin/go get -u github.com/psampaz/go-mod-outdated && \ + env GO111MODULE=off /usr/local/go/bin/go get -u golang.org/x/tools/cmd/goimports && \ + env GO111MODULE=off /usr/local/go/bin/go get -u github.com/tsliwowicz/go-wrk && \ + env GO111MODULE=off /usr/local/go/bin/go get -u github.com/kyoh86/richgo && \ + env GO111MODULE=off /usr/local/go/bin/go get -u github.com/tianon/gosu && \ + echo "alias go=richgo" > /root/.bash_aliases -# MariaDB test database settings -COPY /docker/development/.my.cnf /root/.my.cnf +# Create photoprism user and directory for deployment +RUN useradd photoprism -m -d /photoprism && \ + chmod 777 /photoprism && \ + echo "alias go=richgo" > /photoprism/.bash_aliases && \ + chown -R photoprism:photoprism /photoprism && \ + find /go -type d -print0 | xargs -0 chmod 777 && \ + find /go -type f -print0 | xargs -0 chmod a+rw + +# Copy mysql client config for development +COPY --chown=root:root /docker/development/.my.cnf /root/.my.cnf +COPY --chown=photoprism:photoprism /docker/development/.my.cnf /photoprism/.my.cnf +RUN chmod 644 /root/.my.cnf /photoprism/.my.cnf # Set up project directory WORKDIR "/go/src/github.com/photoprism/photoprism" diff --git a/docker/photoprism/Dockerfile b/docker/photoprism/Dockerfile index 5817ce6db..800cca226 100644 --- a/docker/photoprism/Dockerfile +++ b/docker/photoprism/Dockerfile @@ -1,4 +1,4 @@ -FROM photoprism/development:20200825 as build +FROM photoprism/development:20201004 as build # Set up project directory WORKDIR "/go/src/github.com/photoprism/photoprism" @@ -36,6 +36,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ ffmpeg # Copy dependencies +COPY --from=build /go/bin/gosu /bin/gosu COPY --from=build /usr/lib/libtensorflow.so /usr/lib/libtensorflow.so COPY --from=build /usr/lib/libtensorflow_framework.so /usr/lib/libtensorflow_framework.so RUN ldconfig @@ -50,12 +51,16 @@ RUN sh -c "echo 'deb http://download.opensuse.org/repositories/graphics:/darktab apt-get clean && \ rm -rf /var/lib/apt/lists/* +# Set default umask and create photoprism user +RUN umask 0000 && useradd photoprism -m -d /photoprism WORKDIR /photoprism ENV TF_CPP_MIN_LOG_LEVEL 2 ENV PATH /photoprism/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin +ENV TMPDIR /tmp +# Storage path names ENV PHOTOPRISM_ASSETS_PATH /photoprism/assets ENV PHOTOPRISM_ORIGINALS_PATH /photoprism/originals ENV PHOTOPRISM_IMPORT_PATH /photoprism/import @@ -63,6 +68,33 @@ ENV PHOTOPRISM_STORAGE_PATH /photoprism/storage ENV PHOTOPRISM_LOG_FILENAME /photoprism/photoprism.log ENV PHOTOPRISM_PID_FILENAME /photoprism/photoprism.pid +# Defaults for common config values +# See https://docs.photoprism.org/getting-started/config-options/ +ENV PHOTOPRISM_DEBUG "false" +ENV PHOTOPRISM_PUBLIC "false" +ENV PHOTOPRISM_READONLY "false" +ENV PHOTOPRISM_UPLOAD_NSFW "true" +ENV PHOTOPRISM_DETECT_NSFW "false" +ENV PHOTOPRISM_EXPERIMENTAL "false" +ENV PHOTOPRISM_SITE_URL "http://localhost:2342/" +ENV PHOTOPRISM_SITE_TITLE "PhotoPrism" +ENV PHOTOPRISM_SITE_CAPTION "Browse Your Life" +ENV PHOTOPRISM_SITE_DESCRIPTION "" +ENV PHOTOPRISM_SITE_AUTHOR "" +ENV PHOTOPRISM_HTTP_HOST "0.0.0.0" +ENV PHOTOPRISM_HTTP_PORT 2342 +ENV PHOTOPRISM_SETTINGS_HIDDEN "false" +ENV PHOTOPRISM_DATABASE_DRIVER "sqlite" +ENV PHOTOPRISM_SIDECAR_JSON "true" +ENV PHOTOPRISM_SIDECAR_YAML "true" +ENV PHOTOPRISM_THUMB_FILTER "lanczos" +ENV PHOTOPRISM_THUMB_UNCACHED "false" +ENV PHOTOPRISM_THUMB_SIZE 2048 +ENV PHOTOPRISM_THUMB_SIZE_UNCACHED 7680 +ENV PHOTOPRISM_JPEG_SIZE 7680 +ENV PHOTOPRISM_JPEG_QUALITY 92 +ENV PHOTOPRISM_DARKTABLE_PRESETS "false" + # Copy files to /photoprism COPY --from=build /root/.local/bin/photoprism /photoprism/bin/photoprism COPY --from=build /root/.photoprism/assets /photoprism/assets @@ -72,9 +104,11 @@ RUN mkdir -p \ /photoprism/originals \ /photoprism/import \ /photoprism/storage/settings \ - /photoprism/storage/cache - -RUN chmod -R 777 /photoprism + /photoprism/storage/cache && \ + chmod 777 /photoprism && \ + chown -R photoprism:photoprism /photoprism && \ + find /photoprism -type d -print0 | xargs -0 chmod 777 && \ + find /photoprism -type f -print0 | xargs -0 chmod a+rw # Show photoprism version RUN photoprism -v @@ -82,5 +116,9 @@ RUN photoprism -v # Expose http port EXPOSE 2342 +# Configure entrypoint +COPY --chown=root:root /docker/photoprism/entrypoint.sh /entrypoint.sh +ENTRYPOINT ["/entrypoint.sh"] + # Run server CMD photoprism start diff --git a/docker/photoprism/arm64/Dockerfile b/docker/photoprism/arm64/Dockerfile index b537f7286..503713a0c 100644 --- a/docker/photoprism/arm64/Dockerfile +++ b/docker/photoprism/arm64/Dockerfile @@ -73,12 +73,12 @@ RUN npm install --unsafe-perm=true --allow-root -g npm RUN npm config set cache ~/.cache/npm # Install Go -ENV GOLANG_VERSION 1.15 +ENV GOLANG_VERSION 1.15.2 RUN set -eux; \ \ url="https://golang.org/dl/go${GOLANG_VERSION}.linux-arm64.tar.gz"; \ wget -O go.tgz "$url"; \ - echo "7e18d92f61ddf480a4f9a57db09389ae7b9dadf68470d0cb9c00d734a0c57f8d *go.tgz" | sha256sum -c -; \ + echo "c8ec460cc82d61604b048f9439c06bd591722efce5cd48f49e19b5f6226bd36d *go.tgz" | sha256sum -c -; \ tar -C /usr/local -xzf go.tgz; \ rm go.tgz; \ export PATH="/usr/local/go/bin:$PATH"; \ @@ -103,6 +103,9 @@ COPY . . # Build PhotoPrism RUN make dep build-js install +# Build gosu +RUN env GO111MODULE=off /usr/local/go/bin/go get -u github.com/tianon/gosu + # Same base image as photoprism/development FROM ubuntu:20.04 @@ -133,17 +136,22 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ rm -rf /var/lib/apt/lists/* # Copy dependencies +COPY --from=build /go/bin/gosu /bin/gosu COPY --from=build /usr/lib/libtensorflow.so /usr/lib/libtensorflow.so COPY --from=build /usr/lib/libtensorflow_framework.so /usr/lib/libtensorflow_framework.so RUN ldconfig +# Set default umask and create photoprism user +RUN umask 0000 && useradd photoprism -m -d /photoprism WORKDIR /photoprism ENV TF_CPP_MIN_LOG_LEVEL 2 ENV PATH /photoprism/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin +ENV TMPDIR /tmp +# Storage path names ENV PHOTOPRISM_ASSETS_PATH /photoprism/assets ENV PHOTOPRISM_ORIGINALS_PATH /photoprism/originals ENV PHOTOPRISM_IMPORT_PATH /photoprism/import @@ -151,6 +159,33 @@ ENV PHOTOPRISM_STORAGE_PATH /photoprism/storage ENV PHOTOPRISM_LOG_FILENAME /photoprism/photoprism.log ENV PHOTOPRISM_PID_FILENAME /photoprism/photoprism.pid +# Defaults for common config values +# See https://docs.photoprism.org/getting-started/config-options/ +ENV PHOTOPRISM_DEBUG "false" +ENV PHOTOPRISM_PUBLIC "false" +ENV PHOTOPRISM_READONLY "false" +ENV PHOTOPRISM_UPLOAD_NSFW "true" +ENV PHOTOPRISM_DETECT_NSFW "false" +ENV PHOTOPRISM_EXPERIMENTAL "false" +ENV PHOTOPRISM_SITE_URL "http://localhost:2342/" +ENV PHOTOPRISM_SITE_TITLE "PhotoPrism" +ENV PHOTOPRISM_SITE_CAPTION "Browse Your Life" +ENV PHOTOPRISM_SITE_DESCRIPTION "" +ENV PHOTOPRISM_SITE_AUTHOR "" +ENV PHOTOPRISM_HTTP_HOST "0.0.0.0" +ENV PHOTOPRISM_HTTP_PORT 2342 +ENV PHOTOPRISM_SETTINGS_HIDDEN "false" +ENV PHOTOPRISM_DATABASE_DRIVER "sqlite" +ENV PHOTOPRISM_SIDECAR_JSON "true" +ENV PHOTOPRISM_SIDECAR_YAML "true" +ENV PHOTOPRISM_THUMB_FILTER "lanczos" +ENV PHOTOPRISM_THUMB_UNCACHED "false" +ENV PHOTOPRISM_THUMB_SIZE 2048 +ENV PHOTOPRISM_THUMB_SIZE_UNCACHED 7680 +ENV PHOTOPRISM_JPEG_SIZE 7680 +ENV PHOTOPRISM_JPEG_QUALITY 92 +ENV PHOTOPRISM_DARKTABLE_PRESETS "false" + # Copy files to /photoprism COPY --from=build /root/.local/bin/photoprism /photoprism/bin/photoprism COPY --from=build /root/.photoprism/assets /photoprism/assets @@ -160,9 +195,11 @@ RUN mkdir -p \ /photoprism/originals \ /photoprism/import \ /photoprism/storage/settings \ - /photoprism/storage/cache - -RUN chmod -R 777 /photoprism + /photoprism/storage/cache && \ + chmod 777 /photoprism && \ + chown -R photoprism:photoprism /photoprism && \ + find /photoprism -type d -print0 | xargs -0 chmod 777 && \ + find /photoprism -type f -print0 | xargs -0 chmod a+rw # Show photoprism version RUN photoprism -v @@ -170,5 +207,9 @@ RUN photoprism -v # Expose http port EXPOSE 2342 +# Configure entrypoint +COPY --chown=root:root /docker/photoprism/entrypoint.sh /entrypoint.sh +ENTRYPOINT ["/entrypoint.sh"] + # Run server CMD photoprism start diff --git a/docker/photoprism/entrypoint.sh b/docker/photoprism/entrypoint.sh new file mode 100755 index 000000000..8bb59548d --- /dev/null +++ b/docker/photoprism/entrypoint.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +if [[ ${UMASK} ]]; then + umask ${UMASK} +fi + +if [[ ${UID} ]] && [[ ${GID} ]] && [[ ${UID} != "0" ]] && [[ $(id -u) = "0" ]]; then + usermod -u ${UID} photoprism + usermod -g ${GID} photoprism + exec gosu ${UID}:${GID} "$@" +elif [[ ${UID} ]] && [[ ${UID} != "0" ]] && [[ $(id -u) = "0" ]]; then + usermod -u ${UID} photoprism + exec gosu ${UID} "$@" +else + exec "$@" +fi \ No newline at end of file diff --git a/internal/remote/webdav/webdav_test.go b/internal/remote/webdav/webdav_test.go index 88a6ae70b..4194a2246 100644 --- a/internal/remote/webdav/webdav_test.go +++ b/internal/remote/webdav/webdav_test.go @@ -2,6 +2,7 @@ package webdav import ( "os" + "path/filepath" "testing" "github.com/photoprism/photoprism/pkg/fs" @@ -84,7 +85,7 @@ func TestClient_Download(t *testing.T) { t.Fatal(err) } - tempDir := os.TempDir() + rnd.UUID() + tempDir := filepath.Join(os.TempDir(), rnd.UUID()) tempFile := tempDir + "/foo.jpg" if len(files) == 0 { @@ -110,7 +111,7 @@ func TestClient_DownloadDir(t *testing.T) { assert.IsType(t, Client{}, c) t.Run("non-recursive", func(t *testing.T) { - tempDir := os.TempDir() + rnd.UUID() + tempDir := filepath.Join(os.TempDir(), rnd.UUID()) if errs := c.DownloadDir("Photos", tempDir, false, false); len(errs) > 0 { t.Fatal(errs) @@ -122,7 +123,7 @@ func TestClient_DownloadDir(t *testing.T) { }) t.Run("recursive", func(t *testing.T) { - tempDir := os.TempDir() + rnd.UUID() + tempDir := filepath.Join(os.TempDir(), rnd.UUID()) if errs := c.DownloadDir("Photos", tempDir, true, false); len(errs) > 0 { t.Fatal(errs)