From 979f1b11e99fca08411c8d1476bad8a908abcfc2 Mon Sep 17 00:00:00 2001 From: Eric Schultz Date: Thu, 6 Nov 2025 23:33:34 -0700 Subject: [PATCH 1/3] 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/3] 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/3] 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(),