mirror of
https://github.com/immich-app/immich.git
synced 2026-05-18 03:10:24 +03:00
feat: move version checks to our own infrastructure (#27450)
This commit is contained in:
@@ -37,6 +37,7 @@ import { CliService } from 'src/services/cli.service';
|
||||
import { DatabaseBackupService } from 'src/services/database-backup.service';
|
||||
import { QueueService } from 'src/services/queue.service';
|
||||
import { getKyselyConfig } from 'src/utils/database';
|
||||
import { configureUserAgent } from 'src/utils/fetch';
|
||||
|
||||
const common = [...repositories, ...services, GlobalExceptionFilter];
|
||||
|
||||
@@ -60,6 +61,8 @@ const commonImports = [
|
||||
|
||||
const bullImports = [BullModule.forRoot(bull.config), BullModule.registerQueue(...bull.queues)];
|
||||
|
||||
configureUserAgent();
|
||||
|
||||
export class BaseModule implements OnModuleInit, OnModuleDestroy {
|
||||
constructor(
|
||||
@Inject(IWorker) private worker: ImmichWorker,
|
||||
|
||||
@@ -17,6 +17,11 @@ export interface GitHubRelease {
|
||||
body: string;
|
||||
}
|
||||
|
||||
export interface VersionResponse {
|
||||
version: string;
|
||||
published_at: string;
|
||||
}
|
||||
|
||||
export interface ServerBuildVersions {
|
||||
nodejs: string;
|
||||
ffmpeg: string;
|
||||
@@ -59,17 +64,17 @@ export class ServerInfoRepository {
|
||||
this.logger.setContext(ServerInfoRepository.name);
|
||||
}
|
||||
|
||||
async getGitHubRelease(): Promise<GitHubRelease> {
|
||||
async getLatestRelease(): Promise<VersionResponse> {
|
||||
try {
|
||||
const response = await fetch('https://api.github.com/repos/immich-app/immich/releases/latest');
|
||||
const response = await fetch('https://version.immich.cloud/version');
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`GitHub API request failed with status ${response.status}: ${await response.text()}`);
|
||||
throw new Error(`Version check request failed with status ${response.status}: ${await response.text()}`);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
} catch (error) {
|
||||
throw new Error('Failed to fetch GitHub release', { cause: error });
|
||||
throw new Error('Failed to fetch latest release', { cause: error });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,14 +8,9 @@ import { mockEnvData } from 'test/repositories/config.repository.mock';
|
||||
import { factory } from 'test/small.factory';
|
||||
import { newTestService, ServiceMocks } from 'test/utils';
|
||||
|
||||
const mockRelease = (version: string) => ({
|
||||
id: 1,
|
||||
url: 'https://api.github.com/repos/owner/repo/releases/1',
|
||||
tag_name: version,
|
||||
name: 'Release 1000',
|
||||
created_at: DateTime.utc().toISO(),
|
||||
const mockVersionResponse = (version: string) => ({
|
||||
version,
|
||||
published_at: DateTime.utc().toISO(),
|
||||
body: '',
|
||||
});
|
||||
|
||||
describe(VersionService.name, () => {
|
||||
@@ -101,7 +96,7 @@ describe(VersionService.name, () => {
|
||||
});
|
||||
|
||||
it('should run if it has been > 60 minutes', async () => {
|
||||
mocks.serverInfo.getGitHubRelease.mockResolvedValue(mockRelease('v100.0.0'));
|
||||
mocks.serverInfo.getLatestRelease.mockResolvedValue(mockVersionResponse('v100.0.0'));
|
||||
mocks.systemMetadata.get.mockResolvedValue({
|
||||
checkedAt: DateTime.utc().minus({ minutes: 65 }).toISO(),
|
||||
releaseVersion: '1.0.0',
|
||||
@@ -113,7 +108,7 @@ describe(VersionService.name, () => {
|
||||
});
|
||||
|
||||
it('should not notify if the version is equal', async () => {
|
||||
mocks.serverInfo.getGitHubRelease.mockResolvedValue(mockRelease(serverVersion.toString()));
|
||||
mocks.serverInfo.getLatestRelease.mockResolvedValue(mockVersionResponse(serverVersion.toString()));
|
||||
await expect(sut.handleVersionCheck()).resolves.toEqual(JobStatus.Success);
|
||||
expect(mocks.systemMetadata.set).toHaveBeenCalledWith(SystemMetadataKey.VersionCheckState, {
|
||||
checkedAt: expect.any(String),
|
||||
@@ -122,8 +117,8 @@ describe(VersionService.name, () => {
|
||||
expect(mocks.websocket.clientBroadcast).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle a github error', async () => {
|
||||
mocks.serverInfo.getGitHubRelease.mockRejectedValue(new Error('GitHub is down'));
|
||||
it('should handle a version check error', async () => {
|
||||
mocks.serverInfo.getLatestRelease.mockRejectedValue(new Error('Version service is down'));
|
||||
await expect(sut.handleVersionCheck()).resolves.toEqual(JobStatus.Failed);
|
||||
expect(mocks.systemMetadata.set).not.toHaveBeenCalled();
|
||||
expect(mocks.websocket.clientBroadcast).not.toHaveBeenCalled();
|
||||
|
||||
@@ -91,8 +91,7 @@ export class VersionService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
const { tag_name: releaseVersion, published_at: publishedAt } =
|
||||
await this.serverInfoRepository.getGitHubRelease();
|
||||
const { version: releaseVersion, published_at: publishedAt } = await this.serverInfoRepository.getLatestRelease();
|
||||
const metadata: VersionCheckMetadata = { checkedAt: DateTime.utc().toISO(), releaseVersion };
|
||||
|
||||
await this.systemMetadataRepository.set(SystemMetadataKey.VersionCheckState, metadata);
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { serverVersion } from 'src/constants';
|
||||
import { configureUserAgent } from 'src/utils/fetch';
|
||||
|
||||
describe('fetch', () => {
|
||||
it('should set the default user-agent header', async () => {
|
||||
const spy = vi.fn().mockResolvedValue(new Response());
|
||||
const original = globalThis.fetch;
|
||||
globalThis.fetch = spy;
|
||||
|
||||
configureUserAgent();
|
||||
await globalThis.fetch('http://test.local');
|
||||
|
||||
const headers: Headers = spy.mock.calls[0][1].headers;
|
||||
expect(headers.get('User-Agent')).toBe(`immich-server/${serverVersion}`);
|
||||
|
||||
globalThis.fetch = original;
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
import { serverVersion } from 'src/constants';
|
||||
|
||||
export function configureUserAgent() {
|
||||
const originalFetch = globalThis.fetch;
|
||||
globalThis.fetch = (input, init) => {
|
||||
const headers = new Headers(init?.headers);
|
||||
if (!headers.has('User-Agent')) {
|
||||
headers.set('User-Agent', `immich-server/${serverVersion}`);
|
||||
}
|
||||
return originalFetch(input, { ...init, headers });
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user