Skip to content
Open
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
3 changes: 2 additions & 1 deletion api/dev/configs/api.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@
"plugins": [
"unraid-api-plugin-connect"
]
}
}

61 changes: 61 additions & 0 deletions api/generated-schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -1355,6 +1355,64 @@ type Docker implements Node {
containerUpdateStatuses: [ExplicitStatusItem!]!
}

"""Progress information for a single image layer"""
type DockerLayerProgress {
"""Layer ID (short hash)"""
layerId: String!

"""Current status of the layer"""
status: String!

"""Download/extract progress percentage (0-100)"""
progress: Float

"""Bytes downloaded/processed"""
current: Int

"""Total bytes for this layer"""
total: Int
}

"""Real-time progress update for a Docker container update operation"""
type DockerUpdateProgress {
"""Container ID being updated"""
containerId: PrefixedID!

"""Container name being updated"""
containerName: String!

"""Type of progress event"""
type: DockerUpdateEventType!

"""Human-readable message or log line"""
message: String

"""Layer ID for layer-specific events"""
layerId: String

"""Overall progress percentage (0-100) for the current operation"""
overallProgress: Float

"""Per-layer progress details"""
layers: [DockerLayerProgress!]

"""Error message if type is ERROR"""
error: String
}

"""Type of Docker update progress event"""
enum DockerUpdateEventType {
STARTED
LAYER_DOWNLOADING
LAYER_EXTRACTING
LAYER_COMPLETE
LAYER_ALREADY_EXISTS
PULLING
LOG
COMPLETE
ERROR
}
Comment on lines +1358 to +1414
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

current/total should not be GraphQL Int (32-bit) for byte counts; use BigInt (or Float) instead.
Large layers can exceed Int limits and break the subscription payload.


type DockerTemplateSyncResult {
scanned: Int!
matched: Int!
Expand Down Expand Up @@ -2898,6 +2956,9 @@ type Subscription {
parityHistorySubscription: ParityCheck!
arraySubscription: UnraidArray!
dockerContainerStats: DockerContainerStats!

"""Real-time progress updates for Docker container update operations"""
dockerUpdateProgress: DockerUpdateProgress!
logFile(path: String!): LogFileContent!
systemMetricsCpu: CpuUtilization!
systemMetricsCpuTelemetry: CpuPackages!
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { Field, Float, Int, ObjectType, registerEnumType } from '@nestjs/graphql';

import { PrefixedID } from '@unraid/shared/prefixed-id-scalar.js';

export enum DockerUpdateEventType {
STARTED = 'STARTED',
LAYER_DOWNLOADING = 'LAYER_DOWNLOADING',
LAYER_EXTRACTING = 'LAYER_EXTRACTING',
LAYER_COMPLETE = 'LAYER_COMPLETE',
LAYER_ALREADY_EXISTS = 'LAYER_ALREADY_EXISTS',
PULLING = 'PULLING',
LOG = 'LOG',
COMPLETE = 'COMPLETE',
ERROR = 'ERROR',
}

registerEnumType(DockerUpdateEventType, {
name: 'DockerUpdateEventType',
description: 'Type of Docker update progress event',
});

@ObjectType({ description: 'Progress information for a single image layer' })
export class DockerLayerProgress {
@Field(() => String, { description: 'Layer ID (short hash)' })
layerId!: string;

@Field(() => String, { description: 'Current status of the layer' })
status!: string;

@Field(() => Float, { nullable: true, description: 'Download/extract progress percentage (0-100)' })
progress?: number;

@Field(() => Int, { nullable: true, description: 'Bytes downloaded/processed' })
current?: number;

@Field(() => Int, { nullable: true, description: 'Total bytes for this layer' })
total?: number;
Comment on lines +33 to +37

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use BigInt for layer byte counts

The current and total fields are exposed as GraphQL Int, which is limited to 32‑bit signed values. Docker layer sizes can exceed 2,147,483,647 bytes for large images, and when that happens GraphQL serialization throws ("Int cannot represent non 32-bit signed integer"), causing the subscription to error and progress streaming to break for those updates. Consider switching these fields to GraphQLBigInt (as used elsewhere for byte sizes) or another 64‑bit friendly scalar.

Useful? React with 👍 / 👎.

}
Comment on lines +22 to +38
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for 64-bit scalar registrations (Long/BigInt)
rg -n --hidden -S "registerScalarType\(|GraphQLBigInt|BigIntResolver|LongResolver|GraphQLLong|class .*Long|scalar\s+Long|scalar\s+BigInt" .

Repository: unraid/api

Length of output: 3432


🏁 Script executed:

# List files in the GraphQL resolvers directory
find api/src/unraid-api/graph -type f -name "*.ts" | head -20

Repository: unraid/api

Length of output: 1247


🏁 Script executed:

# Check the docker-update-progress.model.ts file
cat -n api/src/unraid-api/graph/resolvers/docker/docker-update-progress.model.ts

Repository: unraid/api

Length of output: 2961


🏁 Script executed:

# Search for other byte counter patterns in the codebase
rg -n "bytes|Bytes" api/src --type ts -A 2 | head -50

Repository: unraid/api

Length of output: 3544


Use GraphQLBigInt for byte counters to prevent 32-bit overflow.

The current and total fields (lines 33, 36) represent byte counts but use GraphQL Int (32-bit signed), which overflows beyond ~2.1 GB. The repo already uses GraphQLBigInt from 'graphql-scalars' for byte counters in memory.model.ts, docker.model.ts, and array.model.ts. Update the imports and field types:

Required changes
-import { Field, Float, Int, ObjectType, registerEnumType } from '@nestjs/graphql';
+import { Field, Float, ObjectType, registerEnumType } from '@nestjs/graphql';
+import { GraphQLBigInt } from 'graphql-scalars';
 import { PrefixedID } from '@unraid/shared/prefixed-id-scalar.js';

 @ObjectType({ description: 'Progress information for a single image layer' })
 export class DockerLayerProgress {
     @Field(() => String, { description: 'Layer ID (short hash)' })
     layerId!: string;

     @Field(() => String, { description: 'Current status of the layer' })
     status!: string;

     @Field(() => Float, { nullable: true, description: 'Download/extract progress percentage (0-100)' })
     progress?: number;

-    @Field(() => Int, { nullable: true, description: 'Bytes downloaded/processed' })
+    @Field(() => GraphQLBigInt, { nullable: true, description: 'Bytes downloaded/processed' })
     current?: number;

-    @Field(() => Int, { nullable: true, description: 'Total bytes for this layer' })
+    @Field(() => GraphQLBigInt, { nullable: true, description: 'Total bytes for this layer' })
     total?: number;
 }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In @api/src/unraid-api/graph/resolvers/docker/docker-update-progress.model.ts
around lines 22 - 38, The DockerLayerProgress model uses GraphQL Int for byte
counters which can overflow; update the file to import GraphQLBigInt from
'graphql-scalars', remove the Int import, and change the @Field type for the
current and total properties to use GraphQLBigInt (keeping nullable and
descriptions), and adjust the TypeScript property types accordingly (e.g.,
BigInt or string as used elsewhere) for the current and total fields in the
DockerLayerProgress class.


@ObjectType({ description: 'Real-time progress update for a Docker container update operation' })
export class DockerUpdateProgress {
@Field(() => PrefixedID, { description: 'Container ID being updated' })
containerId!: string;

@Field(() => String, { description: 'Container name being updated' })
containerName!: string;

@Field(() => DockerUpdateEventType, { description: 'Type of progress event' })
type!: DockerUpdateEventType;

@Field(() => String, { nullable: true, description: 'Human-readable message or log line' })
message?: string;

@Field(() => String, { nullable: true, description: 'Layer ID for layer-specific events' })
layerId?: string;

@Field(() => Float, {
nullable: true,
description: 'Overall progress percentage (0-100) for the current operation',
})
overallProgress?: number;

@Field(() => [DockerLayerProgress], {
nullable: true,
description: 'Per-layer progress details',
})
layers?: DockerLayerProgress[];

@Field(() => String, { nullable: true, description: 'Error message if type is ERROR' })
error?: string;
}
Loading
Loading