mirror of
https://github.com/unraid/api.git
synced 2025-12-31 13:39:52 -06:00
fix: ensure no crash if emhttp state configs are missing (#1514)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added new utility functions to improve file writing reliability by ensuring parent directories exist before writing. * Introduced a new watch command for easier development workflow in the shared package. * **Bug Fixes** * Improved startup behavior by logging warnings for missing configuration keys instead of crashing, allowing initialization to proceed. * **Chores** * Updated configuration version number and reformatted plugin list for clarity. * Relocated certain GraphQL schema type and enum declarations without changing their content. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --- - To see the specific tasks where the Asana app for GitHub is being used, see below: - https://app.asana.com/0/0/1210788779106748
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
{
|
||||
"version": "4.8.0",
|
||||
"version": "4.9.5",
|
||||
"extraOrigins": [
|
||||
"https://google.com",
|
||||
"https://test.com"
|
||||
],
|
||||
"sandbox": true,
|
||||
"ssoSubIds": [],
|
||||
"plugins": ["unraid-api-plugin-connect"]
|
||||
"plugins": [
|
||||
"unraid-api-plugin-connect"
|
||||
]
|
||||
}
|
||||
@@ -247,347 +247,6 @@ A field whose value conforms to the standard URL format as specified in RFC3986:
|
||||
"""
|
||||
scalar URL
|
||||
|
||||
type DiskPartition {
|
||||
"""The name of the partition"""
|
||||
name: String!
|
||||
|
||||
"""The filesystem type of the partition"""
|
||||
fsType: DiskFsType!
|
||||
|
||||
"""The size of the partition in bytes"""
|
||||
size: Float!
|
||||
}
|
||||
|
||||
"""The type of filesystem on the disk partition"""
|
||||
enum DiskFsType {
|
||||
XFS
|
||||
BTRFS
|
||||
VFAT
|
||||
ZFS
|
||||
EXT4
|
||||
NTFS
|
||||
}
|
||||
|
||||
type Disk implements Node {
|
||||
id: PrefixedID!
|
||||
|
||||
"""The device path of the disk (e.g. /dev/sdb)"""
|
||||
device: String!
|
||||
|
||||
"""The type of disk (e.g. SSD, HDD)"""
|
||||
type: String!
|
||||
|
||||
"""The model name of the disk"""
|
||||
name: String!
|
||||
|
||||
"""The manufacturer of the disk"""
|
||||
vendor: String!
|
||||
|
||||
"""The total size of the disk in bytes"""
|
||||
size: Float!
|
||||
|
||||
"""The number of bytes per sector"""
|
||||
bytesPerSector: Float!
|
||||
|
||||
"""The total number of cylinders on the disk"""
|
||||
totalCylinders: Float!
|
||||
|
||||
"""The total number of heads on the disk"""
|
||||
totalHeads: Float!
|
||||
|
||||
"""The total number of sectors on the disk"""
|
||||
totalSectors: Float!
|
||||
|
||||
"""The total number of tracks on the disk"""
|
||||
totalTracks: Float!
|
||||
|
||||
"""The number of tracks per cylinder"""
|
||||
tracksPerCylinder: Float!
|
||||
|
||||
"""The number of sectors per track"""
|
||||
sectorsPerTrack: Float!
|
||||
|
||||
"""The firmware revision of the disk"""
|
||||
firmwareRevision: String!
|
||||
|
||||
"""The serial number of the disk"""
|
||||
serialNum: String!
|
||||
|
||||
"""The interface type of the disk"""
|
||||
interfaceType: DiskInterfaceType!
|
||||
|
||||
"""The SMART status of the disk"""
|
||||
smartStatus: DiskSmartStatus!
|
||||
|
||||
"""The current temperature of the disk in Celsius"""
|
||||
temperature: Float
|
||||
|
||||
"""The partitions on the disk"""
|
||||
partitions: [DiskPartition!]!
|
||||
}
|
||||
|
||||
"""The type of interface the disk uses to connect to the system"""
|
||||
enum DiskInterfaceType {
|
||||
SAS
|
||||
SATA
|
||||
USB
|
||||
PCIE
|
||||
UNKNOWN
|
||||
}
|
||||
|
||||
"""
|
||||
The SMART (Self-Monitoring, Analysis and Reporting Technology) status of the disk
|
||||
"""
|
||||
enum DiskSmartStatus {
|
||||
OK
|
||||
UNKNOWN
|
||||
}
|
||||
|
||||
type KeyFile {
|
||||
location: String
|
||||
contents: String
|
||||
}
|
||||
|
||||
type Registration implements Node {
|
||||
id: PrefixedID!
|
||||
type: registrationType
|
||||
keyFile: KeyFile
|
||||
state: RegistrationState
|
||||
expiration: String
|
||||
updateExpiration: String
|
||||
}
|
||||
|
||||
enum registrationType {
|
||||
BASIC
|
||||
PLUS
|
||||
PRO
|
||||
STARTER
|
||||
UNLEASHED
|
||||
LIFETIME
|
||||
INVALID
|
||||
TRIAL
|
||||
}
|
||||
|
||||
enum RegistrationState {
|
||||
TRIAL
|
||||
BASIC
|
||||
PLUS
|
||||
PRO
|
||||
STARTER
|
||||
UNLEASHED
|
||||
LIFETIME
|
||||
EEXPIRED
|
||||
EGUID
|
||||
EGUID1
|
||||
ETRIAL
|
||||
ENOKEYFILE
|
||||
ENOKEYFILE1
|
||||
ENOKEYFILE2
|
||||
ENOFLASH
|
||||
ENOFLASH1
|
||||
ENOFLASH2
|
||||
ENOFLASH3
|
||||
ENOFLASH4
|
||||
ENOFLASH5
|
||||
ENOFLASH6
|
||||
ENOFLASH7
|
||||
EBLACKLISTED
|
||||
EBLACKLISTED1
|
||||
EBLACKLISTED2
|
||||
ENOCONN
|
||||
}
|
||||
|
||||
type Vars implements Node {
|
||||
id: PrefixedID!
|
||||
|
||||
"""Unraid version"""
|
||||
version: String
|
||||
maxArraysz: Int
|
||||
maxCachesz: Int
|
||||
|
||||
"""Machine hostname"""
|
||||
name: String
|
||||
timeZone: String
|
||||
comment: String
|
||||
security: String
|
||||
workgroup: String
|
||||
domain: String
|
||||
domainShort: String
|
||||
hideDotFiles: Boolean
|
||||
localMaster: Boolean
|
||||
enableFruit: String
|
||||
|
||||
"""Should a NTP server be used for time sync?"""
|
||||
useNtp: Boolean
|
||||
|
||||
"""NTP Server 1"""
|
||||
ntpServer1: String
|
||||
|
||||
"""NTP Server 2"""
|
||||
ntpServer2: String
|
||||
|
||||
"""NTP Server 3"""
|
||||
ntpServer3: String
|
||||
|
||||
"""NTP Server 4"""
|
||||
ntpServer4: String
|
||||
domainLogin: String
|
||||
sysModel: String
|
||||
sysArraySlots: Int
|
||||
sysCacheSlots: Int
|
||||
sysFlashSlots: Int
|
||||
useSsl: Boolean
|
||||
|
||||
"""Port for the webui via HTTP"""
|
||||
port: Int
|
||||
|
||||
"""Port for the webui via HTTPS"""
|
||||
portssl: Int
|
||||
localTld: String
|
||||
bindMgt: Boolean
|
||||
|
||||
"""Should telnet be enabled?"""
|
||||
useTelnet: Boolean
|
||||
porttelnet: Int
|
||||
useSsh: Boolean
|
||||
portssh: Int
|
||||
startPage: String
|
||||
startArray: Boolean
|
||||
spindownDelay: String
|
||||
queueDepth: String
|
||||
spinupGroups: Boolean
|
||||
defaultFormat: String
|
||||
defaultFsType: String
|
||||
shutdownTimeout: Int
|
||||
luksKeyfile: String
|
||||
pollAttributes: String
|
||||
pollAttributesDefault: String
|
||||
pollAttributesStatus: String
|
||||
nrRequests: Int
|
||||
nrRequestsDefault: Int
|
||||
nrRequestsStatus: String
|
||||
mdNumStripes: Int
|
||||
mdNumStripesDefault: Int
|
||||
mdNumStripesStatus: String
|
||||
mdSyncWindow: Int
|
||||
mdSyncWindowDefault: Int
|
||||
mdSyncWindowStatus: String
|
||||
mdSyncThresh: Int
|
||||
mdSyncThreshDefault: Int
|
||||
mdSyncThreshStatus: String
|
||||
mdWriteMethod: Int
|
||||
mdWriteMethodDefault: String
|
||||
mdWriteMethodStatus: String
|
||||
shareDisk: String
|
||||
shareUser: String
|
||||
shareUserInclude: String
|
||||
shareUserExclude: String
|
||||
shareSmbEnabled: Boolean
|
||||
shareNfsEnabled: Boolean
|
||||
shareAfpEnabled: Boolean
|
||||
shareInitialOwner: String
|
||||
shareInitialGroup: String
|
||||
shareCacheEnabled: Boolean
|
||||
shareCacheFloor: String
|
||||
shareMoverSchedule: String
|
||||
shareMoverLogging: Boolean
|
||||
fuseRemember: String
|
||||
fuseRememberDefault: String
|
||||
fuseRememberStatus: String
|
||||
fuseDirectio: String
|
||||
fuseDirectioDefault: String
|
||||
fuseDirectioStatus: String
|
||||
shareAvahiEnabled: Boolean
|
||||
shareAvahiSmbName: String
|
||||
shareAvahiSmbModel: String
|
||||
shareAvahiAfpName: String
|
||||
shareAvahiAfpModel: String
|
||||
safeMode: Boolean
|
||||
startMode: String
|
||||
configValid: Boolean
|
||||
configError: ConfigErrorState
|
||||
joinStatus: String
|
||||
deviceCount: Int
|
||||
flashGuid: String
|
||||
flashProduct: String
|
||||
flashVendor: String
|
||||
regCheck: String
|
||||
regFile: String
|
||||
regGuid: String
|
||||
regTy: registrationType
|
||||
regState: RegistrationState
|
||||
|
||||
"""Registration owner"""
|
||||
regTo: String
|
||||
regTm: String
|
||||
regTm2: String
|
||||
regGen: String
|
||||
sbName: String
|
||||
sbVersion: String
|
||||
sbUpdated: String
|
||||
sbEvents: Int
|
||||
sbState: String
|
||||
sbClean: Boolean
|
||||
sbSynced: Int
|
||||
sbSyncErrs: Int
|
||||
sbSynced2: Int
|
||||
sbSyncExit: String
|
||||
sbNumDisks: Int
|
||||
mdColor: String
|
||||
mdNumDisks: Int
|
||||
mdNumDisabled: Int
|
||||
mdNumInvalid: Int
|
||||
mdNumMissing: Int
|
||||
mdNumNew: Int
|
||||
mdNumErased: Int
|
||||
mdResync: Int
|
||||
mdResyncCorr: String
|
||||
mdResyncPos: String
|
||||
mdResyncDb: String
|
||||
mdResyncDt: String
|
||||
mdResyncAction: String
|
||||
mdResyncSize: Int
|
||||
mdState: String
|
||||
mdVersion: String
|
||||
cacheNumDevices: Int
|
||||
cacheSbNumDisks: Int
|
||||
fsState: String
|
||||
|
||||
"""Human friendly string of array events happening"""
|
||||
fsProgress: String
|
||||
|
||||
"""
|
||||
Percentage from 0 - 100 while upgrading a disk or swapping parity drives
|
||||
"""
|
||||
fsCopyPrcnt: Int
|
||||
fsNumMounted: Int
|
||||
fsNumUnmountable: Int
|
||||
fsUnmountableMask: String
|
||||
|
||||
"""Total amount of user shares"""
|
||||
shareCount: Int
|
||||
|
||||
"""Total amount shares with SMB enabled"""
|
||||
shareSmbCount: Int
|
||||
|
||||
"""Total amount shares with NFS enabled"""
|
||||
shareNfsCount: Int
|
||||
|
||||
"""Total amount shares with AFP enabled"""
|
||||
shareAfpCount: Int
|
||||
shareMoverActive: Boolean
|
||||
csrfToken: String
|
||||
}
|
||||
|
||||
"""Possible error states for configuration"""
|
||||
enum ConfigErrorState {
|
||||
UNKNOWN_ERROR
|
||||
INELIGIBLE
|
||||
INVALID
|
||||
NO_KEY_SERVER
|
||||
WITHDRAWN
|
||||
}
|
||||
|
||||
type Permission {
|
||||
resource: Resource!
|
||||
actions: [String!]!
|
||||
@@ -961,6 +620,102 @@ enum ThemeName {
|
||||
white
|
||||
}
|
||||
|
||||
type DiskPartition {
|
||||
"""The name of the partition"""
|
||||
name: String!
|
||||
|
||||
"""The filesystem type of the partition"""
|
||||
fsType: DiskFsType!
|
||||
|
||||
"""The size of the partition in bytes"""
|
||||
size: Float!
|
||||
}
|
||||
|
||||
"""The type of filesystem on the disk partition"""
|
||||
enum DiskFsType {
|
||||
XFS
|
||||
BTRFS
|
||||
VFAT
|
||||
ZFS
|
||||
EXT4
|
||||
NTFS
|
||||
}
|
||||
|
||||
type Disk implements Node {
|
||||
id: PrefixedID!
|
||||
|
||||
"""The device path of the disk (e.g. /dev/sdb)"""
|
||||
device: String!
|
||||
|
||||
"""The type of disk (e.g. SSD, HDD)"""
|
||||
type: String!
|
||||
|
||||
"""The model name of the disk"""
|
||||
name: String!
|
||||
|
||||
"""The manufacturer of the disk"""
|
||||
vendor: String!
|
||||
|
||||
"""The total size of the disk in bytes"""
|
||||
size: Float!
|
||||
|
||||
"""The number of bytes per sector"""
|
||||
bytesPerSector: Float!
|
||||
|
||||
"""The total number of cylinders on the disk"""
|
||||
totalCylinders: Float!
|
||||
|
||||
"""The total number of heads on the disk"""
|
||||
totalHeads: Float!
|
||||
|
||||
"""The total number of sectors on the disk"""
|
||||
totalSectors: Float!
|
||||
|
||||
"""The total number of tracks on the disk"""
|
||||
totalTracks: Float!
|
||||
|
||||
"""The number of tracks per cylinder"""
|
||||
tracksPerCylinder: Float!
|
||||
|
||||
"""The number of sectors per track"""
|
||||
sectorsPerTrack: Float!
|
||||
|
||||
"""The firmware revision of the disk"""
|
||||
firmwareRevision: String!
|
||||
|
||||
"""The serial number of the disk"""
|
||||
serialNum: String!
|
||||
|
||||
"""The interface type of the disk"""
|
||||
interfaceType: DiskInterfaceType!
|
||||
|
||||
"""The SMART status of the disk"""
|
||||
smartStatus: DiskSmartStatus!
|
||||
|
||||
"""The current temperature of the disk in Celsius"""
|
||||
temperature: Float
|
||||
|
||||
"""The partitions on the disk"""
|
||||
partitions: [DiskPartition!]!
|
||||
}
|
||||
|
||||
"""The type of interface the disk uses to connect to the system"""
|
||||
enum DiskInterfaceType {
|
||||
SAS
|
||||
SATA
|
||||
USB
|
||||
PCIE
|
||||
UNKNOWN
|
||||
}
|
||||
|
||||
"""
|
||||
The SMART (Self-Monitoring, Analysis and Reporting Technology) status of the disk
|
||||
"""
|
||||
enum DiskSmartStatus {
|
||||
OK
|
||||
UNKNOWN
|
||||
}
|
||||
|
||||
type InfoApps implements Node {
|
||||
id: PrefixedID!
|
||||
|
||||
@@ -1351,6 +1106,60 @@ type Owner {
|
||||
avatar: String!
|
||||
}
|
||||
|
||||
type KeyFile {
|
||||
location: String
|
||||
contents: String
|
||||
}
|
||||
|
||||
type Registration implements Node {
|
||||
id: PrefixedID!
|
||||
type: registrationType
|
||||
keyFile: KeyFile
|
||||
state: RegistrationState
|
||||
expiration: String
|
||||
updateExpiration: String
|
||||
}
|
||||
|
||||
enum registrationType {
|
||||
BASIC
|
||||
PLUS
|
||||
PRO
|
||||
STARTER
|
||||
UNLEASHED
|
||||
LIFETIME
|
||||
INVALID
|
||||
TRIAL
|
||||
}
|
||||
|
||||
enum RegistrationState {
|
||||
TRIAL
|
||||
BASIC
|
||||
PLUS
|
||||
PRO
|
||||
STARTER
|
||||
UNLEASHED
|
||||
LIFETIME
|
||||
EEXPIRED
|
||||
EGUID
|
||||
EGUID1
|
||||
ETRIAL
|
||||
ENOKEYFILE
|
||||
ENOKEYFILE1
|
||||
ENOKEYFILE2
|
||||
ENOFLASH
|
||||
ENOFLASH1
|
||||
ENOFLASH2
|
||||
ENOFLASH3
|
||||
ENOFLASH4
|
||||
ENOFLASH5
|
||||
ENOFLASH6
|
||||
ENOFLASH7
|
||||
EBLACKLISTED
|
||||
EBLACKLISTED1
|
||||
EBLACKLISTED2
|
||||
ENOCONN
|
||||
}
|
||||
|
||||
type ProfileModel implements Node {
|
||||
id: PrefixedID!
|
||||
username: String!
|
||||
@@ -1416,6 +1225,197 @@ type Settings implements Node {
|
||||
api: ApiConfig!
|
||||
}
|
||||
|
||||
type Vars implements Node {
|
||||
id: PrefixedID!
|
||||
|
||||
"""Unraid version"""
|
||||
version: String
|
||||
maxArraysz: Int
|
||||
maxCachesz: Int
|
||||
|
||||
"""Machine hostname"""
|
||||
name: String
|
||||
timeZone: String
|
||||
comment: String
|
||||
security: String
|
||||
workgroup: String
|
||||
domain: String
|
||||
domainShort: String
|
||||
hideDotFiles: Boolean
|
||||
localMaster: Boolean
|
||||
enableFruit: String
|
||||
|
||||
"""Should a NTP server be used for time sync?"""
|
||||
useNtp: Boolean
|
||||
|
||||
"""NTP Server 1"""
|
||||
ntpServer1: String
|
||||
|
||||
"""NTP Server 2"""
|
||||
ntpServer2: String
|
||||
|
||||
"""NTP Server 3"""
|
||||
ntpServer3: String
|
||||
|
||||
"""NTP Server 4"""
|
||||
ntpServer4: String
|
||||
domainLogin: String
|
||||
sysModel: String
|
||||
sysArraySlots: Int
|
||||
sysCacheSlots: Int
|
||||
sysFlashSlots: Int
|
||||
useSsl: Boolean
|
||||
|
||||
"""Port for the webui via HTTP"""
|
||||
port: Int
|
||||
|
||||
"""Port for the webui via HTTPS"""
|
||||
portssl: Int
|
||||
localTld: String
|
||||
bindMgt: Boolean
|
||||
|
||||
"""Should telnet be enabled?"""
|
||||
useTelnet: Boolean
|
||||
porttelnet: Int
|
||||
useSsh: Boolean
|
||||
portssh: Int
|
||||
startPage: String
|
||||
startArray: Boolean
|
||||
spindownDelay: String
|
||||
queueDepth: String
|
||||
spinupGroups: Boolean
|
||||
defaultFormat: String
|
||||
defaultFsType: String
|
||||
shutdownTimeout: Int
|
||||
luksKeyfile: String
|
||||
pollAttributes: String
|
||||
pollAttributesDefault: String
|
||||
pollAttributesStatus: String
|
||||
nrRequests: Int
|
||||
nrRequestsDefault: Int
|
||||
nrRequestsStatus: String
|
||||
mdNumStripes: Int
|
||||
mdNumStripesDefault: Int
|
||||
mdNumStripesStatus: String
|
||||
mdSyncWindow: Int
|
||||
mdSyncWindowDefault: Int
|
||||
mdSyncWindowStatus: String
|
||||
mdSyncThresh: Int
|
||||
mdSyncThreshDefault: Int
|
||||
mdSyncThreshStatus: String
|
||||
mdWriteMethod: Int
|
||||
mdWriteMethodDefault: String
|
||||
mdWriteMethodStatus: String
|
||||
shareDisk: String
|
||||
shareUser: String
|
||||
shareUserInclude: String
|
||||
shareUserExclude: String
|
||||
shareSmbEnabled: Boolean
|
||||
shareNfsEnabled: Boolean
|
||||
shareAfpEnabled: Boolean
|
||||
shareInitialOwner: String
|
||||
shareInitialGroup: String
|
||||
shareCacheEnabled: Boolean
|
||||
shareCacheFloor: String
|
||||
shareMoverSchedule: String
|
||||
shareMoverLogging: Boolean
|
||||
fuseRemember: String
|
||||
fuseRememberDefault: String
|
||||
fuseRememberStatus: String
|
||||
fuseDirectio: String
|
||||
fuseDirectioDefault: String
|
||||
fuseDirectioStatus: String
|
||||
shareAvahiEnabled: Boolean
|
||||
shareAvahiSmbName: String
|
||||
shareAvahiSmbModel: String
|
||||
shareAvahiAfpName: String
|
||||
shareAvahiAfpModel: String
|
||||
safeMode: Boolean
|
||||
startMode: String
|
||||
configValid: Boolean
|
||||
configError: ConfigErrorState
|
||||
joinStatus: String
|
||||
deviceCount: Int
|
||||
flashGuid: String
|
||||
flashProduct: String
|
||||
flashVendor: String
|
||||
regCheck: String
|
||||
regFile: String
|
||||
regGuid: String
|
||||
regTy: registrationType
|
||||
regState: RegistrationState
|
||||
|
||||
"""Registration owner"""
|
||||
regTo: String
|
||||
regTm: String
|
||||
regTm2: String
|
||||
regGen: String
|
||||
sbName: String
|
||||
sbVersion: String
|
||||
sbUpdated: String
|
||||
sbEvents: Int
|
||||
sbState: String
|
||||
sbClean: Boolean
|
||||
sbSynced: Int
|
||||
sbSyncErrs: Int
|
||||
sbSynced2: Int
|
||||
sbSyncExit: String
|
||||
sbNumDisks: Int
|
||||
mdColor: String
|
||||
mdNumDisks: Int
|
||||
mdNumDisabled: Int
|
||||
mdNumInvalid: Int
|
||||
mdNumMissing: Int
|
||||
mdNumNew: Int
|
||||
mdNumErased: Int
|
||||
mdResync: Int
|
||||
mdResyncCorr: String
|
||||
mdResyncPos: String
|
||||
mdResyncDb: String
|
||||
mdResyncDt: String
|
||||
mdResyncAction: String
|
||||
mdResyncSize: Int
|
||||
mdState: String
|
||||
mdVersion: String
|
||||
cacheNumDevices: Int
|
||||
cacheSbNumDisks: Int
|
||||
fsState: String
|
||||
|
||||
"""Human friendly string of array events happening"""
|
||||
fsProgress: String
|
||||
|
||||
"""
|
||||
Percentage from 0 - 100 while upgrading a disk or swapping parity drives
|
||||
"""
|
||||
fsCopyPrcnt: Int
|
||||
fsNumMounted: Int
|
||||
fsNumUnmountable: Int
|
||||
fsUnmountableMask: String
|
||||
|
||||
"""Total amount of user shares"""
|
||||
shareCount: Int
|
||||
|
||||
"""Total amount shares with SMB enabled"""
|
||||
shareSmbCount: Int
|
||||
|
||||
"""Total amount shares with NFS enabled"""
|
||||
shareNfsCount: Int
|
||||
|
||||
"""Total amount shares with AFP enabled"""
|
||||
shareAfpCount: Int
|
||||
shareMoverActive: Boolean
|
||||
csrfToken: String
|
||||
}
|
||||
|
||||
"""Possible error states for configuration"""
|
||||
enum ConfigErrorState {
|
||||
UNKNOWN_ERROR
|
||||
INELIGIBLE
|
||||
INVALID
|
||||
NO_KEY_SERVER
|
||||
WITHDRAWN
|
||||
}
|
||||
|
||||
type VmDomain implements Node {
|
||||
"""The unique identifier for the vm (uuid)"""
|
||||
id: PrefixedID!
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { writeFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
import { ensureWriteSync } from '@unraid/shared/util/file.js';
|
||||
import { isEqual } from 'lodash-es';
|
||||
|
||||
import type { RootState } from '@app/store/index.js';
|
||||
@@ -27,8 +27,11 @@ export const startStoreSync = async () => {
|
||||
!isEqual(state, lastState) &&
|
||||
state.paths['myservers-config-states']
|
||||
) {
|
||||
writeFileSync(join(state.paths.states, 'config.log'), JSON.stringify(state.config, null, 2));
|
||||
writeFileSync(
|
||||
ensureWriteSync(
|
||||
join(state.paths.states, 'config.log'),
|
||||
JSON.stringify(state.config, null, 2)
|
||||
);
|
||||
ensureWriteSync(
|
||||
join(state.paths.states, 'graphql.log'),
|
||||
JSON.stringify(state.minigraph, null, 2)
|
||||
);
|
||||
|
||||
@@ -130,11 +130,19 @@ export class MothershipConnectionService implements OnModuleInit, OnModuleDestro
|
||||
}
|
||||
|
||||
async onModuleInit() {
|
||||
// Crash on startup if these config values are not set initially
|
||||
// Warn on startup if these config values are not set initially
|
||||
const { unraidVersion, flashGuid, apiVersion } = this.configKeys;
|
||||
const warnings: string[] = [];
|
||||
[unraidVersion, flashGuid, apiVersion].forEach((key) => {
|
||||
this.configService.getOrThrow(key);
|
||||
try {
|
||||
this.configService.getOrThrow(key);
|
||||
} catch (error) {
|
||||
warnings.push(`${key} is not set`);
|
||||
}
|
||||
});
|
||||
if (warnings.length > 0) {
|
||||
this.logger.warn('Missing config values: %s', warnings.join(', '));
|
||||
}
|
||||
// Setup IDENTITY_CHANGED & METADATA_CHANGED events
|
||||
this.setupIdentitySubscription();
|
||||
this.setupMetadataChangedEvent();
|
||||
|
||||
9
packages/unraid-shared/justfile
Normal file
9
packages/unraid-shared/justfile
Normal file
@@ -0,0 +1,9 @@
|
||||
# Justfile for unraid-shared
|
||||
|
||||
# Default recipe to run when just is called without arguments
|
||||
default:
|
||||
@just --list
|
||||
|
||||
# Watch for changes in src files and run clean + build
|
||||
watch:
|
||||
watchexec -r -e ts,tsx -w src -- pnpm build
|
||||
@@ -1,11 +1,24 @@
|
||||
import { accessSync } from 'fs';
|
||||
import { access } from 'fs/promises';
|
||||
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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
@@ -14,3 +27,44 @@ export const fileExistsSync = (path: string) => {
|
||||
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}`);
|
||||
}
|
||||
|
||||
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}`);
|
||||
}
|
||||
|
||||
mkdirSync(dirname(path), { recursive: true });
|
||||
return writeFileSync(path, data);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user