Skip to content

Conversation

@elibosley
Copy link
Member

@elibosley elibosley commented Jul 31, 2025

Summary by CodeRabbit

  • New Features

    • Added mothership proxy integration with WebSocket support for remote GraphQL queries and subscriptions.
    • Implemented connection status tracking for development environments.
  • Bug Fixes

    • Disabled file modifications in development mode to prevent unintended changes.
  • Chores

    • Updated development configuration and environment variables.
    • Updated internal package dependencies for plugin integration.
    • Updated API version configuration to 4.27.2.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jul 31, 2025

Walkthrough

This pull request adds mothership proxy infrastructure to the Unraid API, introducing GraphQL forwarding services, subscription handling, and WebSocket-based server communication. Development configurations are updated with new environment variables, state files, and a package path alias change from unraid-api-plugin-connect to unraid-api-plugin-connect-2.

Changes

Cohort / File(s) Summary
Development Environment Configuration
api/.env.development
Added environment variables for development: PATHS_CONNECT_STATUS, CHOKIDAR_USEPOLLING, LOG_TRANSPORT, LOG_LEVEL, ENABLE_NEXT_DOCKER_RELEASE, SKIP_CONNECT_PLUGIN_CHECK. Updated MOTHERSHIP_GRAPHQL_LINK to WebSocket URL and added MOTHERSHIP_BASE_URL.
Development State & Config
api/dev/states/connectStatus.json, api/dev/configs/api.json, api/dev/configs/connect.json
Added connection status state file with PRE\_INIT status. Updated API version from 4.25.3 to 4.27.2. Updated connect config apikey placeholder to LOCAL\_API\_KEY\_HERE.
File Modification Service
api/src/unraid-api/unraid-file-modifier/...
Added early-return checks to skip file modifications when NODE_ENV is 'development' in both file-modification.ts and unraid-file-modifier.service.ts.
Build & Configuration
api/scripts/build.ts, api/vite.config.ts, api/docs/developer/api-plugins.md
Updated vendored package path from packages/unraid-api-plugin-connect to packages/unraid-api-plugin-connect-2 across build script, Vite config, and documentation. Updated import path in build script to use alias.
Mothership Proxy Services
packages/unraid-api-plugin-connect-2/src/mothership-proxy/local-graphql-executor.service.ts, mothership-subscription.handler.ts, unraid-server-client.service.ts, mothership.module.ts
Added LocalGraphQLExecutor for transforming and forwarding GraphQL queries locally. Introduced MothershipSubscriptionHandler for subscription lifecycle management. Created UnraidServerClient and UnraidServerClientService for WebSocket-based mothership communication with GraphQL routing and reconnection logic. Established MothershipModule to wire these components.

Sequence Diagram

sequenceDiagram
    participant Mothership as Mothership Server
    participant Client as UnraidServerClient
    participant Executor as LocalGraphQLExecutor
    participant Handler as MothershipSubscriptionHandler
    participant Local as Local API

    Mothership->>Client: WebSocket Connect
    Client->>Mothership: Send API Key (Headers)
    activate Client
    Client->>Client: Periodic Ping/Keep-Alive
    
    Mothership->>Client: Send GraphQL Query (remote_query)
    activate Client
    Client->>Client: Parse Message
    
    Client->>Executor: execute({ query, operationName, variables })
    activate Executor
    Executor->>Executor: Transform Remote Query to Local<br/>(AST manipulation)
    Executor->>Local: Execute Transformed Query
    activate Local
    Local-->>Executor: Query Result
    deactivate Local
    Executor-->>Client: Return Data or Error
    deactivate Executor
    
    Client->>Mothership: Send GraphQLResponse<br/>(operationId, messageId, data/error)
    deactivate Client
    
    Mothership->>Client: Subscription Start (subscription_start)
    activate Client
    Client->>Handler: addSubscription(sha256, operationId)
    activate Handler
    Handler->>Handler: Track in activeSubscriptions
    Handler-->>Client: Subscription Registered
    deactivate Handler
    
    Client->>Mothership: Periodically Ping Subscriptions
    activate Handler
    Handler->>Handler: pingSubscription(sha256)
    Handler->>Handler: Update lastPing
    deactivate Handler
    
    Mothership->>Client: Subscription Stop (subscription_stop)
    activate Client
    Client->>Handler: removeSubscription(sha256)
    activate Handler
    Handler->>Handler: Remove from activeSubscriptions
    deactivate Handler
    deactivate Client
    
    rect rgba(200, 220, 255, 0.3)
        note over Client: Reconnection with Exponential Backoff<br/>triggered on disconnect
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • High-logic-density services: UnraidServerClient and LocalGraphQLExecutor involve WebSocket connection management, GraphQL AST transformation, and error handling that require careful verification
  • Interconnected dependencies: Multiple services (UnraidServerClient, LocalGraphQLExecutor, MothershipSubscriptionHandler, MothershipConnectionService) work together with injected dependencies
  • Configuration consistency: Verify that environment variable changes, path aliasing across build/vite configs, and package vendoring updates are synchronized
  • Subscription lifecycle logic: Ensure state tracking in MothershipSubscriptionHandler (tracking by sha256, ping updates, stale removal) is correct and thread-safe
  • Error handling paths: Both services include error transformation and structured error responses that should be validated

Poem

🐰 Whiskers twitch with delight!
New mothership bridges connect left and right,
WebSockets dance, subscriptions aligned,
GraphQL queries transformed and refined.
Dev mode skips files with a hop and a bound,
Oh what a day for the carrot-filled ground! 🥕✨

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Feat/mothership v2' is vague and does not clearly convey the specific changes; it uses a generic feature branch naming pattern rather than a descriptive summary of the actual modifications. Replace with a more specific title describing the main change, such as 'Add Mothership v2 GraphQL proxy service with WebSocket support' or similar that clarifies the primary functionality being introduced.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/mothership-v2

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai bot requested review from mdatelle, pujitm and zackspear July 31, 2025 01:16
@elibosley elibosley force-pushed the feat/mothership-v2 branch from 9ab5977 to 5caeb09 Compare July 31, 2025 01:19
@claude
Copy link

claude bot commented Jul 31, 2025

Claude finished @elibosley's task —— View job


PR Review: Mothership v2

I've completed a comprehensive review of this PR. Here's my detailed feedback:

Review Tasks

  • Analyze project structure and dependencies
  • Review new services (local-graphql-executor, unraid-server-client)
  • Examine refactored mothership components
  • Check configuration and environment changes
  • Assess security and performance implications
  • Evaluate test coverage
  • Provide comprehensive feedback

🔍 Code Quality & Architecture

Strengths:

  • Clean separation of concerns with LocalGraphQLExecutor and UnraidServerClient
  • Good use of NestJS patterns with proper dependency injection
  • Comprehensive WebSocket message handling with multiple format support
  • Proper cleanup in OnModuleDestroy lifecycle hooks

Areas for Improvement:

1. Error Handling & Logging 🚨

  • UnraidServerClient (unraid-server-client.service.ts:64, 76, 86): Uses console.log/error instead of proper logger
    // Instead of console.log
    console.log('Connected to mothership')
    // Use:
    this.logger.log('Connected to mothership')

2. Hardcoded Values & Magic Numbers

  • Ping interval: 30000 ms hardcoded (unraid-server-client.service.ts:200)
  • Max reconnect delay: 30 * 60 * 1000 hardcoded (unraid-server-client.service.ts:40)
  • Should be configurable via environment/config service

3. Code Duplication

  • Three similar handleRequestMessage, handleRouteMessage, and handleNewFormatGraphQLRequest methods with nearly identical logic (unraid-server-client.service.ts:314-400)
  • Consider extracting common GraphQL execution logic

🔒 Security Concerns

1. API Key Exposure ⚠️

  • API keys passed in WebSocket headers (unraid-server-client.service.ts:59) - consider if this is secure over the wire
  • Ensure mothership connection uses TLS in production

2. Input Validation

  • LocalGraphQLExecutor.transformRemoteQueryToLocal() (local-graphql-executor.service.ts:68) parses user-provided GraphQL without validation
  • Should validate/sanitize incoming queries before transformation

3. Message Size Limits

  • No limits on WebSocket message sizes could lead to DoS attacks
  • Consider implementing message size validation

Performance Considerations

1. Memory Management

  • WebSocket reconnection uses exponential backoff (good!)
  • But no cleanup of failed connection objects could lead to memory leaks
  • Consider implementing connection pool limits

2. GraphQL Query Transformation

  • transformRemoteQueryToLocal() parses and transforms every query (local-graphql-executor.service.ts:68-109)
  • Consider caching transformed queries for frequently used operations

3. Subscription Management

  • MothershipSubscriptionHandler has TODO comments for incomplete subscription cleanup (mothership-subscription.handler.ts:34, 51)
  • Incomplete subscription handling could lead to resource leaks

🧪 Test Coverage

Critical Gap:

  • No tests for the core new services:
    • LocalGraphQLExecutor - critical GraphQL transformation logic untested
    • UnraidServerClient - complex WebSocket handling untested
    • UnraidServerClientService - NestJS service wrapper untested

Existing Test:

  • Only mothership.events.test.ts exists, but it's for the event handler, not the core new functionality

Recommendations:

  • Add unit tests for GraphQL query transformation logic
  • Add integration tests for WebSocket connection/reconnection
  • Add tests for error handling scenarios
  • Mock the WebSocket dependency properly for testing

🐛 Potential Bugs

1. Race Conditions

  • shouldReconnect flag and WebSocket state management could have race conditions (unraid-server-client.service.ts:43, 52, 403)
  • Multiple rapid connect/disconnect calls might cause issues

2. Error Response Format

  • Inconsistent error response formats across different message handlers
  • Some return { errors: [...] }, others might return different formats

3. Incomplete Implementation

  • executeQuery() method in MothershipSubscriptionHandler returns hardcoded success response (mothership-subscription.handler.ts:127)
  • Should implement actual query execution

📝 Additional Recommendations

1. Configuration Management

  • Move hardcoded values to configuration:
    // Add to environment.ts
    export const WEBSOCKET_PING_INTERVAL = process.env.WS_PING_INTERVAL ? parseInt(process.env.WS_PING_INTERVAL) : 30000;
    export const MAX_RECONNECT_DELAY = process.env.MAX_RECONNECT_DELAY ? parseInt(process.env.MAX_RECONNECT_DELAY) : 30 * 60 * 1000;

2. Documentation

  • Add JSDoc comments to complex methods like transformRemoteQueryToLocal()
  • Document the WebSocket message protocol and expected formats

3. Monitoring & Observability

  • Add metrics for connection health, message counts, error rates
  • Add structured logging for better debugging

🎯 Priority Actions

  1. High Priority:

    • Add comprehensive tests for new services
    • Fix console.log usage (replace with proper logger)
    • Complete subscription cleanup implementation
  2. Medium Priority:

    • Refactor duplicate GraphQL execution logic
    • Add input validation for GraphQL queries
    • Make configuration values configurable
  3. Low Priority:

    • Add JSDoc documentation
    • Implement monitoring/metrics
    • Consider caching for query transformations

Overall Assessment

This is a solid architectural improvement moving to WebSocket-based communication. The code structure is clean and follows good patterns. However, test coverage is critically missing and there are several security and performance considerations that should be addressed before production deployment.

The refactoring successfully centralizes the mothership communication logic, which should improve maintainability and reliability compared to the previous implementation.


Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (5)
api/.env.development (1)

20-20: LGTM: New environment variable supports centralized configuration.

The addition of PATHS_CONNECT_STATUS aligns with the shift toward centralized configuration management via ConfigService.

Consider adding quotes around the value to match the project's .env formatting conventions:

-PATHS_CONNECT_STATUS=./dev/states/connectStatus.json # Connect status file for development
+PATHS_CONNECT_STATUS="./dev/states/connectStatus.json" # Connect status file for development
packages/unraid-api-plugin-connect/src/mothership-proxy/mothership.controller.ts (1)

39-41: Simplify the disconnect logic.

The null check is redundant since the optional chaining operator already handles null/undefined cases safely.

-        if (this.clientService.getClient()) {
-            this.clientService.getClient()?.disconnect();
-        }
+        this.clientService.getClient()?.disconnect();
packages/unraid-api-plugin-connect/src/mothership-proxy/local-graphql-executor.service.ts (2)

125-150: Consider making field name mappings configurable.

The hardcoded field name mappings in transformRemoteFieldName could become a maintenance burden as the API evolves. Consider extracting these mappings to a configuration object.

+private readonly fieldNameMappings: Record<string, string> = {
+  'remoteGetDockerInfo': 'docker',
+  'remoteGetVms': 'vms',
+  'remoteGetSystemInfo': 'system',
+};
+
 private transformRemoteFieldName(fieldName: string): string {
-    // Handle common patterns
-    if (fieldName === 'remoteGetDockerInfo') {
-      return 'docker';
-    }
-    if (fieldName === 'remoteGetVms') {
-      return 'vms';
-    }
-    if (fieldName === 'remoteGetSystemInfo') {
-      return 'system';
-    }
+    // Check configured mappings first
+    if (this.fieldNameMappings[fieldName]) {
+      return this.fieldNameMappings[fieldName];
+    }
     
     // Generic transformation: remove "remoteGet" and convert to camelCase
     if (fieldName.startsWith('remoteGet')) {

152-155: Implement the stopSubscription method.

The stopSubscription method is currently just a stub. This could lead to memory leaks if subscriptions are not properly cleaned up.

Do you want me to help implement the subscription cleanup logic or open an issue to track this task?

packages/unraid-api-plugin-connect/src/mothership-proxy/unraid-server-client.service.ts (1)

56-56: Validate WebSocket URL construction.

The WebSocket URL is constructed by appending to the base URL. Ensure the base URL doesn't already end with a slash to avoid double slashes.

-        const wsUrl = `${this.mothershipUrl}/ws/server`
+        const wsUrl = `${this.mothershipUrl.replace(/\/$/, '')}/ws/server`
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 52f2267 and 5caeb09.

📒 Files selected for processing (16)
  • api/.env.development (1 hunks)
  • api/dev/configs/api.json (1 hunks)
  • api/dev/configs/connect.json (1 hunks)
  • api/dev/keys/7789353b-40f4-4f3b-a230-b1f22909abff.json (1 hunks)
  • api/dev/keys/b5b4aa3d-8e40-4c92-bc40-d50182071886.json (0 hunks)
  • api/generated-schema.graphql (5 hunks)
  • api/src/environment.ts (1 hunks)
  • packages/unraid-api-plugin-connect/codegen.ts (1 hunks)
  • packages/unraid-api-plugin-connect/package.json (1 hunks)
  • packages/unraid-api-plugin-connect/src/connection-status/cloud.service.ts (2 hunks)
  • packages/unraid-api-plugin-connect/src/connection-status/connect-status-writer.service.ts (3 hunks)
  • packages/unraid-api-plugin-connect/src/mothership-proxy/local-graphql-executor.service.ts (1 hunks)
  • packages/unraid-api-plugin-connect/src/mothership-proxy/mothership-subscription.handler.ts (1 hunks)
  • packages/unraid-api-plugin-connect/src/mothership-proxy/mothership.controller.ts (4 hunks)
  • packages/unraid-api-plugin-connect/src/mothership-proxy/mothership.module.ts (1 hunks)
  • packages/unraid-api-plugin-connect/src/mothership-proxy/unraid-server-client.service.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • api/dev/keys/b5b4aa3d-8e40-4c92-bc40-d50182071886.json
🧰 Additional context used
📓 Path-based instructions (1)
api/**/*

📄 CodeRabbit Inference Engine (.cursor/rules/api-rules.mdc)

api/**/*: Use pnpm as the only package manager
Always run scripts from api/package.json unless specifically requested otherwise

Files:

  • api/dev/configs/api.json
  • api/dev/keys/7789353b-40f4-4f3b-a230-b1f22909abff.json
  • api/dev/configs/connect.json
  • api/src/environment.ts
  • api/generated-schema.graphql
🧠 Learnings (15)
📓 Common learnings
Learnt from: mdatelle
PR: unraid/api#1106
File: unraid-ui/src/components/index.ts:2-2
Timestamp: 2025-02-04T17:21:39.710Z
Learning: The unraid-ui package is undergoing a major refactoring process, and breaking changes are expected during this transition period.
Learnt from: pujitm
PR: unraid/api#1211
File: web/composables/gql/gql.ts:17-18
Timestamp: 2025-03-12T13:48:14.850Z
Learning: In the Unraid API project, the duplicate GraphQL query and mutation strings in gql.ts files are intentionally generated by GraphQL CodeGen tool and are necessary for the type system to function properly.
Learnt from: elibosley
PR: unraid/api#969
File: web/justfile:7-9
Timestamp: 2024-11-27T15:30:02.252Z
Learning: In the Unraid Connect project, the different implementations of the `setup` commands in `web/justfile` and `api/justfile` are intentional and correct behavior.
Learnt from: elibosley
PR: unraid/api#1101
File: api/src/unraid-api/unraid-file-modifier/modifications/patches/default-page-layout.patch:24-27
Timestamp: 2025-01-31T22:01:22.708Z
Learning: The Unraid UI uses a modern notification system with a custom `unraid-toaster` component replacing the legacy jGrowl notifications. The system is backed by a comprehensive GraphQL API with real-time subscription support for notification updates.
Learnt from: pujitm
PR: unraid/api#1415
File: plugin/plugins/dynamix.unraid.net.plg:234-236
Timestamp: 2025-06-11T14:14:30.348Z
Learning: For the Unraid Connect plugin, the script `/etc/rc.d/rc.unraid-api` is bundled with the plugin package itself, so its presence on the target system is guaranteed during installation.
Learnt from: mdatelle
PR: unraid/api#942
File: api/src/unraid-api/graph/resolvers/auth/auth.resolver.ts:111-113
Timestamp: 2024-11-06T20:59:25.809Z
Learning: In the Unraid API project, error handling for mutations is handled at the service level rather than in the GraphQL resolvers. Specifically, in `api/src/unraid-api/graph/resolvers/auth/auth.resolver.ts`, methods like `removeRoleFromApiKey` rely on service-level error handling.
Learnt from: elibosley
PR: unraid/api#1101
File: api/src/unraid-api/unraid-file-modifier/modifications/__test__/snapshots/DefaultPageLayout.php.modified.snapshot.php:203-207
Timestamp: 2025-02-03T17:21:26.738Z
Learning: The project uses patches to override existing Unraid pages rather than modifying them directly.
api/dev/keys/7789353b-40f4-4f3b-a230-b1f22909abff.json (3)

Learnt from: mdatelle
PR: #942
File: api/src/unraid-api/auth/auth.service.ts:0-0
Timestamp: 2024-11-04T20:41:22.303Z
Learning: In api/src/unraid-api/auth/auth.service.ts, the addRoleToApiKey function operates on API keys stored as JSON files in a directory, not a database, so concurrency is not a concern for modifying apiKey.roles.

Learnt from: mdatelle
PR: #942
File: api/src/unraid-api/auth/auth.service.ts:0-0
Timestamp: 2024-11-04T20:44:46.432Z
Learning: When modifying apiKey.roles in removeRoleFromApiKey and addRoleToApiKey within api/src/unraid-api/auth/auth.service.ts, concurrency issues are not a concern because the keys are stored in the file system.

Learnt from: elibosley
PR: #1425
File: api/dev/configs/connect.json:5-13
Timestamp: 2025-06-20T20:50:14.050Z
Learning: Connect configuration files like api/dev/configs/connect.json use their own naming conventions (e.g., apikey, accesstoken, idtoken, refreshtoken) as implementation details, and these should not be changed to follow general camelCase patterns.

api/dev/configs/connect.json (2)

Learnt from: elibosley
PR: #1425
File: api/dev/configs/connect.json:5-13
Timestamp: 2025-06-20T20:50:14.050Z
Learning: Connect configuration files like api/dev/configs/connect.json use their own naming conventions (e.g., apikey, accesstoken, idtoken, refreshtoken) as implementation details, and these should not be changed to follow general camelCase patterns.

Learnt from: pujitm
PR: #1211
File: web/codegen.ts:14-14
Timestamp: 2025-03-12T13:35:43.900Z
Learning: The JSON scalar type in web/codegen.ts was temporarily changed from 'string' to 'any' for compatibility with JsonForms integration. This change facilitates the implementation of the Connect settings web component.

packages/unraid-api-plugin-connect/src/connection-status/cloud.service.ts (6)

Learnt from: elibosley
PR: #942
File: api/src/unraid-api/auth/api-key.service.ts:62-70
Timestamp: 2024-11-05T14:49:07.308Z
Learning: In api/src/unraid-api/auth/api-key.service.ts, when handling read errors in the findById method, throw a GraphQLError instead of an InternalServerErrorException.

Learnt from: pujitm
PR: #1211
File: api/src/graphql/schema/types/connect/connect.graphql:142-146
Timestamp: 2025-03-14T16:10:38.562Z
Learning: The updateApiSettings mutation in api/src/unraid-api/graph/connect/connect.resolver.ts is protected with the @UsePermissions decorator that requires UPDATE permission on the CONFIG resource.

Learnt from: pujitm
PR: #1211
File: web/codegen.ts:14-14
Timestamp: 2025-03-12T13:35:43.900Z
Learning: The JSON scalar type in web/codegen.ts was temporarily changed from 'string' to 'any' for compatibility with JsonForms integration. This change facilitates the implementation of the Connect settings web component.

Learnt from: elibosley
PR: #1381
File: plugin/source/dynamix.unraid.net/usr/local/share/dynamix.unraid.net/install/scripts/verify_install.sh:11-16
Timestamp: 2025-05-08T19:31:52.417Z
Learning: The dynamix.my.servers namespace is still valid and should not be changed to dynamix.unraid.net in file paths, as both namespaces coexist in the codebase.

Learnt from: mdatelle
PR: #942
File: api/src/unraid-api/graph/resolvers/auth/auth.resolver.ts:111-113
Timestamp: 2024-11-06T20:59:25.809Z
Learning: In the Unraid API project, error handling for mutations is handled at the service level rather than in the GraphQL resolvers. Specifically, in api/src/unraid-api/graph/resolvers/auth/auth.resolver.ts, methods like removeRoleFromApiKey rely on service-level error handling.

Learnt from: pujitm
PR: #1211
File: web/composables/gql/gql.ts:17-18
Timestamp: 2025-03-12T13:48:14.850Z
Learning: In the Unraid API project, the duplicate GraphQL query and mutation strings in gql.ts files are intentionally generated by GraphQL CodeGen tool and are necessary for the type system to function properly.

packages/unraid-api-plugin-connect/src/mothership-proxy/mothership.module.ts (9)

Learnt from: pujitm
PR: #1211
File: web/composables/gql/gql.ts:17-18
Timestamp: 2025-03-12T13:48:14.850Z
Learning: In the Unraid API project, the duplicate GraphQL query and mutation strings in gql.ts files are intentionally generated by GraphQL CodeGen tool and are necessary for the type system to function properly.

Learnt from: mdatelle
PR: #942
File: api/src/unraid-api/graph/resolvers/auth/auth.resolver.ts:111-113
Timestamp: 2024-11-06T20:59:25.809Z
Learning: In the Unraid API project, error handling for mutations is handled at the service level rather than in the GraphQL resolvers. Specifically, in api/src/unraid-api/graph/resolvers/auth/auth.resolver.ts, methods like removeRoleFromApiKey rely on service-level error handling.

Learnt from: elibosley
PR: #942
File: api/src/unraid-api/auth/api-key.service.ts:62-70
Timestamp: 2024-11-05T14:49:07.308Z
Learning: In api/src/unraid-api/auth/api-key.service.ts, when handling read errors in the findById method, throw a GraphQLError instead of an InternalServerErrorException.

Learnt from: mdatelle
PR: #1106
File: unraid-ui/src/components/index.ts:2-2
Timestamp: 2025-02-04T17:21:39.710Z
Learning: The unraid-ui package is undergoing a major refactoring process, and breaking changes are expected during this transition period.

Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/api-rules.mdc:0-0
Timestamp: 2025-07-21T14:00:06.258Z
Learning: Applies to api/src/unraid-api/**/* : Prefer adding new files to the NestJS repository located at api/src/unraid-api/ instead of the legacy code

Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-28T15:34:24.715Z
Learning: Applies to api/src/unraid-api/**/* : Prefer adding new files to the NestJS repo located at api/src/unraid-api/ instead of the legacy code

Learnt from: elibosley
PR: #1408
File: web/components/ApiKey/PermissionCounter.vue:6-6
Timestamp: 2025-05-23T21:59:29.632Z
Learning: This codebase uses ESM (ECMAScript Modules) and requires .js extensions in import statements, even when importing from TypeScript files, as the imports refer to the compiled JavaScript output.

Learnt from: elibosley
PR: #1308
File: unraid-ui/src/components/common/loading/Error.vue:2-2
Timestamp: 2025-04-02T21:21:29.168Z
Learning: Components in the unraid-ui folder require explicit imports and are not autoloaded, unlike other parts of the project that may use Nuxt.js autoloading features.

Learnt from: elibosley
PR: #1211
File: api/src/unraid-api/graph/connect/connect-settings.service.ts:48-53
Timestamp: 2025-03-13T16:17:55.820Z
Learning: In NestJS services, dynamic imports should be wrapped in try/catch blocks with proper error logging and fallback handling to prevent unhandled rejections from crashing the application.

packages/unraid-api-plugin-connect/package.json (14)

Learnt from: pujitm
PR: #1211
File: web/composables/gql/gql.ts:17-18
Timestamp: 2025-03-12T13:48:14.850Z
Learning: In the Unraid API project, the duplicate GraphQL query and mutation strings in gql.ts files are intentionally generated by GraphQL CodeGen tool and are necessary for the type system to function properly.

Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-28T15:34:24.715Z
Learning: Always run pnpm codegen for GraphQL code generation in the web directory

Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-graphql.mdc:0-0
Timestamp: 2025-07-21T14:00:25.140Z
Learning: Always run pnpm codegen for GraphQL code generation in the web directory

Learnt from: pujitm
PR: #1211
File: web/codegen.ts:14-14
Timestamp: 2025-03-12T13:35:43.900Z
Learning: The JSON scalar type in web/codegen.ts was temporarily changed from 'string' to 'any' for compatibility with JsonForms integration. This change facilitates the implementation of the Connect settings web component.

Learnt from: mdatelle
PR: #1122
File: web/package.json:16-16
Timestamp: 2025-02-06T17:25:45.397Z
Learning: The build script in web/package.json should include type-check command (npm run type-check) even when NODE_ENV is production, as it provides value for local builds by catching type errors before pushing to CI/CD.

Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-28T15:34:24.715Z
Learning: Always run scripts from api/package.json unless requested

Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/api-rules.mdc:0-0
Timestamp: 2025-07-21T14:00:06.258Z
Learning: Applies to api/**/* : Always run scripts from api/package.json unless specifically requested otherwise

Learnt from: mdatelle
PR: #942
File: api/src/unraid-api/graph/resolvers/auth/auth.resolver.ts:111-113
Timestamp: 2024-11-06T20:59:25.809Z
Learning: In the Unraid API project, error handling for mutations is handled at the service level rather than in the GraphQL resolvers. Specifically, in api/src/unraid-api/graph/resolvers/auth/auth.resolver.ts, methods like removeRoleFromApiKey rely on service-level error handling.

Learnt from: pujitm
PR: #1211
File: api/src/graphql/schema/types/connect/connect.graphql:142-146
Timestamp: 2025-03-14T16:10:38.562Z
Learning: The updateApiSettings mutation in api/src/unraid-api/graph/connect/connect.resolver.ts is protected with the @UsePermissions decorator that requires UPDATE permission on the CONFIG resource.

Learnt from: pujitm
PR: #1415
File: plugin/plugins/dynamix.unraid.net.plg:234-236
Timestamp: 2025-06-11T14:14:30.348Z
Learning: For the Unraid Connect plugin, the script /etc/rc.d/rc.unraid-api is bundled with the plugin package itself, so its presence on the target system is guaranteed during installation.

Learnt from: elibosley
PR: #1120
File: plugin/package.json:0-0
Timestamp: 2025-02-05T14:43:48.568Z
Learning: In Node.js projects, npm scripts should be organized with clear namespacing (e.g., build:, docker:, env:*) and include proper environment validation and error handling. Each script should follow the single responsibility principle.

Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-graphql.mdc:0-0
Timestamp: 2025-07-21T14:00:25.140Z
Learning: Applies to web/**/*.{query,mutation}.ts : All GraphQL under web/ must follow this naming convention

Learnt from: elibosley
PR: #1408
File: web/components/ApiKey/PermissionCounter.vue:6-6
Timestamp: 2025-05-23T21:59:29.632Z
Learning: This codebase uses ESM (ECMAScript Modules) and requires .js extensions in import statements, even when importing from TypeScript files, as the imports refer to the compiled JavaScript output.

Learnt from: elibosley
PR: #1120
File: plugin/Dockerfile:1-3
Timestamp: 2025-02-05T14:25:37.316Z
Learning: The Dockerfile in the plugin directory is specifically for building artifacts during CI and is not used for production deployments. It creates a build environment with Node.js and necessary dependencies to generate plugin files.

packages/unraid-api-plugin-connect/src/connection-status/connect-status-writer.service.ts (12)

Learnt from: elibosley
PR: #1151
File: plugin/builder/utils/consts.ts:6-6
Timestamp: 2025-03-04T14:55:00.903Z
Learning: The startingDir constant in plugin/builder/utils/consts.ts is defined using process.cwd(), which can cause issues if directory changes occur after importing this constant. Using __dirname.split('/builder')[0] would make it more reliable by making it relative to the file location rather than the current working directory.

Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/api-rules.mdc:0-0
Timestamp: 2025-07-21T14:00:06.258Z
Learning: Applies to api/src/unraid-api/**/* : Prefer adding new files to the NestJS repository located at api/src/unraid-api/ instead of the legacy code

Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-28T15:34:24.715Z
Learning: Applies to api/src/unraid-api/**/* : Prefer adding new files to the NestJS repo located at api/src/unraid-api/ instead of the legacy code

Learnt from: mdatelle
PR: #942
File: api/src/unraid-api/auth/auth.service.ts:0-0
Timestamp: 2024-11-04T20:44:46.432Z
Learning: When modifying apiKey.roles in removeRoleFromApiKey and addRoleToApiKey within api/src/unraid-api/auth/auth.service.ts, concurrency issues are not a concern because the keys are stored in the file system.

Learnt from: elibosley
PR: #942
File: api/src/unraid-api/auth/api-key.service.ts:62-70
Timestamp: 2024-11-05T14:49:07.308Z
Learning: In api/src/unraid-api/auth/api-key.service.ts, when handling read errors in the findById method, throw a GraphQLError instead of an InternalServerErrorException.

Learnt from: pujitm
PR: #1211
File: api/src/graphql/schema/types/connect/connect.graphql:142-146
Timestamp: 2025-03-14T16:10:38.562Z
Learning: The updateApiSettings mutation in api/src/unraid-api/graph/connect/connect.resolver.ts is protected with the @UsePermissions decorator that requires UPDATE permission on the CONFIG resource.

Learnt from: pujitm
PR: #1367
File: packages/unraid-api-plugin-connect/src/pubsub/user.service.ts:44-52
Timestamp: 2025-04-23T20:19:42.542Z
Learning: The project uses a custom or extended implementation of NestJS ConfigService that includes a set() method for runtime configuration mutation, unlike the standard @nestjs/config package which only provides getter methods.

Learnt from: pujitm
PR: #1367
File: packages/unraid-api-plugin-connect/src/pubsub/user.service.ts:44-52
Timestamp: 2025-04-23T20:19:42.542Z
Learning: The project uses a custom ConfigService implementation that includes a set() method for runtime configuration mutation, unlike the standard @nestjs/config package which only provides getter methods.

Learnt from: pujitm
PR: #1367
File: packages/unraid-api-plugin-connect/src/event-handler/mothership.handler.ts:50-57
Timestamp: 2025-06-05T14:27:57.441Z
Learning: For NestJS applications with async event handlers using @onevent decorator, implement generic error handling using a custom decorator or base class rather than individual try-catch blocks in each handler method to avoid application crashes from unhandled promise rejections.

Learnt from: pujitm
PR: #1367
File: packages/unraid-api-plugin-connect/src/event-handler/mothership.handler.ts:50-57
Timestamp: 2025-06-05T14:27:57.441Z
Learning: NestJS async event handlers using @onevent decorator can crash the entire application if exceptions are not caught. Industry best practices recommend generic error handling approaches like custom decorators, Sentry's automatic wrapping, or global event exception services rather than individual try-catch blocks in each handler.

Learnt from: elibosley
PR: #1211
File: api/src/unraid-api/graph/connect/connect-settings.service.ts:48-53
Timestamp: 2025-03-13T16:17:55.820Z
Learning: In NestJS services, dynamic imports should be wrapped in try/catch blocks with proper error logging and fallback handling to prevent unhandled rejections from crashing the application.

Learnt from: elibosley
PR: #1352
File: packages/unraid-api-plugin-connect/src/config.persistence.ts:0-0
Timestamp: 2025-04-21T18:44:39.643Z
Learning: Use Promise-based file writing instead of callback-based approaches for better error handling and to allow proper propagation of success/failure states to callers.

api/.env.development (5)

Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-28T15:34:24.715Z
Learning: Always run pnpm codegen for GraphQL code generation in the web directory

Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-graphql.mdc:0-0
Timestamp: 2025-07-21T14:00:25.140Z
Learning: Always run pnpm codegen for GraphQL code generation in the web directory

Learnt from: pujitm
PR: #1047
File: api/src/unraid-api/graph/sandbox-plugin.ts:57-57
Timestamp: 2025-01-15T21:34:00.006Z
Learning: In the GraphQL sandbox (api/src/unraid-api/graph/sandbox-plugin.ts), CSRF token validation should fail silently with a fallback value to maintain sandbox accessibility, as it's a development tool where strict security measures aren't required.

Learnt from: pujitm
PR: #1211
File: web/composables/gql/gql.ts:17-18
Timestamp: 2025-03-12T13:48:14.850Z
Learning: In the Unraid API project, the duplicate GraphQL query and mutation strings in gql.ts files are intentionally generated by GraphQL CodeGen tool and are necessary for the type system to function properly.

Learnt from: elibosley
PR: #1063
File: web/_data/serverState.ts:137-147
Timestamp: 2025-01-27T14:57:46.617Z
Learning: The values in web/_data/serverState.ts are used for testing purposes and should remain as hardcoded mock data to facilitate testing different scenarios.

api/src/environment.ts (11)

Learnt from: elibosley
PR: #1381
File: plugin/builder/cli/common-environment.ts:18-22
Timestamp: 2025-05-08T19:28:37.034Z
Learning: In plugin/builder/cli/common-environment.ts, the getDefaultBaseUrl() function intentionally returns an invalid URL when running in CI environments to force explicit specification of the base URL parameter rather than relying on defaults.

Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-graphql.mdc:0-0
Timestamp: 2025-07-21T14:00:25.140Z
Learning: Applies to web/**/*.{query,mutation}.ts : All GraphQL under web/ must follow this naming convention

Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-graphql.mdc:0-0
Timestamp: 2025-07-21T14:00:25.140Z
Learning: Applies to web/**/*.mutation.ts : GraphQL mutations must be placed in .mutation.ts files

Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-28T15:34:24.715Z
Learning: Applies to web/**/*.{query,mutation}.ts : All GraphQL under web/ must follow this naming convention (.query.ts for queries, .mutation.ts for mutations)

Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-28T15:34:24.715Z
Learning: Applies to web/**/*.mutation.ts : GraphQL mutations must be placed in .mutation.ts files under web/

Learnt from: elibosley
PR: #1063
File: web/_data/serverState.ts:137-147
Timestamp: 2025-01-27T14:57:46.617Z
Learning: The values in web/_data/serverState.ts are used for testing purposes and should remain as hardcoded mock data to facilitate testing different scenarios.

Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-28T15:34:24.715Z
Learning: Always run pnpm codegen for GraphQL code generation in the web directory

Learnt from: pujitm
PR: #1211
File: web/composables/gql/gql.ts:17-18
Timestamp: 2025-03-12T13:48:14.850Z
Learning: In the Unraid API project, the duplicate GraphQL query and mutation strings in gql.ts files are intentionally generated by GraphQL CodeGen tool and are necessary for the type system to function properly.

Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-graphql.mdc:0-0
Timestamp: 2025-07-21T14:00:25.140Z
Learning: Always run pnpm codegen for GraphQL code generation in the web directory

Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-28T15:34:24.715Z
Learning: Always run scripts from api/package.json unless requested

Learnt from: elibosley
PR: #1408
File: web/components/ApiKey/PermissionCounter.vue:6-6
Timestamp: 2025-05-23T21:59:29.632Z
Learning: This codebase uses ESM (ECMAScript Modules) and requires .js extensions in import statements, even when importing from TypeScript files, as the imports refer to the compiled JavaScript output.

packages/unraid-api-plugin-connect/codegen.ts (13)

Learnt from: pujitm
PR: #1211
File: web/composables/gql/gql.ts:17-18
Timestamp: 2025-03-12T13:48:14.850Z
Learning: In the Unraid API project, the duplicate GraphQL query and mutation strings in gql.ts files are intentionally generated by GraphQL CodeGen tool and are necessary for the type system to function properly.

Learnt from: elibosley
PR: #942
File: api/src/unraid-api/graph/resolvers/auth/auth.resolver.ts:41-47
Timestamp: 2024-11-05T14:48:56.949Z
Learning: Input types for GraphQL mutations can be pulled from the GraphQL codegen types, as they are generated into the API files. Therefore, explicit typing in the resolver files may be unnecessary.

Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-28T15:34:24.715Z
Learning: Always run pnpm codegen for GraphQL code generation in the web directory

Learnt from: pujitm
PR: #1211
File: web/codegen.ts:14-14
Timestamp: 2025-03-12T13:35:43.900Z
Learning: The JSON scalar type in web/codegen.ts was temporarily changed from 'string' to 'any' for compatibility with JsonForms integration. This change facilitates the implementation of the Connect settings web component.

Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-graphql.mdc:0-0
Timestamp: 2025-07-21T14:00:25.140Z
Learning: Always run pnpm codegen for GraphQL code generation in the web directory

Learnt from: mdatelle
PR: #942
File: api/src/unraid-api/graph/resolvers/auth/auth.resolver.ts:111-113
Timestamp: 2024-11-06T20:59:25.809Z
Learning: In the Unraid API project, error handling for mutations is handled at the service level rather than in the GraphQL resolvers. Specifically, in api/src/unraid-api/graph/resolvers/auth/auth.resolver.ts, methods like removeRoleFromApiKey rely on service-level error handling.

Learnt from: pujitm
PR: #1047
File: api/src/unraid-api/graph/sandbox-plugin.ts:57-57
Timestamp: 2025-01-15T21:34:00.006Z
Learning: In the GraphQL sandbox (api/src/unraid-api/graph/sandbox-plugin.ts), CSRF token validation should fail silently with a fallback value to maintain sandbox accessibility, as it's a development tool where strict security measures aren't required.

Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-graphql.mdc:0-0
Timestamp: 2025-07-21T14:00:25.140Z
Learning: Applies to web/**/*.{query,mutation}.ts : All GraphQL under web/ must follow this naming convention

Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-28T15:34:24.715Z
Learning: Applies to web/**/*.{query,mutation}.ts : All GraphQL under web/ must follow this naming convention (.query.ts for queries, .mutation.ts for mutations)

Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-graphql.mdc:0-0
Timestamp: 2025-07-21T14:00:25.140Z
Learning: Applies to web/**/*.query.ts : GraphQL queries must be placed in .query.ts files

Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-28T15:34:24.715Z
Learning: Applies to web/**/*.query.ts : GraphQL queries must be placed in .query.ts files under web/

Learnt from: elibosley
PR: #1408
File: web/components/ApiKey/PermissionCounter.vue:6-6
Timestamp: 2025-05-23T21:59:29.632Z
Learning: This codebase uses ESM (ECMAScript Modules) and requires .js extensions in import statements, even when importing from TypeScript files, as the imports refer to the compiled JavaScript output.

Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-28T15:34:24.715Z
Learning: Applies to web/**/*.mutation.ts : GraphQL mutations must be placed in .mutation.ts files under web/

packages/unraid-api-plugin-connect/src/mothership-proxy/mothership.controller.ts (5)

Learnt from: mdatelle
PR: #942
File: api/src/unraid-api/graph/resolvers/auth/auth.resolver.ts:111-113
Timestamp: 2024-11-06T20:59:25.809Z
Learning: In the Unraid API project, error handling for mutations is handled at the service level rather than in the GraphQL resolvers. Specifically, in api/src/unraid-api/graph/resolvers/auth/auth.resolver.ts, methods like removeRoleFromApiKey rely on service-level error handling.

Learnt from: elibosley
PR: #942
File: api/src/unraid-api/auth/api-key.service.ts:62-70
Timestamp: 2024-11-05T14:49:07.308Z
Learning: In api/src/unraid-api/auth/api-key.service.ts, when handling read errors in the findById method, throw a GraphQLError instead of an InternalServerErrorException.

Learnt from: mdatelle
PR: #1106
File: unraid-ui/src/components/index.ts:2-2
Timestamp: 2025-02-04T17:21:39.710Z
Learning: The unraid-ui package is undergoing a major refactoring process, and breaking changes are expected during this transition period.

Learnt from: pujitm
PR: #1211
File: web/composables/gql/gql.ts:17-18
Timestamp: 2025-03-12T13:48:14.850Z
Learning: In the Unraid API project, the duplicate GraphQL query and mutation strings in gql.ts files are intentionally generated by GraphQL CodeGen tool and are necessary for the type system to function properly.

Learnt from: pujitm
PR: #1211
File: api/src/graphql/schema/types/connect/connect.graphql:142-146
Timestamp: 2025-03-14T16:10:38.562Z
Learning: The updateApiSettings mutation in api/src/unraid-api/graph/connect/connect.resolver.ts is protected with the @UsePermissions decorator that requires UPDATE permission on the CONFIG resource.

packages/unraid-api-plugin-connect/src/mothership-proxy/local-graphql-executor.service.ts (7)

Learnt from: mdatelle
PR: #942
File: api/src/unraid-api/graph/resolvers/auth/auth.resolver.ts:111-113
Timestamp: 2024-11-06T20:59:25.809Z
Learning: In the Unraid API project, error handling for mutations is handled at the service level rather than in the GraphQL resolvers. Specifically, in api/src/unraid-api/graph/resolvers/auth/auth.resolver.ts, methods like removeRoleFromApiKey rely on service-level error handling.

Learnt from: elibosley
PR: #942
File: api/src/unraid-api/auth/api-key.service.ts:62-70
Timestamp: 2024-11-05T14:49:07.308Z
Learning: In api/src/unraid-api/auth/api-key.service.ts, when handling read errors in the findById method, throw a GraphQLError instead of an InternalServerErrorException.

Learnt from: pujitm
PR: #1211
File: web/composables/gql/gql.ts:17-18
Timestamp: 2025-03-12T13:48:14.850Z
Learning: In the Unraid API project, the duplicate GraphQL query and mutation strings in gql.ts files are intentionally generated by GraphQL CodeGen tool and are necessary for the type system to function properly.

Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-graphql.mdc:0-0
Timestamp: 2025-07-21T14:00:25.140Z
Learning: Applies to web/**/*.{query,mutation}.ts : All GraphQL under web/ must follow this naming convention

Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-28T15:34:24.715Z
Learning: Applies to web/**/*.{query,mutation}.ts : All GraphQL under web/ must follow this naming convention (.query.ts for queries, .mutation.ts for mutations)

Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-graphql.mdc:0-0
Timestamp: 2025-07-21T14:00:25.140Z
Learning: Applies to web/**/*.query.ts : GraphQL queries must be placed in .query.ts files

Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-28T15:34:24.715Z
Learning: Applies to web/**/*.query.ts : GraphQL queries must be placed in .query.ts files under web/

packages/unraid-api-plugin-connect/src/mothership-proxy/mothership-subscription.handler.ts (9)

Learnt from: mdatelle
PR: #942
File: api/src/unraid-api/graph/resolvers/auth/auth.resolver.ts:111-113
Timestamp: 2024-11-06T20:59:25.809Z
Learning: In the Unraid API project, error handling for mutations is handled at the service level rather than in the GraphQL resolvers. Specifically, in api/src/unraid-api/graph/resolvers/auth/auth.resolver.ts, methods like removeRoleFromApiKey rely on service-level error handling.

Learnt from: pujitm
PR: #1075
File: web/components/Notifications/Sidebar.vue:52-57
Timestamp: 2025-01-30T19:56:42.055Z
Learning: Notification links in the Unraid API are validated at creation time on the server side, making additional client-side validation unnecessary when receiving notifications through GraphQL subscriptions.

Learnt from: elibosley
PR: #1526
File: api/src/unraid-api/graph/resolvers/ups/ups.resolver.ts:82-85
Timestamp: 2025-07-22T13:29:32.175Z
Learning: In GraphQL subscriptions using the graphql-subscriptions PubSub, the correct method is asyncIterableIterator(), not asyncIterator(). The asyncIterableIterator method returns an AsyncIterableIterator that conforms to both AsyncIterable and AsyncIterator interfaces, which is required for GraphQL subscription resolvers. Using asyncIterator() would cause TypeScript type errors as it only returns an AsyncIterator that doesn't satisfy the AsyncIterable interface requirement.

Learnt from: elibosley
PR: #942
File: api/src/unraid-api/auth/api-key.service.ts:62-70
Timestamp: 2024-11-05T14:49:07.308Z
Learning: In api/src/unraid-api/auth/api-key.service.ts, when handling read errors in the findById method, throw a GraphQLError instead of an InternalServerErrorException.

Learnt from: elibosley
PR: #1222
File: api/src/unraid-api/graph/resolvers/logs/logs.resolver.ts:37-65
Timestamp: 2025-03-17T13:41:52.496Z
Learning: The LogsService in Unraid API uses a reference counting mechanism to track log file subscriptions, ensuring file watchers remain active until all subscribers have unsubscribed.

Learnt from: pujitm
PR: #978
File: web/helpers/apollo-cache/index.ts:133-157
Timestamp: 2024-12-11T17:20:09.604Z
Learning: In web/helpers/apollo-cache/index.ts, when updating the cache in the cache.updateQuery function, it's sufficient to check if data is present, as type checking inside data is handled at the GraphQL layer.

Learnt from: pujitm
PR: #1211
File: web/composables/gql/gql.ts:17-18
Timestamp: 2025-03-12T13:48:14.850Z
Learning: In the Unraid API project, the duplicate GraphQL query and mutation strings in gql.ts files are intentionally generated by GraphQL CodeGen tool and are necessary for the type system to function properly.

Learnt from: pujitm
PR: #1148
File: web/composables/gql/graphql.ts:1812-1821
Timestamp: 2025-02-19T17:30:55.418Z
Learning: When using Apollo Vue's subscribeToMore, throttling can be implemented by wrapping the update function with lodash's throttle or using RxJS's throttleTime operator to prevent flooding the client with updates.

Learnt from: elibosley
PR: #1526
File: api/src/unraid-api/graph/resolvers/ups/ups.resolver.ts:82-85
Timestamp: 2025-07-22T13:29:32.175Z
Learning: In GraphQL subscriptions using the graphql-subscriptions PubSub, the correct method to create an async iterator is asyncIterableIterator(), not asyncIterator(). This is the modern/current method used in the graphql-subscriptions library.

packages/unraid-api-plugin-connect/src/mothership-proxy/unraid-server-client.service.ts (7)

Learnt from: elibosley
PR: #942
File: api/src/unraid-api/auth/api-key.service.ts:62-70
Timestamp: 2024-11-05T14:49:07.308Z
Learning: In api/src/unraid-api/auth/api-key.service.ts, when handling read errors in the findById method, throw a GraphQLError instead of an InternalServerErrorException.

Learnt from: mdatelle
PR: #942
File: api/src/unraid-api/graph/resolvers/auth/auth.resolver.ts:111-113
Timestamp: 2024-11-06T20:59:25.809Z
Learning: In the Unraid API project, error handling for mutations is handled at the service level rather than in the GraphQL resolvers. Specifically, in api/src/unraid-api/graph/resolvers/auth/auth.resolver.ts, methods like removeRoleFromApiKey rely on service-level error handling.

Learnt from: pujitm
PR: #1211
File: web/composables/gql/gql.ts:17-18
Timestamp: 2025-03-12T13:48:14.850Z
Learning: In the Unraid API project, the duplicate GraphQL query and mutation strings in gql.ts files are intentionally generated by GraphQL CodeGen tool and are necessary for the type system to function properly.

Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/api-rules.mdc:0-0
Timestamp: 2025-07-21T14:00:06.258Z
Learning: Applies to api/src/unraid-api/**/* : Prefer adding new files to the NestJS repository located at api/src/unraid-api/ instead of the legacy code

Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-28T15:34:24.715Z
Learning: Applies to api/src/unraid-api/**/* : Prefer adding new files to the NestJS repo located at api/src/unraid-api/ instead of the legacy code

Learnt from: mdatelle
PR: #1106
File: unraid-ui/src/components/index.ts:2-2
Timestamp: 2025-02-04T17:21:39.710Z
Learning: The unraid-ui package is undergoing a major refactoring process, and breaking changes are expected during this transition period.

Learnt from: pujitm
PR: #1367
File: packages/unraid-api-plugin-connect/src/pubsub/user.service.ts:44-52
Timestamp: 2025-04-23T20:19:42.542Z
Learning: The project uses a custom or extended implementation of NestJS ConfigService that includes a set() method for runtime configuration mutation, unlike the standard @nestjs/config package which only provides getter methods.

api/generated-schema.graphql (1)

Learnt from: elibosley
PR: #942
File: api/src/unraid-api/graph/resolvers/auth/auth.resolver.ts:41-47
Timestamp: 2024-11-05T14:48:56.949Z
Learning: Input types for GraphQL mutations can be pulled from the GraphQL codegen types, as they are generated into the API files. Therefore, explicit typing in the resolver files may be unnecessary.

🪛 Gitleaks (8.27.2)
api/dev/keys/7789353b-40f4-4f3b-a230-b1f22909abff.json

5-5: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🪛 GitHub Actions: CI - Main (API)
packages/unraid-api-plugin-connect/src/connection-status/connect-status-writer.service.ts

[error] 64-64: TypeError: The "path" argument must be of type string. Received an instance of Object. This error occurs multiple times during writeStatus calls.

🪛 dotenv-linter (3.3.0)
api/.env.development

[warning] 20-20: [UnorderedKey] The PATHS_CONNECT_STATUS key should go before the PATHS_CONNECT_STATUS_FILE_PATH key


[warning] 20-20: [ValueWithoutQuotes] This value needs to be surrounded in quotes


[warning] 21-21: [QuoteCharacter] The value has quote characters (', ")


[warning] 21-21: [UnorderedKey] The ENVIRONMENT key should go before the PATHS_ACTIVATION_BASE key


[warning] 22-22: [QuoteCharacter] The value has quote characters (', ")


[warning] 22-22: [UnorderedKey] The NODE_ENV key should go before the PATHS_ACTIVATION_BASE key


[warning] 23-23: [QuoteCharacter] The value has quote characters (', ")


[warning] 24-24: [UnorderedKey] The PLAYGROUND key should go before the PORT key


[warning] 25-25: [UnorderedKey] The INTROSPECTION key should go before the NODE_ENV key


[warning] 26-26: [QuoteCharacter] The value has quote characters (', ")


[warning] 26-26: [UnorderedKey] The MOTHERSHIP_BASE_URL key should go before the NODE_ENV key

🔇 Additional comments (20)
api/src/environment.ts (1)

96-100: LGTM! Clean environment variable refactor.

The renaming from MOTHERSHIP_GRAPHQL_LINK to MOTHERSHIP_BASE_URL and removal of the /ws suffix aligns well with the architectural shift from GraphQL-over-HTTP to WebSocket-based communication. The conditional logic and fallback behavior are preserved correctly.

api/dev/configs/api.json (1)

2-2: Appropriate version bump for the architectural changes.

The minor version increment from "4.11.0" to "4.12.0" correctly reflects the introduction of new WebSocket-based mothership communication features while maintaining backward compatibility.

packages/unraid-api-plugin-connect/src/connection-status/cloud.service.ts (2)

96-96: LGTM! Consistent environment variable update.

The change from MOTHERSHIP_GRAPHQL_LINK to MOTHERSHIP_BASE_URL aligns with the environment configuration updates and maintains the existing connectivity check functionality.


207-207: LGTM! Consistent environment variable update.

The change from MOTHERSHIP_GRAPHQL_LINK to MOTHERSHIP_BASE_URL aligns with the environment configuration updates and maintains the existing DNS check functionality.

api/dev/keys/7789353b-40f4-4f3b-a230-b1f22909abff.json (1)

1-11: Development API key correctly scoped to development environment

No references to this key or its secret were found in production code. Keeping the JSON file under api/dev/keys/ is appropriate for development/testing purposes.

packages/unraid-api-plugin-connect/package.json (1)

16-16: LGTM! Script cleanup aligns with architectural changes.

Removing the inline MOTHERSHIP_GRAPHQL_LINK environment variable from the codegen script is consistent with the transition to WebSocket-based communication and the renaming of environment variables throughout the codebase.

packages/unraid-api-plugin-connect/src/mothership-proxy/mothership.module.ts (2)

11-11: LGTM: Clean architectural transition to WebSocket-based services.

The addition of LocalGraphQLExecutor and UnraidServerClientService imports aligns well with the broader shift from HTTP GraphQL to WebSocket-based mothership communication.

Also applies to: 15-15


23-25: LGTM: Provider registration looks correct.

The replacement of MothershipGraphqlClientService with UnraidServerClientService and LocalGraphQLExecutor in the providers array is properly structured and consistent with the architectural changes.

api/.env.development (1)

26-26: LGTM: URL change supports WebSocket architecture transition.

The change from MOTHERSHIP_GRAPHQL_LINK to MOTHERSHIP_BASE_URL with the new localhost URL correctly supports the shift to WebSocket-based communication.

api/dev/configs/connect.json (1)

11-18: LGTM: Configuration additions support enhanced connect functionality.

The new fields (version, extraOrigins, sandbox, and token fields) provide necessary configuration placeholders for the updated connect architecture. The JSON structure remains valid and maintains backward compatibility.

packages/unraid-api-plugin-connect/src/connection-status/connect-status-writer.service.ts (2)

4-5: LGTM: Proper imports for directory handling.

The addition of mkdir and dirname imports supports the enhanced directory creation functionality.


63-66: LGTM: Enhanced robustness with directory creation.

The addition of directory creation before writing the status file improves robustness by ensuring the target directory exists.

packages/unraid-api-plugin-connect/codegen.ts (1)

29-31: LGTM: GraphQL codegen removal aligns with WebSocket architecture.

Removing the mothership GraphQL type generation is the correct approach since the new UnraidServerClientService handles GraphQL operations dynamically over WebSocket connections rather than using pre-generated static types.

packages/unraid-api-plugin-connect/src/mothership-proxy/mothership.controller.ts (2)

5-19: LGTM! Clean transition to WebSocket-based architecture.

The migration from MothershipGraphqlClientService to UnraidServerClientService is well-implemented, with appropriate updates to comments and dependency injection.


51-57: Good defensive programming with proper validation.

The addition of the apiKey check alongside isLoaded ensures the mothership connection is only established when proper credentials are available.

packages/unraid-api-plugin-connect/src/mothership-proxy/local-graphql-executor.service.ts (1)

24-24: LocalGraphQLExecutor correctly injects InternalClientService

  • The constructor’s dependency on InternalClientService is intentional and matches its use elsewhere (e.g., in MothershipSubscriptionHandler). No change to use UnraidServerClientService is needed.
  • Remaining suggestions for this new executor:
    • Implement stopSubscription (around line 154) or document its intended behavior.
    • Externalize the hard-coded field-name mappings (currently in lines 68–109 and 125–150) into a configuration or constants file for easier maintenance.
      [f​ix_required]
packages/unraid-api-plugin-connect/src/mothership-proxy/unraid-server-client.service.ts (2)

402-418: LGTM! Proper cleanup in disconnect method.

The disconnect method correctly:

  • Disables reconnection
  • Clears ping interval
  • Clears reconnection timeout
  • Closes the WebSocket connection

This prevents memory leaks and ensures clean shutdown.


210-248: Excellent reconnection implementation with exponential backoff.

The reconnection logic is well-implemented with:

  • Exponential backoff starting at 1 second
  • Maximum delay cap of 30 minutes
  • Clear logging of reconnection attempts
  • Proper cleanup of existing timeouts
api/generated-schema.graphql (2)

1-3: Auto-generated file - changes look appropriate.

This schema file is automatically generated and reflects the architectural changes in the PR. The removal of remote access/cloud types and addition of comprehensive UPS management features align with the WebSocket-based mothership communication refactor.


1398-1526: Well-documented UPS types with clear field descriptions.

The new UPS-related types are thoroughly documented with units, examples, and clear explanations. This will greatly help API consumers understand the data structure.

Comment on lines 17 to 19
// Use environment variable if set, otherwise default to /var/local/emhttp/connectStatus.json
return this.configService.get('PATHS_CONNECT_STATUS') || '/var/local/emhttp/connectStatus.json';
}
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Fix critical path handling issue causing pipeline failures.

The pipeline failures indicate that configService.get('PATHS_CONNECT_STATUS') is returning an object instead of a string, causing a TypeError when passed to dirname().

The issue is likely that the configuration value is not being properly typed or retrieved. Apply this fix:

get statusFilePath() {
-    // Use environment variable if set, otherwise default to /var/local/emhttp/connectStatus.json
-    return this.configService.get('PATHS_CONNECT_STATUS') || '/var/local/emhttp/connectStatus.json';
+    // Use environment variable if set, otherwise default to /var/local/emhttp/connectStatus.json
+    return this.configService.get<string>('PATHS_CONNECT_STATUS') || '/var/local/emhttp/connectStatus.json';
}

Run this script to verify the configuration service implementation:


🏁 Script executed:

#!/bin/bash
# Description: Check how PATHS_CONNECT_STATUS is configured in the environment and config service

# Find configuration files that might define PATHS_CONNECT_STATUS
rg -n "PATHS_CONNECT_STATUS" --type json --type ts

# Look for ConfigService usage patterns
ast-grep --pattern 'configService.get($$$)'

Length of output: 428


Enforce string type and validate PATHS_CONNECT_STATUS to prevent TypeError

Ensure the config value is both statically typed and runtime-validated in connect-status-writer.service.ts so that a non-string never reaches dirname().

• File: packages/unraid-api-plugin-connect/src/connection-status/connect-status-writer.service.ts
Method: statusFilePath

get statusFilePath(): string {
-    // Use environment variable if set, otherwise default to /var/local/emhttp/connectStatus.json
-    return this.configService.get('PATHS_CONNECT_STATUS') || '/var/local/emhttp/connectStatus.json';
+    // Use environment variable if set, otherwise default to /var/local/emhttp/connectStatus.json
+    const rawPath = this.configService.get<string>('PATHS_CONNECT_STATUS');
+    if (typeof rawPath !== 'string') {
+        throw new TypeError(
+            `Invalid config "PATHS_CONNECT_STATUS": expected string, received ${typeof rawPath}`
+        );
+    }
+    return rawPath || '/var/local/emhttp/connectStatus.json';
}

Also verify in your configuration files (JSON, .env or TS) that PATHS_CONNECT_STATUS is defined as a string.

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

🤖 Prompt for AI Agents
In
packages/unraid-api-plugin-connect/src/connection-status/connect-status-writer.service.ts
around lines 17 to 19, the config value for PATHS_CONNECT_STATUS should be
explicitly checked to ensure it is a string before returning it. Update the
statusFilePath method to validate the type of the config value at runtime,
returning the default path only if the config value is either undefined or not a
string. Also, verify that PATHS_CONNECT_STATUS is defined as a string in all
configuration files such as JSON, .env, or TypeScript files.

Comment on lines 32 to 50
const client = this.mothershipClient.getClient();
if (client && subscription.operationId) {
// Note: We can't directly call stopSubscription on the client since it's private
// This would need to be exposed or handled differently in a real implementation
this.logger.debug(`Should stop subscription with operationId: ${subscription.operationId}`);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Address the architectural limitation with stopSubscription.

The comment indicates that stopSubscription cannot be called on the client because it's private. This is an architectural issue that should be resolved by exposing the necessary methods on the UnraidServerClient.

Consider exposing a public method on UnraidServerClient to stop subscriptions:

// In UnraidServerClient class
public async stopSubscription(operationId: string): Promise<void> {
    if (this.executor.stopSubscription) {
        await this.executor.stopSubscription(operationId);
    }
    // Send subscription_stop message to mothership
    this.sendResponse({
        operationId,
        event: 'query_response',
        type: 'complete',
        payload: { data: null },
    });
}
🤖 Prompt for AI Agents
In
packages/unraid-api-plugin-connect/src/mothership-proxy/mothership-subscription.handler.ts
around lines 32 to 37, the code cannot call stopSubscription on the client
because it is private. To fix this, add a public async method
stopSubscription(operationId: string) to the UnraidServerClient class that
internally calls the private executor.stopSubscription if available and sends
the appropriate subscription_stop message to mothership. Then update this
handler to call the new public stopSubscription method on the client instead of
accessing the private method directly.

Comment on lines 104 to 132
stopMothershipSubscription() {
this.logger.verbose('Stopping mothership subscription (not implemented yet)');
}

async handleRemoteGraphQLEvent(event: RemoteGraphQlEventFragmentFragment) {
const { body, type, sha256 } = event.remoteGraphQLEventData;
switch (type) {
case RemoteGraphQlEventType.REMOTE_QUERY_EVENT:
return this.safeExecuteQuery(sha256, body);
case RemoteGraphQlEventType.REMOTE_SUBSCRIPTION_EVENT:
return this.addSubscription(event.remoteGraphQLEventData);
case RemoteGraphQlEventType.REMOTE_SUBSCRIPTION_EVENT_PING:
return this.pingSubscription(sha256);
default:
return;
async subscribeToMothershipEvents() {
this.logger.log('Subscribing to mothership events via UnraidServerClient');

// For now, just log that we're connected
// The UnraidServerClient handles the WebSocket connection automatically
const client = this.mothershipClient.getClient();
if (client) {
this.logger.log('UnraidServerClient is connected and handling mothership communication');
} else {
this.logger.warn('UnraidServerClient is not available');
}
}
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

Implement proper mothership event subscription.

The current implementation only logs connection status. This should set up actual event subscriptions through the WebSocket client.

The method needs to:

  1. Subscribe to mothership events via the WebSocket client
  2. Handle incoming events and route them appropriately
  3. Manage subscription lifecycle
🤖 Prompt for AI Agents
In
packages/unraid-api-plugin-connect/src/mothership-proxy/mothership-subscription.handler.ts
around lines 104 to 119, the subscribeToMothershipEvents method currently only
logs connection status without setting up real event subscriptions. Update this
method to use the WebSocket client from mothershipClient to subscribe to actual
mothership events, add event listeners to handle incoming events and route them
properly, and implement logic to manage the subscription lifecycle including
cleanup or reconnection as needed.

Comment on lines 121 to 157
async executeQuery(sha256: string, body: string) {
this.logger.debug(`Request to execute query ${sha256}: ${body} (simplified implementation)`);

try {
// For now, just return a success response
// TODO: Implement actual query execution via the UnraidServerClient
return {
data: {
message: 'Query executed successfully (simplified)',
sha256,
}
},
});
};
} catch (error: any) {
this.logger.error(`Error executing query ${sha256}:`, error);
return {
errors: [
{
message: `Query execution failed: ${error?.message || 'Unknown error'}`,
extensions: { code: 'EXECUTION_ERROR' },
},
],
};
}
}
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

Complete the executeQuery implementation.

The method currently returns mock data with a TODO comment. This needs to be implemented to execute actual queries via the UnraidServerClient.

This method should execute queries through the WebSocket client. Do you want me to help implement the proper query execution logic?

🤖 Prompt for AI Agents
In
packages/unraid-api-plugin-connect/src/mothership-proxy/mothership-subscription.handler.ts
between lines 121 and 144, the executeQuery method currently returns mock data
and has a TODO to implement actual query execution. Replace the mock response by
invoking the appropriate method on the UnraidServerClient WebSocket client to
execute the query using the provided sha256 and body parameters. Handle the
response and errors properly, returning the actual query result or formatted
error information as needed.

Comment on lines +25 to +33
interface GraphQLExecutor {
execute(params: {
query: string
variables?: Record<string, any>
operationName?: string
operationType?: 'query' | 'mutation' | 'subscription'
}): Promise<any>
stopSubscription?(operationId: string): Promise<void>
}
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

Remove duplicate GraphQLExecutor interface.

This interface is already defined in local-graphql-executor.service.ts. Import it instead of duplicating the definition.

+import { GraphQLExecutor } from './local-graphql-executor.service.js';
+
-interface GraphQLExecutor {
-  execute(params: {
-    query: string
-    variables?: Record<string, any>
-    operationName?: string
-    operationType?: 'query' | 'mutation' | 'subscription'
-  }): Promise<any>
-  stopSubscription?(operationId: string): Promise<void>
-}
🤖 Prompt for AI Agents
In
packages/unraid-api-plugin-connect/src/mothership-proxy/unraid-server-client.service.ts
around lines 25 to 33, the GraphQLExecutor interface is duplicated but already
exists in local-graphql-executor.service.ts. Remove this local interface
definition and import the GraphQLExecutor interface from
local-graphql-executor.service.ts to avoid duplication and maintain consistency.

@claude
Copy link

claude bot commented Jul 31, 2025

Claude finished @elibosley's task —— View job


PR Review: Mothership v2

I've completed a comprehensive review of this PR. Here's my detailed feedback:

Review Tasks

  • Analyze project structure and dependencies
  • Review new services (local-graphql-executor, unraid-server-client)
  • Examine refactored mothership components
  • Check configuration and environment changes
  • Assess security and performance implications
  • Evaluate test coverage
  • Provide comprehensive feedback

🔍 Code Quality & Architecture

Strengths:

  • Clean separation of concerns with LocalGraphQLExecutor and UnraidServerClient
  • Good use of NestJS patterns with proper dependency injection
  • Comprehensive WebSocket message handling with multiple format support
  • Proper cleanup in OnModuleDestroy lifecycle hooks

Areas for Improvement:

1. Error Handling & Logging 🚨

  • UnraidServerClient (unraid-server-client.service.ts:64, 76, 86): Uses console.log/error instead of proper logger
    // Instead of console.log
    console.log('Connected to mothership')
    // Use:
    this.logger.log('Connected to mothership')

2. Hardcoded Values & Magic Numbers

  • Ping interval: 30000 ms hardcoded (unraid-server-client.service.ts:200)
  • Max reconnect delay: 30 * 60 * 1000 hardcoded (unraid-server-client.service.ts:40)
  • Should be configurable via environment/config service

3. Code Duplication

  • Three similar handleRequestMessage, handleRouteMessage, and handleNewFormatGraphQLRequest methods with nearly identical logic (unraid-server-client.service.ts:314-400)
  • Consider extracting common GraphQL execution logic

🔒 Security Concerns

1. API Key Exposure ⚠️

  • API keys passed in WebSocket headers (unraid-server-client.service.ts:59) - consider if this is secure over the wire
  • Ensure mothership connection uses TLS in production

2. Input Validation

  • LocalGraphQLExecutor.transformRemoteQueryToLocal() (local-graphql-executor.service.ts:68) parses user-provided GraphQL without validation
  • Should validate/sanitize incoming queries before transformation

3. Message Size Limits

  • No limits on WebSocket message sizes could lead to DoS attacks
  • Consider implementing message size validation

Performance Considerations

1. Memory Management

  • WebSocket reconnection uses exponential backoff (good!)
  • But no cleanup of failed connection objects could lead to memory leaks
  • Consider implementing connection pool limits

2. GraphQL Query Transformation

  • transformRemoteQueryToLocal() parses and transforms every query (local-graphql-executor.service.ts:68-109)
  • Consider caching transformed queries for frequently used operations

3. Subscription Management

  • MothershipSubscriptionHandler has TODO comments for incomplete subscription cleanup (mothership-subscription.handler.ts:34, 51)
  • Incomplete subscription handling could lead to resource leaks

🧪 Test Coverage

Critical Gap:

  • No tests for the core new services:
    • LocalGraphQLExecutor - critical GraphQL transformation logic untested
    • UnraidServerClient - complex WebSocket handling untested
    • UnraidServerClientService - NestJS service wrapper untested

Existing Test:

  • Only mothership.events.test.ts exists, but it's for the event handler, not the core new functionality

Recommendations:

  • Add unit tests for GraphQL query transformation logic
  • Add integration tests for WebSocket connection/reconnection
  • Add tests for error handling scenarios
  • Mock the WebSocket dependency properly for testing

🐛 Potential Bugs

1. Race Conditions

  • shouldReconnect flag and WebSocket state management could have race conditions (unraid-server-client.service.ts:43, 52, 403)
  • Multiple rapid connect/disconnect calls might cause issues

2. Error Response Format

  • Inconsistent error response formats across different message handlers
  • Some return { errors: [...] }, others might return different formats

3. Incomplete Implementation

  • executeQuery() method in MothershipSubscriptionHandler returns hardcoded success response (mothership-subscription.handler.ts:127)
  • Should implement actual query execution

📝 Additional Recommendations

1. Configuration Management

  • Move hardcoded values to configuration:
    // Add to environment.ts
    export const WEBSOCKET_PING_INTERVAL = process.env.WS_PING_INTERVAL ? parseInt(process.env.WS_PING_INTERVAL) : 30000;
    export const MAX_RECONNECT_DELAY = process.env.MAX_RECONNECT_DELAY ? parseInt(process.env.MAX_RECONNECT_DELAY) : 30 * 60 * 1000;

2. Documentation

  • Add JSDoc comments to complex methods like transformRemoteQueryToLocal()
  • Document the WebSocket message protocol and expected formats

3. Monitoring & Observability

  • Add metrics for connection health, message counts, error rates
  • Add structured logging for better debugging

🎯 Priority Actions

  1. High Priority:

    • Add comprehensive tests for new services
    • Fix console.log usage (replace with proper logger)
    • Complete subscription cleanup implementation
  2. Medium Priority:

    • Refactor duplicate GraphQL execution logic
    • Add input validation for GraphQL queries
    • Make configuration values configurable
  3. Low Priority:

    • Add JSDoc documentation
    • Implement monitoring/metrics
    • Consider caching for query transformations

Overall Assessment

This is a solid architectural improvement moving to WebSocket-based communication. The code structure is clean and follows good patterns. However, test coverage is critically missing and there are several security and performance considerations that should be addressed before production deployment.

The refactoring successfully centralizes the mothership communication logic, which should improve maintainability and reliability compared to the previous implementation.


@coderabbitai coderabbitai bot requested review from mdatelle, pujitm and zackspear July 31, 2025 16:06
…ient

- Updated package.json scripts to remove MOTHERSHIP_GRAPHQL_LINK environment variable.
- Changed MOTHERSHIP_GRAPHQL_LINK to MOTHERSHIP_BASE_URL in environment.ts.
- Removed GraphQL code generation for Mothership types in codegen.ts.
- Updated connection status services to use MOTHERSHIP_BASE_URL.
- Refactored MothershipSubscriptionHandler to utilize UnraidServerClient instead of GraphQL client.
- Implemented UnraidServerClient for WebSocket communication with Mothership.
- Enhanced MothershipController to manage UnraidServerClient lifecycle.
- Added reconnection logic and ping/pong handling in UnraidServerClient.
- Simplified GraphQL execution logic in UnraidServerClient.
- Introduced new types and enums for managing remote access configurations, including AccessUrl, RemoteAccess, and Connect settings.
- Added mutations for updating API settings and managing remote access.
- Updated the API configuration to include the new connect plugin.
- Enhanced the pnpm lock file with the addition of the pify package.
- Implemented logic to skip file modifications in development mode.
…port

- Added a new package for the Unraid API plugin, featuring a modular structure for connection management and remote access.
- Implemented GraphQL resolvers and services for cloud connection status, dynamic remote access, and network management.
- Updated code generation configuration to support new GraphQL types and queries.
- Refactored existing services to utilize the new GraphQL client for improved performance and maintainability.
- Included comprehensive tests for new functionalities to ensure reliability and stability.
portSsl,
}: UrlForFieldInputInsecure | UrlForFieldInputSecure): URL {
let portToUse = '';
let httpMode = 'https://';

Check warning

Code scanning / CodeQL

Useless assignment to local variable Warning

The initial value of httpMode is unused, since it is always overwritten.

Copilot Autofix

AI about 7 hours ago

To fix the useless assignment, simply remove the initialization of httpMode on line 135. Instead, declare httpMode as a local let variable without a value (i.e., let httpMode;). This makes it clear that its value will be set later based on conditional logic. The assignment of 'http://' or 'https://' remains as part of the flow in the branches, so this edit preserves all existing functionality.

Edits are restricted to lines 135 of the method inside the shown code in packages/unraid-api-plugin-connect-2/src/network/url-resolver.service.ts.

No new methods or imports are required.


Suggested changeset 1
packages/unraid-api-plugin-connect-2/src/network/url-resolver.service.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/unraid-api-plugin-connect-2/src/network/url-resolver.service.ts b/packages/unraid-api-plugin-connect-2/src/network/url-resolver.service.ts
--- a/packages/unraid-api-plugin-connect-2/src/network/url-resolver.service.ts
+++ b/packages/unraid-api-plugin-connect-2/src/network/url-resolver.service.ts
@@ -132,7 +132,7 @@
         portSsl,
     }: UrlForFieldInputInsecure | UrlForFieldInputSecure): URL {
         let portToUse = '';
-        let httpMode = 'https://';
+        let httpMode;
 
         if (!url || url === '') {
             throw new Error('No URL Provided');
EOF
@@ -132,7 +132,7 @@
portSsl,
}: UrlForFieldInputInsecure | UrlForFieldInputSecure): URL {
let portToUse = '';
let httpMode = 'https://';
let httpMode;

if (!url || url === '') {
throw new Error('No URL Provided');
Copilot is powered by AI and may make mistakes. Always verify output.
}

private async fastCheckCloud(): Promise<CloudResponse> {
let ip = 'FAST_CHECK_NO_IP_FOUND';

Check warning

Code scanning / CodeQL

Useless assignment to local variable Warning

The initial value of ip is unused, since it is always overwritten.

Copilot Autofix

AI about 7 hours ago

To fix the problem, simply remove the initial assignment to ip on line 170 and declare the variable as a let without initialization (let ip;). This ensures ip is only set by the results of the try and catch blocks and eliminates the useless assignment, making the code cleaner without altering its behaviour. This change is local to the declaration of ip in the method fastCheckCloud() within packages/unraid-api-plugin-connect-2/src/connection-status/cloud.service.ts. No further changes or imports are required.


Suggested changeset 1
packages/unraid-api-plugin-connect-2/src/connection-status/cloud.service.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/unraid-api-plugin-connect-2/src/connection-status/cloud.service.ts b/packages/unraid-api-plugin-connect-2/src/connection-status/cloud.service.ts
--- a/packages/unraid-api-plugin-connect-2/src/connection-status/cloud.service.ts
+++ b/packages/unraid-api-plugin-connect-2/src/connection-status/cloud.service.ts
@@ -167,7 +167,7 @@
     }
 
     private async fastCheckCloud(): Promise<CloudResponse> {
-        let ip = 'FAST_CHECK_NO_IP_FOUND';
+        let ip;
         try {
             ip = await this.checkDns();
         } catch (error) {
EOF
@@ -167,7 +167,7 @@
}

private async fastCheckCloud(): Promise<CloudResponse> {
let ip = 'FAST_CHECK_NO_IP_FOUND';
let ip;
try {
ip = await this.checkDns();
} catch (error) {
Copilot is powered by AI and may make mistakes. Always verify output.
import { UserSettingsService } from '@unraid/shared/services/user-settings.js';
import { execa } from 'execa';
import { GraphQLError } from 'graphql/error/GraphQLError.js';
import { decodeJwt } from 'jose';

Check notice

Code scanning / CodeQL

Unused variable, import, function or class Note

Unused import decodeJwt.

Copilot Autofix

AI about 7 hours ago

To resolve this issue, the unused import of decodeJwt from the 'jose' package should be removed. This can be done by deleting line 14 from the file packages/unraid-api-plugin-connect-2/src/unraid-connect/connect-settings.service.ts, which reads import { decodeJwt } from 'jose';. No other code or imports need to be changed, as no usage of this import is shown elsewhere in the provided code.


Suggested changeset 1
packages/unraid-api-plugin-connect-2/src/unraid-connect/connect-settings.service.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/unraid-api-plugin-connect-2/src/unraid-connect/connect-settings.service.ts b/packages/unraid-api-plugin-connect-2/src/unraid-connect/connect-settings.service.ts
--- a/packages/unraid-api-plugin-connect-2/src/unraid-connect/connect-settings.service.ts
+++ b/packages/unraid-api-plugin-connect-2/src/unraid-connect/connect-settings.service.ts
@@ -11,7 +11,6 @@
 import { UserSettingsService } from '@unraid/shared/services/user-settings.js';
 import { execa } from 'execa';
 import { GraphQLError } from 'graphql/error/GraphQLError.js';
-import { decodeJwt } from 'jose';
 
 import type {
     ConnectSettingsInput,
EOF
@@ -11,7 +11,6 @@
import { UserSettingsService } from '@unraid/shared/services/user-settings.js';
import { execa } from 'execa';
import { GraphQLError } from 'graphql/error/GraphQLError.js';
import { decodeJwt } from 'jose';

import type {
ConnectSettingsInput,
Copilot is powered by AI and may make mistakes. Always verify output.
Comment on lines +14 to +23
import {
AllowedOriginInput,
ConnectSettings,
ConnectSettingsInput,
ConnectSettingsValues,
ConnectSignInInput,
EnableDynamicRemoteAccessInput,
RemoteAccess,
SetupRemoteAccessInput,
} from './connect.model.js';

Check notice

Code scanning / CodeQL

Unused variable, import, function or class Note

Unused import AllowedOriginInput.

Copilot Autofix

AI about 7 hours ago

To fix the issue, we should remove the unused import AllowedOriginInput from the destructured import statement at line 15. Only changes to the import statement are required; there is no need to modify any other part of the code. The rest of the imports from './connect.model.js' remain untouched, as they might be used elsewhere in the file.

This change is scoped to the file packages/unraid-api-plugin-connect-2/src/unraid-connect/connect-settings.resolver.ts, specifically the import region at lines 14–23.

Suggested changeset 1
packages/unraid-api-plugin-connect-2/src/unraid-connect/connect-settings.resolver.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/unraid-api-plugin-connect-2/src/unraid-connect/connect-settings.resolver.ts b/packages/unraid-api-plugin-connect-2/src/unraid-connect/connect-settings.resolver.ts
--- a/packages/unraid-api-plugin-connect-2/src/unraid-connect/connect-settings.resolver.ts
+++ b/packages/unraid-api-plugin-connect-2/src/unraid-connect/connect-settings.resolver.ts
@@ -12,7 +12,6 @@
 import { EVENTS } from '../helper/nest-tokens.js';
 import { ConnectSettingsService } from './connect-settings.service.js';
 import {
-    AllowedOriginInput,
     ConnectSettings,
     ConnectSettingsInput,
     ConnectSettingsValues,
EOF
@@ -12,7 +12,6 @@
import { EVENTS } from '../helper/nest-tokens.js';
import { ConnectSettingsService } from './connect-settings.service.js';
import {
AllowedOriginInput,
ConnectSettings,
ConnectSettingsInput,
ConnectSettingsValues,
Copilot is powered by AI and may make mistakes. Always verify output.
import { ConfigService } from '@nestjs/config';
import { EventEmitter2 } from '@nestjs/event-emitter';

import { ConfigType, DynamicRemoteAccessType, MyServersConfig } from '../config/connect.config.js';

Check notice

Code scanning / CodeQL

Unused variable, import, function or class Note

Unused imports DynamicRemoteAccessType, MyServersConfig.

Copilot Autofix

AI about 7 hours ago

To fix this problem, simply remove the unused imports DynamicRemoteAccessType and MyServersConfig from the import statement on line 5. This will improve code clarity and maintainability without changing existing functionality. No additional methods, imports, or definitions are needed. Only the relevant names should be deleted from the curly-brace list of imports.


Suggested changeset 1
packages/unraid-api-plugin-connect-2/src/remote-access/static-remote-access.service.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/unraid-api-plugin-connect-2/src/remote-access/static-remote-access.service.ts b/packages/unraid-api-plugin-connect-2/src/remote-access/static-remote-access.service.ts
--- a/packages/unraid-api-plugin-connect-2/src/remote-access/static-remote-access.service.ts
+++ b/packages/unraid-api-plugin-connect-2/src/remote-access/static-remote-access.service.ts
@@ -2,7 +2,7 @@
 import { ConfigService } from '@nestjs/config';
 import { EventEmitter2 } from '@nestjs/event-emitter';
 
-import { ConfigType, DynamicRemoteAccessType, MyServersConfig } from '../config/connect.config.js';
+import { ConfigType } from '../config/connect.config.js';
 import { EVENTS } from '../helper/nest-tokens.js';
 import { AccessUrl, UrlResolverService } from '../network/url-resolver.service.js';
 
EOF
@@ -2,7 +2,7 @@
import { ConfigService } from '@nestjs/config';
import { EventEmitter2 } from '@nestjs/event-emitter';

import { ConfigType, DynamicRemoteAccessType, MyServersConfig } from '../config/connect.config.js';
import { ConfigType } from '../config/connect.config.js';
import { EVENTS } from '../helper/nest-tokens.js';
import { AccessUrl, UrlResolverService } from '../network/url-resolver.service.js';

Copilot is powered by AI and may make mistakes. Always verify output.
Comment on lines +7 to +17
import {
IsArray,
IsBoolean,
IsEmail,
IsEnum,
IsNumber,
IsOptional,
IsString,
Matches,
ValidateIf,
} from 'class-validator';

Check notice

Code scanning / CodeQL

Unused variable, import, function or class Note

Unused imports IsArray, Matches.

Copilot Autofix

AI about 7 hours ago

To resolve the issue, remove the unused imports IsArray and Matches from the import statement for class-validator in packages/unraid-api-plugin-connect-2/src/config/connect.config.ts. This will improve code clarity, maintainability, and possibly build performance (especially for tools that perform tree shaking). The only change needed is to update the import block on lines 7–17, removing these two names. No other regions of code need to be edited, and no further actions or definitions are required.

Suggested changeset 1
packages/unraid-api-plugin-connect-2/src/config/connect.config.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/unraid-api-plugin-connect-2/src/config/connect.config.ts b/packages/unraid-api-plugin-connect-2/src/config/connect.config.ts
--- a/packages/unraid-api-plugin-connect-2/src/config/connect.config.ts
+++ b/packages/unraid-api-plugin-connect-2/src/config/connect.config.ts
@@ -5,14 +5,12 @@
 import { URL_TYPE } from '@unraid/shared/network.model.js';
 import { plainToInstance } from 'class-transformer';
 import {
-    IsArray,
     IsBoolean,
     IsEmail,
     IsEnum,
     IsNumber,
     IsOptional,
     IsString,
-    Matches,
     ValidateIf,
 } from 'class-validator';
 
EOF
@@ -5,14 +5,12 @@
import { URL_TYPE } from '@unraid/shared/network.model.js';
import { plainToInstance } from 'class-transformer';
import {
IsArray,
IsBoolean,
IsEmail,
IsEnum,
IsNumber,
IsOptional,
IsString,
Matches,
ValidateIf,
} from 'class-validator';

Copilot is powered by AI and may make mistakes. Always verify output.
import { beforeEach, describe, expect, it, vi } from 'vitest';

import { MinigraphStatus } from '../config/connect.config.js';
import { EVENTS, GRAPHQL_PUBSUB_CHANNEL } from '../helper/nest-tokens.js';

Check notice

Code scanning / CodeQL

Unused variable, import, function or class Note

Unused import EVENTS.

Copilot Autofix

AI about 7 hours ago

To fix this problem, remove the unused EVENTS import from the import statement on line 7 in packages/unraid-api-plugin-connect-2/src/__test__/mothership.events.test.ts. You should retain any other actually-used imports in the same statement—in this case, GRAPHQL_PUBSUB_CHANNEL. This change does not affect any existing functionality as the identifier was never referenced in the current file.

Suggested changeset 1
packages/unraid-api-plugin-connect-2/src/__test__/mothership.events.test.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/unraid-api-plugin-connect-2/src/__test__/mothership.events.test.ts b/packages/unraid-api-plugin-connect-2/src/__test__/mothership.events.test.ts
--- a/packages/unraid-api-plugin-connect-2/src/__test__/mothership.events.test.ts
+++ b/packages/unraid-api-plugin-connect-2/src/__test__/mothership.events.test.ts
@@ -4,7 +4,7 @@
 import { beforeEach, describe, expect, it, vi } from 'vitest';
 
 import { MinigraphStatus } from '../config/connect.config.js';
-import { EVENTS, GRAPHQL_PUBSUB_CHANNEL } from '../helper/nest-tokens.js';
+import { GRAPHQL_PUBSUB_CHANNEL } from '../helper/nest-tokens.js';
 import { MothershipConnectionService } from '../mothership-proxy/connection.service.js';
 import { MothershipController } from '../mothership-proxy/mothership.controller.js';
 import { MothershipHandler } from '../mothership-proxy/mothership.events.js';
EOF
@@ -4,7 +4,7 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';

import { MinigraphStatus } from '../config/connect.config.js';
import { EVENTS, GRAPHQL_PUBSUB_CHANNEL } from '../helper/nest-tokens.js';
import { GRAPHQL_PUBSUB_CHANNEL } from '../helper/nest-tokens.js';
import { MothershipConnectionService } from '../mothership-proxy/connection.service.js';
import { MothershipController } from '../mothership-proxy/mothership.controller.js';
import { MothershipHandler } from '../mothership-proxy/mothership.events.js';
Copilot is powered by AI and may make mistakes. Always verify output.

// Since we're not mocking Apollo Client, this will create a real client
// We just want to verify the state check works
const client = service.getClient();

Check notice

Code scanning / CodeQL

Unused variable, import, function or class Note

Unused variable client.

Copilot Autofix

AI about 7 hours ago

The best way to fix the identified issue is simply to remove the unused declaration of client on line 130. This means deleting the entire line:

const client = service.getClient();

This line is not referenced later in the test, and its removal will not alter the logic or clarity of the test case. No other changes are needed elsewhere in the file, as getClient() is still invoked for side-effect and verified in the very next lines. Keeping the invocation only in the assertion (as is already the case immediately below with expect(() => service.getClient()).not.toThrow();) is sufficient.

Suggested changeset 1
packages/unraid-api-plugin-connect-2/src/__test__/graphql.client.test.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/unraid-api-plugin-connect-2/src/__test__/graphql.client.test.ts b/packages/unraid-api-plugin-connect-2/src/__test__/graphql.client.test.ts
--- a/packages/unraid-api-plugin-connect-2/src/__test__/graphql.client.test.ts
+++ b/packages/unraid-api-plugin-connect-2/src/__test__/graphql.client.test.ts
@@ -127,7 +127,6 @@
 
             // Since we're not mocking Apollo Client, this will create a real client
             // We just want to verify the state check works
-            const client = service.getClient();
 
             // The client should either be null (if not created yet) or an Apollo client instance
             // The key is that it doesn't throw an error when state is valid
EOF
@@ -127,7 +127,6 @@

// Since we're not mocking Apollo Client, this will create a real client
// We just want to verify the state check works
const client = service.getClient();

// The client should either be null (if not created yet) or an Apollo client instance
// The key is that it doesn't throw an error when state is valid
Copilot is powered by AI and may make mistakes. Always verify output.
@@ -0,0 +1,304 @@
import { ConfigService } from '@nestjs/config';

Check notice

Code scanning / CodeQL

Unused variable, import, function or class Note

Unused import ConfigService.

Copilot Autofix

AI about 7 hours ago

To remediate this issue, remove the unused import of ConfigService from @nestjs/config at the top of the file (packages/unraid-api-plugin-connect-2/src/__test__/config.validation.test.ts). This is done by deleting line 1, as ConfigService is not referenced anywhere in the file. No other code changes are necessary.


Suggested changeset 1
packages/unraid-api-plugin-connect-2/src/__test__/config.validation.test.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/unraid-api-plugin-connect-2/src/__test__/config.validation.test.ts b/packages/unraid-api-plugin-connect-2/src/__test__/config.validation.test.ts
--- a/packages/unraid-api-plugin-connect-2/src/__test__/config.validation.test.ts
+++ b/packages/unraid-api-plugin-connect-2/src/__test__/config.validation.test.ts
@@ -1,5 +1,5 @@
-import { ConfigService } from '@nestjs/config';
 
+
 import { faker } from '@faker-js/faker';
 import * as fc from 'fast-check';
 import { beforeEach, describe, expect, it, vi } from 'vitest';
EOF
@@ -1,5 +1,5 @@
import { ConfigService } from '@nestjs/config';


import { faker } from '@faker-js/faker';
import * as fc from 'fast-check';
import { beforeEach, describe, expect, it, vi } from 'vitest';
Copilot is powered by AI and may make mistakes. Always verify output.
import { beforeEach, describe, expect, it, vi } from 'vitest';

import { ConnectConfigPersister } from '../config/config.persistence.js';
import { ConfigType, DynamicRemoteAccessType } from '../config/connect.config.js';

Check notice

Code scanning / CodeQL

Unused variable, import, function or class Note

Unused import DynamicRemoteAccessType.

Copilot Autofix

AI about 7 hours ago

To resolve this issue, the unused import DynamicRemoteAccessType should be removed from the file packages/unraid-api-plugin-connect-2/src/__test__/config.persistence.test.ts. Specifically, the import statement on line 8 should be edited to only import ConfigType from ../config/connect.config.js. This change will improve code quality and readability, with no effect on existing functionality.


Suggested changeset 1
packages/unraid-api-plugin-connect-2/src/__test__/config.persistence.test.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/unraid-api-plugin-connect-2/src/__test__/config.persistence.test.ts b/packages/unraid-api-plugin-connect-2/src/__test__/config.persistence.test.ts
--- a/packages/unraid-api-plugin-connect-2/src/__test__/config.persistence.test.ts
+++ b/packages/unraid-api-plugin-connect-2/src/__test__/config.persistence.test.ts
@@ -5,7 +5,7 @@
 import { beforeEach, describe, expect, it, vi } from 'vitest';
 
 import { ConnectConfigPersister } from '../config/config.persistence.js';
-import { ConfigType, DynamicRemoteAccessType } from '../config/connect.config.js';
+import { ConfigType } from '../config/connect.config.js';
 
 describe('ConnectConfigPersister', () => {
     let service: ConnectConfigPersister;
EOF
@@ -5,7 +5,7 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';

import { ConnectConfigPersister } from '../config/config.persistence.js';
import { ConfigType, DynamicRemoteAccessType } from '../config/connect.config.js';
import { ConfigType } from '../config/connect.config.js';

describe('ConnectConfigPersister', () => {
let service: ConnectConfigPersister;
Copilot is powered by AI and may make mistakes. Always verify output.
- Removed the deprecated internal API key JSON file.
- Renamed environment variable from MOTHERSHIP_BASE_URL to MOTHERSHIP_GRAPHQL_LINK for clarity.
- Updated CloudService and UnraidServerClientService to utilize the new MOTHERSHIP_GRAPHQL_LINK for API calls, ensuring consistent access to the Mothership API.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
api/generated-schema.graphql (1)

1983-2111: Duplicate UPS type definitions in schema.

The UPS types (UPSBattery, UPSPower, UPSDevice, UPSConfiguration) are defined twice in the schema. Lines 2113–2241 are exact duplicates of lines 1983–2111. This will cause GraphQL schema validation to fail with "Type already defined" errors.

Since this file is auto-generated (per the header on line 2), verify the upstream schema source or code generation pipeline to eliminate duplicate type definitions. Remove one set of duplicate type definitions.

Also applies to: 2113-2241

🧹 Nitpick comments (1)
api/.env.development (1)

22-22: Add quotes around the PATHS_CONNECT_STATUS value for consistency.

Line 22 defines PATHS_CONNECT_STATUS without surrounding quotes, while line 28 (MOTHERSHIP_BASE_URL) does use quotes. To maintain consistency and follow dotenv best practices, wrap the value in quotes.

-PATHS_CONNECT_STATUS=./dev/states/connectStatus.json # Connect status file for development
+PATHS_CONNECT_STATUS="./dev/states/connectStatus.json" # Connect status file for development
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4dd3718 and 90ed837.

📒 Files selected for processing (3)
  • api/.env.development (1 hunks)
  • api/dev/states/connectStatus.json (1 hunks)
  • api/generated-schema.graphql (2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
api/**/*

📄 CodeRabbit inference engine (CLAUDE.md)

Prefer adding new files to the NestJS repo located at api/src/unraid-api/ instead of the legacy code

Files:

  • api/dev/states/connectStatus.json
  • api/generated-schema.graphql
**/*

📄 CodeRabbit inference engine (.cursor/rules/default.mdc)

Never add comments unless they are needed for clarity of function

Files:

  • api/dev/states/connectStatus.json
  • api/generated-schema.graphql
🧠 Learnings (1)
📚 Learning: 2025-06-20T20:50:14.050Z
Learnt from: elibosley
Repo: unraid/api PR: 1425
File: api/dev/configs/connect.json:5-13
Timestamp: 2025-06-20T20:50:14.050Z
Learning: Connect configuration files like api/dev/configs/connect.json use their own naming conventions (e.g., apikey, accesstoken, idtoken, refreshtoken) as implementation details, and these should not be changed to follow general camelCase patterns.

Applied to files:

  • api/.env.development
🪛 dotenv-linter (4.0.0)
api/.env.development

[warning] 22-22: [UnorderedKey] The PATHS_CONNECT_STATUS key should go before the PATHS_CONNECT_STATUS_FILE_PATH key

(UnorderedKey)


[warning] 22-22: [ValueWithoutQuotes] This value needs to be surrounded in quotes

(ValueWithoutQuotes)


[warning] 23-23: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 23-23: [UnorderedKey] The ENVIRONMENT key should go before the PATHS_ACTIVATION_BASE key

(UnorderedKey)


[warning] 24-24: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 24-24: [UnorderedKey] The NODE_ENV key should go before the PATHS_ACTIVATION_BASE key

(UnorderedKey)


[warning] 25-25: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 26-26: [UnorderedKey] The PLAYGROUND key should go before the PORT key

(UnorderedKey)


[warning] 27-27: [UnorderedKey] The INTROSPECTION key should go before the NODE_ENV key

(UnorderedKey)


[warning] 28-28: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 28-28: [UnorderedKey] The MOTHERSHIP_BASE_URL key should go before the NODE_ENV key

(UnorderedKey)

🔇 Additional comments (2)
api/dev/states/connectStatus.json (1)

1-7: LGTM!

Valid development state file with appropriate structure for the connect status initialization.

api/generated-schema.graphql (1)

2529-2531: Duplicate UPS query fields in Query type.

The UPS-related query fields (upsDevices, upsDeviceById, upsConfiguration) appear twice in the Query type definition—at lines 2529–2531 and again at lines 2548–2550. GraphQL schema validation will fail with "Field already defined" errors.

Remove the duplicate query field definitions at lines 2548–2550. Only one set of UPS query definitions should remain in the Query type.

Also applies to: 2548-2550

⛔ Skipped due to learnings
Learnt from: pujitm
Repo: unraid/api PR: 1211
File: web/composables/gql/gql.ts:17-18
Timestamp: 2025-03-12T13:48:14.850Z
Learning: In the Unraid API project, the duplicate GraphQL query and mutation strings in gql.ts files are intentionally generated by GraphQL CodeGen tool and are necessary for the type system to function properly.

…raphQL integration

- Renamed the unraid-api-plugin-connect package to unraid-api-plugin-connect-2 for improved clarity.
- Updated dependencies in pnpm-lock.yaml to reflect the new package structure.
- Modified environment variables for Mothership API integration to use a new GraphQL link.
- Refactored services to utilize the updated package and improved internal client handling.
- Removed deprecated UPS-related types from the GraphQL schema to streamline the API.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (3)
packages/unraid-api-plugin-connect-2/src/mothership-proxy/unraid-server-client.service.ts (1)

25-33: Remove duplicate GraphQLExecutor interface.

This interface is already defined in local-graphql-executor.service.ts. Import it instead.

+import { GraphQLExecutor } from './local-graphql-executor.service.js';
+
-interface GraphQLExecutor {
-  execute(params: {
-    query: string
-    variables?: Record<string, any>
-    operationName?: string
-    operationType?: 'query' | 'mutation' | 'subscription'
-  }): Promise<any>
-  stopSubscription?(operationId: string): Promise<void>
-}
packages/unraid-api-plugin-connect-2/src/mothership-proxy/mothership-subscription.handler.ts (2)

60-66: Unused loop variable sha256.

The sha256 variable is destructured but never used in the loop body. Use an underscore prefix to indicate intentional non-use.

-        for (const [sha256, subscription] of this.activeSubscriptions.entries()) {
+        for (const [_sha256, subscription] of this.activeSubscriptions.entries()) {
             if (subscription.operationId) {
                 this.logger.debug(`Should stop subscription with operationId: ${subscription.operationId}`);
             }
         }

3-17: Remove unused imports.

Static analysis correctly identified multiple unused imports. Remove them to clean up the file.

 import { Inject, Injectable, Logger } from '@nestjs/common';
 
-import { isDefined } from 'class-validator';
 import { type Subscription } from 'zen-observable-ts';
 import { CANONICAL_INTERNAL_CLIENT_TOKEN, type CanonicalInternalClientService } from '@unraid/shared';
 
-import { EVENTS_SUBSCRIPTION, RemoteGraphQL_Fragment } from '../graphql/event.js';
-import {
-    ClientType,
-    RemoteGraphQlEventFragmentFragment,
-    RemoteGraphQlEventType,
-} from '../graphql/generated/client/graphql.js';
-import { useFragment } from '../graphql/generated/client/index.js';
-import { SEND_REMOTE_QUERY_RESPONSE } from '../graphql/remote-response.js';
-import { parseGraphQLQuery } from '../helper/parse-graphql.js';
 import { MothershipConnectionService } from './connection.service.js';
 import { UnraidServerClientService } from './unraid-server-client.service.js';

Note: Also remove the Subscription type import if it's not used elsewhere in the file.

🧹 Nitpick comments (9)
packages/unraid-api-plugin-connect-2/src/mothership-proxy/local-graphql-executor.service.ts (3)

10-18: Consider exporting the interface for reuse.

The GraphQLExecutor interface is duplicated in unraid-server-client.service.ts. Export it from this file and import it where needed to avoid duplication.

Additionally, prefer a more specific type over any for the variables parameter and return type per coding guidelines.

-interface GraphQLExecutor {
+export interface GraphQLExecutor {
   execute(params: {
     query: string
-    variables?: Record<string, any>
+    variables?: Record<string, unknown>
     operationName?: string
     operationType?: 'query' | 'mutation' | 'subscription'
-  }): Promise<any>
+  }): Promise<{ data?: unknown; errors?: Array<{ message: string; extensions?: Record<string, unknown> }> }>
   stopSubscription?(operationId: string): Promise<void>
 }

58-68: Prefer unknown over any for the error catch clause.

As per coding guidelines, avoid using the any type.

-    } catch (error: any) {
-      this.logger.error(`GraphQL execution error: ${error?.message}`);
+    } catch (error: unknown) {
+      const errorMessage = error instanceof Error ? error.message : 'Unknown error';
+      this.logger.error(`GraphQL execution error: ${errorMessage}`);
       return {
         errors: [
           {
-            message: error?.message || 'Unknown error',
+            message: errorMessage,
             extensions: { code: 'EXECUTION_ERROR' },
           },
         ],
       };
     }

158-161: Stub implementation for subscription cleanup.

The stopSubscription method is a stub. Ensure this is tracked for completion before the feature is finalized.

Would you like me to open an issue to track implementing proper subscription cleanup logic?

packages/unraid-api-plugin-connect-2/src/mothership-proxy/unraid-server-client.service.ts (3)

16-23: Use stricter types for GraphQLResponse interface.

Replace any with more specific types per coding guidelines.

 interface GraphQLResponse {
   operationId: string
   messageId?: string
   event: 'query_response'
   type: 'data' | 'error' | 'complete'
-  payload: any
+  payload: { data?: unknown; errors?: Array<{ message: string; extensions?: Record<string, unknown> }> }
   requestHash?: string
 }

63-89: Replace console.log/error with Logger for consistency.

The UnraidServerClientService uses NestJS Logger, but UnraidServerClient uses console.log. For consistency and proper log level control, inject or instantiate a Logger.

 export class UnraidServerClient {
+  private readonly logger = new Logger(UnraidServerClient.name)
   private ws: WebSocket | null = null
   // ...

         this.ws.onopen = () => {
-          console.log('Connected to mothership')
+          this.logger.log('Connected to mothership')
           this.reconnectAttempts = 0
           this.setupPingInterval()
           resolve()
         }
         // ... apply similar changes to other console.log/error calls

You'll need to add the Logger import from @nestjs/common.


109-117: Consider stricter typing for parsed message.

The message variable is typed as any. Define an interface for expected message formats.

+interface MothershipMessage {
+  type?: string
+  event?: string
+  ping?: boolean
+  pong?: boolean
+  operationId?: string
+  messageId?: string
+  payload?: { query?: string; variables?: Record<string, unknown>; operationName?: string }
+}

       // Try to parse as JSON for structured messages
-      let message: any
+      let message: MothershipMessage
       try {
         message = JSON.parse(data)
packages/unraid-api-plugin-connect-2/src/mothership-proxy/mothership-subscription.handler.ts (1)

117-132: Multiple stub implementations need completion.

Both stopMothershipSubscription and subscribeToMothershipEvents are stubs. These should be tracked for completion before release.

Would you like me to open an issue to track completing these subscription management methods?

api/vite.config.ts (1)

24-27: Workspace path change is consistent; value currently informational only

Updating the workspaceDependencies entry to point unraid-api-plugin-connect at packages/unraid-api-plugin-connect-2 keeps this config in sync with the build script and docs. Since this file only uses Object.keys(workspaceDependencies), the behavior of optimize/external/ssr is unchanged; the path value is effectively documentation here. If this remains the case long term, you could optionally simplify this to a list of package names instead of a name→path map, but it’s not required for this PR.

api/scripts/build.ts (1)

10-10: Alias-based import may depend on zx/TS path-alias setup

Importing getDeploymentVersion via '@app/../scripts/get-deployment-version.js' will only work if your zx TypeScript runtime honors the @app tsconfig path alias and correctly normalizes the ../scripts segment. This is a bit opaque compared to a direct path to the scripts directory.

It’s worth double-checking that the script still runs correctly in all environments (local, CI) with this alias-based path; if not, consider reverting to a simpler, explicit relative or dedicated alias for the scripts directory to avoid surprises.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 90ed837 and 81cc9ff.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (11)
  • api/.env.development (1 hunks)
  • api/dev/configs/api.json (1 hunks)
  • api/dev/configs/connect.json (1 hunks)
  • api/dev/states/connectStatus.json (1 hunks)
  • api/docs/developer/api-plugins.md (2 hunks)
  • api/scripts/build.ts (2 hunks)
  • api/vite.config.ts (1 hunks)
  • packages/unraid-api-plugin-connect-2/src/mothership-proxy/local-graphql-executor.service.ts (1 hunks)
  • packages/unraid-api-plugin-connect-2/src/mothership-proxy/mothership-subscription.handler.ts (1 hunks)
  • packages/unraid-api-plugin-connect-2/src/mothership-proxy/mothership.module.ts (1 hunks)
  • packages/unraid-api-plugin-connect-2/src/mothership-proxy/unraid-server-client.service.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • api/dev/configs/api.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • api/dev/states/connectStatus.json
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: Always use TypeScript imports with .js extensions for ESM compatibility
Never add comments unless they are needed for clarity of function
Never add comments for obvious things, and avoid commenting when starting and ending code blocks

Files:

  • packages/unraid-api-plugin-connect-2/src/mothership-proxy/mothership.module.ts
  • api/scripts/build.ts
  • packages/unraid-api-plugin-connect-2/src/mothership-proxy/local-graphql-executor.service.ts
  • api/vite.config.ts
  • packages/unraid-api-plugin-connect-2/src/mothership-proxy/mothership-subscription.handler.ts
  • packages/unraid-api-plugin-connect-2/src/mothership-proxy/unraid-server-client.service.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Never use the any type. Always prefer proper typing
Avoid using casting whenever possible, prefer proper typing from the start

Files:

  • packages/unraid-api-plugin-connect-2/src/mothership-proxy/mothership.module.ts
  • api/scripts/build.ts
  • packages/unraid-api-plugin-connect-2/src/mothership-proxy/local-graphql-executor.service.ts
  • api/vite.config.ts
  • packages/unraid-api-plugin-connect-2/src/mothership-proxy/mothership-subscription.handler.ts
  • packages/unraid-api-plugin-connect-2/src/mothership-proxy/unraid-server-client.service.ts
**/*

📄 CodeRabbit inference engine (.cursor/rules/default.mdc)

Never add comments unless they are needed for clarity of function

Files:

  • packages/unraid-api-plugin-connect-2/src/mothership-proxy/mothership.module.ts
  • api/docs/developer/api-plugins.md
  • api/scripts/build.ts
  • api/dev/configs/connect.json
  • packages/unraid-api-plugin-connect-2/src/mothership-proxy/local-graphql-executor.service.ts
  • api/vite.config.ts
  • packages/unraid-api-plugin-connect-2/src/mothership-proxy/mothership-subscription.handler.ts
  • packages/unraid-api-plugin-connect-2/src/mothership-proxy/unraid-server-client.service.ts
api/**/*

📄 CodeRabbit inference engine (CLAUDE.md)

Prefer adding new files to the NestJS repo located at api/src/unraid-api/ instead of the legacy code

Files:

  • api/docs/developer/api-plugins.md
  • api/scripts/build.ts
  • api/dev/configs/connect.json
  • api/vite.config.ts
api/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

cache-manager v7 expects TTL values in milliseconds, not seconds (e.g., 600000 for 10 minutes, not 600)

Files:

  • api/scripts/build.ts
  • api/vite.config.ts
🧠 Learnings (11)
📓 Common learnings
Learnt from: elibosley
Repo: unraid/api PR: 1639
File: web/components/standalone-mount.ts:38-54
Timestamp: 2025-09-02T19:35:14.427Z
Learning: In the unraid/api project, exposing Apollo client and GraphQL utilities (apolloClient, gql, graphqlParse) on the global window object is intentional to allow developers to interact with GraphQL from the browser console without needing to be inside Vue components. Developer experience is prioritized over minimizing global exposure in this context.
Learnt from: mdatelle
Repo: unraid/api PR: 1106
File: unraid-ui/src/components/index.ts:2-2
Timestamp: 2025-02-04T17:21:39.710Z
Learning: The unraid-ui package is undergoing a major refactoring process, and breaking changes are expected during this transition period.
📚 Learning: 2025-09-02T19:35:14.427Z
Learnt from: elibosley
Repo: unraid/api PR: 1639
File: web/components/standalone-mount.ts:38-54
Timestamp: 2025-09-02T19:35:14.427Z
Learning: In the unraid/api project, exposing Apollo client and GraphQL utilities (apolloClient, gql, graphqlParse) on the global window object is intentional to allow developers to interact with GraphQL from the browser console without needing to be inside Vue components. Developer experience is prioritized over minimizing global exposure in this context.

Applied to files:

  • packages/unraid-api-plugin-connect-2/src/mothership-proxy/mothership.module.ts
  • packages/unraid-api-plugin-connect-2/src/mothership-proxy/local-graphql-executor.service.ts
  • packages/unraid-api-plugin-connect-2/src/mothership-proxy/mothership-subscription.handler.ts
  • packages/unraid-api-plugin-connect-2/src/mothership-proxy/unraid-server-client.service.ts
📚 Learning: 2025-03-12T13:48:14.850Z
Learnt from: pujitm
Repo: unraid/api PR: 1211
File: web/composables/gql/gql.ts:17-18
Timestamp: 2025-03-12T13:48:14.850Z
Learning: In the Unraid API project, the duplicate GraphQL query and mutation strings in gql.ts files are intentionally generated by GraphQL CodeGen tool and are necessary for the type system to function properly.

Applied to files:

  • packages/unraid-api-plugin-connect-2/src/mothership-proxy/mothership.module.ts
  • packages/unraid-api-plugin-connect-2/src/mothership-proxy/local-graphql-executor.service.ts
  • packages/unraid-api-plugin-connect-2/src/mothership-proxy/mothership-subscription.handler.ts
  • packages/unraid-api-plugin-connect-2/src/mothership-proxy/unraid-server-client.service.ts
📚 Learning: 2025-03-04T14:55:00.903Z
Learnt from: elibosley
Repo: unraid/api PR: 1151
File: plugin/builder/utils/consts.ts:6-6
Timestamp: 2025-03-04T14:55:00.903Z
Learning: The startingDir constant in plugin/builder/utils/consts.ts is defined using process.cwd(), which can cause issues if directory changes occur after importing this constant. Using __dirname.split('/builder')[0] would make it more reliable by making it relative to the file location rather than the current working directory.

Applied to files:

  • api/scripts/build.ts
📚 Learning: 2025-11-24T17:51:46.348Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/api-rules.mdc:0-0
Timestamp: 2025-11-24T17:51:46.348Z
Learning: Always run scripts from api/package.json unless explicitly requested otherwise

Applied to files:

  • api/scripts/build.ts
📚 Learning: 2025-02-06T17:25:45.397Z
Learnt from: mdatelle
Repo: unraid/api PR: 1122
File: web/package.json:16-16
Timestamp: 2025-02-06T17:25:45.397Z
Learning: The build script in web/package.json should include type-check command (`npm run type-check`) even when NODE_ENV is production, as it provides value for local builds by catching type errors before pushing to CI/CD.

Applied to files:

  • api/scripts/build.ts
📚 Learning: 2025-06-20T20:50:14.050Z
Learnt from: elibosley
Repo: unraid/api PR: 1425
File: api/dev/configs/connect.json:5-13
Timestamp: 2025-06-20T20:50:14.050Z
Learning: Connect configuration files like api/dev/configs/connect.json use their own naming conventions (e.g., apikey, accesstoken, idtoken, refreshtoken) as implementation details, and these should not be changed to follow general camelCase patterns.

Applied to files:

  • api/dev/configs/connect.json
📚 Learning: 2024-11-06T20:59:25.809Z
Learnt from: mdatelle
Repo: unraid/api PR: 942
File: api/src/unraid-api/graph/resolvers/auth/auth.resolver.ts:111-113
Timestamp: 2024-11-06T20:59:25.809Z
Learning: In the Unraid API project, error handling for mutations is handled at the service level rather than in the GraphQL resolvers. Specifically, in `api/src/unraid-api/graph/resolvers/auth/auth.resolver.ts`, methods like `removeRoleFromApiKey` rely on service-level error handling.

Applied to files:

  • packages/unraid-api-plugin-connect-2/src/mothership-proxy/local-graphql-executor.service.ts
  • packages/unraid-api-plugin-connect-2/src/mothership-proxy/unraid-server-client.service.ts
📚 Learning: 2024-11-05T14:49:07.308Z
Learnt from: elibosley
Repo: unraid/api PR: 942
File: api/src/unraid-api/auth/api-key.service.ts:62-70
Timestamp: 2024-11-05T14:49:07.308Z
Learning: In `api/src/unraid-api/auth/api-key.service.ts`, when handling read errors in the `findById` method, throw a `GraphQLError` instead of an `InternalServerErrorException`.

Applied to files:

  • packages/unraid-api-plugin-connect-2/src/mothership-proxy/local-graphql-executor.service.ts
  • packages/unraid-api-plugin-connect-2/src/mothership-proxy/unraid-server-client.service.ts
📚 Learning: 2024-12-10T15:57:42.593Z
Learnt from: pujitm
Repo: unraid/api PR: 975
File: web/helpers/apollo-cache/index.ts:79-82
Timestamp: 2024-12-10T15:57:42.593Z
Learning: The `isDefined` function is autoloaded and placed on `globalThis`; it does not need to be imported or defined explicitly in files.

Applied to files:

  • packages/unraid-api-plugin-connect-2/src/mothership-proxy/mothership-subscription.handler.ts
📚 Learning: 2025-03-03T16:04:20.705Z
Learnt from: pujitm
Repo: unraid/api PR: 1214
File: web/.gitignore:1-2
Timestamp: 2025-03-03T16:04:20.705Z
Learning: In the Unraid web application, environment variables with the VITE_ prefix (like VITE_CALLBACK_KEY) are intentionally exposed client-side and are not considered sensitive secrets, so it's acceptable to track them in Git.

Applied to files:

  • api/.env.development
🧬 Code graph analysis (3)
packages/unraid-api-plugin-connect-2/src/mothership-proxy/mothership.module.ts (1)
packages/unraid-api-plugin-connect/src/mothership-proxy/mothership.module.ts (1)
  • Module (15-30)
packages/unraid-api-plugin-connect-2/src/mothership-proxy/local-graphql-executor.service.ts (3)
packages/unraid-api-plugin-connect-2/src/mothership-proxy/unraid-server-client.service.ts (1)
  • Injectable (421-480)
packages/unraid-shared/src/tokens.ts (1)
  • CANONICAL_INTERNAL_CLIENT_TOKEN (7-7)
api/src/unraid-api/cli/generated/gql.ts (1)
  • gql (96-98)
packages/unraid-api-plugin-connect-2/src/mothership-proxy/unraid-server-client.service.ts (3)
packages/unraid-api-plugin-connect-2/src/mothership-proxy/local-graphql-executor.service.ts (1)
  • Injectable (23-162)
packages/unraid-api-plugin-connect-2/src/mothership-proxy/connection.service.ts (1)
  • Injectable (51-241)
packages/unraid-api-plugin-connect-2/src/mothership-proxy/graphql.client.ts (1)
  • Injectable (29-344)
🪛 dotenv-linter (4.0.0)
api/.env.development

[warning] 22-22: [UnorderedKey] The PATHS_CONNECT_STATUS key should go before the PATHS_CONNECT_STATUS_FILE_PATH key

(UnorderedKey)


[warning] 22-22: [ValueWithoutQuotes] This value needs to be surrounded in quotes

(ValueWithoutQuotes)


[warning] 23-23: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 23-23: [UnorderedKey] The ENVIRONMENT key should go before the PATHS_ACTIVATION_BASE key

(UnorderedKey)


[warning] 24-24: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 24-24: [UnorderedKey] The NODE_ENV key should go before the PATHS_ACTIVATION_BASE key

(UnorderedKey)


[warning] 25-25: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 26-26: [UnorderedKey] The PLAYGROUND key should go before the PORT key

(UnorderedKey)


[warning] 27-27: [UnorderedKey] The INTROSPECTION key should go before the NODE_ENV key

(UnorderedKey)


[warning] 28-28: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 28-28: [UnorderedKey] The MOTHERSHIP_GRAPHQL_LINK key should go before the NODE_ENV key

(UnorderedKey)


[warning] 29-29: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 29-29: [UnorderedKey] The MOTHERSHIP_BASE_URL key should go before the MOTHERSHIP_GRAPHQL_LINK key

(UnorderedKey)

🪛 GitHub Check: CodeQL
packages/unraid-api-plugin-connect-2/src/mothership-proxy/mothership-subscription.handler.ts

[notice] 62-62: Unused variable, import, function or class
Unused variable sha256.


[notice] 15-15: Unused variable, import, function or class
Unused import parseGraphQLQuery.


[notice] 14-14: Unused variable, import, function or class
Unused import SEND_REMOTE_QUERY_RESPONSE.


[notice] 13-13: Unused variable, import, function or class
Unused import useFragment.


[notice] 8-12: Unused variable, import, function or class
Unused imports ClientType, RemoteGraphQlEventFragmentFragment, RemoteGraphQlEventType.


[notice] 7-7: Unused variable, import, function or class
Unused imports EVENTS_SUBSCRIPTION, RemoteGraphQL_Fragment.


[notice] 3-3: Unused variable, import, function or class
Unused import isDefined.

🔇 Additional comments (9)
api/dev/configs/connect.json (1)

5-5: LGTM!

The placeholder value aligns with the development environment setup and matches the existing localApiKey format. Based on learnings, this configuration file follows its own naming conventions and no changes are needed.

api/.env.development (2)

28-29: LGTM!

The mothership endpoints are correctly updated to the preview environment (wss://preview.mothership2.unraid.net for GraphQL subscriptions and https://preview.mothership2.unraid.net for the base URL), aligning with the mothership v2 infrastructure changes.


33-37: LGTM!

The new development environment variables are appropriate: polling enables file watching in containers, logging configuration provides detailed tracing, and the flags control experimental features and plugin checks during development.

packages/unraid-api-plugin-connect-2/src/mothership-proxy/unraid-server-client.service.ts (2)

210-248: Exponential backoff implementation looks correct.

The reconnection logic with exponential backoff capped at 30 minutes is well implemented. The shouldReconnect flag properly prevents reconnection after intentional disconnects.


444-468: Remove this review comment—the concern is already addressed by an event-driven reinitialiation mechanism.

When identity state changes, connectionService emits an IDENTITY_CHANGED event (connection.service.ts:100), which is captured by mothership.events.ts (@OnEvent(EVENTS.IDENTITY_CHANGED)). This triggers mothershipController.initOrRestart(), which calls clientService.reconnect(), and reconnect() invokes initializeClient() (unraid-server-client.service.ts:478). The system properly reinitializes the client whenever identity becomes available.

packages/unraid-api-plugin-connect-2/src/mothership-proxy/mothership.module.ts (1)

14-30: Module structure looks correct.

The imports LocalGraphQLExecutor and UnraidServerClientService flagged by static analysis are correctly used in the providers array. The module properly wires up all the mothership proxy components.

packages/unraid-api-plugin-connect-2/src/mothership-proxy/mothership-subscription.handler.ts (1)

134-157: executeQuery returns mock data.

The method returns hardcoded mock data instead of executing actual queries. The TODO comment indicates this needs implementation.

Ensure this stub is completed before merging or mark it clearly as not production-ready.

api/docs/developer/api-plugins.md (1)

20-36: Doc updates correctly reflect new plugin-connect-2 workspace path

The examples now mapping unraid-api-plugin-connect to packages/unraid-api-plugin-connect-2 in both the build and Vite config snippets look consistent with the corresponding code changes, while keeping the package name unchanged in the peerDependencies section. No issues from a docs/code-alignment standpoint.

api/scripts/build.ts (1)

22-25: Vendoring map update for plugin-connect-2 is consistent

Pointing the WORKSPACE_PACKAGES_TO_VENDOR entry for unraid-api-plugin-connect at packages/unraid-api-plugin-connect-2 looks consistent with the Vite config and docs. Assuming the package in that folder is still named unraid-api-plugin-connect, this keeps the vendoring behavior aligned with the existing peerDependency name.

PATHS_CONNECT_STATUS_FILE_PATH=./dev/connectStatus.json # Connect plugin status file
PATHS_OIDC_JSON=./dev/configs/oidc.local.json
PATHS_LOCAL_SESSION_FILE=./dev/local-session
PATHS_CONNECT_STATUS=./dev/states/connectStatus.json # Connect status file for development
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 | 🟡 Minor

Add quotes around the value on line 22.

The .env file format requires quoted values for consistent parsing by dotenv libraries.

Apply this fix:

-PATHS_CONNECT_STATUS=./dev/states/connectStatus.json # Connect status file for development
+PATHS_CONNECT_STATUS="./dev/states/connectStatus.json" # Connect status file for development
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
PATHS_CONNECT_STATUS=./dev/states/connectStatus.json # Connect status file for development
PATHS_CONNECT_STATUS="./dev/states/connectStatus.json" # Connect status file for development
🧰 Tools
🪛 dotenv-linter (4.0.0)

[warning] 22-22: [UnorderedKey] The PATHS_CONNECT_STATUS key should go before the PATHS_CONNECT_STATUS_FILE_PATH key

(UnorderedKey)


[warning] 22-22: [ValueWithoutQuotes] This value needs to be surrounded in quotes

(ValueWithoutQuotes)

🤖 Prompt for AI Agents
In api/.env.development around line 22, the PATHS_CONNECT_STATUS value is
unquoted; update the line to wrap the value in quotes so dotenv parsers handle
it consistently (e.g., change
PATHS_CONNECT_STATUS=./dev/states/connectStatus.json to
PATHS_CONNECT_STATUS="./dev/states/connectStatus.json").

Comment on lines +48 to +53
// Execute the transformed query against local API
const client = await this.internalClient.getClient();
const result = await client.query({
query: gql`${localQuery}`,
variables,
});
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

Execute method ignores operationType parameter.

The method accepts operationType but always calls client.query(). Mutations should use client.mutate() to ensure proper behavior.

       // Execute the transformed query against local API
       const client = await this.internalClient.getClient();
-      const result = await client.query({
-        query: gql`${localQuery}`,
-        variables,
-      });
+      let result;
+      if (operationType === 'mutation') {
+        result = await client.mutate({
+          mutation: gql`${localQuery}`,
+          variables,
+        });
+      } else {
+        result = await client.query({
+          query: gql`${localQuery}`,
+          variables,
+        });
+      }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Execute the transformed query against local API
const client = await this.internalClient.getClient();
const result = await client.query({
query: gql`${localQuery}`,
variables,
});
// Execute the transformed query against local API
const client = await this.internalClient.getClient();
let result;
if (operationType === 'mutation') {
result = await client.mutate({
mutation: gql`${localQuery}`,
variables,
});
} else {
result = await client.query({
query: gql`${localQuery}`,
variables,
});
}
🤖 Prompt for AI Agents
In
packages/unraid-api-plugin-connect-2/src/mothership-proxy/local-graphql-executor.service.ts
around lines 48 to 53, the method ignores the operationType parameter and always
calls client.query(); change the logic to branch on operationType: if it's
'query' call client.query({ query: gql`${localQuery}`, variables }), if it's
'mutation' call client.mutate({ mutation: gql`${localQuery}`, variables }); keep
using gql for the document, pass the same variables, and return the
mutation/query result as before (no other API changes).

Comment on lines +154 to +176
try {
const errorRequest = JSON.parse(data)
// Only send error response for GraphQL requests that have operationId
if (errorRequest.operationId && (errorRequest.event === 'remote_query' || errorRequest.event === 'route_message')) {
const operationId = errorRequest.operationId || `error-${Date.now()}`
this.sendResponse({
operationId,
event: 'query_response',
type: 'error',
payload: {
errors: [
{
message: error?.message || 'Unknown error',
extensions: { code: 'EXECUTION_ERROR' },
},
],
},
})
}
} catch (e) {
console.error('Failed to send error response:', e)
}
}
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 | 🟡 Minor

Redundant operationId assignment.

Line 158 assigns operationId but line 156 already checks for its existence, making the fallback at line 158 unreachable in the truthy branch.

         // Only send error response for GraphQL requests that have operationId
         if (errorRequest.operationId && (errorRequest.event === 'remote_query' || errorRequest.event === 'route_message')) {
-          const operationId = errorRequest.operationId || `error-${Date.now()}`
+          const operationId = errorRequest.operationId
           this.sendResponse({
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try {
const errorRequest = JSON.parse(data)
// Only send error response for GraphQL requests that have operationId
if (errorRequest.operationId && (errorRequest.event === 'remote_query' || errorRequest.event === 'route_message')) {
const operationId = errorRequest.operationId || `error-${Date.now()}`
this.sendResponse({
operationId,
event: 'query_response',
type: 'error',
payload: {
errors: [
{
message: error?.message || 'Unknown error',
extensions: { code: 'EXECUTION_ERROR' },
},
],
},
})
}
} catch (e) {
console.error('Failed to send error response:', e)
}
}
try {
const errorRequest = JSON.parse(data)
// Only send error response for GraphQL requests that have operationId
if (errorRequest.operationId && (errorRequest.event === 'remote_query' || errorRequest.event === 'route_message')) {
const operationId = errorRequest.operationId
this.sendResponse({
operationId,
event: 'query_response',
type: 'error',
payload: {
errors: [
{
message: error?.message || 'Unknown error',
extensions: { code: 'EXECUTION_ERROR' },
},
],
},
})
}
} catch (e) {
console.error('Failed to send error response:', e)
}
}
🤖 Prompt for AI Agents
In
packages/unraid-api-plugin-connect-2/src/mothership-proxy/unraid-server-client.service.ts
around lines 154-176, the code checks that errorRequest.operationId exists
before entering the branch, so the fallback (`|| \`error-\${Date.now()}\``) when
assigning operationId is redundant and unreachable; remove the fallback and just
use the asserted operationId (e.g., const operationId =
errorRequest.operationId) or, if you intend to handle missing operationId,
change the if-condition to not require operationId and keep the fallback—pick
one and update the condition and assignment to be consistent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants