diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile deleted file mode 100644 index 670d11a06b..0000000000 --- a/.devcontainer/Dockerfile +++ /dev/null @@ -1,16 +0,0 @@ -ARG BASEIMAGE=mcr.microsoft.com/devcontainers/typescript-node:22@sha256:2ef23730ec68d8511ec8e6e0b82550ca728b256805d81f60ed890f3bfb21cfb9 -FROM ${BASEIMAGE} - -# Flutter SDK -# https://flutter.dev/docs/development/tools/sdk/releases?tab=linux -ENV FLUTTER_CHANNEL="stable" -ENV FLUTTER_VERSION="3.29.1" -ENV FLUTTER_HOME=/flutter -ENV PATH=${PATH}:${FLUTTER_HOME}/bin - -# Flutter SDK -RUN mkdir -p ${FLUTTER_HOME} \ - && curl -C - --output flutter.tar.xz https://storage.googleapis.com/flutter_infra_release/releases/${FLUTTER_CHANNEL}/linux/flutter_linux_${FLUTTER_VERSION}-${FLUTTER_CHANNEL}.tar.xz \ - && tar -xf flutter.tar.xz --strip-components=1 -C ${FLUTTER_HOME} \ - && rm flutter.tar.xz \ - && chown -R 1000:1000 ${FLUTTER_HOME} diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json deleted file mode 100644 index 2d567f033a..0000000000 --- a/.devcontainer/devcontainer.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "Immich", - "service": "immich-devcontainer", - "dockerComposeFile": [ - "docker-compose.yml", - "../docker/docker-compose.dev.yml" - ], - "customizations": { - "vscode": { - "extensions": [ - "Dart-Code.dart-code", - "Dart-Code.flutter", - "dbaeumer.vscode-eslint", - "dcmdev.dcm-vscode-extension", - "esbenp.prettier-vscode", - "svelte.svelte-vscode" - ] - } - }, - "forwardPorts": [], - "initializeCommand": "bash .devcontainer/scripts/initializeCommand.sh", - "onCreateCommand": "bash .devcontainer/scripts/onCreateCommand.sh", - "overrideCommand": true, - "workspaceFolder": "/immich", - "remoteUser": "node" -} diff --git a/.devcontainer/mobile/Dockerfile b/.devcontainer/mobile/Dockerfile new file mode 100644 index 0000000000..7208ea8fdc --- /dev/null +++ b/.devcontainer/mobile/Dockerfile @@ -0,0 +1,26 @@ +ARG BASEIMAGE=ghcr.io/immich-app/base-server-dev:202503182202@sha256:45ae044b64a7b518f8d94fa4de718090c1c7689d516ac2ac0976a5331eaeb396 +FROM ${BASEIMAGE} AS dev + +# Flutter SDK +# https://flutter.dev/docs/development/tools/sdk/releases?tab=linux +ENV FLUTTER_CHANNEL="stable" +ENV FLUTTER_VERSION="3.29.1" +ENV FLUTTER_HOME=/flutter +ENV PATH=${PATH}:${FLUTTER_HOME}/bin + +# Flutter SDK +RUN mkdir -p ${FLUTTER_HOME} \ + && curl -C - --output flutter.tar.xz https://storage.googleapis.com/flutter_infra_release/releases/${FLUTTER_CHANNEL}/linux/flutter_linux_${FLUTTER_VERSION}-${FLUTTER_CHANNEL}.tar.xz \ + && tar -xf flutter.tar.xz --strip-components=1 -C ${FLUTTER_HOME} \ + && rm flutter.tar.xz \ + && chown -R 1000:1000 ${FLUTTER_HOME} + +RUN apt-get update \ + && wget -qO- https://dcm.dev/pgp-key.public | gpg --dearmor -o /usr/share/keyrings/dcm.gpg \ + && echo 'deb [signed-by=/usr/share/keyrings/dcm.gpg arch=amd64] https://dcm.dev/debian stable main' | tee /etc/apt/sources.list.d/dart_stable.list \ + && apt-get update \ + && apt-get install dcm + +RUN apt-get install inetutils-ping sudo +RUN usermod -aG sudo sudo node +RUN dart --disable-analytics \ No newline at end of file diff --git a/.devcontainer/mobile/devcontainer.json b/.devcontainer/mobile/devcontainer.json new file mode 100644 index 0000000000..2853fd4e78 --- /dev/null +++ b/.devcontainer/mobile/devcontainer.json @@ -0,0 +1,19 @@ +{ + "name": "Immich - Mobile", + "service": "immich-mobile", + "dockerComposeFile": ["docker-compose.yml"], + "customizations": { + "vscode": { + "extensions": [ + "Dart-Code.dart-code", + "Dart-Code.flutter", + "dcmdev.dcm-vscode-extension", + "esbenp.prettier-vscode" + ] + } + }, + "forwardPorts": [], + "overrideCommand": true, + "workspaceFolder": "/immich", + "remoteUser": "node" +} diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/mobile/docker-compose.yml similarity index 52% rename from .devcontainer/docker-compose.yml rename to .devcontainer/mobile/docker-compose.yml index 25719641d2..612406cade 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/mobile/docker-compose.yml @@ -1,8 +1,9 @@ services: - immich-devcontainer: + immich-mobile: build: dockerfile: Dockerfile extra_hosts: - 'host.docker.internal:host-gateway' volumes: - - ..:/immich:cached + - ../..:/immich + - open_api_node_modules:/immich/open-api/typescript-sdk/node_modules diff --git a/.devcontainer/scripts/installAndRun.sh b/.devcontainer/scripts/installAndRun.sh new file mode 100755 index 0000000000..c028cab9d4 --- /dev/null +++ b/.devcontainer/scripts/installAndRun.sh @@ -0,0 +1,13 @@ +#!/bin/bash +export IMMICH_PORT="${DEV_SERVER_PORT:-2283}" +export DEV_PORT="${DEV_PORT:-3000}" + +sudo chown node -R /immich/cli/node_modules /immich/e2e/node_modules /immich/open-api/typescript-sdk/node_modules /immich/server/node_modules /immich/web/node_modules /mnt/upload + +echo "Installing dependencies (server)" +npm --prefix /immich/server install + +echo "Installing dependencies (web)" +npm --prefix /immich/open-api/typescript-sdk install +npm --prefix /immich/open-api/typescript-sdk run build +npm --prefix /immich/web install diff --git a/.devcontainer/server/.env b/.devcontainer/server/.env new file mode 100644 index 0000000000..ac973c1275 --- /dev/null +++ b/.devcontainer/server/.env @@ -0,0 +1,19 @@ +# You can find documentation for all the supported env variables at https://immich.app/docs/install/environment-variables + +# The location where your uploaded files are stored +UPLOAD_LOCATION=upload-volume + +# The location where your database files are stored +DB_DATA_LOCATION=db-volume + +# To set a timezone, uncomment the next line and change Etc/UTC to a TZ identifier from this list: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List +# TZ=Etc/UTC + +# Connection secret for postgres. You should change it to a random password +# Please use only the characters `A-Za-z0-9`, without special characters or spaces +DB_PASSWORD=postgres + +# The values below this line do not need to be changed +################################################################################### +DB_USERNAME=postgres +DB_DATABASE_NAME=immich diff --git a/.devcontainer/server/Dockerfile b/.devcontainer/server/Dockerfile new file mode 100644 index 0000000000..f3cc71a97a --- /dev/null +++ b/.devcontainer/server/Dockerfile @@ -0,0 +1,8 @@ +ARG BASEIMAGE=ghcr.io/immich-app/base-server-dev:202503182202@sha256:45ae044b64a7b518f8d94fa4de718090c1c7689d516ac2ac0976a5331eaeb396 +FROM ${BASEIMAGE} AS dev + +RUN apt-get install inetutils-ping sudo +RUN usermod -aG sudo node +# RUN echo "node:pass" | chpasswd +# RUN echo "root:root" | chpasswd +RUN echo "node ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers diff --git a/.devcontainer/server/devcontainer.json b/.devcontainer/server/devcontainer.json new file mode 100644 index 0000000000..1460170b46 --- /dev/null +++ b/.devcontainer/server/devcontainer.json @@ -0,0 +1,29 @@ +{ + "name": "Immich - Backend and Frontend", + "service": "immich-server", + "dockerComposeFile": ["docker-compose.yml"], + "customizations": { + "vscode": { + "extensions": [ + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode", + "svelte.svelte-vscode", + "ms-vscode-remote.remote-containers", + "foxundermoon.shell-format", + "timonwong.shellcheck", + "rvest.vs-code-prettier-eslint", + "bluebrown.yamlfmt", + "vkrishna04.cspell-sync" + ] + } + }, + "features": { + "ghcr.io/devcontainers/features/nvidia-cuda:1": {}, + "ghcr.io/meaningful-ooo/devcontainer-features/homebrew:2": {} + }, + "forwardPorts": [3000], + "overrideCommand": true, + "workspaceFolder": "/immich", + "remoteUser": "node", + "postCreateCommand": "/immich/.devcontainer/scripts/installAndRun.sh" +} diff --git a/.devcontainer/server/docker-compose.yml b/.devcontainer/server/docker-compose.yml new file mode 100644 index 0000000000..212a22fe7a --- /dev/null +++ b/.devcontainer/server/docker-compose.yml @@ -0,0 +1,53 @@ +services: + immich-server: + build: + dockerfile: Dockerfile + hostname: immich + extra_hosts: + - 'host.docker.internal:host-gateway' + environment: + - IMMICH_SERVER_URL=http://127.0.0.1:2283/ + volumes: + - ../..:/immich + - cli_node_modules:/immich/cli/node_modules + - e2e_node_modules:/immich/e2e/node_modules + - open_api_node_modules:/immich/open-api/typescript-sdk/node_modules + - server_node_modules:/immich/server/node_modules + - web_node_modules:/immich/web/node_modules + # - immich:/immich + - ${UPLOAD_LOCATION}:/immich/server/upload + + redis: + image: redis:6.2-alpine@sha256:148bb5411c184abd288d9aaed139c98123eeb8824c5d3fce03cf721db58066d8 + healthcheck: + test: redis-cli ping || exit 1 + + database: + image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:739cdd626151ff1f796dc95a6591b55a714f341c737e27f045019ceabf8e8c52 + env_file: + - .env + environment: + POSTGRES_PASSWORD: ${DB_PASSWORD} + POSTGRES_USER: ${DB_USERNAME} + POSTGRES_DB: ${DB_DATABASE_NAME} + POSTGRES_INITDB_ARGS: '--data-checksums' + volumes: + - ${DB_DATA_LOCATION}:/var/lib/postgresql/data + ports: + - 5432:5432 + healthcheck: + test: >- + pg_isready --dbname="$${POSTGRES_DB}" --username="$${POSTGRES_USER}" || exit 1; Chksum="$$(psql --dbname="$${POSTGRES_DB}" --username="$${POSTGRES_USER}" --tuples-only --no-align --command='SELECT COALESCE(SUM(checksum_failures), 0) FROM pg_stat_database')"; echo "checksum failure count is $$Chksum"; [ "$$Chksum" = '0' ] || exit 1 + interval: 5m + start_interval: 30s + start_period: 5m + command: >- + postgres -c shared_preload_libraries=vectors.so -c 'search_path="$$user", public, vectors' -c logging_collector=on -c max_wal_size=2GB -c shared_buffers=512MB -c wal_compression=on + +volumes: + cli_node_modules: + e2e_node_modules: + open_api_node_modules: + server_node_modules: + web_node_modules: + immich: diff --git a/.vscode/settings.json b/.vscode/settings.json index 49dbf3944c..bac54e9a98 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -40,5 +40,7 @@ "explorer.fileNesting.enabled": true, "explorer.fileNesting.patterns": { "*.ts": "${capture}.spec.ts,${capture}.mock.ts" - } + }, + "debug.javascript.autoAttachFilter": "smart", + "eslint.workingDirectories": ["./server", "./web"] } \ No newline at end of file diff --git a/server/src/utils/file.ts b/server/src/utils/file.ts index 716e0b1957..e8e6badae6 100644 --- a/server/src/utils/file.ts +++ b/server/src/utils/file.ts @@ -1,7 +1,7 @@ import { HttpException, StreamableFile } from '@nestjs/common'; import { NextFunction, Response } from 'express'; import { access, constants } from 'node:fs/promises'; -import { basename, extname, isAbsolute } from 'node:path'; +import { basename, extname, isAbsolute, resolve } from 'node:path'; import { promisify } from 'node:util'; import { CacheControl } from 'src/enum'; import { LoggingRepository } from 'src/repositories/logging.repository'; @@ -64,13 +64,13 @@ export const sendFile = async ( // configure options for serving const options: SendFileOptions = { dotfiles: 'allow' }; - if (!isAbsolute(file.path)) { - options.root = process.cwd(); + let filePath = file.path; + if (!isAbsolute(filePath)) { + filePath = resolve(filePath); } + await access(filePath, constants.R_OK); - await access(file.path, constants.R_OK); - - return await _sendFile(file.path, options); + return await _sendFile(filePath, options); } catch (error: Error | any) { // ignore client-closed connection if (isConnectionAborted(error) || res.headersSent) {