Compare commits

...

4 Commits

Author SHA1 Message Date
midzelis
61689258be chore: update devcontainers for trixie, devenv changes 2025-09-19 02:39:36 +00:00
Jason Rasmussen
68f3ed89c5 chore: remove unused init service (#22188) 2025-09-19 01:11:45 +00:00
Sergey Katsubo
78516a97b3 chore(server): proper log context formatting (#22173)
* Fix log formatting for logger.error(..., error)

Rewrite it to avoid printing error msg in [context]

* Fix log formatting for logger.warn(..., error?.stack)

Rewrite it to avoid printing stack in [context]

* Fix log formatting for logger.debug(..., error.message);

Rewrite it to avoid printing error msg in [context]

* Print error msg instead of literal "Error"
2025-09-18 19:56:05 -04:00
Alex
b8a17c3c26 fix: disable scrubbing mode on drag ended (#22186) 2025-09-18 16:42:33 -05:00
14 changed files with 64 additions and 51 deletions

View File

@@ -5,8 +5,7 @@
"immich-server", "immich-server",
"redis", "redis",
"database", "database",
"immich-machine-learning", "immich-machine-learning"
"init"
], ],
"dockerComposeFile": [ "dockerComposeFile": [
"../docker/docker-compose.dev.yml", "../docker/docker-compose.dev.yml",

View File

@@ -6,28 +6,35 @@ services:
- IMMICH_SERVER_URL=http://127.0.0.1:2283/ - IMMICH_SERVER_URL=http://127.0.0.1:2283/
volumes: !override # bind mount host to /workspaces/immich volumes: !override # bind mount host to /workspaces/immich
- ..:/workspaces/immich - ..:/workspaces/immich
- cli_node_modules:/workspaces/immich/cli/node_modules - ${UPLOAD_LOCATION:-upload-devcontainer-volume}${UPLOAD_LOCATION:+/photos}:/data
- e2e_node_modules:/workspaces/immich/e2e/node_modules - pnpm-store:/usr/src/app/.pnpm-store
- open_api_node_modules:/workspaces/immich/open-api/typescript-sdk/node_modules - server-node_modules:/usr/src/app/server/node_modules
- server_node_modules:/workspaces/immich/server/node_modules - web-node_modules:/usr/src/app/web/node_modules
- web_node_modules:/workspaces/immich/web/node_modules - github-node_modules:/usr/src/app/.github/node_modules
- ${UPLOAD_LOCATION}/photos:/data - cli-node_modules:/usr/src/app/cli/node_modules
- docs-node_modules:/usr/src/app/docs/node_modules
- e2e-node_modules:/usr/src/app/e2e/node_modules
- sdk-node_modules:/usr/src/app/open-api/typescript-sdk/node_modules
- app-node_modules:/usr/src/app/node_modules
- sveltekit:/usr/src/app/web/.svelte-kit
- coverage:/usr/src/app/web/coverage
- /etc/localtime:/etc/localtime:ro - /etc/localtime:/etc/localtime:ro
immich-web:
env_file: !reset []
immich-machine-learning:
env_file: !reset []
database: database:
env_file: !reset []
environment: !override
POSTGRES_PASSWORD: ${DB_PASSWORD-postgres}
POSTGRES_USER: ${DB_USERNAME-postgres}
POSTGRES_DB: ${DB_DATABASE_NAME-immich}
POSTGRES_INITDB_ARGS: '--data-checksums'
POSTGRES_HOST_AUTH_METHOD: md5
volumes: volumes:
- ${UPLOAD_LOCATION}/postgres:/var/lib/postgresql/data - ${UPLOAD_LOCATION:-postgres-devcontainer-volume}${UPLOAD_LOCATION:+/postgres}:/var/lib/postgresql/data
redis:
env_file: !reset []
volumes: volumes:
# Node modules for each service to avoid conflicts and ensure consistent dependencies upload-devcontainer-volume:
cli_node_modules: postgres-devcontainer-volume:
e2e_node_modules:
open_api_node_modules:
server_node_modules:
web_node_modules:
# UPLOAD_LOCATION must be set to a absolute path or vol-upload
vol-upload:
# DB_DATA_LOCATION must be set to a absolute path or vol-database
vol-database:

View File

@@ -40,7 +40,7 @@
"userEnvProbe": "loginInteractiveShell", "userEnvProbe": "loginInteractiveShell",
"remoteEnv": { "remoteEnv": {
// The location where your uploaded files are stored // The location where your uploaded files are stored
"UPLOAD_LOCATION": "${localEnv:UPLOAD_LOCATION:./Library}", "UPLOAD_LOCATION": "${localEnv:UPLOAD_LOCATION:./library}",
// Connection secret for postgres. You should change it to a random password // 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 // Please use only the characters `A-Za-z0-9`, without special characters or spaces
"DB_PASSWORD": "${localEnv:DB_PASSWORD:postgres}", "DB_PASSWORD": "${localEnv:DB_PASSWORD:postgres}",

View File

@@ -322,6 +322,8 @@ class ScrubberState extends ConsumerState<Scrubber> with TickerProviderStateMixi
_isDragging = false; _isDragging = false;
}); });
ref.read(timelineStateProvider.notifier).setScrubbing(false);
// Reset scrubber tracking when drag ends // Reset scrubber tracking when drag ends
_currentScrubberDate = null; _currentScrubberDate = null;
_scrubberDebouncer?.dispose(); _scrubberDebouncer?.dispose();

View File

@@ -27,8 +27,8 @@ ENTRYPOINT ["tini", "--", "/bin/bash", "-c"]
FROM dev AS dev-container-server FROM dev AS dev-container-server
RUN apt-get update --allow-releaseinfo-change && \ RUN apt-get update --allow-releaseinfo-change && \
apt-get install sudo inetutils-ping openjdk-11-jre-headless \ apt-get install sudo inetutils-ping openjdk-21-jre-headless \
vim nano \ vim nano curl \
-y --no-install-recommends --fix-missing -y --no-install-recommends --fix-missing
RUN usermod -aG sudo node && \ RUN usermod -aG sudo node && \
@@ -44,13 +44,18 @@ FROM dev-container-server AS dev-container-mobile
USER root USER root
# Enable multiarch for arm64 if necessary # Enable multiarch for arm64 if necessary
RUN if [ "$(dpkg --print-architecture)" = "arm64" ]; then \ RUN if [ "$(dpkg --print-architecture)" = "arm64" ]; then \
dpkg --add-architecture amd64 && \ dpkg --add-architecture amd64 && \
apt-get update && \ apt-get update && \
apt-get install -y --no-install-recommends \ apt-get install -y --no-install-recommends \
qemu-user-static \ gnupg \
libc6:amd64 \ qemu-user-static \
libstdc++6:amd64 \ libc6:amd64 \
libgcc1:amd64; \ libstdc++6:amd64 \
libgcc1:amd64; \
else \
apt-get update && \
apt-get install -y --no-install-recommends \
gnupg; \
fi fi
# Flutter SDK # Flutter SDK
@@ -65,11 +70,11 @@ 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 \ && 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} \ && tar -xf flutter.tar.xz --strip-components=1 -C ${FLUTTER_HOME} \
&& rm flutter.tar.xz \ && rm flutter.tar.xz \
&& chown -R node ${FLUTTER_HOME} && chown -R node ${FLUTTER_HOME} \
&& git config --global --add safe.directory ${FLUTTER_HOME}
RUN apt-get update \ RUN wget -qO- https://dcm.dev/pgp-key.public | gpg --dearmor -o /usr/share/keyrings/dcm.gpg \
&& 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 \ && 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 update \
&& apt-get install dcm -y && apt-get install dcm -y

View File

@@ -57,28 +57,28 @@ export class MediaRepository {
const buffer = await exiftool.extractBinaryTagToBuffer('JpgFromRaw2', input); const buffer = await exiftool.extractBinaryTagToBuffer('JpgFromRaw2', input);
return { buffer, format: RawExtractedFormat.Jpeg }; return { buffer, format: RawExtractedFormat.Jpeg };
} catch (error: any) { } catch (error: any) {
this.logger.debug('Could not extract JpgFromRaw2 buffer from image, trying JPEG from RAW next', error.message); this.logger.debug(`Could not extract JpgFromRaw2 buffer from image, trying JPEG from RAW next: ${error}`);
} }
try { try {
const buffer = await exiftool.extractBinaryTagToBuffer('JpgFromRaw', input); const buffer = await exiftool.extractBinaryTagToBuffer('JpgFromRaw', input);
return { buffer, format: RawExtractedFormat.Jpeg }; return { buffer, format: RawExtractedFormat.Jpeg };
} catch (error: any) { } catch (error: any) {
this.logger.debug('Could not extract JPEG buffer from image, trying PreviewJXL next', error.message); this.logger.debug(`Could not extract JPEG buffer from image, trying PreviewJXL next: ${error}`);
} }
try { try {
const buffer = await exiftool.extractBinaryTagToBuffer('PreviewJXL', input); const buffer = await exiftool.extractBinaryTagToBuffer('PreviewJXL', input);
return { buffer, format: RawExtractedFormat.Jxl }; return { buffer, format: RawExtractedFormat.Jxl };
} catch (error: any) { } catch (error: any) {
this.logger.debug('Could not extract PreviewJXL buffer from image, trying PreviewImage next', error.message); this.logger.debug(`Could not extract PreviewJXL buffer from image, trying PreviewImage next: ${error}`);
} }
try { try {
const buffer = await exiftool.extractBinaryTagToBuffer('PreviewImage', input); const buffer = await exiftool.extractBinaryTagToBuffer('PreviewImage', input);
return { buffer, format: RawExtractedFormat.Jpeg }; return { buffer, format: RawExtractedFormat.Jpeg };
} catch (error: any) { } catch (error: any) {
this.logger.debug('Could not extract preview buffer from image', error.message); this.logger.debug(`Could not extract preview buffer from image: ${error}`);
return null; return null;
} }
} }

View File

@@ -103,7 +103,7 @@ export class MetadataRepository {
readTags(path: string): Promise<ImmichTags> { readTags(path: string): Promise<ImmichTags> {
return this.exiftool.read(path).catch((error) => { return this.exiftool.read(path).catch((error) => {
this.logger.warn(`Error reading exif data (${path}): ${error}`, error?.stack); this.logger.warn(`Error reading exif data (${path}): ${error}\n${error?.stack}`);
return {}; return {};
}) as Promise<ImmichTags>; }) as Promise<ImmichTags>;
} }

View File

@@ -344,7 +344,7 @@ export class AuthService extends BaseService {
await this.jobRepository.queue({ name: JobName.FileDelete, data: { files: [oldPath] } }); await this.jobRepository.queue({ name: JobName.FileDelete, data: { files: [oldPath] } });
} }
} catch (error: Error | any) { } catch (error: Error | any) {
this.logger.warn(`Unable to sync oauth profile picture: ${error}`, error?.stack); this.logger.warn(`Unable to sync oauth profile picture: ${error}\n${error?.stack}`);
} }
} }

View File

@@ -132,12 +132,12 @@ export class BackupService extends BaseService {
gzip.stdout.pipe(fileStream); gzip.stdout.pipe(fileStream);
pgdump.on('error', (err) => { pgdump.on('error', (err) => {
this.logger.error('Backup failed with error', err); this.logger.error(`Backup failed with error: ${err}`);
reject(err); reject(err);
}); });
gzip.on('error', (err) => { gzip.on('error', (err) => {
this.logger.error('Gzip failed with error', err); this.logger.error(`Gzip failed with error: ${err}`);
reject(err); reject(err);
}); });
@@ -175,10 +175,10 @@ export class BackupService extends BaseService {
}); });
await this.storageRepository.rename(backupFilePath, backupFilePath.replace('.tmp', '')); await this.storageRepository.rename(backupFilePath, backupFilePath.replace('.tmp', ''));
} catch (error) { } catch (error) {
this.logger.error('Database Backup Failure', error); this.logger.error(`Database Backup Failure: ${error}`);
await this.storageRepository await this.storageRepository
.unlink(backupFilePath) .unlink(backupFilePath)
.catch((error) => this.logger.error('Failed to delete failed backup file', error)); .catch((error) => this.logger.error(`Failed to delete failed backup file: ${error}`));
throw error; throw error;
} }

View File

@@ -245,7 +245,7 @@ export class LibraryService extends BaseService {
job.paths.map((path) => job.paths.map((path) =>
this.processEntity(path, library.ownerId, job.libraryId) this.processEntity(path, library.ownerId, job.libraryId)
.then((asset) => assetImports.push(asset)) .then((asset) => assetImports.push(asset))
.catch((error: any) => this.logger.error(`Error processing ${path} for library ${job.libraryId}`, error)), .catch((error: any) => this.logger.error(`Error processing ${path} for library ${job.libraryId}: ${error}`)),
), ),
); );

View File

@@ -40,7 +40,7 @@ export class MemoryService extends BaseService {
try { try {
await Promise.all(users.map((owner, i) => this.createOnThisDayMemories(owner.id, usersIds[i], target))); await Promise.all(users.map((owner, i) => this.createOnThisDayMemories(owner.id, usersIds[i], target)));
} catch (error) { } catch (error) {
this.logger.error(`Failed to create memories for ${target.toISO()}`, error); this.logger.error(`Failed to create memories for ${target.toISO()}: ${error}`);
} }
// update system metadata even when there is an error to minimize the chance of duplicates // update system metadata even when there is an error to minimize the chance of duplicates
await this.systemMetadataRepository.set(SystemMetadataKey.MemoriesState, { await this.systemMetadataRepository.set(SystemMetadataKey.MemoriesState, {

View File

@@ -338,7 +338,7 @@ export class StorageTemplateService extends BaseService {
return destination; return destination;
} catch (error: any) { } catch (error: any) {
this.logger.error(`Unable to get template path for ${filename}`, error); this.logger.error(`Unable to get template path for ${filename}: ${error}`);
return asset.originalPath; return asset.originalPath;
} }
} }

View File

@@ -95,7 +95,7 @@ export class VersionService extends BaseService {
this.eventRepository.clientBroadcast('on_new_release', asNotification(metadata)); this.eventRepository.clientBroadcast('on_new_release', asNotification(metadata));
} }
} catch (error: Error | any) { } catch (error: Error | any) {
this.logger.warn(`Unable to run version check: ${error}`, error?.stack); this.logger.warn(`Unable to run version check: ${error}\n${error?.stack}`);
return JobStatus.Failed; return JobStatus.Failed;
} }

View File

@@ -73,7 +73,7 @@ export const sendFile = async (
// log non-http errors // log non-http errors
if (error instanceof HttpException === false) { if (error instanceof HttpException === false) {
logger.error(`Unable to send file: ${error.name}`, error.stack); logger.error(`Unable to send file: ${error}`, error.stack);
} }
res.header('Cache-Control', 'none'); res.header('Cache-Control', 'none');