Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
"@unraid/libvirt": "2.1.0",
"@unraid/shared": "workspace:*",
"accesscontrol": "2.2.1",
"atomically": "2.0.3",
"bycontract": "2.0.11",
"bytes": "3.1.2",
"cache-manager": "7.0.1",
Expand Down
12 changes: 10 additions & 2 deletions api/src/unraid-api/config/api-config.module.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Injectable, Logger, Module } from '@nestjs/common';
import { Injectable, Logger, Module, OnApplicationBootstrap } from '@nestjs/common';
import { ConfigService, registerAs } from '@nestjs/config';
import path from 'path';

Expand Down Expand Up @@ -50,7 +50,10 @@ export const loadApiConfig = async () => {
export const apiConfig = registerAs<ApiConfig>('api', loadApiConfig);

@Injectable()
export class ApiConfigPersistence extends ConfigFilePersister<ApiConfig> {
export class ApiConfigPersistence
extends ConfigFilePersister<ApiConfig>
implements OnApplicationBootstrap
{
constructor(configService: ConfigService) {
super(configService);
}
Expand Down Expand Up @@ -79,12 +82,17 @@ export class ApiConfigPersistence extends ConfigFilePersister<ApiConfig> {
return createDefaultConfig();
}

async onApplicationBootstrap() {
this.configService.set('api.version', API_VERSION);
}

async migrateConfig(): Promise<ApiConfig> {
const legacyConfig = this.configService.get('store.config', {});
const migrated = this.convertLegacyConfig(legacyConfig);
return {
...this.defaultConfig(),
...migrated,
version: API_VERSION,
};
}

Expand Down
12 changes: 1 addition & 11 deletions api/src/unraid-api/plugin/plugin-management.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,12 @@ import { ConfigService } from '@nestjs/config';
import { ApiConfig } from '@unraid/shared/services/api-config.js';

import { DependencyService } from '@app/unraid-api/app/dependency.service.js';
import { ApiConfigPersistence } from '@app/unraid-api/config/api-config.module.js';

@Injectable()
export class PluginManagementService {
constructor(
private readonly configService: ConfigService<{ api: ApiConfig }, true>,
private readonly dependencyService: DependencyService,
private readonly apiConfigPersistence: ApiConfigPersistence
private readonly dependencyService: DependencyService
) {}

get plugins() {
Expand All @@ -20,14 +18,12 @@ export class PluginManagementService {

async addPlugin(...plugins: string[]) {
const added = this.addPluginToConfig(...plugins);
await this.persistConfig();
await this.installPlugins(...added);
await this.dependencyService.rebuildVendorArchive();
}

async removePlugin(...plugins: string[]) {
const removed = this.removePluginFromConfig(...plugins);
await this.persistConfig();
await this.uninstallPlugins(...removed);
await this.dependencyService.rebuildVendorArchive();
}
Expand Down Expand Up @@ -101,17 +97,11 @@ export class PluginManagementService {

async addBundledPlugin(...plugins: string[]) {
const added = this.addPluginToConfig(...plugins);
await this.persistConfig();
return added;
}

async removeBundledPlugin(...plugins: string[]) {
const removed = this.removePluginFromConfig(...plugins);
await this.persistConfig();
return removed;
}

private async persistConfig() {
return await this.apiConfigPersistence.persist();
}
}
1 change: 1 addition & 0 deletions packages/unraid-shared/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"@nestjs/common": "11.1.5",
"@nestjs/config": "4.0.2",
"@nestjs/graphql": "13.1.0",
"atomically": "2.0.3",
"class-validator": "0.14.2",
"graphql": "16.11.0",
"graphql-scalars": "1.24.2",
Expand Down
4 changes: 2 additions & 2 deletions packages/unraid-shared/src/util/config-file-handler.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Logger } from "@nestjs/common";
import { readFile, writeFile } from "node:fs/promises";
import { readFile, writeFile } from "atomically";
import { isEqual } from "lodash-es";
import { ConfigDefinition } from "./config-definition.js";
import { fileExists } from "./file.js";
Expand Down Expand Up @@ -122,7 +122,7 @@ export class ConfigFileHandler<T extends object> {

try {
const data = JSON.stringify(config, null, 2);
this.logger.verbose("Writing config");
this.logger.verbose(`Writing config to ${this.definition.configPath()}`);
await writeFile(this.definition.configPath(), data);
return true;
} catch (error) {
Expand Down
57 changes: 28 additions & 29 deletions packages/unraid-shared/src/util/file.ts
Original file line number Diff line number Diff line change
@@ -1,70 +1,69 @@
import { accessSync } from 'fs';
import { access, mkdir, writeFile } from 'fs/promises';
import { mkdirSync, writeFileSync } from 'fs';
import { F_OK } from 'node:constants';
import { dirname } from 'path';
import { accessSync } from "fs";
import { access, mkdir, writeFile } from "fs/promises";
import { mkdirSync, writeFileSync } from "fs";
import { F_OK } from "node:constants";
import { dirname } from "path";

/**
* Checks if a file exists asynchronously.
* @param path - The file path to check
* @returns Promise that resolves to true if file exists, false otherwise
*/
export const fileExists = async (path: string) =>
access(path, F_OK)
.then(() => true)
.catch(() => false);
access(path, F_OK)
.then(() => true)
.catch(() => false);

/**
* Checks if a file exists synchronously.
* @param path - The file path to check
* @returns true if file exists, false otherwise
*/
export const fileExistsSync = (path: string) => {
try {
accessSync(path, F_OK);
return true;
} catch (error: unknown) {
return false;
}
try {
accessSync(path, F_OK);
return true;
} catch (error: unknown) {
return false;
}
};

/**
* Writes data to a file, creating parent directories if they don't exist.
*
*
* This function ensures the directory structure exists before writing the file,
* equivalent to `mkdir -p` followed by file writing.
*
*
* @param path - The file path to write to
* @param data - The data to write (string or Buffer)
* @throws {Error} If path is invalid (null, empty, or not a string)
* @throws {Error} For any file system errors (EACCES, EPERM, ENOSPC, EISDIR, etc.)
*/
export const ensureWrite = async (path: string, data: string | Buffer) => {
if (!path || typeof path !== 'string') {
throw new Error(`Invalid path provided: ${path}`);
}
if (!path || typeof path !== "string") {
throw new Error(`Invalid path provided: ${path}`);
}

await mkdir(dirname(path), { recursive: true });
return await writeFile(path, data);
await mkdir(dirname(path), { recursive: true });
return await writeFile(path, data);
};

/**
* Writes data to a file synchronously, creating parent directories if they don't exist.
*
*
* This function ensures the directory structure exists before writing the file,
* equivalent to `mkdir -p` followed by file writing.
*
*
* @param path - The file path to write to
* @param data - The data to write (string or Buffer)
* @throws {Error} If path is invalid (null, empty, or not a string)
* @throws {Error} For any file system errors (EACCES, EPERM, ENOSPC, EISDIR, etc.)
*/
export const ensureWriteSync = (path: string, data: string | Buffer) => {
if (!path || typeof path !== 'string') {
throw new Error(`Invalid path provided: ${path}`);
}
if (!path || typeof path !== "string") {
throw new Error(`Invalid path provided: ${path}`);
}

mkdirSync(dirname(path), { recursive: true });
return writeFileSync(path, data);
mkdirSync(dirname(path), { recursive: true });
return writeFileSync(path, data);
};

5 changes: 4 additions & 1 deletion plugin/plugins/dynamix.unraid.net.plg
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,10 @@ exit 0

if [ "$is_7_2_or_higher" = true ]; then
echo "Unraid 7.2+ detected. Using safe removal method."

if ! /etc/rc.d/rc.unraid-api plugins remove unraid-api-plugin-connect -b; then
echo "Warning: Failed to remove connect API plugin"
fi

# Send notification to user
/usr/local/emhttp/webGui/scripts/notify \
-e "Unraid Connect" \
Expand Down
26 changes: 25 additions & 1 deletion pnpm-lock.yaml

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