From 979f1b11e99fca08411c8d1476bad8a908abcfc2 Mon Sep 17 00:00:00 2001 From: Eric Schultz Date: Thu, 6 Nov 2025 23:33:34 -0700 Subject: [PATCH 1/9] Adds version parsing and comparison utilities Introduces utilities for parsing, comparing, and checking if a version is within a specified range. --- eshtek/versions.ts | 87 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 eshtek/versions.ts diff --git a/eshtek/versions.ts b/eshtek/versions.ts new file mode 100644 index 0000000..1897bf2 --- /dev/null +++ b/eshtek/versions.ts @@ -0,0 +1,87 @@ +export interface VersionParts { + year: number; + month: number; + build: number; + patch: number; +} + +export const parseVersion = (version: string): VersionParts => { + const cleanVersion = version.split('-')[0]; + const parts = cleanVersion.split('.').map(p => { + const num = Number(p); + return isNaN(num) ? 0 : num; + }); + return { + year: parts[0] || 0, + month: parts[1] || 0, + build: parts[2] || 0, + patch: parts[3] || 0, + }; +}; + +export const compareVersions = (a: VersionParts, b: VersionParts): number => { + if (a.year !== b.year) return a.year - b.year; + if (a.month !== b.month) return a.month - b.month; + if (a.build !== b.build) return a.build - b.build; + return a.patch - b.patch; +}; + +export const parseRange = (range: string): { min?: VersionParts; max?: VersionParts; minInclusive: boolean; maxInclusive: boolean } => { + const parts = range.split(/\s+/); + let min: VersionParts | undefined; + let max: VersionParts | undefined; + let minInclusive = false; + let maxInclusive = false; + + for (let i = 0; i < parts.length; i++) { + const part = parts[i]; + if (part.startsWith('>=')) { + min = parseVersion(part.substring(2)); + minInclusive = true; + } else if (part.startsWith('>')) { + min = parseVersion(part.substring(1)); + minInclusive = false; + } else if (part.startsWith('<=')) { + max = parseVersion(part.substring(2)); + maxInclusive = true; + } else if (part.startsWith('<')) { + max = parseVersion(part.substring(1)); + maxInclusive = false; + } + } + + return { min, max, minInclusive, maxInclusive }; +}; + +export const isTrueNASVersionInRange = (buildVersion: string, versionRange: string): boolean => { + if (!buildVersion) { + return false; + } + + try { + const version = parseVersion(buildVersion); + const { min, max, minInclusive, maxInclusive } = parseRange(versionRange); + + if (min) { + const cmp = compareVersions(version, min); + if (minInclusive) { + if (cmp < 0) return false; + } else { + if (cmp <= 0) return false; + } + } + + if (max) { + const cmp = compareVersions(version, max); + if (maxInclusive) { + if (cmp > 0) return false; + } else { + if (cmp >= 0) return false; + } + } + + return true; + } catch (error) { + return false; + } +}; From 9955ec3a84b5b0bcf761ad334ee58a2b9bb88e2b Mon Sep 17 00:00:00 2001 From: Eric Schultz Date: Thu, 6 Nov 2025 23:39:16 -0700 Subject: [PATCH 2/9] npm run generate-schemas --- eshtek/server-schema.ts | 114 ++++++++++++++++++++-------------------- eshtek/user-schema.ts | 14 ++--- 2 files changed, 64 insertions(+), 64 deletions(-) diff --git a/eshtek/server-schema.ts b/eshtek/server-schema.ts index c8191d1..ad4e600 100644 --- a/eshtek/server-schema.ts +++ b/eshtek/server-schema.ts @@ -190,13 +190,6 @@ export const serverHealthWarningSchema = z.nativeEnum(ServerHealthWarning); export const serverActionsSchema = z.nativeEnum(ServerActions); -export const serverHealthSchema = z.object({ - healthy: z.boolean(), - errors: z.array(serverHealthErrorSchema), - warnings: z.array(serverHealthWarningSchema), - actions_available: z.array(serverActionsSchema), -}); - const diskTypeSchema = z.any(); const poolStatusSchema = z.any(); @@ -239,21 +232,6 @@ export const serverPoolNewSchema = serverPoolBasicsSchema.extend({ devnames: z.array(z.string()), }); -export const serverPoolSchema = serverPoolBasicsSchema.extend({ - id: z.number().optional(), - guid: z.string().optional(), - path: z.string(), - errors: z.array(serverPoolErrorSchema).optional(), - warnings: z.array(serverPoolWarningSchema).optional(), - useable_storage: z.string().optional(), - healthy: z.boolean().optional(), - status: poolStatusSchema, - status_detail: z.string(), - used_storage: z.string().optional(), - used_percentage: z.number().optional(), - drives: z.array(serverDriveSchema), -}); - export const serversSchema = z.object({ claimed: z.array(serverRecordSchema), configured: z.array(serverRecordSchema), @@ -265,32 +243,15 @@ export const serverFileSchema = z.object({ type: fileTypeSchema, }); -export const serverFolderSchema = z.object({ - label: z.string(), - access: serverAccessSchema, - pool: serverPoolSchema.optional(), - users: z.array(serverFolderUserSchema).optional(), - used_by: z.array(serverFolderUseSchema).optional(), - timeMachine: z.boolean().optional(), - quota: z.number().optional(), - encryption: z.boolean().optional(), - locked: z.boolean().optional(), - encryptionPassphrase: z.string().optional(), -}); - -export const serverFoldersSchema = z.object({ - user: z.array(serverFolderSchema), - system: z.array(serverFolderSchema), -}); - export const serverDrivesGroupedBySizeSchema = z.record( z.array(serverDriveSchema), ); -export const serverSystemDataSystemSchema = serverStatusBasicsSchema.extend({ - type: z.literal(ServerStatusType.SYSTEM), - data: serverSystemDataSystemDeviceDataSchema, - health: serverHealthSchema, +export const serverHealthSchema = z.object({ + healthy: z.boolean(), + errors: z.array(serverHealthErrorSchema), + warnings: z.array(serverHealthWarningSchema), + actions_available: z.array(serverActionsSchema), }); export const serverSystemDataStorageSchema = serverStatusBasicsSchema.extend({ @@ -306,19 +267,6 @@ export const serverSystemDataApplicationsSchema = health: appsHealthSchema, }); -export const serverSystemDataSchema = z.union([ - serverSystemDataSystemSchema, - serverSystemDataStorageSchema, - serverSystemDataApplicationsSchema, - serverSystemDataVirtualizationSchema, - serverSystemDataEmptySchema, -]); - -export const serverSystemSchema = serverStatusBasicsSchema.extend({ - type: z.literal(ServerStatusType.SYSTEM_OVERVIEW), - data: z.array(serverSystemDataSchema).optional(), -}); - export const serverNetworkInterfaceSchema = z.object({ id: z.string(), name: z.string(), @@ -338,7 +286,59 @@ export const serverNetworkInterfaceDetailedSchema = data: z.array(z.array(z.number())), }); +export const serverPoolSchema = serverPoolBasicsSchema.extend({ + id: z.number().optional(), + guid: z.string().optional(), + path: z.string(), + errors: z.array(serverPoolErrorSchema).optional(), + warnings: z.array(serverPoolWarningSchema).optional(), + useable_storage: z.string().optional(), + healthy: z.boolean().optional(), + status: poolStatusSchema, + status_detail: z.string(), + used_storage: z.string().optional(), + used_percentage: z.number().optional(), + drives: z.array(serverDriveSchema), +}); + export const serverStorageSchema = z.object({ pools: z.array(serverPoolSchema), unassigned: z.array(serverDriveSchema), }); + +export const serverFolderSchema = z.object({ + label: z.string(), + access: serverAccessSchema, + pool: serverPoolSchema.optional(), + users: z.array(serverFolderUserSchema).optional(), + used_by: z.array(serverFolderUseSchema).optional(), + timeMachine: z.boolean().optional(), + quota: z.number().optional(), + encryption: z.boolean().optional(), + locked: z.boolean().optional(), + encryptionPassphrase: z.string().optional(), +}); + +export const serverFoldersSchema = z.object({ + user: z.array(serverFolderSchema), + system: z.array(serverFolderSchema), +}); + +export const serverSystemDataSystemSchema = serverStatusBasicsSchema.extend({ + type: z.literal(ServerStatusType.SYSTEM), + data: serverSystemDataSystemDeviceDataSchema, + health: serverHealthSchema, +}); + +export const serverSystemDataSchema = z.union([ + serverSystemDataSystemSchema, + serverSystemDataStorageSchema, + serverSystemDataApplicationsSchema, + serverSystemDataVirtualizationSchema, + serverSystemDataEmptySchema, +]); + +export const serverSystemSchema = serverStatusBasicsSchema.extend({ + type: z.literal(ServerStatusType.SYSTEM_OVERVIEW), + data: z.array(serverSystemDataSchema).optional(), +}); diff --git a/eshtek/user-schema.ts b/eshtek/user-schema.ts index 7cb85c6..1f10198 100644 --- a/eshtek/user-schema.ts +++ b/eshtek/user-schema.ts @@ -17,13 +17,6 @@ export const userPreferenceTemperatureSchema = z.nativeEnum( export const userPurchaseTypeSchema = z.nativeEnum(UserPurchaseType); -export const userPurchaseSchema = z.object({ - active: z.boolean(), - purchase_type: userPurchaseTypeSchema, - purchased: z.date().optional(), - expiraration: z.date().optional(), -}); - export const newUserRequestSchema = z.object({ name: z.string(), email: z.string(), @@ -38,6 +31,13 @@ export const userPreferencesSchema = z.object({ temperature: userPreferenceTemperatureSchema, }); +export const userPurchaseSchema = z.object({ + active: z.boolean(), + purchase_type: userPurchaseTypeSchema, + purchased: z.date().optional(), + expiraration: z.date().optional(), +}); + export const userSchema = z.object({ id: idSchema, email: z.string(), From c889605e9e94859a14d724655fb019a68b73444c Mon Sep 17 00:00:00 2001 From: Eric Schultz Date: Sat, 8 Nov 2025 09:37:10 -0700 Subject: [PATCH 3/9] npm run generate-schemas --- eshtek/server-schema.ts | 114 ++++++++++++++++++++-------------------- eshtek/user-schema.ts | 14 ++--- 2 files changed, 64 insertions(+), 64 deletions(-) diff --git a/eshtek/server-schema.ts b/eshtek/server-schema.ts index ad4e600..c8191d1 100644 --- a/eshtek/server-schema.ts +++ b/eshtek/server-schema.ts @@ -190,6 +190,13 @@ export const serverHealthWarningSchema = z.nativeEnum(ServerHealthWarning); export const serverActionsSchema = z.nativeEnum(ServerActions); +export const serverHealthSchema = z.object({ + healthy: z.boolean(), + errors: z.array(serverHealthErrorSchema), + warnings: z.array(serverHealthWarningSchema), + actions_available: z.array(serverActionsSchema), +}); + const diskTypeSchema = z.any(); const poolStatusSchema = z.any(); @@ -232,60 +239,6 @@ export const serverPoolNewSchema = serverPoolBasicsSchema.extend({ devnames: z.array(z.string()), }); -export const serversSchema = z.object({ - claimed: z.array(serverRecordSchema), - configured: z.array(serverRecordSchema), -}); - -export const serverFileSchema = z.object({ - name: z.string(), - path: z.string(), - type: fileTypeSchema, -}); - -export const serverDrivesGroupedBySizeSchema = z.record( - z.array(serverDriveSchema), -); - -export const serverHealthSchema = z.object({ - healthy: z.boolean(), - errors: z.array(serverHealthErrorSchema), - warnings: z.array(serverHealthWarningSchema), - actions_available: z.array(serverActionsSchema), -}); - -export const serverSystemDataStorageSchema = serverStatusBasicsSchema.extend({ - type: z.literal(ServerStatusType.STORAGE), - data: z.object({ - drives: z.array(serverDriveSchema).optional(), - }), -}); - -export const serverSystemDataApplicationsSchema = - serverStatusBasicsSchema.extend({ - type: z.literal(ServerStatusType.APPLICATIONS), - health: appsHealthSchema, - }); - -export const serverNetworkInterfaceSchema = z.object({ - id: z.string(), - name: z.string(), - type: networkInterfaceTypeSchema, - in: z.number(), - out: z.number(), -}); - -export const serverNetworkInterfaceWithConfigurationSchema = - serverNetworkInterfaceSchema.extend({ - configuration: serverNetworkInterfaceConfigurationSchema, - supported_media: z.array(z.unknown()), - }); - -export const serverNetworkInterfaceDetailedSchema = - serverNetworkInterfaceWithConfigurationSchema.extend({ - data: z.array(z.array(z.number())), - }); - export const serverPoolSchema = serverPoolBasicsSchema.extend({ id: z.number().optional(), guid: z.string().optional(), @@ -301,9 +254,15 @@ export const serverPoolSchema = serverPoolBasicsSchema.extend({ drives: z.array(serverDriveSchema), }); -export const serverStorageSchema = z.object({ - pools: z.array(serverPoolSchema), - unassigned: z.array(serverDriveSchema), +export const serversSchema = z.object({ + claimed: z.array(serverRecordSchema), + configured: z.array(serverRecordSchema), +}); + +export const serverFileSchema = z.object({ + name: z.string(), + path: z.string(), + type: fileTypeSchema, }); export const serverFolderSchema = z.object({ @@ -324,12 +283,29 @@ export const serverFoldersSchema = z.object({ system: z.array(serverFolderSchema), }); +export const serverDrivesGroupedBySizeSchema = z.record( + z.array(serverDriveSchema), +); + export const serverSystemDataSystemSchema = serverStatusBasicsSchema.extend({ type: z.literal(ServerStatusType.SYSTEM), data: serverSystemDataSystemDeviceDataSchema, health: serverHealthSchema, }); +export const serverSystemDataStorageSchema = serverStatusBasicsSchema.extend({ + type: z.literal(ServerStatusType.STORAGE), + data: z.object({ + drives: z.array(serverDriveSchema).optional(), + }), +}); + +export const serverSystemDataApplicationsSchema = + serverStatusBasicsSchema.extend({ + type: z.literal(ServerStatusType.APPLICATIONS), + health: appsHealthSchema, + }); + export const serverSystemDataSchema = z.union([ serverSystemDataSystemSchema, serverSystemDataStorageSchema, @@ -342,3 +318,27 @@ export const serverSystemSchema = serverStatusBasicsSchema.extend({ type: z.literal(ServerStatusType.SYSTEM_OVERVIEW), data: z.array(serverSystemDataSchema).optional(), }); + +export const serverNetworkInterfaceSchema = z.object({ + id: z.string(), + name: z.string(), + type: networkInterfaceTypeSchema, + in: z.number(), + out: z.number(), +}); + +export const serverNetworkInterfaceWithConfigurationSchema = + serverNetworkInterfaceSchema.extend({ + configuration: serverNetworkInterfaceConfigurationSchema, + supported_media: z.array(z.unknown()), + }); + +export const serverNetworkInterfaceDetailedSchema = + serverNetworkInterfaceWithConfigurationSchema.extend({ + data: z.array(z.array(z.number())), + }); + +export const serverStorageSchema = z.object({ + pools: z.array(serverPoolSchema), + unassigned: z.array(serverDriveSchema), +}); diff --git a/eshtek/user-schema.ts b/eshtek/user-schema.ts index 1f10198..7cb85c6 100644 --- a/eshtek/user-schema.ts +++ b/eshtek/user-schema.ts @@ -17,6 +17,13 @@ export const userPreferenceTemperatureSchema = z.nativeEnum( export const userPurchaseTypeSchema = z.nativeEnum(UserPurchaseType); +export const userPurchaseSchema = z.object({ + active: z.boolean(), + purchase_type: userPurchaseTypeSchema, + purchased: z.date().optional(), + expiraration: z.date().optional(), +}); + export const newUserRequestSchema = z.object({ name: z.string(), email: z.string(), @@ -31,13 +38,6 @@ export const userPreferencesSchema = z.object({ temperature: userPreferenceTemperatureSchema, }); -export const userPurchaseSchema = z.object({ - active: z.boolean(), - purchase_type: userPurchaseTypeSchema, - purchased: z.date().optional(), - expiraration: z.date().optional(), -}); - export const userSchema = z.object({ id: idSchema, email: z.string(), From fc317350a8befc415a7efd89533479b747e5c162 Mon Sep 17 00:00:00 2001 From: Eric Schultz Date: Mon, 10 Nov 2025 15:27:38 -0700 Subject: [PATCH 4/9] Adds new error codes Adds new error codes to handle edge cases related to server updates and email action validation. These codes improve error handling and user experience during server upgrade processes and email-related actions. --- eshtek/errors.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/eshtek/errors.ts b/eshtek/errors.ts index f6ebfc5..eade801 100644 --- a/eshtek/errors.ts +++ b/eshtek/errors.ts @@ -8,6 +8,11 @@ export enum GlobalErrorCode { GENERIC_USER_ERROR = 'GENERIC_USER_ERROR', GENERIC_WEBSOCKET_ERROR = 'GENERIC_WEBSOCKET_ERROR', SERVER_NOT_CONNECTED = 'Server not currently connected', + SERVER_NOT_FOUND = 'Server not found', + SERVER_UPDATE_ALREADY_IN_PROGRESS = 'An update is already in progress for this server', + SERVER_UPDATE_NOT_AVAILABLE = 'No update is available for this server', + SERVER_UPDATE_NOT_SUPPORTED = 'Updates are not supported on TrueNAS 25.10.0+', + EMAIL_ACTION_INVALID_KEY = 'Invalid or expired link', INSTALL_APP_NO_APP_ID = 'App id not provided in request', INSTALL_APP_NOT_SUPPORTED_AND_NO_SCRIPT = 'Supported app not found and no install script provided', INSTALL_APP_NO_TRAIN_PROVIDED = 'Train is required if install script is provided', From 659833c82217e8072fd6edc891e3785c8807f2d4 Mon Sep 17 00:00:00 2001 From: Jerod Fritz Date: Tue, 11 Nov 2025 08:15:47 -0500 Subject: [PATCH 5/9] use same pattern as before install scripts errors --- eshtek/errors.ts | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/eshtek/errors.ts b/eshtek/errors.ts index f6ebfc5..7f937c3 100644 --- a/eshtek/errors.ts +++ b/eshtek/errors.ts @@ -7,23 +7,28 @@ export enum GlobalErrorCode { GENERIC_SERVER_ERROR = 'GENERIC_SERVER_ERROR', GENERIC_USER_ERROR = 'GENERIC_USER_ERROR', GENERIC_WEBSOCKET_ERROR = 'GENERIC_WEBSOCKET_ERROR', - SERVER_NOT_CONNECTED = 'Server not currently connected', - INSTALL_APP_NO_APP_ID = 'App id not provided in request', - INSTALL_APP_NOT_SUPPORTED_AND_NO_SCRIPT = 'Supported app not found and no install script provided', - INSTALL_APP_NO_TRAIN_PROVIDED = 'Train is required if install script is provided', - INSTALL_APP_DOCKER_HUB_RATE_LIMIT = 'Docker Hub rate limit exceeded, wait a few hours for it to reset', - PROCESS_INSTALL_SCRIPT_FAILED_JSON_PARSE = 'Failed to JSON parse install script', - PROCESS_INSTALL_SCRIPT_INVALID_JSON = 'Install script must be a valid JSON object', - PROCESS_INSTALL_SCRIPT_MISSING_VERSION = 'Install script must have a "version" field', - PROCESS_INSTALL_SCRIPT_UNSUPPORTED_VERSION = 'Unsupported install script version', - PROCESS_INSTALL_SCRIPT_MISSING_APP_VALUES = 'Install script must have an "app_values" field', - PROCESS_INSTALL_SCRIPT_UNKNOWN_LOCATION = 'Install script has an unknown location', - PROCESS_INSTALL_SCRIPT_INVALID_RANDOM_STRING_LENGTH = 'Install script has an invalid random string length', - PROCESS_INSTALL_SCRIPT_FAILED_JSON_PARSE_AFTER = 'Failed to JSON parse install script after processing', - PROCESS_INSTALL_SCRIPT_INVALID_MEMORY_PERCENTAGE = 'Install script has an invalid memory percentage', - PROCESS_INSTALL_SCRIPT_INVALID_MEMORY_VALUE = 'Install script has an invalid memory value', - PROCESS_INSTALL_SCRIPT_MISSING_QUESTION_RESPONSE = 'Install script requires a response for a question', - PROCESS_INSTALL_SCRIPT_INVALID_QUESTION_RESPONSE_TYPE = 'Install script question response has invalid type', - RUN_INSTALL_SCRIPT_UNSUPPORTED_VERSION = 'Install script must have a "version" field', - DDP_CLIENT_DISCONNECTED = 'DDPClient disconnected from DDP server', + SERVER_NOT_CONNECTED = 'SERVER_NOT_CONNECTED', + SERVER_NOT_FOUND = 'SERVER_NOT_FOUND', + SERVER_UPDATE_ALREADY_IN_PROGRESS = 'SERVER_UPDATE_ALREADY_IN_PROGRESS', + SERVER_UPDATE_NOT_AVAILABLE = 'SERVER_UPDATE_NOT_AVAILABLE', + SERVER_UPDATE_NOT_SUPPORTED = 'SERVER_UPDATE_NOT_SUPPORTED', + EMAIL_ACTION_INVALID_KEY = 'EMAIL_ACTION_INVALID_KEY', + INSTALL_APP_NO_APP_ID = 'INSTALL_APP_NO_APP_ID', + INSTALL_APP_NOT_SUPPORTED_AND_NO_SCRIPT = 'INSTALL_APP_NOT_SUPPORTED_AND_NO_SCRIPT', + INSTALL_APP_NO_TRAIN_PROVIDED = 'INSTALL_APP_NO_TRAIN_PROVIDED', + INSTALL_APP_DOCKER_HUB_RATE_LIMIT = 'INSTALL_APP_DOCKER_HUB_RATE_LIMIT', + PROCESS_INSTALL_SCRIPT_FAILED_JSON_PARSE = 'PROCESS_INSTALL_SCRIPT_FAILED_JSON_PARSE', + PROCESS_INSTALL_SCRIPT_INVALID_JSON = 'PROCESS_INSTALL_SCRIPT_INVALID_JSON', + PROCESS_INSTALL_SCRIPT_MISSING_VERSION = 'PROCESS_INSTALL_SCRIPT_MISSING_VERSION', + PROCESS_INSTALL_SCRIPT_UNSUPPORTED_VERSION = 'PROCESS_INSTALL_SCRIPT_UNSUPPORTED_VERSION', + PROCESS_INSTALL_SCRIPT_MISSING_APP_VALUES = 'PROCESS_INSTALL_SCRIPT_MISSING_APP_VALUES', + PROCESS_INSTALL_SCRIPT_UNKNOWN_LOCATION = 'PROCESS_INSTALL_SCRIPT_UNKNOWN_LOCATION', + PROCESS_INSTALL_SCRIPT_INVALID_RANDOM_STRING_LENGTH = 'PROCESS_INSTALL_SCRIPT_INVALID_RANDOM_STRING_LENGTH', + PROCESS_INSTALL_SCRIPT_FAILED_JSON_PARSE_AFTER = 'PROCESS_INSTALL_SCRIPT_FAILED_JSON_PARSE_AFTER', + PROCESS_INSTALL_SCRIPT_INVALID_MEMORY_PERCENTAGE = 'PROCESS_INSTALL_SCRIPT_INVALID_MEMORY_PERCENTAGE', + PROCESS_INSTALL_SCRIPT_INVALID_MEMORY_VALUE = 'PROCESS_INSTALL_SCRIPT_INVALID_MEMORY_VALUE', + PROCESS_INSTALL_SCRIPT_MISSING_QUESTION_RESPONSE = 'PROCESS_INSTALL_SCRIPT_MISSING_QUESTION_RESPONSE', + PROCESS_INSTALL_SCRIPT_INVALID_QUESTION_RESPONSE_TYPE = 'PROCESS_INSTALL_SCRIPT_INVALID_QUESTION_RESPONSE_TYPE', + RUN_INSTALL_SCRIPT_UNSUPPORTED_VERSION = 'RUN_INSTALL_SCRIPT_UNSUPPORTED_VERSION', + DDP_CLIENT_DISCONNECTED = 'DDP_CLIENT_DISCONNECTED', } From 4b25dade453afa87b6467cf40455126aa6674b18 Mon Sep 17 00:00:00 2001 From: Eric Schultz Date: Tue, 11 Nov 2025 15:47:34 -0700 Subject: [PATCH 6/9] Adds server upgrade info interface Defines a new interface for representing server upgrade information. This interface includes details such as the server name, host ID, current version, target version, and update status, which will be used to display upgrade related information. --- eshtek/server.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/eshtek/server.ts b/eshtek/server.ts index 3931ae8..88aafe3 100644 --- a/eshtek/server.ts +++ b/eshtek/server.ts @@ -407,3 +407,11 @@ export interface ServerHealth { warnings: ServerHealthWarning[]; actions_available: ServerActions[]; } + +export interface ServerUpgradeInfo { + serverName: string; + hostId: string; + currentVersion: string; + targetVersion: string; + updateStatus: string; +} From 93e17cce3be1177fb5f40530ec89e763adec2bb9 Mon Sep 17 00:00:00 2001 From: Eric Schultz Date: Tue, 11 Nov 2025 15:50:56 -0700 Subject: [PATCH 7/9] npm run generate-schemas --- eshtek/server-schema.ts | 8 ++++++++ package-lock.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/eshtek/server-schema.ts b/eshtek/server-schema.ts index c8191d1..cd52456 100644 --- a/eshtek/server-schema.ts +++ b/eshtek/server-schema.ts @@ -197,6 +197,14 @@ export const serverHealthSchema = z.object({ actions_available: z.array(serverActionsSchema), }); +export const serverUpgradeInfoSchema = z.object({ + serverName: z.string(), + hostId: z.string(), + currentVersion: z.string(), + targetVersion: z.string(), + updateStatus: z.string(), +}); + const diskTypeSchema = z.any(); const poolStatusSchema = z.any(); diff --git a/package-lock.json b/package-lock.json index e1c57f8..f50155e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "shared", + "name": "hexos-shared", "lockfileVersion": 3, "requires": true, "packages": { From 6544dbbad0fc1a10678e1ade45342076cabaf724 Mon Sep 17 00:00:00 2001 From: Eric Schultz Date: Tue, 11 Nov 2025 16:28:31 -0700 Subject: [PATCH 8/9] Adds InstallScriptCuration interface Adds InstallScriptCuration interface, defining the structure for curated install scripts. This interface includes fields for the script's name, URL, and the actual script content. --- eshtek/apps.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/eshtek/apps.ts b/eshtek/apps.ts index 045e105..2d74168 100644 --- a/eshtek/apps.ts +++ b/eshtek/apps.ts @@ -144,3 +144,9 @@ interface AppsInstallScriptV2 { } export type AppsInstallScript = AppsInstallScriptV1 | AppsInstallScriptV2; + +export interface InstallScriptCuration { + name: string; + url: string; + script: AppsInstallScript; +} From 1622a42efa1275fab156d2483dd52b35b1bcb8a5 Mon Sep 17 00:00:00 2001 From: Jerod Fritz Date: Wed, 12 Nov 2025 10:26:02 -0500 Subject: [PATCH 9/9] add new interfaces for etc.generate and service.reload --- .../api/api-call-directory.interface.ts | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/truenas/webui/interfaces/api/api-call-directory.interface.ts b/truenas/webui/interfaces/api/api-call-directory.interface.ts index ab3cb11..1f4bf14 100644 --- a/truenas/webui/interfaces/api/api-call-directory.interface.ts +++ b/truenas/webui/interfaces/api/api-call-directory.interface.ts @@ -881,6 +881,17 @@ export interface ApiCallDirectory { params: [ServiceName, { silent: boolean }]; response: boolean; // False indicates that service has been stopped. }; + 'service.reload': { + params: [ + service: string, + options?: { + ha_propagate?: boolean; + silent?: boolean; + timeout?: number | null; + } + ]; + response: boolean; + }; 'service.update': { params: [number | ServiceName, Partial]; response: number }; // Sharing @@ -902,7 +913,20 @@ export interface ApiCallDirectory { response: null | { reason: string }; }; 'sharing.smb.update': { params: [id: number, update: SmbShareUpdate]; response: SmbShare }; - 'sharing.smb.sync_registry': { params: []} + 'sharing.smb.sync_registry': { params: []; response: void }; + + // Etc + 'etc.generate': { + params: [ + name: string, + checkpoint?: 'initial' | 'interface_sync' | 'post_init' | 'pool_import' | 'pre_interface_sync' | null + ]; + response: Array<{ + path: string; + status: 'CHANGED' | 'REMOVED'; + changes: string[]; + }>; + }; // SMART 'smart.config': { params: void; response: SmartConfig };