Skip to content

Commit

Permalink
Merge branch 'immich-app:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
yjiang-c authored Jul 4, 2024
2 parents 1c22f29 + 10ea894 commit 49ec41f
Show file tree
Hide file tree
Showing 54 changed files with 947 additions and 600 deletions.
6 changes: 3 additions & 3 deletions server/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# dev build
FROM ghcr.io/immich-app/base-server-dev:20240625@sha256:3899114e00a55cb4138b02e1a5da4d2bd8d401a30c72a706134e0d5567c296eb as dev
FROM ghcr.io/immich-app/base-server-dev:20240702@sha256:5d675b67826ac643ee64ecf2ef78adac1e491eef9a845f30818a1c0d1338ecc8 as dev

RUN apt-get install --no-install-recommends -yqq tini
WORKDIR /usr/src/app
Expand Down Expand Up @@ -41,7 +41,7 @@ RUN npm run build


# prod build
FROM ghcr.io/immich-app/base-server-prod:20240625@sha256:19d095589b75bee07c4fd72f53a25c527cd6c5b78162a723d2d8db8e7cd51483
FROM ghcr.io/immich-app/base-server-prod:20240702@sha256:419a873052cf2f012ed1977e4a771a38e68ce64ea1c66047cc06232b1a79bafe

WORKDIR /usr/src/app
ENV NODE_ENV=production \
Expand Down Expand Up @@ -80,4 +80,4 @@ EXPOSE 3001
ENTRYPOINT ["tini", "--", "/bin/bash"]
CMD ["start.sh"]

HEALTHCHECK CMD npm run healthcheck
HEALTHCHECK CMD immich-healthcheck
3 changes: 3 additions & 0 deletions server/bin/immich-healthcheck
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env bash

node /usr/src/app/dist/utils/healthcheck.js
118 changes: 78 additions & 40 deletions server/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"@opentelemetry/exporter-prometheus": "^0.52.0",
"@opentelemetry/sdk-node": "^0.52.0",
"@react-email/components": "^0.0.19",
"@socket.io/postgres-adapter": "^0.3.1",
"@socket.io/redis-adapter": "^8.3.0",
"archiver": "^7.0.0",
"async-lock": "^1.4.0",
"bcrypt": "^5.1.1",
Expand Down
2 changes: 1 addition & 1 deletion server/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ export const immichAppConfig: ConfigModuleOptions = {
}),
};

function parseRedisConfig(): RedisOptions {
export function parseRedisConfig(): RedisOptions {
const redisUrl = process.env.REDIS_URL;
if (redisUrl && redisUrl.startsWith('ioredis://')) {
try {
Expand Down
27 changes: 23 additions & 4 deletions server/src/interfaces/event.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,36 @@ import { ReleaseNotification, ServerVersionResponseDto } from 'src/dtos/server.d

export const IEventRepository = 'IEventRepository';

export type SystemConfigUpdateEvent = { newConfig: SystemConfig; oldConfig: SystemConfig };
export type AlbumUpdateEvent = {
id: string;
/** user id */
updatedBy: string;
};
export type AlbumInviteEvent = { id: string; userId: string };
export type UserSignupEvent = { notify: boolean; id: string; tempPassword?: string };

type MaybePromise<T> = Promise<T> | T;
type Handler<T = undefined> = (data: T) => MaybePromise<void>;

const noop = () => {};
const dummyHandlers = {
onBootstrapEvent: noop as (app: 'api' | 'microservices') => MaybePromise<void>,
// app events
onBootstrapEvent: noop as Handler<'api' | 'microservices'>,
onShutdownEvent: noop as () => MaybePromise<void>,
onConfigUpdateEvent: noop as (update: SystemConfigUpdate) => MaybePromise<void>,
onConfigValidateEvent: noop as (update: SystemConfigUpdate) => MaybePromise<void>,

// config events
onConfigUpdateEvent: noop as Handler<SystemConfigUpdateEvent>,
onConfigValidateEvent: noop as Handler<SystemConfigUpdateEvent>,

// album events
onAlbumUpdateEvent: noop as Handler<AlbumUpdateEvent>,
onAlbumInviteEvent: noop as Handler<AlbumInviteEvent>,

// user events
onUserSignupEvent: noop as Handler<UserSignupEvent>,
};

export type SystemConfigUpdate = { newConfig: SystemConfig; oldConfig: SystemConfig };
export type EventHandlers = typeof dummyHandlers;
export type EmitEvent = keyof EventHandlers;
export type EmitEventHandler<T extends EmitEvent> = (...args: Parameters<EventHandlers[T]>) => MaybePromise<void>;
Expand Down
11 changes: 6 additions & 5 deletions server/src/middleware/websocket.adapter.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { INestApplicationContext } from '@nestjs/common';
import { IoAdapter } from '@nestjs/platform-socket.io';
import { createAdapter } from '@socket.io/postgres-adapter';
import { createAdapter } from '@socket.io/redis-adapter';
import { Redis } from 'ioredis';
import { ServerOptions } from 'socket.io';
import { DataSource } from 'typeorm';
import { PostgresDriver } from 'typeorm/driver/postgres/PostgresDriver.js';
import { parseRedisConfig } from 'src/config';

export class WebSocketAdapter extends IoAdapter {
constructor(private app: INestApplicationContext) {
Expand All @@ -12,8 +12,9 @@ export class WebSocketAdapter extends IoAdapter {

createIOServer(port: number, options?: ServerOptions): any {
const server = super.createIOServer(port, options);
const pool = (this.app.get(DataSource).driver as PostgresDriver).master;
server.adapter(createAdapter(pool));
const pubClient = new Redis(parseRedisConfig());
const subClient = pubClient.duplicate();
server.adapter(createAdapter(pubClient, subClient));
return server;
}
}
61 changes: 43 additions & 18 deletions server/src/migrations/1718486162779-AddFaceSearchRelation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,50 @@ export class AddFaceSearchRelation1718486162779 implements MigrationInterface {
await queryRunner.query(`SET vectors.pgvector_compatibility=on`);
}

const hasEmbeddings = async (tableName: string): Promise<boolean> => {
const columns = await queryRunner.query(
`SELECT column_name as name
FROM information_schema.columns
WHERE table_name = '${tableName}'`);
return columns.some((column: { name: string }) => column.name === 'embedding');
}

const hasAssetEmbeddings = await hasEmbeddings('smart_search');
if (!hasAssetEmbeddings) {
await queryRunner.query(`TRUNCATE smart_search`);
await queryRunner.query(`ALTER TABLE smart_search ADD COLUMN IF NOT EXISTS embedding vector(512) NOT NULL`);
}

await queryRunner.query(`
CREATE TABLE face_search (
"faceId" uuid PRIMARY KEY REFERENCES asset_faces(id) ON DELETE CASCADE,
embedding vector(512) NOT NULL )`);
CREATE TABLE face_search (
"faceId" uuid PRIMARY KEY REFERENCES asset_faces(id) ON DELETE CASCADE,
embedding vector(512) NOT NULL )`);

await queryRunner.query(`ALTER TABLE face_search ALTER COLUMN embedding SET STORAGE EXTERNAL`);
await queryRunner.query(`ALTER TABLE smart_search ALTER COLUMN embedding SET STORAGE EXTERNAL`);

await queryRunner.query(`
INSERT INTO face_search("faceId", embedding)
SELECT id, embedding
FROM asset_faces faces`);
const hasFaceEmbeddings = await hasEmbeddings('asset_faces')
if (hasFaceEmbeddings) {
await queryRunner.query(`
INSERT INTO face_search("faceId", embedding)
SELECT id, embedding
FROM asset_faces faces`);
}

await queryRunner.query(`ALTER TABLE asset_faces DROP COLUMN IF EXISTS embedding`);

await queryRunner.query(`ALTER TABLE asset_faces DROP COLUMN "embedding"`);
await queryRunner.query(`ALTER TABLE face_search ALTER COLUMN embedding SET DATA TYPE real[]`);
await queryRunner.query(`ALTER TABLE face_search ALTER COLUMN embedding SET DATA TYPE vector(512)`);

await queryRunner.query(`
CREATE INDEX IF NOT EXISTS clip_index ON smart_search
USING hnsw (embedding vector_cosine_ops)
WITH (ef_construction = 300, m = 16)`);

await queryRunner.query(`
CREATE INDEX face_index ON face_search
USING hnsw (embedding vector_cosine_ops)
WITH (ef_construction = 300, m = 16)`);
CREATE INDEX face_index ON face_search
USING hnsw (embedding vector_cosine_ops)
WITH (ef_construction = 300, m = 16)`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
Expand All @@ -40,15 +65,15 @@ export class AddFaceSearchRelation1718486162779 implements MigrationInterface {
await queryRunner.query(`ALTER TABLE face_search ALTER COLUMN embedding SET STORAGE DEFAULT`);
await queryRunner.query(`ALTER TABLE smart_search ALTER COLUMN embedding SET STORAGE DEFAULT`);
await queryRunner.query(`
UPDATE asset_faces
SET embedding = fs.embedding
FROM face_search fs
WHERE id = fs."faceId"`);
UPDATE asset_faces
SET embedding = fs.embedding
FROM face_search fs
WHERE id = fs."faceId"`);
await queryRunner.query(`DROP TABLE face_search`);

await queryRunner.query(`
CREATE INDEX face_index ON asset_faces
USING hnsw (embedding vector_cosine_ops)
WITH (ef_construction = 300, m = 16)`);
CREATE INDEX face_index ON asset_faces
USING hnsw (embedding vector_cosine_ops)
WITH (ef_construction = 300, m = 16)`);
}
}
Loading

0 comments on commit 49ec41f

Please sign in to comment.