refactor: improve code organization

This commit is contained in:
Pujit Mehrotra
2025-08-20 13:21:02 -04:00
parent cf96f14a4b
commit 5f728c06f7
4 changed files with 43 additions and 49 deletions

View File

@@ -1,15 +1,15 @@
import { Injectable } from '@nestjs/common';
import { Cron, CronExpression, Timeout } from '@nestjs/schedule';
import { DockerPhpService } from '@app/unraid-api/graph/resolvers/docker/docker-php.service.js';
import { DockerManifestService } from '@app/unraid-api/graph/resolvers/docker/docker-manifest.service.js';
@Injectable()
export class ContainerStatusJob {
constructor(private readonly dockerPhpService: DockerPhpService) {}
constructor(private readonly dockerManifestService: DockerManifestService) {}
@Cron(CronExpression.EVERY_DAY_AT_6AM)
async refreshContainerDigests() {
await this.dockerPhpService.refreshDigests();
await this.dockerManifestService.refreshDigests();
}
/**
@@ -17,6 +17,6 @@ export class ContainerStatusJob {
*/
@Timeout(5_000)
async refreshContainerDigestsAfterStartup() {
await this.dockerPhpService.refreshDigests();
await this.dockerManifestService.refreshDigests();
}
}

View File

@@ -25,10 +25,7 @@ import { OrganizerV1, ResolvedOrganizerV1 } from '@app/unraid-api/organizer/orga
@Resolver(() => DockerContainer)
export class DockerContainerResolver {
private readonly logger = new Logger(DockerContainerResolver.name);
constructor(
private readonly dockerManifestService: DockerManifestService,
private readonly dockerPhpService: DockerPhpService
) {}
constructor(private readonly dockerManifestService: DockerManifestService) {}
@UsePermissions({
action: AuthActionVerb.READ,
@@ -62,6 +59,6 @@ export class DockerContainerResolver {
})
@Mutation(() => Boolean)
public async refreshDockerDigests() {
return this.dockerPhpService.refreshDigests();
return this.dockerManifestService.refreshDigests();
}
}

View File

@@ -1,38 +1,36 @@
import { Injectable, Logger } from '@nestjs/common';
import { readFile } from 'fs/promises';
import { ExtendOptions, Got, got as gotClient, OptionsOfTextResponseBody } from 'got';
import { Injectable } from '@nestjs/common';
import { docker } from '@app/core/utils/index.js';
/** Accept header for Docker API manifest listing */
const ACCEPT_MANIFEST =
'application/vnd.docker.distribution.manifest.list.v2+json,application/vnd.docker.distribution.manifest.v2+json,application/vnd.oci.image.index.v1+json';
export type CachedStatusEntry = {
/** sha256 digest - "sha256:..." */
local: string;
/** sha256 digest - "sha256:..." */
remote: string;
/** whether update is available (true), not available (false), or unknown (null) */
status: 'true' | 'false' | null;
};
import {
CachedStatusEntry,
DockerPhpService,
} from '@app/unraid-api/graph/resolvers/docker/docker-php.service.js';
@Injectable()
export class DockerManifestService {
constructor() {}
constructor(private readonly dockerPhpService: DockerPhpService) {}
async readCachedUpdateStatus(cacheFile = '/var/lib/docker/unraid-update-status.json') {
const cache = await readFile(cacheFile, 'utf8');
const cacheData = JSON.parse(cache);
return cacheData as Record<string, CachedStatusEntry>;
private readonly refreshDigestsMutex = new AsyncMutex(() => {
return this.dockerPhpService.refreshDigestsViaPhp();
});
/**
* Recomputes local/remote docker container digests and writes them to /var/lib/docker/unraid-update-status.json
* @param mutex - Optional mutex to use for the operation. If not provided, a default mutex will be used.
* @param dockerUpdatePath - Optional path to the DockerUpdate.php file. If not provided, the default path will be used.
* @returns True if the digests were refreshed, false if the operation failed
*/
async refreshDigests(mutex = this.refreshDigestsMutex, dockerUpdatePath?: string) {
return mutex.do(() => {
return this.dockerPhpService.refreshDigestsViaPhp(dockerUpdatePath);
});
}
async isUpdateAvailableCached(imageRef: string, cacheData?: Record<string, CachedStatusEntry>) {
let taggedRef = imageRef;
if (!taggedRef.includes(':')) taggedRef += ':latest';
cacheData ??= await this.readCachedUpdateStatus();
cacheData ??= await this.dockerPhpService.readCachedUpdateStatus();
const containerData = cacheData[taggedRef];
if (!containerData) return null;
return containerData.status?.toLowerCase() === 'true';

View File

@@ -1,4 +1,5 @@
import { Injectable } from '@nestjs/common';
import { readFile } from 'fs/promises';
import { AsyncMutex } from '@unraid/shared/util/processing.js';
@@ -9,37 +10,35 @@ type ExplicitStatusItem = {
name: string;
updateStatus: 'up to date' | 'update available' | 'rebuild ready' | 'unknown';
};
export type CachedStatusEntry = {
/** sha256 digest - "sha256:..." */
local: string;
/** sha256 digest - "sha256:..." */
remote: string;
/** whether update is available (true), not available (false), or unknown (null) */
status: 'true' | 'false' | null;
};
@Injectable()
export class DockerPhpService {
constructor() {}
async readCachedUpdateStatus(cacheFile = '/var/lib/docker/unraid-update-status.json') {
const cache = await readFile(cacheFile, 'utf8');
const cacheData = JSON.parse(cache);
return cacheData as Record<string, CachedStatusEntry>;
}
/**----------------------
* Refresh Container Digests
*------------------------**/
private readonly refreshDigestsMutex = new AsyncMutex(() => {
return this.refreshDigestsViaPhp();
});
/**
* Recomputes local/remote docker container digests and writes them to /var/lib/docker/unraid-update-status.json
* @param mutex - Optional mutex to use for the operation. If not provided, a default mutex will be used.
* @param dockerUpdatePath - Optional path to the DockerUpdate.php file. If not provided, the default path will be used.
* @returns True if the digests were refreshed, false if the operation failed
*/
async refreshDigests(mutex = this.refreshDigestsMutex, dockerUpdatePath?: string) {
return mutex.do(() => {
return this.refreshDigestsViaPhp(dockerUpdatePath);
});
}
/**
* Recomputes local/remote digests by triggering `DockerTemplates->getAllInfo(true)` via DockerUpdate.php
* @param dockerUpdatePath - Path to the DockerUpdate.php file
* @returns True if the digests were refreshed, false if the file is not found or the operation failed
*/
private async refreshDigestsViaPhp(
async refreshDigestsViaPhp(
dockerUpdatePath = '/usr/local/emhttp/plugins/dynamix.docker.manager/include/DockerUpdate.php'
) {
try {