Skip to content

Conversation

@elibosley
Copy link
Member

@elibosley elibosley commented Aug 8, 2025

Summary by CodeRabbit

  • New Features

    • Unified exports for common and form UI components; added socket configuration service, DI token, and factory type for internal GraphQL clients.
  • Refactor

    • Centralized internal GraphQL client creation/lifecycle with cached, serialized creation and clear/recreate behavior; CLI now uses the shared factory.
  • Tests

    • Extensive new/updated tests for client factory, internal client lifecycle (including CLI), socket config, and Unix-socket WebSocket scenarios.
  • Chores

    • Added undici, migrated tests to Vitest, enabled skipLibCheck, and updated package metadata/dependencies.

@elibosley elibosley requested a review from pujitm August 8, 2025 20:48
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 8, 2025

Walkthrough

Adds an InternalGraphQLClientFactory and SocketConfigService, introduces INTERNAL_CLIENT_SERVICE_TOKEN, refactors CLI and Connect internal clients to use the injected factory with caching/concurrency/clear semantics, adds undici and Vitest migration, many unit/integration tests, and new UI barrel exports.

Changes

Cohort / File(s) Summary
Dependency & package metadata
api/package.json, packages/unraid-api-plugin-connect/package.json, packages/unraid-shared/package.json
Add undici to dependencies/peer/dev where appropriate; switch shared tests to Vitest and update dev/peer dependency lists.
Shared types & token exports
packages/unraid-shared/src/types/internal-graphql-client.factory.ts, packages/unraid-shared/src/tokens.ts, packages/unraid-shared/src/index.ts
Add InternalGraphQLClientFactory interface, add INTERNAL_CLIENT_SERVICE_TOKEN, and export new types/services from the shared package.
Socket configuration service
packages/unraid-shared/src/services/socket-config.service.ts, packages/unraid-shared/src/services/socket-config.service.spec.ts
New SocketConfigService for socket/port/URI resolution with comprehensive tests for socket vs TCP vs nginx behaviours.
InternalGraphQLClientFactory implementation & tests
api/src/unraid-api/shared/internal-graphql-client.factory.ts, api/src/unraid-api/shared/internal-graphql-client.factory.spec.ts
New factory to create Apollo clients (unix-socket and HTTP, optional WS subscriptions, per-request API key, error link), with tests covering variants and origins.
Global DI registration
api/src/unraid-api/plugin/global-deps.module.ts
Register SocketConfigService, provide/export INTERNAL_CLIENT_SERVICE_TOKEN using InternalGraphQLClientFactory.
Refactor: CLI internal client & tests
api/src/unraid-api/cli/internal-client.service.ts, api/src/unraid-api/cli/internal-client.service.spec.ts, api/src/unraid-api/cli/cli.module.spec.ts
Replace inline Apollo construction with injected factory; use admin key provider; add concurrency guard, caching, clear behavior, and tests verifying lifecycle and error paths.
Refactor: Connect plugin internal client & tests
packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts, packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.spec.ts
Delegate client creation to injected factory (subscriptions enabled), add caching and client-creation promise serialization; add lifecycle and concurrency tests.
Shared usage & ws tests
packages/unraid-shared/src/services/internal-graphql-client-usage.spec.ts, packages/unraid-shared/src/services/ws-unix-socket-test.spec.ts
Add usage-pattern tests for factory-based clients and WebSocket-over-unix-socket integration tests.
Test harness updates
packages/unraid-shared/src/jsonforms/__tests__/settings.test.ts, packages/unraid-shared/src/services/__tests__/config-file.test.ts, packages/unraid-shared/src/util/__tests__/*
Replace bun:test imports with vitest across shared tests.
UI barrel exports
unraid-ui/src/components/common/index.ts, unraid-ui/src/components/form/index.ts
New barrel files re-exporting UI component submodules.
Config/tsconfig tweaks & formatting
api/dev/configs/api.json, packages/unraid-api-plugin-connect/tsconfig.json, packages/unraid-api-plugin-generator/tsconfig.json, packages/unraid-api-plugin-generator/tsconfig.build.json, packages/unraid-api-plugin-health/tsconfig.json
Minor formatting changes and addition of skipLibCheck to generator tsconfigs.

Sequence Diagram(s)

sequenceDiagram
  participant CLI as CliInternalClientService
  participant Admin as AdminKeyService
  participant Factory as InternalGraphQLClientFactory
  participant SocketCfg as SocketConfigService
  participant Apollo as ApolloClient

  CLI->>Admin: getOrCreateLocalAdminKey()
  Admin-->>CLI: adminApiKey
  CLI->>Factory: createClient({ getApiKey, enableSubscriptions: false })
  Factory->>SocketCfg: getApiAddress()/getWebSocketUri()
  Factory-->>CLI: Apollo client
  CLI->>Apollo: cache client / use client
  CLI->>Apollo: clearClient() -> stop()
Loading
sequenceDiagram
  participant Connect as Connect InternalClientService
  participant KeySvc as ConnectApiKeyService
  participant Factory as InternalGraphQLClientFactory
  participant Apollo as ApolloClient

  Connect->>Connect: check cached client
  alt no client
    Connect->>KeySvc: getOrCreateLocalApiKey()
    KeySvc-->>Connect: apiKey
    Connect->>Factory: createClient({ getApiKey, enableSubscriptions: true })
    Factory-->>Connect: Apollo client
    Connect->>Connect: cache client
  end
  Connect-->>Apollo: return client
  Connect->>Apollo: clearClient() -> stop()
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~35 minutes

Possibly related PRs

Suggested reviewers

  • zackspear
  • mdatelle

Poem

I hop on sockets, nibble bytes,
I stitch the token through the nights.
Clients spawn and caches hum,
Subscriptions whisper, webs become.
Tests cheer loud — a rabbit drum. 🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/local-fetching-using-sockets

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@claude
Copy link

claude bot commented Aug 8, 2025

Claude encountered an error —— View job


I'll analyze this and get back to you.

@coderabbitai coderabbitai bot requested review from mdatelle and zackspear August 8, 2025 20:48
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: 2

🧹 Nitpick comments (1)
api/src/unraid-api/cli/internal-client.service.ts (1)

37-58: Cache the PORT lookup once & handle NaN

isRunningOnSocket, getSocketPath, and getNumericPort each read PORT separately and getNumericPort may return NaN if the var is an empty string.

-const port = this.configService.get<string>('PORT', '/var/run/unraid-api.sock');
+const rawPort = this.configService.get<string>('PORT', '/var/run/unraid-api.sock') ?? '';

Consider reading the value once in the constructor or a private readonly field to avoid triple I/O and guard against NaN.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between db0e725 and 7634919.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (3)
  • api/package.json (1 hunks)
  • api/src/unraid-api/cli/internal-client.service.ts (4 hunks)
  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts (3 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
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/package.json
  • api/src/unraid-api/cli/internal-client.service.ts
api/src/unraid-api/**/*

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

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

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

Files:

  • api/src/unraid-api/cli/internal-client.service.ts
🧠 Learnings (16)
📚 Learning: 2025-03-14T19:18:23.324Z
Learnt from: pujitm
PR: unraid/api#1211
File: unraid-ui/package.json:43-43
Timestamp: 2025-03-14T19:18:23.324Z
Learning: The internationalized/number package is a dependency of reka-ui, which is used for number formatting in the Unraid UI components.

Applied to files:

  • api/package.json
📚 Learning: 2025-02-04T17:21:39.710Z
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.

Applied to files:

  • api/package.json
📚 Learning: 2025-02-05T14:43:25.062Z
Learnt from: elibosley
PR: unraid/api#1120
File: plugin/package.json:1-8
Timestamp: 2025-02-05T14:43:25.062Z
Learning: The repository uses Renovate for automated dependency updates, making strict version pinning in package.json less critical as updates are handled automatically through PRs.

Applied to files:

  • api/package.json
📚 Learning: 2025-07-21T14:00:06.258Z
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

Applied to files:

  • api/package.json
  • api/src/unraid-api/cli/internal-client.service.ts
📚 Learning: 2025-07-28T15:34:24.715Z
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

Applied to files:

  • api/package.json
  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts
  • api/src/unraid-api/cli/internal-client.service.ts
📚 Learning: 2025-03-12T13:48:14.850Z
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.

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts
  • api/src/unraid-api/cli/internal-client.service.ts
📚 Learning: 2024-11-05T14:49:07.308Z
Learnt from: elibosley
PR: unraid/api#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/src/internal-rpc/internal.client.ts
  • api/src/unraid-api/cli/internal-client.service.ts
📚 Learning: 2024-11-06T20:59:25.809Z
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.

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts
  • api/src/unraid-api/cli/internal-client.service.ts
📚 Learning: 2025-03-14T16:10:38.562Z
Learnt from: pujitm
PR: unraid/api#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.

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts
📚 Learning: 2024-12-13T17:28:15.384Z
Learnt from: pujitm
PR: unraid/api#982
File: web/helpers/apollo-cache/index.ts:142-142
Timestamp: 2024-12-13T17:28:15.384Z
Learning: In `web/helpers/apollo-cache/index.ts`, it's acceptable to use `structuredClone` since the project targets Node.js v20 and browsers where `structuredClone` is supported.

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts
  • api/src/unraid-api/cli/internal-client.service.ts
📚 Learning: 2024-11-15T16:22:03.485Z
Learnt from: elibosley
PR: unraid/api#942
File: api/src/unraid-api/auth/api-key.service.ts:176-188
Timestamp: 2024-11-15T16:22:03.485Z
Learning: Atomic writes are not required for the `saveApiKey` method in `ApiKeyService` (`api/src/unraid-api/auth/api-key.service.ts`) unless specifically needed.

Applied to files:

  • api/src/unraid-api/cli/internal-client.service.ts
📚 Learning: 2024-11-04T20:44:46.432Z
Learnt from: mdatelle
PR: unraid/api#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.

Applied to files:

  • api/src/unraid-api/cli/internal-client.service.ts
📚 Learning: 2024-11-04T22:00:41.946Z
Learnt from: mdatelle
PR: unraid/api#942
File: api/src/unraid-api/auth/auth.service.ts:42-105
Timestamp: 2024-11-04T22:00:41.946Z
Learning: In the 'AuthService' within `api/src/unraid-api/auth/auth.service.ts`, rate limiting is not required because the API is not public facing.

Applied to files:

  • api/src/unraid-api/cli/internal-client.service.ts
📚 Learning: 2024-11-15T16:22:12.460Z
Learnt from: elibosley
PR: unraid/api#942
File: api/src/unraid-api/auth/auth.module.ts:94-96
Timestamp: 2024-11-15T16:22:12.460Z
Learning: In `CookieService.defaultOpts()` in `api/src/unraid-api/auth/cookie.service.ts`, use `sameSite: 'lax'` for session cookie configuration.

Applied to files:

  • api/src/unraid-api/cli/internal-client.service.ts
📚 Learning: 2024-12-11T20:39:02.511Z
Learnt from: pujitm
PR: unraid/api#978
File: web/helpers/apollo-cache/index.ts:134-139
Timestamp: 2024-12-11T20:39:02.511Z
Learning: In `web/helpers/apollo-cache/index.ts`, the `cache.readQuery` method returns `null` when it fails to read from the cache, rather than throwing an exception.

Applied to files:

  • api/src/unraid-api/cli/internal-client.service.ts
📚 Learning: 2024-12-11T17:20:09.604Z
Learnt from: pujitm
PR: unraid/api#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.

Applied to files:

  • api/src/unraid-api/cli/internal-client.service.ts
🔇 Additional comments (2)
api/package.json (1)

142-142: Verify necessity & compatibility of the new undici runtime dependency

undici is now bundled as a prod-dependency while got is already present.
Please double-check:

  1. The minimal Node version deployed in production is ≥ 18 ‑ if so, bundling undici merely for its fetch wrapper/Agent may be redundant.
  2. There are no conflicting global-fetch polyfills at runtime (globalThis.fetch from Node vs undici polyfill).
  3. Whether got usage can be consolidated or removed to avoid two HTTP stacks.

If all three points check out, no action needed.

api/src/unraid-api/cli/internal-client.service.ts (1)

116-120: Non-standard Origin header value

Origin: '/var/run/unraid-cli.sock' is not a valid origin per RFC 6454 (must be scheme://host[:port]).
Servers tolerant today may break under stricter CORS/proxy layers later. Suggest using a conventional origin such as http://localhost unless there is a compelling reason.

@claude
Copy link

claude bot commented Aug 8, 2025

Claude finished @elibosley's task —— View job


Code Review Complete

Review Tasks

  • Analyze package.json dependency changes
  • Review CliInternalClientService refactoring
  • Review InternalClientService refactoring
  • Check for security concerns with Unix socket handling
  • Assess performance implications
  • Evaluate test coverage needs
  • Provide final review feedback

Overall Assessment

This PR successfully refactors the internal API clients to support Unix socket connections. The implementation is well-structured and follows good practices.

✅ Strengths

Code Quality & Architecture:

  • Clean separation of concerns with dedicated methods for socket detection, path resolution, and client creation
  • Consistent implementation pattern across both services
  • Good error handling and logging
  • Proper TypeScript typing throughout

Socket Implementation:

  • Correct use of undici Agent with socketPath configuration
  • Appropriate fallback logic for port-based connections
  • Consistent Origin header usage for internal requests

Performance:

  • Client instance caching to avoid repeated initialization
  • Proper cleanup with clearClient() methods
  • Efficient socket connection reuse via undici Agent

⚠️ Areas for Improvement

Code Duplication:
Both services contain nearly identical logic for socket detection and address resolution. Consider extracting common functionality:

// api/src/unraid-api/cli/internal-client.service.ts:37-65
// packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts:44-65

Security Considerations:

  • The hardcoded Origin header /var/run/unraid-cli.sock in both services should be consistent with actual socket usage
  • Consider validating socket file permissions and ownership
  • Socket path comes from config but should be sanitized

Error Handling:

// api/src/unraid-api/cli/internal-client.service.ts:106-114
fetch: (uri, options) => {
    return undiciFetch(
        uri as string,
        {
            ...options,
            dispatcher: agent,
        } as any  // ← Consider stronger typing here
    );
},

Testing Gap:
No test files found covering the Unix socket functionality. The refactored code should include tests for:

  • Socket detection logic
  • Agent configuration with socketPath
  • Fallback behavior between socket and port modes
  • Error scenarios (socket not accessible, etc.)

🔧 Dependencies Review

New Dependencies Added:

  • undici: ^7.13.0 - Appropriate choice for HTTP client with Unix socket support
  • Missing pify in dependencies - Referenced in PR description but not found in package.json

Dependency Concerns:

  • undici v7.13.0 is recent (good for security/features)
  • No version conflicts detected with existing dependencies
  • Consider if pify dependency is actually needed

📋 Recommendations

  1. Extract Common Logic: Create a shared utility class for socket detection and address resolution
  2. Add Comprehensive Tests: Cover socket vs port scenarios, error handling, and edge cases
  3. Strengthen Type Safety: Replace as any casting with proper types
  4. Security Review: Validate socket permissions and sanitize paths
  5. Documentation: Add inline docs explaining the socket vs port detection logic

🎯 Priority Issues to Address

  1. HIGH: Add test coverage for the new Unix socket functionality
  2. MEDIUM: Extract duplicated code into shared utilities
  3. LOW: Improve TypeScript typing for undici fetch wrapper

The core functionality appears solid and ready for production use. The main concern is the lack of test coverage for the new socket handling logic.


@claude
Copy link

claude bot commented Aug 8, 2025

Claude finished @elibosley's task —— View job


Code Review Complete

Review Tasks

  • Analyze package.json dependency changes
  • Review CliInternalClientService refactoring
  • Review InternalClientService refactoring
  • Check for security concerns with Unix socket handling
  • Assess performance implications
  • Evaluate test coverage needs
  • Provide final review feedback

Overall Assessment

This PR successfully refactors the internal API clients to support Unix socket connections. The implementation is well-structured and follows good practices.

✅ Strengths

Code Quality & Architecture:

  • Clean separation of concerns with dedicated methods for socket detection, path resolution, and client creation
  • Consistent implementation pattern across both services
  • Good error handling and logging
  • Proper TypeScript typing throughout

Socket Implementation:

  • Correct use of undici Agent with socketPath configuration
  • Appropriate fallback logic for port-based connections
  • Consistent Origin header usage for internal requests

Performance:

  • Client instance caching to avoid repeated initialization
  • Proper cleanup with clearClient() methods
  • Efficient socket connection reuse via undici Agent

⚠️ Areas for Improvement

Code Duplication:
Both services contain nearly identical logic for socket detection and address resolution. Consider extracting common functionality:

// api/src/unraid-api/cli/internal-client.service.ts:37-65
// packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts:44-65

Security Considerations:

  • The hardcoded Origin header /var/run/unraid-cli.sock in both services should be consistent with actual socket usage
  • Consider validating socket file permissions and ownership
  • Socket path comes from config but should be sanitized

Error Handling:

// api/src/unraid-api/cli/internal-client.service.ts:106-114
fetch: (uri, options) => {
    return undiciFetch(
        uri as string,
        {
            ...options,
            dispatcher: agent,
        } as any  // ← Consider stronger typing here
    );
},

Testing Gap:
No test files found covering the Unix socket functionality. The refactored code should include tests for:

  • Socket detection logic
  • Agent configuration with socketPath
  • Fallback behavior between socket and port modes
  • Error scenarios (socket not accessible, etc.)

🔧 Dependencies Review

New Dependencies Added:

  • undici: ^7.13.0 - Appropriate choice for HTTP client with Unix socket support
  • Missing pify in dependencies - Referenced in PR description but not found in package.json

Dependency Concerns:

  • undici v7.13.0 is recent (good for security/features)
  • No version conflicts detected with existing dependencies
  • Consider if pify dependency is actually needed

📋 Recommendations

  1. Extract Common Logic: Create a shared utility class for socket detection and address resolution
  2. Add Comprehensive Tests: Cover socket vs port scenarios, error handling, and edge cases
  3. Strengthen Type Safety: Replace as any casting with proper types
  4. Security Review: Validate socket permissions and sanitize paths
  5. Documentation: Add inline docs explaining the socket vs port detection logic

🎯 Priority Issues to Address

  1. HIGH: Add test coverage for the new Unix socket functionality
  2. MEDIUM: Extract duplicated code into shared utilities
  3. LOW: Improve TypeScript typing for undici fetch wrapper

The core functionality appears solid and ready for production use. The main concern is the lack of test coverage for the new socket handling logic.


@claude
Copy link

claude bot commented Aug 8, 2025

Claude finished @elibosley's task —— View job


Code Review Complete

Review Tasks

  • Analyze package.json dependency changes
  • Review CliInternalClientService refactoring
  • Review InternalClientService refactoring
  • Check for security concerns with Unix socket handling
  • Assess performance implications
  • Evaluate test coverage needs
  • Provide final review feedback

Overall Assessment

This PR successfully refactors the internal API clients to support Unix socket connections. The implementation is well-structured and follows good practices.

✅ Strengths

Code Quality & Architecture:

  • Clean separation of concerns with dedicated methods for socket detection, path resolution, and client creation
  • Consistent implementation pattern across both services
  • Good error handling and logging
  • Proper TypeScript typing throughout

Socket Implementation:

  • Correct use of undici Agent with socketPath configuration
  • Appropriate fallback logic for port-based connections
  • Consistent Origin header usage for internal requests

Performance:

  • Client instance caching to avoid repeated initialization
  • Proper cleanup with clearClient() methods
  • Efficient socket connection reuse via undici Agent

⚠️ Areas for Improvement

Code Duplication:
Both services contain nearly identical logic for socket detection and address resolution. Consider extracting common functionality:

// api/src/unraid-api/cli/internal-client.service.ts:37-65
// packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts:44-65

Security Considerations:

  • The hardcoded Origin header /var/run/unraid-cli.sock in both services should be consistent with actual socket usage
  • Consider validating socket file permissions and ownership
  • Socket path comes from config but should be sanitized

Error Handling:

// api/src/unraid-api/cli/internal-client.service.ts:106-114
fetch: (uri, options) => {
    return undiciFetch(
        uri as string,
        {
            ...options,
            dispatcher: agent,
        } as any  // ← Consider stronger typing here
    );
},

Testing Gap:
No test files found covering the Unix socket functionality. The refactored code should include tests for:

  • Socket detection logic
  • Agent configuration with socketPath
  • Fallback behavior between socket and port modes
  • Error scenarios (socket not accessible, etc.)

🔧 Dependencies Review

New Dependencies Added:

  • undici: ^7.13.0 - Appropriate choice for HTTP client with Unix socket support
  • Missing pify in dependencies - Referenced in PR description but not found in package.json

Dependency Concerns:

  • undici v7.13.0 is recent (good for security/features)
  • No version conflicts detected with existing dependencies
  • Consider if pify dependency is actually needed

📋 Recommendations

  1. Extract Common Logic: Create a shared utility class for socket detection and address resolution
  2. Add Comprehensive Tests: Cover socket vs port scenarios, error handling, and edge cases
  3. Strengthen Type Safety: Replace as any casting with proper types
  4. Security Review: Validate socket permissions and sanitize paths
  5. Documentation: Add inline docs explaining the socket vs port detection logic

🎯 Priority Issues to Address

  1. HIGH: Add test coverage for the new Unix socket functionality
  2. MEDIUM: Extract duplicated code into shared utilities
  3. LOW: Improve TypeScript typing for undici fetch wrapper

The core functionality appears solid and ready for production use. The main concern is the lack of test coverage for the new socket handling logic.


View job

@claude
Copy link

claude bot commented Aug 8, 2025

Claude finished @elibosley's task —— View job


Code Review Complete

Review Tasks

  • Analyze package.json dependency changes
  • Review CliInternalClientService refactoring
  • Review InternalClientService refactoring
  • Examine new shared utility classes
  • Check for security concerns with Unix socket handling
  • Assess performance implications
  • Evaluate test coverage needs
  • Provide final review feedback

Overall Assessment

This PR successfully refactors the internal API clients to support Unix socket connections. The implementation is well-structured and follows good practices.

✅ Strengths

Code Quality & Architecture:

  • Clean separation of concerns with dedicated methods for socket detection, path resolution, and client creation
  • Consistent implementation pattern across both services
  • Good error handling and logging
  • Proper TypeScript typing throughout

Socket Implementation:

  • Correct use of undici Agent with socketPath configuration
  • Appropriate fallback logic for port-based connections
  • Consistent Origin header usage for internal requests

Performance:

  • Client instance caching to avoid repeated initialization
  • Proper cleanup with clearClient() methods
  • Efficient socket connection reuse via undici Agent

⚠️ Areas for Improvement

Code Duplication:
Both services contain nearly identical logic for socket detection and address resolution. Consider extracting common functionality:

// api/src/unraid-api/cli/internal-client.service.ts:37-65
// packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts:44-65

Security Considerations:

  • The hardcoded Origin header /var/run/unraid-cli.sock in both services should be consistent with actual socket usage
  • Consider validating socket file permissions and ownership
  • Socket path comes from config but should be sanitized

Error Handling:

// api/src/unraid-api/cli/internal-client.service.ts:106-114
fetch: (uri, options) => {
    return undiciFetch(
        uri as string,
        {
            ...options,
            dispatcher: agent,
        } as any  // ← Consider stronger typing here
    );
},

Testing Gap:
No test files found covering the Unix socket functionality. The refactored code should include tests for:

  • Socket detection logic
  • Agent configuration with socketPath
  • Fallback behavior between socket and port modes
  • Error scenarios (socket not accessible, etc.)

🔧 Dependencies Review

New Dependencies Added:

  • undici: ^7.13.0 - Appropriate choice for HTTP client with Unix socket support
  • Missing pify in dependencies - Referenced in PR description but not found in package.json

Dependency Concerns:

  • undici v7.13.0 is recent (good for security/features)
  • No version conflicts detected with existing dependencies
  • Consider if pify dependency is actually needed

📋 Recommendations

  1. Extract Common Logic: Create a shared utility class for socket detection and address resolution
  2. Add Comprehensive Tests: Cover socket vs port scenarios, error handling, and edge cases
  3. Strengthen Type Safety: Replace as any casting with proper types
  4. Security Review: Validate socket permissions and sanitize paths
  5. Documentation: Add inline docs explaining the socket vs port detection logic

🎯 Priority Issues to Address

  1. HIGH: Add test coverage for the new Unix socket functionality
  2. MEDIUM: Extract duplicated code into shared utilities
  3. LOW: Improve TypeScript typing for undici fetch wrapper

The core functionality appears solid and ready for production use. The main concern is the lack of test coverage for the new socket handling logic.


@claude
Copy link

claude bot commented Aug 8, 2025

Claude finished @elibosley's task —— View job


Code Review Complete

Review Tasks

  • Analyze package.json dependency changes
  • Review new InternalGraphQLClientFactory implementation
  • Review CliInternalClientService refactoring
  • Review InternalClientService refactoring
  • Examine new shared utility classes
  • Check for security concerns with Unix socket handling
  • Assess performance implications
  • Evaluate test coverage needs
  • Provide final review feedback

Overall Assessment

This PR successfully refactors the internal API clients to support Unix socket connections. The implementation is well-structured and follows good practices.

✅ Strengths

Code Quality & Architecture:

  • Clean separation of concerns with the new InternalGraphQLClientFactory
  • Consistent implementation pattern across both services
  • Good error handling and logging throughout
  • Proper TypeScript typing and dependency injection
  • Smart client caching to avoid repeated initialization

Socket Implementation:

  • Correct use of undici Agent with socketPath configuration for Unix sockets
  • Appropriate fallback logic between socket and TCP port connections
  • Consistent Origin header usage for internal requests
  • Proper cleanup methods with clearClient()

Performance:

  • Client instance caching prevents repeated initialization overhead
  • Efficient connection reuse via undici Agent for socket connections
  • no-cache fetch policy appropriate for internal operations

⚠️ Areas for Improvement

Code Duplication:
The InternalGraphQLClientFactory and BaseInternalClientService contain nearly identical socket detection and address resolution logic. This creates maintenance overhead and potential for inconsistency.

Security Considerations:

  • The hardcoded Origin header /var/run/unraid-cli.sock should be validated as appropriate
  • Socket path validation/sanitization is missing - paths come from config but should be verified
  • Consider validating socket file permissions and ownership for additional security

WebSocket Socket Mode Issue:

// api/src/unraid-api/shared/internal-graphql-client.factory.ts:99
if (enableSubscriptions) {
    wsUri = 'ws://localhost/graphql';  // ← This will fail if API only runs on socket
}

When the API runs exclusively on Unix socket, WebSocket subscriptions will fail as there's no TCP listener. This should either disable subscriptions or provide socket-based WebSocket support.

Type Safety:

// Multiple locations with `as any` casting
dispatcher: agent,
} as any  // ← Consider stronger typing here

Testing Gap:
No test files found covering the new Unix socket functionality. The refactored code needs tests for:

  • Socket vs port detection logic
  • undici Agent configuration with socketPath
  • Fallback behavior between connection modes
  • Error scenarios (socket not accessible, permission issues, etc.)

🔧 Dependencies Review

New Dependencies Added:

  • undici: ^7.13.0 - Good choice for HTTP client with Unix socket support
  • Version is recent and appropriate for security/features
  • No version conflicts detected with existing dependencies

📋 Recommendations

  1. HIGH Priority - Add Test Coverage: Create comprehensive tests for the Unix socket functionality covering both success and failure scenarios

  2. MEDIUM Priority - Extract Common Logic: Consider consolidating the duplicated socket detection logic between InternalGraphQLClientFactory and BaseInternalClientService

  3. MEDIUM Priority - Fix WebSocket Issue: Address the WebSocket connection problem when running on socket-only mode

  4. LOW Priority - Improve Type Safety: Replace as any casting with proper TypeScript types for the undici fetch wrapper

  5. LOW Priority - Security Hardening: Add socket path validation and permission checks

🎯 Priority Issues to Address

  1. HIGH: Add test coverage for Unix socket functionality
  2. MEDIUM: Fix WebSocket subscriptions in socket-only mode
  3. MEDIUM: Reduce code duplication between factory and base service
  4. LOW: Strengthen TypeScript typing for undici integration

The core functionality appears solid and ready for production use. The main concerns are the lack of test coverage and the WebSocket subscription issue in socket-only deployments.


View job

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: 4

♻️ Duplicate comments (1)
api/src/unraid-api/shared/internal-graphql-client.factory.ts (1)

98-101: WebSocket URI may break on pure Unix-socket deployments

When running solely on the Unix socket, 'ws://localhost/graphql' will fail because no TCP listener exists.

Guard against this (e.g., disable subscriptions or expose a bridge) before attempting WS connections.

🧹 Nitpick comments (4)
packages/unraid-api-plugin-connect/package.json (1)

65-66: Avoid duplicating undici across dependency blocks

undici is now listed in both devDependencies and peerDependencies.
Keeping it in one place (peer or runtime deps, depending on actual runtime use) avoids version-resolution surprises and audit noise.

Also applies to: 101-102

api/src/unraid-api/shared/internal-graphql-client.factory.ts (1)

102-118: Consider reusing a shared Agent

new Agent({ connect: { socketPath }}) is instantiated on every createClient call.
If many modules call the factory, this can leak file descriptors. Holding a singleton agent or using keepAlive: true would be lighter.

packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts (1)

24-49: Concurrency race is minor but worth noting

Two concurrent getClient() calls before the first finishes will build two Apollo clients, with the second discarded.
Caching the pending promise or guarding with a mutex removes this micro-race.

packages/unraid-shared/src/services/base-internal-client.service.ts (1)

41-44: Avoid mutating the caller-supplied options object

this.options.origin = … mutates the options instance that was passed in, which may be shared by other consumers. Create a shallow clone instead to prevent unintended side-effects.

-        this.options.origin = this.options.origin ?? '/var/run/unraid-cli.sock';
+        this.options = {
+            origin: '/var/run/unraid-cli.sock',
+            ...this.options,
+        };
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 7634919 and f955ef5.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (15)
  • api/dev/configs/api.json (1 hunks)
  • api/src/unraid-api/cli/cli-services.module.ts (2 hunks)
  • api/src/unraid-api/cli/internal-client.service.ts (2 hunks)
  • api/src/unraid-api/plugin/global-deps.module.ts (4 hunks)
  • api/src/unraid-api/shared/internal-graphql-client.factory.ts (1 hunks)
  • packages/unraid-api-plugin-connect/package.json (2 hunks)
  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts (1 hunks)
  • packages/unraid-api-plugin-connect/tsconfig.json (1 hunks)
  • packages/unraid-api-plugin-generator/tsconfig.build.json (1 hunks)
  • packages/unraid-api-plugin-generator/tsconfig.json (1 hunks)
  • packages/unraid-api-plugin-health/tsconfig.json (1 hunks)
  • packages/unraid-shared/package.json (2 hunks)
  • packages/unraid-shared/src/index.ts (1 hunks)
  • packages/unraid-shared/src/services/base-internal-client.service.ts (1 hunks)
  • packages/unraid-shared/src/tokens.ts (1 hunks)
✅ Files skipped from review due to trivial changes (8)
  • packages/unraid-api-plugin-connect/tsconfig.json
  • api/dev/configs/api.json
  • packages/unraid-api-plugin-generator/tsconfig.build.json
  • packages/unraid-shared/src/tokens.ts
  • packages/unraid-api-plugin-generator/tsconfig.json
  • packages/unraid-api-plugin-health/tsconfig.json
  • packages/unraid-shared/src/index.ts
  • packages/unraid-shared/package.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • api/src/unraid-api/cli/internal-client.service.ts
🧰 Additional context used
📓 Path-based instructions (2)
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/src/unraid-api/cli/cli-services.module.ts
  • api/src/unraid-api/plugin/global-deps.module.ts
  • api/src/unraid-api/shared/internal-graphql-client.factory.ts
api/src/unraid-api/**/*

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

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

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

Files:

  • api/src/unraid-api/cli/cli-services.module.ts
  • api/src/unraid-api/plugin/global-deps.module.ts
  • api/src/unraid-api/shared/internal-graphql-client.factory.ts
🧠 Learnings (39)
📓 Common learnings
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#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: unraid/api#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.
📚 Learning: 2025-06-11T14:14:30.348Z
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.

Applied to files:

  • packages/unraid-api-plugin-connect/package.json
📚 Learning: 2025-02-04T17:21:39.710Z
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.

Applied to files:

  • packages/unraid-api-plugin-connect/package.json
📚 Learning: 2025-03-14T19:18:23.324Z
Learnt from: pujitm
PR: unraid/api#1211
File: unraid-ui/package.json:43-43
Timestamp: 2025-03-14T19:18:23.324Z
Learning: The internationalized/number package is a dependency of reka-ui, which is used for number formatting in the Unraid UI components.

Applied to files:

  • packages/unraid-api-plugin-connect/package.json
📚 Learning: 2025-02-05T14:43:25.062Z
Learnt from: elibosley
PR: unraid/api#1120
File: plugin/package.json:1-8
Timestamp: 2025-02-05T14:43:25.062Z
Learning: The repository uses Renovate for automated dependency updates, making strict version pinning in package.json less critical as updates are handled automatically through PRs.

Applied to files:

  • packages/unraid-api-plugin-connect/package.json
📚 Learning: 2024-11-27T15:30:02.252Z
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.

Applied to files:

  • packages/unraid-api-plugin-connect/package.json
📚 Learning: 2025-07-21T14:00:06.258Z
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/**/*.{test,spec}.{js,ts,tsx} : Use Vitest as the test suite; do not use Jest

Applied to files:

  • packages/unraid-api-plugin-connect/package.json
📚 Learning: 2025-05-23T21:59:29.632Z
Learnt from: elibosley
PR: unraid/api#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.

Applied to files:

  • packages/unraid-api-plugin-connect/package.json
  • api/src/unraid-api/cli/cli-services.module.ts
  • api/src/unraid-api/plugin/global-deps.module.ts
📚 Learning: 2025-07-28T15:34:24.715Z
Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-28T15:34:24.715Z
Learning: Applies to api/**/*.{test,spec}.{ts,js} : Test suite is VITEST, do not use jest in the API package

Applied to files:

  • packages/unraid-api-plugin-connect/package.json
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/{components,store,mocks}/**/*.ts : Avoid relying on Nuxt's auto-imports in test environment

Applied to files:

  • packages/unraid-api-plugin-connect/package.json
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/{components,store,mocks}/**/*.ts : Mock external dependencies and services in tests

Applied to files:

  • packages/unraid-api-plugin-connect/package.json
  • api/src/unraid-api/cli/cli-services.module.ts
  • api/src/unraid-api/plugin/global-deps.module.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/store/**/*.ts : Mock external dependencies appropriately in store tests

Applied to files:

  • packages/unraid-api-plugin-connect/package.json
  • api/src/unraid-api/plugin/global-deps.module.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/{components,store,mocks}/**/*.ts : Don't rely on Nuxt auto-imports in tests

Applied to files:

  • packages/unraid-api-plugin-connect/package.json
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/{components,store,mocks}/**/*.ts : Remember that `vi.mock()` calls are hoisted

Applied to files:

  • packages/unraid-api-plugin-connect/package.json
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/{components,store,mocks}/**/*.ts : Use `vi.mock()` for module-level mocks

Applied to files:

  • packages/unraid-api-plugin-connect/package.json
📚 Learning: 2025-02-06T17:25:45.397Z
Learnt from: mdatelle
PR: unraid/api#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:

  • packages/unraid-api-plugin-connect/package.json
📚 Learning: 2024-12-13T17:28:15.384Z
Learnt from: pujitm
PR: unraid/api#982
File: web/helpers/apollo-cache/index.ts:142-142
Timestamp: 2024-12-13T17:28:15.384Z
Learning: In `web/helpers/apollo-cache/index.ts`, it's acceptable to use `structuredClone` since the project targets Node.js v20 and browsers where `structuredClone` is supported.

Applied to files:

  • packages/unraid-api-plugin-connect/package.json
  • api/src/unraid-api/shared/internal-graphql-client.factory.ts
  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-07-28T15:34:24.715Z
Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-28T15:34:24.715Z
Learning: Applies to web/__test__/**/*.{test,spec}.{ts,js} : Mock external dependencies and services in web/__test__

Applied to files:

  • packages/unraid-api-plugin-connect/package.json
  • api/src/unraid-api/cli/cli-services.module.ts
  • api/src/unraid-api/plugin/global-deps.module.ts
📚 Learning: 2024-11-05T14:49:07.308Z
Learnt from: elibosley
PR: unraid/api#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:

  • api/src/unraid-api/cli/cli-services.module.ts
  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts
  • api/src/unraid-api/plugin/global-deps.module.ts
  • api/src/unraid-api/shared/internal-graphql-client.factory.ts
  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-03-12T13:48:14.850Z
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.

Applied to files:

  • api/src/unraid-api/cli/cli-services.module.ts
  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts
  • api/src/unraid-api/plugin/global-deps.module.ts
  • api/src/unraid-api/shared/internal-graphql-client.factory.ts
  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-04-02T21:21:29.168Z
Learnt from: elibosley
PR: unraid/api#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.

Applied to files:

  • api/src/unraid-api/cli/cli-services.module.ts
  • api/src/unraid-api/plugin/global-deps.module.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/{components,store,mocks}/**/*.ts : Use factory functions for module mocks to avoid hoisting issues

Applied to files:

  • api/src/unraid-api/cli/cli-services.module.ts
  • api/src/unraid-api/plugin/global-deps.module.ts
📚 Learning: 2024-11-06T20:59:25.809Z
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.

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts
  • api/src/unraid-api/plugin/global-deps.module.ts
  • api/src/unraid-api/shared/internal-graphql-client.factory.ts
  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2024-11-15T16:22:03.485Z
Learnt from: elibosley
PR: unraid/api#942
File: api/src/unraid-api/auth/api-key.service.ts:176-188
Timestamp: 2024-11-15T16:22:03.485Z
Learning: Atomic writes are not required for the `saveApiKey` method in `ApiKeyService` (`api/src/unraid-api/auth/api-key.service.ts`) unless specifically needed.

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts
  • api/src/unraid-api/shared/internal-graphql-client.factory.ts
  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-03-14T16:10:38.562Z
Learnt from: pujitm
PR: unraid/api#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.

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts
📚 Learning: 2025-07-28T15:34:24.715Z
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

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts
📚 Learning: 2025-07-21T14:00:06.258Z
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

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts
📚 Learning: 2024-11-15T16:22:12.460Z
Learnt from: elibosley
PR: unraid/api#942
File: api/src/unraid-api/auth/auth.module.ts:94-96
Timestamp: 2024-11-15T16:22:12.460Z
Learning: In `CookieService.defaultOpts()` in `api/src/unraid-api/auth/cookie.service.ts`, use `sameSite: 'lax'` for session cookie configuration.

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts
📚 Learning: 2025-01-15T21:34:00.006Z
Learnt from: pujitm
PR: unraid/api#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.

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts
  • api/src/unraid-api/plugin/global-deps.module.ts
  • api/src/unraid-api/shared/internal-graphql-client.factory.ts
📚 Learning: 2025-03-10T18:43:13.016Z
Learnt from: mdatelle
PR: unraid/api#1219
File: api/src/unraid-api/types/fastify.ts:25-30
Timestamp: 2025-03-10T18:43:13.016Z
Learning: In the Unraid API, cookies should be marked as optional (with `?`) in the FastifyRequest type definition since the cookies may not always be present, which is consistent with the authentication flow throughout the codebase.

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts
📚 Learning: 2025-07-21T14:00:25.140Z
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

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts
📚 Learning: 2025-01-30T19:56:42.055Z
Learnt from: pujitm
PR: unraid/api#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.

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts
📚 Learning: 2025-07-28T15:34:24.715Z
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)

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts
📚 Learning: 2025-01-30T19:38:02.478Z
Learnt from: pujitm
PR: unraid/api#1075
File: unraid-ui/src/register.ts:15-34
Timestamp: 2025-01-30T19:38:02.478Z
Learning: In the web components registration process for unraid-ui, use a soft-fail approach (logging warnings/errors) instead of throwing errors, to ensure other components can still register successfully even if one component fails.

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts
📚 Learning: 2025-04-23T20:22:36.001Z
Learnt from: pujitm
PR: unraid/api#1367
File: api/src/unraid-api/app/pubsub.module.ts:24-28
Timestamp: 2025-04-23T20:22:36.001Z
Learning: In the Unraid API project, string literals are intentionally used for dependency injection tokens instead of Symbols to enable "collision" behavior across modules that don't directly import from each other. This is a temporary approach during refactoring until a shared library for constants is implemented.

Applied to files:

  • api/src/unraid-api/plugin/global-deps.module.ts
📚 Learning: 2025-03-12T13:35:43.900Z
Learnt from: pujitm
PR: unraid/api#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.

Applied to files:

  • api/src/unraid-api/plugin/global-deps.module.ts
📚 Learning: 2025-03-03T16:04:20.705Z
Learnt from: pujitm
PR: unraid/api#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/src/unraid-api/plugin/global-deps.module.ts
📚 Learning: 2024-11-04T20:44:46.432Z
Learnt from: mdatelle
PR: unraid/api#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.

Applied to files:

  • api/src/unraid-api/shared/internal-graphql-client.factory.ts
  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-04-23T20:19:42.542Z
Learnt from: pujitm
PR: unraid/api#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.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
🔇 Additional comments (2)
api/src/unraid-api/cli/cli-services.module.ts (1)

15-16: Provider registration LGTM

Importing and providing InternalGraphQLClientFactory here is correct and keeps the CLI module self-contained.

Also applies to: 36-37

packages/unraid-shared/src/services/base-internal-client.service.ts (1)

112-135: Origin header value is not a valid origin

An HTTP Origin header must be a scheme + host (e.g. http://localhost).
Using the default socket path (/var/run/unraid-cli.sock) violates the spec and may be stripped or rejected by intermediaries.

Consider mapping socket mode to a conventional origin, e.g.:

-                    Origin: this.options.origin!,
+                    Origin: this.isRunningOnSocket() ? 'http://localhost' : this.options.origin!,

@claude
Copy link

claude bot commented Aug 8, 2025

Claude finished @elibosley's task —— View job


Code Review Complete

Review Tasks

  • Analyze package.json dependency changes
  • Review new InternalGraphQLClientFactory implementation
  • Review CliInternalClientService refactoring
  • Review InternalClientService refactoring
  • Examine new shared utility classes
  • Check for security concerns with Unix socket handling
  • Assess performance implications
  • Evaluate test coverage needs
  • Provide final review feedback

Overall Assessment

This PR successfully refactors the internal API clients to support Unix socket connections. The implementation is well-structured and follows good practices.

✅ Strengths

Code Quality & Architecture:

  • Clean separation of concerns with the new InternalGraphQLClientFactory
  • Consistent implementation pattern across both services
  • Good error handling and logging throughout
  • Proper TypeScript typing and dependency injection
  • Smart client caching to avoid repeated initialization

Socket Implementation:

  • Correct use of undici Agent with socketPath configuration for Unix sockets
  • Appropriate fallback logic between socket and TCP port connections
  • Consistent Origin header usage for internal requests
  • Proper cleanup methods with clearClient()

Performance:

  • Client instance caching prevents repeated initialization overhead
  • Efficient connection reuse via undici Agent for socket connections
  • no-cache fetch policy appropriate for internal operations

⚠️ Areas for Improvement

Code Duplication:
The InternalGraphQLClientFactory and BaseInternalClientService contain nearly identical socket detection and address resolution logic. This creates maintenance overhead and potential for inconsistency.

Security Considerations:

  • The hardcoded Origin header /var/run/unraid-cli.sock should be validated as appropriate
  • Socket path validation/sanitization is missing - paths come from config but should be verified
  • Consider validating socket file permissions and ownership for additional security

WebSocket Socket Mode Issue:

// api/src/unraid-api/shared/internal-graphql-client.factory.ts:99
if (enableSubscriptions) {
    wsUri = 'ws://localhost/graphql';  // ← This will fail if API only runs on socket
}

When the API runs exclusively on Unix socket, WebSocket subscriptions will fail as there's no TCP listener. This should either disable subscriptions or provide socket-based WebSocket support.

Type Safety:

// Multiple locations with `as any` casting
dispatcher: agent,
} as any  // ← Consider stronger typing here

Testing Gap:
No test files found covering the new Unix socket functionality. The refactored code needs tests for:

  • Socket vs port detection logic
  • undici Agent configuration with socketPath
  • Fallback behavior between connection modes
  • Error scenarios (socket not accessible, permission issues, etc.)

🔧 Dependencies Review

New Dependencies Added:

  • undici: ^7.13.0 - Good choice for HTTP client with Unix socket support
  • Version is recent and appropriate for security/features
  • No version conflicts detected with existing dependencies

📋 Recommendations

  1. HIGH Priority - Add Test Coverage: Create comprehensive tests for the Unix socket functionality covering both success and failure scenarios

  2. MEDIUM Priority - Extract Common Logic: Consider consolidating the duplicated socket detection logic between InternalGraphQLClientFactory and BaseInternalClientService

  3. MEDIUM Priority - Fix WebSocket Issue: Address the WebSocket connection problem when running on socket-only mode

  4. LOW Priority - Improve Type Safety: Replace as any casting with proper TypeScript types for the undici fetch wrapper

  5. LOW Priority - Security Hardening: Add socket path validation and permission checks

🎯 Priority Issues to Address

  1. HIGH: Add test coverage for Unix socket functionality
  2. MEDIUM: Fix WebSocket subscriptions in socket-only mode
  3. MEDIUM: Reduce code duplication between factory and base service
  4. LOW: Strengthen TypeScript typing for undici integration

The core functionality appears solid and ready for production use. The main concerns are the lack of test coverage and the WebSocket subscription issue in socket-only deployments.


View job

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

🔭 Outside diff range comments (1)
api/src/unraid-api/plugin/global-deps.module.ts (1)

55-64: Export the class provider to prevent per-module re-instantiation

Export InternalGraphQLClientFactory so modules (e.g., CLI) can inject by class without re-providing it.

     exports: [
         UPNP_CLIENT_TOKEN,
         GRAPHQL_PUBSUB_TOKEN,
         API_KEY_SERVICE_TOKEN,
         NGINX_SERVICE_TOKEN,
         INTERNAL_CLIENT_SERVICE_TOKEN,
+        InternalGraphQLClientFactory,
         PrefixedID,
         LIFECYCLE_SERVICE_TOKEN,
         LifecycleService,
     ],
♻️ Duplicate comments (3)
api/src/unraid-api/plugin/global-deps.module.ts (1)

27-27: Deduplicate factory provider with useExisting

Registering the class and again with useClass under INTERNAL_CLIENT_SERVICE_TOKEN creates two instances. Alias the token to the existing class provider.

     providers: [
-        InternalGraphQLClientFactory,
+        InternalGraphQLClientFactory,
         {
             provide: UPNP_CLIENT_TOKEN,
             useValue: upnpClient,
         },
         {
             provide: GRAPHQL_PUBSUB_TOKEN,
             useValue: pubsub,
         },
         {
             provide: API_KEY_SERVICE_TOKEN,
             useClass: ApiKeyService,
         },
         {
             provide: NGINX_SERVICE_TOKEN,
             useClass: NginxService,
         },
         {
-            provide: INTERNAL_CLIENT_SERVICE_TOKEN,
-            useClass: InternalGraphQLClientFactory,
+            provide: INTERNAL_CLIENT_SERVICE_TOKEN,
+            useExisting: InternalGraphQLClientFactory,
         },
         PrefixedID,
         LifecycleService,

Also applies to: 44-47

api/src/unraid-api/cli/internal-client.service.ts (1)

59-62: ApolloClient.stop() is not part of Apollo 3.x; guard or use clearStore/resetStore

Calling this.client?.stop() will throw at runtime on Apollo 3.x. Guard the call and clear/reset the store instead.

Apply:

-    // Stop the Apollo client to terminate any active processes
-    this.client?.stop();
-    this.client = null;
+    // Best-effort shutdown to avoid dangling timers/pollers
+    if (this.client) {
+        // Some custom client wrappers expose stop(); Apollo 3.x doesn't.
+        (this.client as any).stop?.();
+        // Avoid awaiting in a void method; swallow errors.
+        void this.client.clearStore().catch(() => undefined);
+    }
+    this.client = null;
packages/unraid-shared/src/services/base-internal-client.service.ts (1)

226-235: Guard client shutdown; Apollo 3.x has no stop()

Same concern as elsewhere: avoid calling a non-existent stop(). Dispose WS client and clear the store.

Apply:

-    // Stop the Apollo client to terminate any active processes
-    this.client?.stop();
-    // Clean up WebSocket client if it exists
+    // Best-effort shutdown
+    if (this.client) {
+        (this.client as any).stop?.(); // optional if a custom client adds it
+        void this.client.clearStore().catch(() => undefined);
+    }
+    // Clean up WebSocket client if it exists
     if (this.wsClient) {
         this.wsClient.dispose();
         this.wsClient = null;
     }
     this.client = null;
🧹 Nitpick comments (11)
packages/unraid-shared/package.json (2)

47-47: Peer dep pin may be too strict for Apollo Client

Exact pinning in peerDependencies can cause version conflicts in consumers. Consider widening the range.

Example:

-        "@apollo/client": "3.13.8",
+        "@apollo/client": "^3.13.0"

If you intentionally pin for mono-repo alignment, ignore. Otherwise, please confirm this won’t conflict with web/api packages that may float on minor versions.


60-61: Are rxjs and undici intended as peers?

  • rxjs as peer implies consumers must provide the same major. If only types are used, devDependency suffices; if emitted code imports rxjs, keeping as peer is fine.
  • undici as peer means consumers must install it. Since Node 18+ bundles undici APIs, consider making undici a regular dependency where needed (e.g., api package) and not a peer here, unless shared code directly imports undici at runtime.

If you keep them as peers, consider marking them optional to ease downstream installs:

   "peerDependencies": {
     "rxjs": "7.8.2",
     "undici": "7.13.0"
   },
+  "peerDependenciesMeta": {
+    "rxjs": { "optional": true },
+    "undici": { "optional": true }
+  }
api/src/unraid-api/shared/internal-graphql-client.factory.ts (3)

97-98: Logger placeholders are not formatted — use template strings / params

Nest Logger doesn’t apply %s/%o formatting. Use template strings or pass objects as additional params.

-            this.logger.debug('Creating GraphQL client using Unix socket: %s', socketPath);
+            this.logger.debug(`Creating GraphQL client using Unix socket: ${socketPath}`);
...
-                this.logger.debug('WebSocket subscriptions over Unix socket: %s', wsUri);
+                this.logger.debug(`WebSocket subscriptions over Unix socket: ${wsUri}`);
...
-            this.logger.debug('Creating GraphQL client using HTTP: %s', httpUri);
+            this.logger.debug(`Creating GraphQL client using HTTP: ${httpUri}`);
...
-                this.logger.warn('[GRAPHQL-CLIENT] NETWORK ERROR ENCOUNTERED %o', networkError);
+                this.logger.warn('[GRAPHQL-CLIENT] NETWORK ERROR ENCOUNTERED', networkError as any);

Also applies to: 102-103, 130-130, 148-148


152-160: Explicit WebSocket implementation for Node

In Node, pass a WebSocket implementation to graphql-ws or ensure ws is installed. Otherwise, subscriptions may fail at runtime.

If ws is available:

-            const wsLink = new GraphQLWsLink(
-                createClient({
-                    url: wsUri,
-                    connectionParams: () => ({ 'x-api-key': apiKey }),
-                })
-            );
+            const wsLink = new GraphQLWsLink(
+                createClient({
+                    url: wsUri,
+                    // @ts-expect-error Provide WebSocket impl in Node
+                    webSocketImpl: (globalThis as any).WebSocket ?? (await import('ws')).default,
+                    connectionParams: () => ({ 'x-api-key': apiKey }),
+                })
+            );

If you prefer not to add dynamic import, add ws as a dependency in the API package and import it statically. I can open a follow-up PR.


105-121: Close undici Agent on client teardown

When creating a new Agent per client, consider exposing a cleanup to close the dispatcher when the Apollo client is stopped to avoid FD leaks on short-lived clients.

Approach: wrap the link to capture the Agent and register a disposer, or make the Agent a singleton per-socketPath within this factory.

packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts (1)

41-45: Confirm Origin header for Connect

Factory defaults Origin to '/var/run/unraid-cli.sock'. If Connect requires a distinct origin, pass it explicitly.

-        const client = await this.clientFactory.createClient({
-            apiKey: localApiKey,
-            enableSubscriptions: true
-        });
+        const client = await this.clientFactory.createClient({
+            apiKey: localApiKey,
+            enableSubscriptions: true,
+            origin: '/var/run/unraid-connect.sock', // if required for Connect
+        });

Please confirm the expected Origin value used for policy checks upstream.

packages/unraid-shared/src/services/base-internal-client.service.ts (5)

123-136: Headers duplication: consider a small helper to avoid drift

The HTTP headers are duplicated in both socket and TCP branches. Factor to a single object to reduce maintenance risk.

Apply:

-        if (this.isRunningOnSocket()) {
+        const commonHeaders = {
+            Origin: this.options.origin!,
+            'x-api-key': apiKey,
+            'Content-Type': 'application/json',
+        };
+
+        if (this.isRunningOnSocket()) {
             // ...
             httpLink = new HttpLink({
                 uri: 'http://localhost/graphql',
                 fetch: ((uri: any, options: any) => {
                     return undiciFetch(uri as string, {
                         ...options,
                         dispatcher: agent,
                     } as any);
                 }) as unknown as typeof fetch,
-                headers: {
-                    Origin: this.options.origin!,
-                    'x-api-key': apiKey,
-                    'Content-Type': 'application/json',
-                },
+                headers: commonHeaders,
             });
         } else {
             const httpUri = this.getApiAddress('http');
             this.logger.debug('Internal GraphQL URL: %s', httpUri);
 
             httpLink = new HttpLink({
                 uri: httpUri,
                 fetch,
-                headers: {
-                    Origin: this.options.origin!,
-                    'x-api-key': apiKey,
-                    'Content-Type': 'application/json',
-                },
+                headers: commonHeaders,
             });
         }

Also applies to: 141-149


152-156: Also log GraphQL errors for better diagnostics

Currently only networkError is logged. Include graphQLErrors summaries to aid debugging.

Apply:

-        const errorLink = onError(({ networkError }) => {
-            if (networkError) {
-                this.logger.warn('[GRAPHQL-CLIENT] NETWORK ERROR ENCOUNTERED %o', networkError);
-            }
-        });
+        const errorLink = onError(({ graphQLErrors, networkError }) => {
+            if (graphQLErrors?.length) {
+                for (const err of graphQLErrors) {
+                    this.logger.warn(`[GRAPHQL-CLIENT] GQL ERROR: ${err.message}`, (err.extensions as any)?.code);
+                }
+            }
+            if (networkError) {
+                this.logger.warn('[GRAPHQL-CLIENT] NETWORK ERROR %o', networkError);
+            }
+        });

192-204: Default options inconsistent between WS and HTTP-only branches

WS branch sets mutate.fetchPolicy: 'no-cache', HTTP-only branch doesn't. Align behavior to avoid cache surprises.

Apply:

         return new ApolloClient({
             defaultOptions: {
                 query: {
                     fetchPolicy: 'no-cache',
                 },
+                mutate: {
+                    fetchPolicy: 'no-cache',
+                },
             },
             cache: new InMemoryCache(),
             link: errorLink.concat(httpLink),
         });

Also applies to: 206-215


98-106: Error type: consider including the underlying message/stack

Wrapping and rethrowing as a generic Error loses context. Include cause or append error?.message for easier diagnosis.

Apply:

-            throw new Error(
-                'Unable to get API key for internal client. Ensure the API server is running.'
-            );
+            throw new Error(
+                `Unable to get API key for internal client. Ensure the API server is running. ${(error as Error)?.message ?? ''}`,
+                { cause: error as Error }
+            );

38-46: Avoid mutating the options argument

Mutating this.options.origin in the constructor can surprise callers who reuse the same object. Prefer an immutable merge.

Apply:

-    ) {
-        this.logger = new Logger(this.constructor.name);
-        this.options.origin = this.options.origin ?? '/var/run/unraid-cli.sock';
-    }
+    ) {
+        this.logger = new Logger(this.constructor.name);
+        this.options = { origin: 'http://127.0.0.1', ...options };
+    }

Note: if you adopt this, update the constructor signature to options: InternalClientOptions = {} without readonly.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 7634919 and d4175f1.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (15)
  • api/dev/configs/api.json (1 hunks)
  • api/src/unraid-api/cli/cli-services.module.ts (2 hunks)
  • api/src/unraid-api/cli/internal-client.service.ts (2 hunks)
  • api/src/unraid-api/plugin/global-deps.module.ts (4 hunks)
  • api/src/unraid-api/shared/internal-graphql-client.factory.ts (1 hunks)
  • packages/unraid-api-plugin-connect/package.json (2 hunks)
  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts (1 hunks)
  • packages/unraid-api-plugin-connect/tsconfig.json (1 hunks)
  • packages/unraid-api-plugin-generator/tsconfig.build.json (1 hunks)
  • packages/unraid-api-plugin-generator/tsconfig.json (1 hunks)
  • packages/unraid-api-plugin-health/tsconfig.json (1 hunks)
  • packages/unraid-shared/package.json (2 hunks)
  • packages/unraid-shared/src/index.ts (1 hunks)
  • packages/unraid-shared/src/services/base-internal-client.service.ts (1 hunks)
  • packages/unraid-shared/src/tokens.ts (1 hunks)
✅ Files skipped from review due to trivial changes (8)
  • packages/unraid-api-plugin-generator/tsconfig.json
  • packages/unraid-api-plugin-connect/tsconfig.json
  • api/dev/configs/api.json
  • packages/unraid-api-plugin-connect/package.json
  • packages/unraid-api-plugin-health/tsconfig.json
  • packages/unraid-api-plugin-generator/tsconfig.build.json
  • packages/unraid-shared/src/index.ts
  • packages/unraid-shared/src/tokens.ts
🧰 Additional context used
📓 Path-based instructions (2)
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/src/unraid-api/cli/cli-services.module.ts
  • api/src/unraid-api/shared/internal-graphql-client.factory.ts
  • api/src/unraid-api/plugin/global-deps.module.ts
  • api/src/unraid-api/cli/internal-client.service.ts
api/src/unraid-api/**/*

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

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

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

Files:

  • api/src/unraid-api/cli/cli-services.module.ts
  • api/src/unraid-api/shared/internal-graphql-client.factory.ts
  • api/src/unraid-api/plugin/global-deps.module.ts
  • api/src/unraid-api/cli/internal-client.service.ts
🧠 Learnings (48)
📓 Common learnings
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: pujitm
PR: unraid/api#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.
📚 Learning: 2024-11-05T14:49:07.308Z
Learnt from: elibosley
PR: unraid/api#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:

  • api/src/unraid-api/cli/cli-services.module.ts
  • api/src/unraid-api/shared/internal-graphql-client.factory.ts
  • api/src/unraid-api/plugin/global-deps.module.ts
  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts
  • api/src/unraid-api/cli/internal-client.service.ts
  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-03-12T13:48:14.850Z
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.

Applied to files:

  • api/src/unraid-api/cli/cli-services.module.ts
  • packages/unraid-shared/package.json
  • api/src/unraid-api/shared/internal-graphql-client.factory.ts
  • api/src/unraid-api/plugin/global-deps.module.ts
  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts
  • api/src/unraid-api/cli/internal-client.service.ts
  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2024-11-06T20:59:25.809Z
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.

Applied to files:

  • api/src/unraid-api/cli/cli-services.module.ts
  • api/src/unraid-api/shared/internal-graphql-client.factory.ts
  • api/src/unraid-api/plugin/global-deps.module.ts
  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts
  • api/src/unraid-api/cli/internal-client.service.ts
  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-04-02T21:21:29.168Z
Learnt from: elibosley
PR: unraid/api#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.

Applied to files:

  • api/src/unraid-api/cli/cli-services.module.ts
  • api/src/unraid-api/plugin/global-deps.module.ts
📚 Learning: 2025-05-23T21:59:29.632Z
Learnt from: elibosley
PR: unraid/api#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.

Applied to files:

  • api/src/unraid-api/cli/cli-services.module.ts
  • api/src/unraid-api/plugin/global-deps.module.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/{components,store,mocks}/**/*.ts : Use factory functions for module mocks to avoid hoisting issues

Applied to files:

  • api/src/unraid-api/cli/cli-services.module.ts
  • api/src/unraid-api/plugin/global-deps.module.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/{components,store,mocks}/**/*.ts : Mock external dependencies and services in tests

Applied to files:

  • api/src/unraid-api/cli/cli-services.module.ts
  • api/src/unraid-api/plugin/global-deps.module.ts
📚 Learning: 2025-07-28T15:34:24.715Z
Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-28T15:34:24.715Z
Learning: Applies to web/__test__/**/*.{test,spec}.{ts,js} : Mock external dependencies and services in web/__test__

Applied to files:

  • api/src/unraid-api/cli/cli-services.module.ts
  • api/src/unraid-api/plugin/global-deps.module.ts
📚 Learning: 2025-02-04T17:21:39.710Z
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.

Applied to files:

  • packages/unraid-shared/package.json
📚 Learning: 2025-02-05T14:43:25.062Z
Learnt from: elibosley
PR: unraid/api#1120
File: plugin/package.json:1-8
Timestamp: 2025-02-05T14:43:25.062Z
Learning: The repository uses Renovate for automated dependency updates, making strict version pinning in package.json less critical as updates are handled automatically through PRs.

Applied to files:

  • packages/unraid-shared/package.json
📚 Learning: 2025-03-14T19:18:23.324Z
Learnt from: pujitm
PR: unraid/api#1211
File: unraid-ui/package.json:43-43
Timestamp: 2025-03-14T19:18:23.324Z
Learning: The internationalized/number package is a dependency of reka-ui, which is used for number formatting in the Unraid UI components.

Applied to files:

  • packages/unraid-shared/package.json
📚 Learning: 2024-12-13T17:28:15.384Z
Learnt from: pujitm
PR: unraid/api#982
File: web/helpers/apollo-cache/index.ts:142-142
Timestamp: 2024-12-13T17:28:15.384Z
Learning: In `web/helpers/apollo-cache/index.ts`, it's acceptable to use `structuredClone` since the project targets Node.js v20 and browsers where `structuredClone` is supported.

Applied to files:

  • packages/unraid-shared/package.json
  • api/src/unraid-api/shared/internal-graphql-client.factory.ts
  • api/src/unraid-api/cli/internal-client.service.ts
  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-07-21T14:00:25.140Z
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

Applied to files:

  • packages/unraid-shared/package.json
  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts
  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-07-28T15:34:24.715Z
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

Applied to files:

  • packages/unraid-shared/package.json
📚 Learning: 2025-07-21T14:00:25.140Z
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

Applied to files:

  • packages/unraid-shared/package.json
📚 Learning: 2025-07-28T15:34:24.715Z
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)

Applied to files:

  • packages/unraid-shared/package.json
  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts
📚 Learning: 2025-07-28T15:34:24.715Z
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/

Applied to files:

  • packages/unraid-shared/package.json
📚 Learning: 2025-07-28T15:34:24.715Z
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/

Applied to files:

  • packages/unraid-shared/package.json
📚 Learning: 2025-07-21T14:00:25.140Z
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

Applied to files:

  • packages/unraid-shared/package.json
📚 Learning: 2025-04-21T18:44:24.383Z
Learnt from: elibosley
PR: unraid/api#1352
File: packages/unraid-api-plugin-connect/src/config.entity.ts:37-39
Timestamp: 2025-04-21T18:44:24.383Z
Learning: The project prefers using class-validator decorators directly on entity classes rather than separate Zod schemas to maintain type consistency between GraphQL entities and validation rules.

Applied to files:

  • packages/unraid-shared/package.json
📚 Learning: 2025-07-21T14:00:25.140Z
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

Applied to files:

  • packages/unraid-shared/package.json
📚 Learning: 2024-11-15T16:22:03.485Z
Learnt from: elibosley
PR: unraid/api#942
File: api/src/unraid-api/auth/api-key.service.ts:176-188
Timestamp: 2024-11-15T16:22:03.485Z
Learning: Atomic writes are not required for the `saveApiKey` method in `ApiKeyService` (`api/src/unraid-api/auth/api-key.service.ts`) unless specifically needed.

Applied to files:

  • api/src/unraid-api/shared/internal-graphql-client.factory.ts
  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts
  • api/src/unraid-api/cli/internal-client.service.ts
  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-01-15T21:34:00.006Z
Learnt from: pujitm
PR: unraid/api#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.

Applied to files:

  • api/src/unraid-api/shared/internal-graphql-client.factory.ts
  • api/src/unraid-api/plugin/global-deps.module.ts
  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts
  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2024-11-04T20:44:46.432Z
Learnt from: mdatelle
PR: unraid/api#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.

Applied to files:

  • api/src/unraid-api/shared/internal-graphql-client.factory.ts
  • api/src/unraid-api/cli/internal-client.service.ts
  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-04-23T20:22:36.001Z
Learnt from: pujitm
PR: unraid/api#1367
File: api/src/unraid-api/app/pubsub.module.ts:24-28
Timestamp: 2025-04-23T20:22:36.001Z
Learning: In the Unraid API project, string literals are intentionally used for dependency injection tokens instead of Symbols to enable "collision" behavior across modules that don't directly import from each other. This is a temporary approach during refactoring until a shared library for constants is implemented.

Applied to files:

  • api/src/unraid-api/plugin/global-deps.module.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/store/**/*.ts : Mock external dependencies appropriately in store tests

Applied to files:

  • api/src/unraid-api/plugin/global-deps.module.ts
📚 Learning: 2025-03-12T13:35:43.900Z
Learnt from: pujitm
PR: unraid/api#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.

Applied to files:

  • api/src/unraid-api/plugin/global-deps.module.ts
📚 Learning: 2025-07-21T14:00:06.258Z
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

Applied to files:

  • api/src/unraid-api/plugin/global-deps.module.ts
  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts
📚 Learning: 2025-07-28T15:34:24.715Z
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

Applied to files:

  • api/src/unraid-api/plugin/global-deps.module.ts
  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts
📚 Learning: 2025-03-07T19:29:12.540Z
Learnt from: pujitm
PR: unraid/api#1219
File: api/src/unraid-api/main.ts:18-25
Timestamp: 2025-03-07T19:29:12.540Z
Learning: When handling type mismatches between dependencies in TypeScript (such as between NestJS and Fastify), prefer using `ts-expect-error` with a detailed explanation comment instead of `as any` type assertions. This better documents the reason for the type bypass and makes the issue more visible for future maintenance.

Applied to files:

  • api/src/unraid-api/plugin/global-deps.module.ts
  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-07-28T15:34:24.715Z
Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-28T15:34:24.715Z
Learning: Applies to web/__test__/**/*store*.{test,spec}.{ts,js} : Use factory functions for module mocks to avoid hoisting issues in store tests

Applied to files:

  • api/src/unraid-api/plugin/global-deps.module.ts
📚 Learning: 2025-03-03T16:04:20.705Z
Learnt from: pujitm
PR: unraid/api#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/src/unraid-api/plugin/global-deps.module.ts
📚 Learning: 2025-03-14T16:10:38.562Z
Learnt from: pujitm
PR: unraid/api#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.

Applied to files:

  • api/src/unraid-api/plugin/global-deps.module.ts
  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts
📚 Learning: 2024-11-15T16:22:12.460Z
Learnt from: elibosley
PR: unraid/api#942
File: api/src/unraid-api/auth/auth.module.ts:94-96
Timestamp: 2024-11-15T16:22:12.460Z
Learning: In `CookieService.defaultOpts()` in `api/src/unraid-api/auth/cookie.service.ts`, use `sameSite: 'lax'` for session cookie configuration.

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts
  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-03-10T18:43:13.016Z
Learnt from: mdatelle
PR: unraid/api#1219
File: api/src/unraid-api/types/fastify.ts:25-30
Timestamp: 2025-03-10T18:43:13.016Z
Learning: In the Unraid API, cookies should be marked as optional (with `?`) in the FastifyRequest type definition since the cookies may not always be present, which is consistent with the authentication flow throughout the codebase.

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts
  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-01-30T19:56:42.055Z
Learnt from: pujitm
PR: unraid/api#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.

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts
📚 Learning: 2025-01-30T19:38:02.478Z
Learnt from: pujitm
PR: unraid/api#1075
File: unraid-ui/src/register.ts:15-34
Timestamp: 2025-01-30T19:38:02.478Z
Learning: In the web components registration process for unraid-ui, use a soft-fail approach (logging warnings/errors) instead of throwing errors, to ensure other components can still register successfully even if one component fails.

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts
📚 Learning: 2024-11-04T22:00:41.946Z
Learnt from: mdatelle
PR: unraid/api#942
File: api/src/unraid-api/auth/auth.service.ts:42-105
Timestamp: 2024-11-04T22:00:41.946Z
Learning: In the 'AuthService' within `api/src/unraid-api/auth/auth.service.ts`, rate limiting is not required because the API is not public facing.

Applied to files:

  • api/src/unraid-api/cli/internal-client.service.ts
📚 Learning: 2024-11-04T20:41:22.303Z
Learnt from: mdatelle
PR: unraid/api#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`.

Applied to files:

  • api/src/unraid-api/cli/internal-client.service.ts
📚 Learning: 2025-04-23T20:19:42.542Z
Learnt from: pujitm
PR: unraid/api#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.

Applied to files:

  • api/src/unraid-api/cli/internal-client.service.ts
  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-03-27T13:34:53.438Z
Learnt from: pujitm
PR: unraid/api#1252
File: api/src/environment.ts:56-56
Timestamp: 2025-03-27T13:34:53.438Z
Learning: For critical components in the Unraid API, such as retrieving version information from package.json, failing fast (allowing crashes) is preferred over graceful degradation with fallback values.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-04-07T14:34:47.255Z
Learnt from: elibosley
PR: unraid/api#1334
File: api/src/unraid-api/graph/resolvers/docker/docker-event.service.ts:63-66
Timestamp: 2025-04-07T14:34:47.255Z
Learning: In DockerEventService, the chokidar file watcher is configured with `ignoreInitial: false` to ensure that existing files (like the Docker socket) are detected and processed at application startup, not just when files change after the watcher is initialized.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-01-28T16:39:50.278Z
Learnt from: elibosley
PR: unraid/api#1078
File: api/src/unraid-api/unraid-file-modifier/modifications/sso.modification.ts:35-36
Timestamp: 2025-01-28T16:39:50.278Z
Learning: When reviewing TypeScript files containing PHP code snippets intended for file modifications (like in `SSOFileModification`), PHP functions that appear undefined should not be flagged as issues since they will be available in the target PHP environment where the code is injected.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-07-22T13:29:32.175Z
Learnt from: elibosley
PR: unraid/api#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.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-01-27T14:57:46.617Z
Learnt from: elibosley
PR: unraid/api#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.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2024-12-11T20:39:02.511Z
Learnt from: pujitm
PR: unraid/api#978
File: web/helpers/apollo-cache/index.ts:134-139
Timestamp: 2024-12-11T20:39:02.511Z
Learning: In `web/helpers/apollo-cache/index.ts`, the `cache.readQuery` method returns `null` when it fails to read from the cache, rather than throwing an exception.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2024-12-11T17:20:09.604Z
Learnt from: pujitm
PR: unraid/api#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.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
🔇 Additional comments (3)
api/src/unraid-api/cli/internal-client.service.ts (1)

48-53: Good DI refactor; factory usage is clean

Shifting client construction to InternalGraphQLClientFactory and disabling subscriptions for CLI is the right call. Keeps concerns separated and consistent with the new architecture.

packages/unraid-shared/src/services/base-internal-client.service.ts (2)

69-75: Fix NaN handling for PORT and falsy check in getApiAddress

Number(port) can yield NaN, and if (numericPort) treats NaN like "no port" (and will also skip on port 0). Return undefined for invalid input and explicitly branch on number type.
[ suggest_essential_refactor ]
[ duplicate_comment ]
Apply:

-    private getNumericPort() {
-        const port = this.configService.get<string>('PORT', '/var/run/unraid-api.sock');
-        if (port.includes('.sock')) {
-            return undefined;
-        }
-        return Number(port);
-    }
+    private getNumericPort() {
+        const port = this.configService.get<string>('PORT', '/var/run/unraid-api.sock');
+        if (port.includes('.sock')) return undefined;
+        const parsed = Number(port);
+        return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;
+    }
-        const numericPort = this.getNumericPort();
-        if (numericPort) {
+        const numericPort = this.getNumericPort();
+        if (typeof numericPort === 'number') {
             return `${protocol}://127.0.0.1:${numericPort}/graphql`;
         }

Also applies to: 83-92


44-45: I don’t see any CSRF enforcement or origin validation on the server—CORS is wide-open (origin: true) and only applies to browser clients, and Node-to-Node calls aren’t subject to it. Defaulting options.origin to a filesystem path does not break functionality. As this header is only used for internal service calls (never in a browser) and the API server does not validate it, no change is needed here.

Likely an incorrect or invalid review comment.

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

🧹 Nitpick comments (4)
api/src/unraid-api/cli/cli.module.spec.ts (1)

29-31: Nit: Add generics to module.get for stronger typing (align with other spec)

Use explicit generics as in the other spec to improve type safety and consistency.

-        const service = module.get(CliInternalClientService);
+        const service = module.get<CliInternalClientService>(CliInternalClientService);
...
-        const service = module.get(AdminKeyService);
+        const service = module.get<AdminKeyService>(AdminKeyService);
...
-        const factory = module.get(InternalGraphQLClientFactory);
+        const factory = module.get<InternalGraphQLClientFactory>(InternalGraphQLClientFactory);

Also applies to: 35-38, 41-44

api/src/unraid-api/cli/internal-client.service.spec.ts (3)

48-50: Ensure test isolation: clear/reset mocks in afterEach

mockApolloClient is created once at describe scope and its spies persist across tests. Clear/reset between tests to avoid cross-test leakage.

-    afterEach(async () => {
-        await module?.close();
-    });
+    afterEach(async () => {
+        // Ensure no cross-test leakage of calls/state
+        vi.clearAllMocks();
+        // Explicitly reset shared client spies
+        mockApolloClient.query.mockReset();
+        mockApolloClient.mutate.mockReset();
+        mockApolloClient.stop.mockReset();
+        await module?.close();
+    });

23-41: Optional: Re-initialize the shared Apollo client mocks per test

An alternative to resetting in afterEach is to re-create fresh spies in beforeEach, ensuring every test starts clean.

     beforeEach(async () => {
+        // Fresh spies each test to ensure isolation
+        mockApolloClient.query = vi.fn();
+        mockApolloClient.mutate = vi.fn();
+        mockApolloClient.stop = vi.fn();
         module = await Test.createTestingModule({
             imports: [ConfigModule.forRoot()],
             providers: [
                 CliInternalClientService,
                 {
                     provide: InternalGraphQLClientFactory,
                     useValue: {
-                        createClient: vi.fn().mockResolvedValue(mockApolloClient),
+                        createClient: vi.fn().mockResolvedValue(mockApolloClient),
                     },
                 },
                 {
                     provide: AdminKeyService,
                     useValue: {
                         getOrCreateLocalAdminKey: vi.fn().mockResolvedValue('test-admin-key'),
                     },
                 },
             ],
         }).compile();

96-96: Add coverage for factory creation failure

Currently only the admin key failure is tested. Also verify behavior when createClient rejects.

     describe('getClient', () => {
       ...
       it('should handle errors when getting admin key', async () => {
         const error = new Error('Failed to get admin key');
         vi.mocked(adminKeyService.getOrCreateLocalAdminKey).mockRejectedValueOnce(error);
         await expect(service.getClient()).rejects.toThrow(
           'Unable to get admin API key for internal client'
         );
       });
+
+      it('should surface errors when client factory creation fails', async () => {
+        const error = new Error('factory boom');
+        vi.mocked(clientFactory.createClient).mockRejectedValueOnce(error);
+        await expect(service.getClient()).rejects.toThrow(); // assert rejection without overfitting message
+      });
     });
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between d4175f1 and 08689ca.

📒 Files selected for processing (3)
  • api/src/unraid-api/cli/cli.module.spec.ts (1 hunks)
  • api/src/unraid-api/cli/cli.module.ts (2 hunks)
  • api/src/unraid-api/cli/internal-client.service.spec.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • api/src/unraid-api/cli/cli.module.ts
🧰 Additional context used
📓 Path-based instructions (4)
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/src/unraid-api/cli/cli.module.spec.ts
  • api/src/unraid-api/cli/internal-client.service.spec.ts
api/src/unraid-api/**/*

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

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

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

Files:

  • api/src/unraid-api/cli/cli.module.spec.ts
  • api/src/unraid-api/cli/internal-client.service.spec.ts
api/**/*.{test,spec}.{js,ts,tsx}

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

api/**/*.{test,spec}.{js,ts,tsx}: Use Vitest as the test suite; do not use Jest
Prefer not to mock simple dependencies in tests

Files:

  • api/src/unraid-api/cli/cli.module.spec.ts
  • api/src/unraid-api/cli/internal-client.service.spec.ts
api/**/*.{test,spec}.{ts,js}

📄 CodeRabbit Inference Engine (CLAUDE.md)

Test suite is VITEST, do not use jest in the API package

Files:

  • api/src/unraid-api/cli/cli.module.spec.ts
  • api/src/unraid-api/cli/internal-client.service.spec.ts
🧠 Learnings (17)
📓 Common learnings
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: pujitm
PR: unraid/api#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.
📚 Learning: 2025-07-21T14:00:06.258Z
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/**/*.{test,spec}.{js,ts,tsx} : Use Vitest as the test suite; do not use Jest

Applied to files:

  • api/src/unraid-api/cli/cli.module.spec.ts
  • api/src/unraid-api/cli/internal-client.service.spec.ts
📚 Learning: 2025-07-28T15:34:24.715Z
Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-28T15:34:24.715Z
Learning: Applies to api/**/*.{test,spec}.{ts,js} : Test suite is VITEST, do not use jest in the API package

Applied to files:

  • api/src/unraid-api/cli/cli.module.spec.ts
  • api/src/unraid-api/cli/internal-client.service.spec.ts
📚 Learning: 2025-07-28T15:34:24.715Z
Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-28T15:34:24.715Z
Learning: Applies to web/__test__/**/*.{test,spec}.{ts,js} : Mock external dependencies and services in web/__test__

Applied to files:

  • api/src/unraid-api/cli/cli.module.spec.ts
  • api/src/unraid-api/cli/internal-client.service.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/{components,store,mocks}/**/*.ts : Mock external dependencies and services in tests

Applied to files:

  • api/src/unraid-api/cli/cli.module.spec.ts
  • api/src/unraid-api/cli/internal-client.service.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/store/**/*.ts : Verify state changes by updating the store in store tests

Applied to files:

  • api/src/unraid-api/cli/cli.module.spec.ts
  • api/src/unraid-api/cli/internal-client.service.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/store/**/*.ts : Mock external dependencies appropriately in store tests

Applied to files:

  • api/src/unraid-api/cli/cli.module.spec.ts
  • api/src/unraid-api/cli/internal-client.service.spec.ts
📚 Learning: 2025-07-28T15:34:24.715Z
Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-28T15:34:24.715Z
Learning: Applies to web/__test__/**/*store*.{test,spec}.{ts,js} : Use factory functions for module mocks to avoid hoisting issues in store tests

Applied to files:

  • api/src/unraid-api/cli/cli.module.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/{components,store,mocks}/**/*.ts : Use `vi.mock()` for module-level mocks

Applied to files:

  • api/src/unraid-api/cli/cli.module.spec.ts
  • api/src/unraid-api/cli/internal-client.service.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/{components,store,mocks}/**/*.ts : Use factory functions for module mocks to avoid hoisting issues

Applied to files:

  • api/src/unraid-api/cli/cli.module.spec.ts
  • api/src/unraid-api/cli/internal-client.service.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/store/**/*.ts : Verify state changes after actions in store tests

Applied to files:

  • api/src/unraid-api/cli/cli.module.spec.ts
  • api/src/unraid-api/cli/internal-client.service.spec.ts
📚 Learning: 2025-07-21T14:00:06.258Z
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/**/*.{test,spec}.{js,ts,tsx} : Prefer not to mock simple dependencies in tests

Applied to files:

  • api/src/unraid-api/cli/cli.module.spec.ts
  • api/src/unraid-api/cli/internal-client.service.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/{components,store,mocks}/**/*.ts : Avoid relying on Nuxt's auto-imports in test environment

Applied to files:

  • api/src/unraid-api/cli/cli.module.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/store/**/*.ts : Test getter dependencies are properly mocked in store tests

Applied to files:

  • api/src/unraid-api/cli/cli.module.spec.ts
  • api/src/unraid-api/cli/internal-client.service.spec.ts
📚 Learning: 2024-11-05T14:49:07.308Z
Learnt from: elibosley
PR: unraid/api#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:

  • api/src/unraid-api/cli/internal-client.service.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/{components,store,mocks}/**/*.ts : Clear mocks between tests to ensure isolation

Applied to files:

  • api/src/unraid-api/cli/internal-client.service.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/{components,store,mocks}/**/*.ts : Reset mocks between tests with `vi.clearAllMocks()`

Applied to files:

  • api/src/unraid-api/cli/internal-client.service.spec.ts
🔇 Additional comments (1)
api/src/unraid-api/cli/cli.module.spec.ts (1)

4-4: Good: Using Vitest in api tests

Correct test runner and imports for api/* per project rules. No Jest usage.

@claude
Copy link

claude bot commented Aug 8, 2025

Claude finished @elibosley's task —— View job


I'll analyze this and get back to you.

@unraid unraid deleted a comment from claude bot Aug 8, 2025
@unraid unraid deleted a comment from claude bot Aug 8, 2025
@claude
Copy link

claude bot commented Aug 8, 2025

Claude finished @elibosley's task —— View job


I'll analyze this and get back to you.

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 (2)
packages/unraid-shared/src/services/base-internal-client.service.ts (2)

120-126: Still missing explicit webSocketImpl for Node / ws+unix URLs

Prior feedback noted that Node’s global WebSocket (undici) doesn’t handle ws+unix:// schemes.
createClient() should be given a compatible implementation (e.g. the ws package) or subscriptions disabled in socket mode.


173-176: ApolloClient.stop() is not part of the public API

Apollo Client 3.x exposes clearStore() / resetStore() but no stop() method.
Calling it risks a runtime crash.

Reuse the earlier suggestion:

-        this.client?.stop();
+        if (this.client) {
+            (this.client as any).stop?.();           // no-op if absent
+            await this.client.clearStore().catch(() => undefined);
+        }
🧹 Nitpick comments (8)
packages/unraid-shared/src/services/socket-config.service.ts (1)

52-61: Truthiness check hides valid port 0

if (numericPort) will treat 0 as false and fall through to the nginx-port branch even though 0 is a valid numeric value. Check for numericPort !== undefined instead.

api/src/unraid-api/cli/internal-client.service.ts (2)

21-24: Consider exposing a dedicated DI token for the factory

INTERNAL_CLIENT_SERVICE_TOKEN resolves to an InternalGraphQLClientFactory instance, but the token name suggests a service rather than a factory. Renaming the constant (e.g. INTERNAL_GRAPHQL_CLIENT_FACTORY_TOKEN) or injecting the class directly would make intent clearer and avoid future confusion in module registration.


60-64: stop() is asynchronous – await or ignore errors explicitly

ApolloClient.stop() returns a promise.
If the CLI process exits immediately after clearClient(), outstanding asynchronous work could be abandoned. Either:

-// Stop the Apollo client to terminate any active processes
-this.client?.stop();
+// Ensure the promise resolves before clearing reference
+await this.client?.stop().catch(err => this.logger.debug('Client stop failed', err));

or document why fire-and-forget is acceptable here.

packages/unraid-shared/src/services/base-internal-client.service.spec.ts (2)

55-59: Unused stub can be removed

mockApolloClient is created but never injected or asserted, leaving dead code in the suite.


156-168: stop spy may break on future Apollo upgrades

The test spies on a private method (stop) that is not part of Apollo’s public API and may disappear.
A safer pattern is to spy on service.clearClient() side-effects (e.g., this.client = null) or inject a custom client with a controlled stop stub.

packages/unraid-shared/src/services/socket-config.service.spec.ts (1)

10-13: Prefer a lightweight stub over real ConfigService

Instantiating the real ConfigService pulls in its internal validation logic that the unit under test doesn’t need. A plain object with a get mock keeps the test faster and eliminates implicit behaviour changes across NestJS versions.

packages/unraid-shared/src/services/base-internal-client.service.ts (2)

73-87: Unnecessary wrapper URI may break absolute-path routing

When using a Unix socket, the request still targets http://localhost/graphql, meaning the HTTP Host header is set to localhost.
If the backend relies on virtual-host routing (or absolute redirects) this diverges from production behaviour.

Either:

  1. Use http://unix/graphql (commonly used dummy host for socket mode), or
  2. Add an explicit Host header that mirrors external traffic expectations.

139-150: mutate defaultOptions omitted in HTTP-only branch

The subscription branch sets both query and mutate fetch policies to 'no-cache', but the HTTP-only path below omits mutate, causing inconsistent caching behaviour.

Add the same mutate block for consistency.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 08689ca and a21e0c6.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (12)
  • api/src/unraid-api/cli/cli.module.spec.ts (1 hunks)
  • api/src/unraid-api/cli/internal-client.service.spec.ts (1 hunks)
  • api/src/unraid-api/cli/internal-client.service.ts (2 hunks)
  • api/src/unraid-api/plugin/global-deps.module.ts (3 hunks)
  • api/src/unraid-api/shared/internal-graphql-client.factory.spec.ts (1 hunks)
  • api/src/unraid-api/shared/internal-graphql-client.factory.ts (1 hunks)
  • packages/unraid-shared/package.json (4 hunks)
  • packages/unraid-shared/src/index.ts (1 hunks)
  • packages/unraid-shared/src/services/base-internal-client.service.spec.ts (1 hunks)
  • packages/unraid-shared/src/services/base-internal-client.service.ts (1 hunks)
  • packages/unraid-shared/src/services/socket-config.service.spec.ts (1 hunks)
  • packages/unraid-shared/src/services/socket-config.service.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • packages/unraid-shared/package.json
  • api/src/unraid-api/plugin/global-deps.module.ts
  • api/src/unraid-api/cli/internal-client.service.spec.ts
  • packages/unraid-shared/src/index.ts
  • api/src/unraid-api/cli/cli.module.spec.ts
  • api/src/unraid-api/shared/internal-graphql-client.factory.ts
🧰 Additional context used
📓 Path-based instructions (4)
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/src/unraid-api/shared/internal-graphql-client.factory.spec.ts
  • api/src/unraid-api/cli/internal-client.service.ts
api/src/unraid-api/**/*

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

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

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

Files:

  • api/src/unraid-api/shared/internal-graphql-client.factory.spec.ts
  • api/src/unraid-api/cli/internal-client.service.ts
api/**/*.{test,spec}.{js,ts,tsx}

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

api/**/*.{test,spec}.{js,ts,tsx}: Use Vitest as the test suite; do not use Jest
Prefer not to mock simple dependencies in tests

Files:

  • api/src/unraid-api/shared/internal-graphql-client.factory.spec.ts
api/**/*.{test,spec}.{ts,js}

📄 CodeRabbit Inference Engine (CLAUDE.md)

Test suite is VITEST, do not use jest in the API package

Files:

  • api/src/unraid-api/shared/internal-graphql-client.factory.spec.ts
🧠 Learnings (45)
📓 Common learnings
Learnt from: pujitm
PR: unraid/api#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: unraid/api#982
File: web/helpers/apollo-cache/index.ts:140-145
Timestamp: 2024-12-13T17:26:55.220Z
Learning: In Apollo Client, the `cache.updateQuery` method can be used on the `InMemoryCache` instance to update queries, even when not using an `ApolloClient` instance directly.
Learnt from: pujitm
PR: unraid/api#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: unraid/api#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.
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/{components,store,mocks}/**/*.ts : Use factory functions for module mocks to avoid hoisting issues

Applied to files:

  • api/src/unraid-api/shared/internal-graphql-client.factory.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/store/**/*.ts : Mock external dependencies appropriately in store tests

Applied to files:

  • api/src/unraid-api/shared/internal-graphql-client.factory.spec.ts
  • packages/unraid-shared/src/services/socket-config.service.spec.ts
  • packages/unraid-shared/src/services/base-internal-client.service.spec.ts
  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-07-28T15:34:24.715Z
Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-28T15:34:24.715Z
Learning: Applies to web/__test__/**/*store*.{test,spec}.{ts,js} : Use factory functions for module mocks to avoid hoisting issues in store tests

Applied to files:

  • api/src/unraid-api/shared/internal-graphql-client.factory.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/store/**/*.ts : Test getter dependencies are properly mocked in store tests

Applied to files:

  • api/src/unraid-api/shared/internal-graphql-client.factory.spec.ts
  • packages/unraid-shared/src/services/socket-config.service.spec.ts
  • packages/unraid-shared/src/services/base-internal-client.service.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/store/**/*.ts : Verify state changes by updating the store in store tests

Applied to files:

  • api/src/unraid-api/shared/internal-graphql-client.factory.spec.ts
  • packages/unraid-shared/src/services/socket-config.service.spec.ts
  • packages/unraid-shared/src/services/base-internal-client.service.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/{components,store,mocks}/**/*.ts : Mock external dependencies and services in tests

Applied to files:

  • api/src/unraid-api/shared/internal-graphql-client.factory.spec.ts
  • packages/unraid-shared/src/services/socket-config.service.spec.ts
  • packages/unraid-shared/src/services/base-internal-client.service.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/store/**/*.ts : Verify state changes after actions in store tests

Applied to files:

  • api/src/unraid-api/shared/internal-graphql-client.factory.spec.ts
  • packages/unraid-shared/src/services/socket-config.service.spec.ts
  • packages/unraid-shared/src/services/base-internal-client.service.spec.ts
📚 Learning: 2024-11-05T14:49:07.308Z
Learnt from: elibosley
PR: unraid/api#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:

  • api/src/unraid-api/shared/internal-graphql-client.factory.spec.ts
  • api/src/unraid-api/cli/internal-client.service.ts
  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/{components,store,mocks}/**/*.ts : Clear mocks between tests to ensure isolation

Applied to files:

  • api/src/unraid-api/shared/internal-graphql-client.factory.spec.ts
  • packages/unraid-shared/src/services/base-internal-client.service.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/mocks/**/*.ts : Frequently used mocks are stored under `web/test/mocks`

Applied to files:

  • api/src/unraid-api/shared/internal-graphql-client.factory.spec.ts
  • packages/unraid-shared/src/services/socket-config.service.spec.ts
  • packages/unraid-shared/src/services/base-internal-client.service.spec.ts
📚 Learning: 2025-07-28T15:34:24.715Z
Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-28T15:34:24.715Z
Learning: Applies to web/__test__/**/*.{test,spec}.{ts,js} : Mock external dependencies and services in web/__test__

Applied to files:

  • api/src/unraid-api/shared/internal-graphql-client.factory.spec.ts
  • packages/unraid-shared/src/services/socket-config.service.spec.ts
  • packages/unraid-shared/src/services/base-internal-client.service.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/{components,store,mocks}/**/*.ts : Use `vi.mock()` for module-level mocks

Applied to files:

  • api/src/unraid-api/shared/internal-graphql-client.factory.spec.ts
  • packages/unraid-shared/src/services/socket-config.service.spec.ts
  • packages/unraid-shared/src/services/base-internal-client.service.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/{components,store,mocks}/**/*.ts : Remember that `vi.mock()` calls are hoisted

Applied to files:

  • api/src/unraid-api/shared/internal-graphql-client.factory.spec.ts
  • packages/unraid-shared/src/services/socket-config.service.spec.ts
  • packages/unraid-shared/src/services/base-internal-client.service.spec.ts
📚 Learning: 2025-04-23T20:19:42.542Z
Learnt from: pujitm
PR: unraid/api#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.

Applied to files:

  • packages/unraid-shared/src/services/socket-config.service.ts
  • api/src/unraid-api/cli/internal-client.service.ts
📚 Learning: 2025-04-23T20:19:42.542Z
Learnt from: pujitm
PR: unraid/api#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.

Applied to files:

  • packages/unraid-shared/src/services/socket-config.service.ts
📚 Learning: 2025-07-28T15:34:24.715Z
Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-28T15:34:24.715Z
Learning: Applies to web/__test__/**/*.{test,spec}.{ts,js} : Test component behavior and output, not implementation details in web/__test__

Applied to files:

  • packages/unraid-shared/src/services/socket-config.service.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/store/**/*.ts : Verify proper error handling in store tests

Applied to files:

  • packages/unraid-shared/src/services/socket-config.service.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/store/**/*.ts : Test async operations completely in store tests

Applied to files:

  • packages/unraid-shared/src/services/socket-config.service.spec.ts
  • packages/unraid-shared/src/services/base-internal-client.service.spec.ts
📚 Learning: 2025-07-28T15:34:24.715Z
Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-28T15:34:24.715Z
Learning: Applies to api/**/*.{test,spec}.{ts,js} : Test suite is VITEST, do not use jest in the API package

Applied to files:

  • packages/unraid-shared/src/services/socket-config.service.spec.ts
  • packages/unraid-shared/src/services/base-internal-client.service.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/{components,store,mocks}/**/*.ts : Specify return values for component methods with `vi.spyOn()`

Applied to files:

  • packages/unraid-shared/src/services/socket-config.service.spec.ts
📚 Learning: 2025-07-21T14:00:06.258Z
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/**/*.{test,spec}.{js,ts,tsx} : Use Vitest as the test suite; do not use Jest

Applied to files:

  • packages/unraid-shared/src/services/socket-config.service.spec.ts
  • packages/unraid-shared/src/services/base-internal-client.service.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/store/**/*.ts : Test action side effects and state changes in store tests

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/{components,store,mocks}/**/*.ts : Reset mocks between tests with `vi.clearAllMocks()`

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.spec.ts
📚 Learning: 2024-11-15T16:22:03.485Z
Learnt from: elibosley
PR: unraid/api#942
File: api/src/unraid-api/auth/api-key.service.ts:176-188
Timestamp: 2024-11-15T16:22:03.485Z
Learning: Atomic writes are not required for the `saveApiKey` method in `ApiKeyService` (`api/src/unraid-api/auth/api-key.service.ts`) unless specifically needed.

Applied to files:

  • api/src/unraid-api/cli/internal-client.service.ts
  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2024-11-06T20:59:25.809Z
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.

Applied to files:

  • api/src/unraid-api/cli/internal-client.service.ts
  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-03-12T13:48:14.850Z
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.

Applied to files:

  • api/src/unraid-api/cli/internal-client.service.ts
  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2024-11-04T20:44:46.432Z
Learnt from: mdatelle
PR: unraid/api#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.

Applied to files:

  • api/src/unraid-api/cli/internal-client.service.ts
  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2024-11-04T22:00:41.946Z
Learnt from: mdatelle
PR: unraid/api#942
File: api/src/unraid-api/auth/auth.service.ts:42-105
Timestamp: 2024-11-04T22:00:41.946Z
Learning: In the 'AuthService' within `api/src/unraid-api/auth/auth.service.ts`, rate limiting is not required because the API is not public facing.

Applied to files:

  • api/src/unraid-api/cli/internal-client.service.ts
📚 Learning: 2024-11-15T16:22:12.460Z
Learnt from: elibosley
PR: unraid/api#942
File: api/src/unraid-api/auth/auth.module.ts:94-96
Timestamp: 2024-11-15T16:22:12.460Z
Learning: In `CookieService.defaultOpts()` in `api/src/unraid-api/auth/cookie.service.ts`, use `sameSite: 'lax'` for session cookie configuration.

Applied to files:

  • api/src/unraid-api/cli/internal-client.service.ts
📚 Learning: 2024-12-13T17:28:15.384Z
Learnt from: pujitm
PR: unraid/api#982
File: web/helpers/apollo-cache/index.ts:142-142
Timestamp: 2024-12-13T17:28:15.384Z
Learning: In `web/helpers/apollo-cache/index.ts`, it's acceptable to use `structuredClone` since the project targets Node.js v20 and browsers where `structuredClone` is supported.

Applied to files:

  • api/src/unraid-api/cli/internal-client.service.ts
  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-03-27T13:34:53.438Z
Learnt from: pujitm
PR: unraid/api#1252
File: api/src/environment.ts:56-56
Timestamp: 2025-03-27T13:34:53.438Z
Learning: For critical components in the Unraid API, such as retrieving version information from package.json, failing fast (allowing crashes) is preferred over graceful degradation with fallback values.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-03-10T18:43:13.016Z
Learnt from: mdatelle
PR: unraid/api#1219
File: api/src/unraid-api/types/fastify.ts:25-30
Timestamp: 2025-03-10T18:43:13.016Z
Learning: In the Unraid API, cookies should be marked as optional (with `?`) in the FastifyRequest type definition since the cookies may not always be present, which is consistent with the authentication flow throughout the codebase.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-01-15T21:34:00.006Z
Learnt from: pujitm
PR: unraid/api#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.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-04-07T14:34:47.255Z
Learnt from: elibosley
PR: unraid/api#1334
File: api/src/unraid-api/graph/resolvers/docker/docker-event.service.ts:63-66
Timestamp: 2025-04-07T14:34:47.255Z
Learning: In DockerEventService, the chokidar file watcher is configured with `ignoreInitial: false` to ensure that existing files (like the Docker socket) are detected and processed at application startup, not just when files change after the watcher is initialized.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-01-30T19:56:42.055Z
Learnt from: pujitm
PR: unraid/api#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.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-07-22T13:29:32.175Z
Learnt from: elibosley
PR: unraid/api#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.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2024-11-27T15:30:02.252Z
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.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-01-28T16:39:50.278Z
Learnt from: elibosley
PR: unraid/api#1078
File: api/src/unraid-api/unraid-file-modifier/modifications/sso.modification.ts:35-36
Timestamp: 2025-01-28T16:39:50.278Z
Learning: When reviewing TypeScript files containing PHP code snippets intended for file modifications (like in `SSOFileModification`), PHP functions that appear undefined should not be flagged as issues since they will be available in the target PHP environment where the code is injected.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-07-21T14:00:25.140Z
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

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2024-12-11T20:39:02.511Z
Learnt from: pujitm
PR: unraid/api#978
File: web/helpers/apollo-cache/index.ts:134-139
Timestamp: 2024-12-11T20:39:02.511Z
Learning: In `web/helpers/apollo-cache/index.ts`, the `cache.readQuery` method returns `null` when it fails to read from the cache, rather than throwing an exception.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2024-12-11T17:20:09.604Z
Learnt from: pujitm
PR: unraid/api#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.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-03-07T19:29:12.540Z
Learnt from: pujitm
PR: unraid/api#1219
File: api/src/unraid-api/main.ts:18-25
Timestamp: 2025-03-07T19:29:12.540Z
Learning: When handling type mismatches between dependencies in TypeScript (such as between NestJS and Fastify), prefer using `ts-expect-error` with a detailed explanation comment instead of `as any` type assertions. This better documents the reason for the type bypass and makes the issue more visible for future maintenance.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-07-28T15:34:24.715Z
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)

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-02-06T17:25:45.397Z
Learnt from: mdatelle
PR: unraid/api#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:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
🧬 Code Graph Analysis (1)
api/src/unraid-api/cli/internal-client.service.ts (3)
api/src/unraid-api/shared/internal-graphql-client.factory.ts (1)
  • Injectable (23-152)
packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts (1)
  • Injectable (22-57)
packages/unraid-shared/src/tokens.ts (1)
  • INTERNAL_CLIENT_SERVICE_TOKEN (5-5)
🪛 GitHub Actions: CI - Main (API)
packages/unraid-shared/src/services/base-internal-client.service.spec.ts

[error] 111-111: TestInternalClientService failed to get API key due to 'API key error'.

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: 1

♻️ Duplicate comments (2)
packages/unraid-shared/src/services/base-internal-client.service.ts (2)

45-48: Default origin is not a valid HTTP origin
Using a filesystem path (/var/run/unraid-cli.sock) as the Origin header is semantically wrong and may be rejected by strict CORS checks. Default to a proper scheme/host string (e.g. http://localhost) or omit the header for internal calls.


175-178: ApolloClient.stop() is not part of the public API
Apollo 3 exposes clearStore() / resetStore(), but no stop(). Calling it will throw at runtime.

- this.client?.stop();
+ if (this.client) {
+   // End polling links if present
+   (this.client as any).stop?.();
+   await this.client.clearStore().catch(() => undefined);
+ }
🧹 Nitpick comments (4)
packages/unraid-shared/src/services/base-internal-client.service.ts (1)

109-113: Consider logging GraphQL errors as well
Only networkError is handled; graphQLErrors are silently ignored, making debugging harder.

packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.spec.ts (3)

3-3: Import extension may break TS-time resolution

./internal.client.js refers to a file that actually lives as internal.client.ts in sources.
Vitest’s ts-node resolver usually maps this automatically, but the extra “.js” can bite in IDE tooling and when the compiled JS ends up in a different directory structure. Safer to drop the explicit extension:

-import { InternalClientService } from './internal.client.js';
+import { InternalClientService } from './internal.client';

6-9: Avoid any in test doubles

Using any for clientFactory and apiKeyService loses type-safety and can hide breaking refactors.
Define minimal interfaces or use vi.mocked<Interface>() to keep the compiler on your side.

interface MockClientFactory {
  createClient: ReturnType<typeof vi.fn>;
}

31-33: Prefer vi.resetAllMocks() for full isolation

vi.clearAllMocks() resets call counts but preserves stubbed implementations modified in a test.
If a test mutates mockImplementation, the next test could inherit it. Using

afterEach(() => vi.resetAllMocks());

restores originals and avoids inter-test leakage.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between a21e0c6 and 634faca.

📒 Files selected for processing (4)
  • api/src/unraid-api/shared/internal-graphql-client.factory.ts (1 hunks)
  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.spec.ts (1 hunks)
  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts (1 hunks)
  • packages/unraid-shared/src/services/base-internal-client.service.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts
  • api/src/unraid-api/shared/internal-graphql-client.factory.ts
🧰 Additional context used
🧠 Learnings (42)
📓 Common learnings
Learnt from: pujitm
PR: unraid/api#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: unraid/api#982
File: web/helpers/apollo-cache/index.ts:140-145
Timestamp: 2024-12-13T17:26:55.220Z
Learning: In Apollo Client, the `cache.updateQuery` method can be used on the `InMemoryCache` instance to update queries, even when not using an `ApolloClient` instance directly.
Learnt from: pujitm
PR: unraid/api#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: unraid/api#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.
📚 Learning: 2025-07-28T15:34:24.715Z
Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-28T15:34:24.715Z
Learning: Applies to api/**/*.{test,spec}.{ts,js} : Test suite is VITEST, do not use jest in the API package

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.spec.ts
📚 Learning: 2025-07-21T14:00:06.258Z
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/**/*.{test,spec}.{js,ts,tsx} : Use Vitest as the test suite; do not use Jest

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/store/**/*.ts : Mock external dependencies appropriately in store tests

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.spec.ts
  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/{components,store,mocks}/**/*.ts : Clear mocks between tests to ensure isolation

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/store/**/*.ts : Verify state changes by updating the store in store tests

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/{components,store,mocks}/**/*.ts : Mock external dependencies and services in tests

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/store/**/*.ts : Test async operations completely in store tests

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/store/**/*.ts : Verify proper error handling in store tests

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/store/**/*.ts : Verify state changes after actions in store tests

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.spec.ts
📚 Learning: 2025-07-28T15:34:24.715Z
Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-28T15:34:24.715Z
Learning: Applies to web/__test__/**/*.{test,spec}.{ts,js} : Mock external dependencies and services in web/__test__

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/{components,store,mocks}/**/*.ts : Reset mocks between tests with `vi.clearAllMocks()`

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/{components,store,mocks}/**/*.ts : Remember that `vi.mock()` calls are hoisted

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/{components,store,mocks}/**/*.ts : Use `vi.mock()` for module-level mocks

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/{components,store,mocks}/**/*.ts : Specify return values for component methods with `vi.spyOn()`

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.spec.ts
📚 Learning: 2024-11-05T14:49:07.308Z
Learnt from: elibosley
PR: unraid/api#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-shared/src/services/base-internal-client.service.ts
📚 Learning: 2024-11-15T16:22:03.485Z
Learnt from: elibosley
PR: unraid/api#942
File: api/src/unraid-api/auth/api-key.service.ts:176-188
Timestamp: 2024-11-15T16:22:03.485Z
Learning: Atomic writes are not required for the `saveApiKey` method in `ApiKeyService` (`api/src/unraid-api/auth/api-key.service.ts`) unless specifically needed.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2024-11-06T20:59:25.809Z
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.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-03-12T13:48:14.850Z
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.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2024-11-04T20:44:46.432Z
Learnt from: mdatelle
PR: unraid/api#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.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-03-27T13:34:53.438Z
Learnt from: pujitm
PR: unraid/api#1252
File: api/src/environment.ts:56-56
Timestamp: 2025-03-27T13:34:53.438Z
Learning: For critical components in the Unraid API, such as retrieving version information from package.json, failing fast (allowing crashes) is preferred over graceful degradation with fallback values.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-03-10T18:43:13.016Z
Learnt from: mdatelle
PR: unraid/api#1219
File: api/src/unraid-api/types/fastify.ts:25-30
Timestamp: 2025-03-10T18:43:13.016Z
Learning: In the Unraid API, cookies should be marked as optional (with `?`) in the FastifyRequest type definition since the cookies may not always be present, which is consistent with the authentication flow throughout the codebase.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-01-15T21:34:00.006Z
Learnt from: pujitm
PR: unraid/api#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.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-04-07T14:34:47.255Z
Learnt from: elibosley
PR: unraid/api#1334
File: api/src/unraid-api/graph/resolvers/docker/docker-event.service.ts:63-66
Timestamp: 2025-04-07T14:34:47.255Z
Learning: In DockerEventService, the chokidar file watcher is configured with `ignoreInitial: false` to ensure that existing files (like the Docker socket) are detected and processed at application startup, not just when files change after the watcher is initialized.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-01-30T19:56:42.055Z
Learnt from: pujitm
PR: unraid/api#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.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-07-22T13:29:32.175Z
Learnt from: elibosley
PR: unraid/api#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.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2024-11-27T15:30:02.252Z
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.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-01-28T16:39:50.278Z
Learnt from: elibosley
PR: unraid/api#1078
File: api/src/unraid-api/unraid-file-modifier/modifications/sso.modification.ts:35-36
Timestamp: 2025-01-28T16:39:50.278Z
Learning: When reviewing TypeScript files containing PHP code snippets intended for file modifications (like in `SSOFileModification`), PHP functions that appear undefined should not be flagged as issues since they will be available in the target PHP environment where the code is injected.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-07-21T14:00:25.140Z
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

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2024-12-13T17:28:15.384Z
Learnt from: pujitm
PR: unraid/api#982
File: web/helpers/apollo-cache/index.ts:142-142
Timestamp: 2024-12-13T17:28:15.384Z
Learning: In `web/helpers/apollo-cache/index.ts`, it's acceptable to use `structuredClone` since the project targets Node.js v20 and browsers where `structuredClone` is supported.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2024-12-11T20:39:02.511Z
Learnt from: pujitm
PR: unraid/api#978
File: web/helpers/apollo-cache/index.ts:134-139
Timestamp: 2024-12-11T20:39:02.511Z
Learning: In `web/helpers/apollo-cache/index.ts`, the `cache.readQuery` method returns `null` when it fails to read from the cache, rather than throwing an exception.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2024-12-11T17:20:09.604Z
Learnt from: pujitm
PR: unraid/api#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.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-03-07T19:29:12.540Z
Learnt from: pujitm
PR: unraid/api#1219
File: api/src/unraid-api/main.ts:18-25
Timestamp: 2025-03-07T19:29:12.540Z
Learning: When handling type mismatches between dependencies in TypeScript (such as between NestJS and Fastify), prefer using `ts-expect-error` with a detailed explanation comment instead of `as any` type assertions. This better documents the reason for the type bypass and makes the issue more visible for future maintenance.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-07-28T15:34:24.715Z
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)

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-02-06T17:25:45.397Z
Learnt from: mdatelle
PR: unraid/api#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:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-03-07T17:36:52.790Z
Learnt from: mdatelle
PR: unraid/api#1219
File: api/src/unraid-api/main.ts:57-63
Timestamp: 2025-03-07T17:36:52.790Z
Learning: The CORS configuration in the unraid API is intentionally set to allow all origins (`origin: true`). This was a deliberate architectural decision when removing the previous custom CORS implementation and implementing helmet security headers instead.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2024-11-15T16:22:12.460Z
Learnt from: elibosley
PR: unraid/api#942
File: api/src/unraid-api/auth/auth.module.ts:94-96
Timestamp: 2024-11-15T16:22:12.460Z
Learning: In `CookieService.defaultOpts()` in `api/src/unraid-api/auth/cookie.service.ts`, use `sameSite: 'lax'` for session cookie configuration.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-05-08T19:28:37.034Z
Learnt from: elibosley
PR: unraid/api#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.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-03-04T14:55:00.903Z
Learnt from: elibosley
PR: unraid/api#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:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-07-28T15:34:24.715Z
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

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-07-21T14:00:06.258Z
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

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-03-07T17:35:50.406Z
Learnt from: mdatelle
PR: unraid/api#1219
File: api/src/unraid-api/main.ts:32-55
Timestamp: 2025-03-07T17:35:50.406Z
Learning: Helmet security configuration in the Unraid API is intentionally relaxed (with disabled CSP, CORS policies, and HSTS) to maintain compatibility with existing Unraid plugins. Stricter security settings might break current plugin functionality.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
🪛 GitHub Actions: CI - Main (API)
packages/unraid-shared/src/services/base-internal-client.service.ts

[error] 12-12: TypeScript error TS2307: Cannot find module 'ws' or its corresponding type declarations.

@elibosley elibosley added the bug Something isn't working label Aug 8, 2025
@elibosley elibosley linked an issue Aug 8, 2025 that may be closed by this pull request
3 tasks
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

♻️ Duplicate comments (1)
packages/unraid-shared/src/services/base-internal-client.service.ts (1)

181-186: ApolloClient.stop() is still not a public API – will throw at runtime

Apollo 3.x exposes clearStore() / resetStore(), but no stop() method.
If stop is undefined this line throws, breaking every consumer attempting to clear the client.

-        // Stop the Apollo client to terminate any active processes
-        this.client?.stop();
+        // Best-effort teardown – clear polling / terminate links if available
+        if (this.client) {
+            (this.client as any).stop?.();          // optional: community patches
+            await this.client.clearStore().catch(() => undefined);
+        }
🧹 Nitpick comments (2)
packages/unraid-shared/src/services/base-internal-client.service.ts (2)

45-47: Prefer DI over manual instantiation for SocketConfigService

Constructing the service with new bypasses Nest’s lifecycle hooks and makes testing harder.
Inject it instead:

constructor(
  private readonly socketConfig: SocketConfigService,
  protected readonly configService: ConfigService,
  
) {}

183-186: this.wsClient.dispose() returns a Promise – consider awaiting

dispose() is async; ignoring the promise risks unresolved handles during shutdown (notably in Jest).

-        if (this.wsClient) {
-            this.wsClient.dispose();
+        if (this.wsClient) {
+            await this.wsClient.dispose();
             this.wsClient = null;
         }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between b8c6a51 and 9a277b9.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (12)
  • api/src/unraid-api/shared/internal-graphql-client.factory.spec.ts (1 hunks)
  • api/src/unraid-api/shared/internal-graphql-client.factory.ts (1 hunks)
  • packages/unraid-shared/package.json (3 hunks)
  • packages/unraid-shared/src/jsonforms/__tests__/settings.test.ts (1 hunks)
  • packages/unraid-shared/src/services/__tests__/config-file.test.ts (1 hunks)
  • packages/unraid-shared/src/services/base-internal-client.service.spec.ts (1 hunks)
  • packages/unraid-shared/src/services/base-internal-client.service.ts (1 hunks)
  • packages/unraid-shared/src/services/socket-config.service.spec.ts (1 hunks)
  • packages/unraid-shared/src/services/socket-config.service.ts (1 hunks)
  • packages/unraid-shared/src/util/__tests__/config-definition.test.ts (1 hunks)
  • packages/unraid-shared/src/util/__tests__/config-file-handler.test.ts (1 hunks)
  • packages/unraid-shared/src/util/__tests__/key-order.test.ts (1 hunks)
✅ Files skipped from review due to trivial changes (5)
  • packages/unraid-shared/src/util/tests/key-order.test.ts
  • packages/unraid-shared/src/services/tests/config-file.test.ts
  • packages/unraid-shared/src/util/tests/config-file-handler.test.ts
  • packages/unraid-shared/src/jsonforms/tests/settings.test.ts
  • packages/unraid-shared/src/util/tests/config-definition.test.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • packages/unraid-shared/package.json
  • api/src/unraid-api/shared/internal-graphql-client.factory.ts
  • packages/unraid-shared/src/services/base-internal-client.service.spec.ts
  • packages/unraid-shared/src/services/socket-config.service.ts
  • packages/unraid-shared/src/services/socket-config.service.spec.ts
  • api/src/unraid-api/shared/internal-graphql-client.factory.spec.ts
🧰 Additional context used
🧠 Learnings (29)
📚 Learning: 2024-11-05T14:49:07.308Z
Learnt from: elibosley
PR: unraid/api#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-shared/src/services/base-internal-client.service.ts
📚 Learning: 2024-11-15T16:22:03.485Z
Learnt from: elibosley
PR: unraid/api#942
File: api/src/unraid-api/auth/api-key.service.ts:176-188
Timestamp: 2024-11-15T16:22:03.485Z
Learning: Atomic writes are not required for the `saveApiKey` method in `ApiKeyService` (`api/src/unraid-api/auth/api-key.service.ts`) unless specifically needed.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2024-11-06T20:59:25.809Z
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.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-03-12T13:48:14.850Z
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.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2024-11-04T20:44:46.432Z
Learnt from: mdatelle
PR: unraid/api#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.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-03-27T13:34:53.438Z
Learnt from: pujitm
PR: unraid/api#1252
File: api/src/environment.ts:56-56
Timestamp: 2025-03-27T13:34:53.438Z
Learning: For critical components in the Unraid API, such as retrieving version information from package.json, failing fast (allowing crashes) is preferred over graceful degradation with fallback values.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-03-10T18:43:13.016Z
Learnt from: mdatelle
PR: unraid/api#1219
File: api/src/unraid-api/types/fastify.ts:25-30
Timestamp: 2025-03-10T18:43:13.016Z
Learning: In the Unraid API, cookies should be marked as optional (with `?`) in the FastifyRequest type definition since the cookies may not always be present, which is consistent with the authentication flow throughout the codebase.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-01-15T21:34:00.006Z
Learnt from: pujitm
PR: unraid/api#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.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-04-07T14:34:47.255Z
Learnt from: elibosley
PR: unraid/api#1334
File: api/src/unraid-api/graph/resolvers/docker/docker-event.service.ts:63-66
Timestamp: 2025-04-07T14:34:47.255Z
Learning: In DockerEventService, the chokidar file watcher is configured with `ignoreInitial: false` to ensure that existing files (like the Docker socket) are detected and processed at application startup, not just when files change after the watcher is initialized.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-01-30T19:56:42.055Z
Learnt from: pujitm
PR: unraid/api#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.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-07-22T13:29:32.175Z
Learnt from: elibosley
PR: unraid/api#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.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2024-11-27T15:30:02.252Z
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.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-07-21T14:00:25.140Z
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

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-01-28T16:39:50.278Z
Learnt from: elibosley
PR: unraid/api#1078
File: api/src/unraid-api/unraid-file-modifier/modifications/sso.modification.ts:35-36
Timestamp: 2025-01-28T16:39:50.278Z
Learning: When reviewing TypeScript files containing PHP code snippets intended for file modifications (like in `SSOFileModification`), PHP functions that appear undefined should not be flagged as issues since they will be available in the target PHP environment where the code is injected.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2024-12-13T17:28:15.384Z
Learnt from: pujitm
PR: unraid/api#982
File: web/helpers/apollo-cache/index.ts:142-142
Timestamp: 2024-12-13T17:28:15.384Z
Learning: In `web/helpers/apollo-cache/index.ts`, it's acceptable to use `structuredClone` since the project targets Node.js v20 and browsers where `structuredClone` is supported.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2024-12-11T20:39:02.511Z
Learnt from: pujitm
PR: unraid/api#978
File: web/helpers/apollo-cache/index.ts:134-139
Timestamp: 2024-12-11T20:39:02.511Z
Learning: In `web/helpers/apollo-cache/index.ts`, the `cache.readQuery` method returns `null` when it fails to read from the cache, rather than throwing an exception.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2024-12-11T17:20:09.604Z
Learnt from: pujitm
PR: unraid/api#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.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-03-07T19:29:12.540Z
Learnt from: pujitm
PR: unraid/api#1219
File: api/src/unraid-api/main.ts:18-25
Timestamp: 2025-03-07T19:29:12.540Z
Learning: When handling type mismatches between dependencies in TypeScript (such as between NestJS and Fastify), prefer using `ts-expect-error` with a detailed explanation comment instead of `as any` type assertions. This better documents the reason for the type bypass and makes the issue more visible for future maintenance.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-07-28T15:34:24.715Z
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)

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-02-06T17:25:45.397Z
Learnt from: mdatelle
PR: unraid/api#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:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/store/**/*.ts : Mock external dependencies appropriately in store tests

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-03-07T17:36:52.790Z
Learnt from: mdatelle
PR: unraid/api#1219
File: api/src/unraid-api/main.ts:57-63
Timestamp: 2025-03-07T17:36:52.790Z
Learning: The CORS configuration in the unraid API is intentionally set to allow all origins (`origin: true`). This was a deliberate architectural decision when removing the previous custom CORS implementation and implementing helmet security headers instead.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2024-11-15T16:22:12.460Z
Learnt from: elibosley
PR: unraid/api#942
File: api/src/unraid-api/auth/auth.module.ts:94-96
Timestamp: 2024-11-15T16:22:12.460Z
Learning: In `CookieService.defaultOpts()` in `api/src/unraid-api/auth/cookie.service.ts`, use `sameSite: 'lax'` for session cookie configuration.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-07-28T15:34:24.715Z
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

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-07-21T14:00:06.258Z
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

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-05-08T19:28:37.034Z
Learnt from: elibosley
PR: unraid/api#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.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-03-04T14:55:00.903Z
Learnt from: elibosley
PR: unraid/api#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:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-03-07T17:35:50.406Z
Learnt from: mdatelle
PR: unraid/api#1219
File: api/src/unraid-api/main.ts:32-55
Timestamp: 2025-03-07T17:35:50.406Z
Learning: Helmet security configuration in the Unraid API is intentionally relaxed (with disabled CSP, CORS policies, and HSTS) to maintain compatibility with existing Unraid plugins. Stricter security settings might break current plugin functionality.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-05-23T21:59:29.632Z
Learnt from: elibosley
PR: unraid/api#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.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
🔇 Additional comments (1)
packages/unraid-shared/src/services/base-internal-client.service.ts (1)

11-13: ws dependency is already declared in packages/unraid-shared

packages/unraid-shared/package.json includes:

  • ws: ^8.18.3
  • @types/ws: ^8.5.13

No other package compiles this file, so CI will not fail for missing ws.

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: 4

♻️ Duplicate comments (1)
packages/unraid-shared/src/services/base-internal-client.service.ts (1)

181-181: ApolloClient.stop() is undefined in v3

This call still risks a runtime crash; earlier review already covered it.

🧹 Nitpick comments (5)
packages/unraid-shared/src/services/socket-config.service.spec.ts (3)

1-1: Restore spies between tests to avoid leakage

Add afterEach to restore mocks; this prevents any accidental spy bleed between tests and aligns with Vitest best practices.

-import { describe, it, expect, beforeEach, vi } from 'vitest';
+import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';

 beforeEach(() => {
   configService = new ConfigService();
   service = new SocketConfigService(configService);
 });
+
+afterEach(() => {
+  vi.restoreAllMocks();
+});

Also applies to: 10-13


71-75: Remove duplicate test for string numeric port

“should handle string ports correctly” duplicates “should return numeric port when configured.” Consider deleting the duplicate or consolidating via table-driven tests.

-        it('should handle string ports correctly', () => {
-            vi.spyOn(configService, 'get').mockReturnValue('8080');
-            
-            expect(service.getNumericPort()).toBe(8080);
-        });

Optionally, consolidate with it.each for multiple valid inputs.

Also applies to: 83-88


161-169: Parity check: omit port for ws when nginx port is 80?

You test omission for http@80; consider verifying the same behavior for ws if that’s the intended contract.

If desired, add:

+        it('should omit port for WS when nginx port is default (80)', () => {
+            vi.spyOn(configService, 'get').mockImplementation((key, defaultValue) => {
+                if (key === 'PORT') return '/var/run/unraid-api.sock';
+                if (key === 'store.emhttp.nginx.httpPort') return '80';
+                return defaultValue;
+            });
+            // Not on socket, no numeric PORT → fallback to nginx port 80
+            vi.spyOn(configService, 'get').mockImplementation((key, defaultValue) => {
+                if (key === 'PORT') return 'invalid-port';
+                if (key === 'store.emhttp.nginx.httpPort') return '80';
+                return defaultValue;
+            });
+            expect(service.getApiAddress('ws')).toBe('ws://127.0.0.1/graphql');
+        });

Confirm whether ws should mirror http’s default-port omission.

packages/unraid-shared/src/services/base-internal-client.service.ts (2)

86-92: Replace broad any casts with a typed overload or // @ts-expect-error

Casting to unknown as typeof fetch (and using any params) hides type issues and conflicts with the project’s rule favouring ts-expect-error for deliberate escapes (see learning in PR #1219).


160-167: Align defaultOptions for mutations in HTTP-only client

The WebSocket-enabled branch sets mutate.fetchPolicy = 'no-cache', but the HTTP-only branch omits it, leading to inconsistent behaviour.

-            defaultOptions: {
-                query: { fetchPolicy: 'no-cache' },
-            },
+            defaultOptions: {
+                query:  { fetchPolicy: 'no-cache' },
+                mutate: { fetchPolicy: 'no-cache' },
+            },
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between b8c6a51 and 9a277b9.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (12)
  • api/src/unraid-api/shared/internal-graphql-client.factory.spec.ts (1 hunks)
  • api/src/unraid-api/shared/internal-graphql-client.factory.ts (1 hunks)
  • packages/unraid-shared/package.json (3 hunks)
  • packages/unraid-shared/src/jsonforms/__tests__/settings.test.ts (1 hunks)
  • packages/unraid-shared/src/services/__tests__/config-file.test.ts (1 hunks)
  • packages/unraid-shared/src/services/base-internal-client.service.spec.ts (1 hunks)
  • packages/unraid-shared/src/services/base-internal-client.service.ts (1 hunks)
  • packages/unraid-shared/src/services/socket-config.service.spec.ts (1 hunks)
  • packages/unraid-shared/src/services/socket-config.service.ts (1 hunks)
  • packages/unraid-shared/src/util/__tests__/config-definition.test.ts (1 hunks)
  • packages/unraid-shared/src/util/__tests__/config-file-handler.test.ts (1 hunks)
  • packages/unraid-shared/src/util/__tests__/key-order.test.ts (1 hunks)
✅ Files skipped from review due to trivial changes (5)
  • packages/unraid-shared/src/services/tests/config-file.test.ts
  • packages/unraid-shared/src/util/tests/config-definition.test.ts
  • packages/unraid-shared/src/jsonforms/tests/settings.test.ts
  • packages/unraid-shared/src/util/tests/key-order.test.ts
  • packages/unraid-shared/src/util/tests/config-file-handler.test.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • packages/unraid-shared/package.json
  • api/src/unraid-api/shared/internal-graphql-client.factory.spec.ts
  • packages/unraid-shared/src/services/socket-config.service.ts
  • api/src/unraid-api/shared/internal-graphql-client.factory.ts
  • packages/unraid-shared/src/services/base-internal-client.service.spec.ts
🧰 Additional context used
🧠 Learnings (45)
📓 Common learnings
Learnt from: pujitm
PR: unraid/api#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: unraid/api#982
File: web/helpers/apollo-cache/index.ts:140-145
Timestamp: 2024-12-13T17:26:55.220Z
Learning: In Apollo Client, the `cache.updateQuery` method can be used on the `InMemoryCache` instance to update queries, even when not using an `ApolloClient` instance directly.
Learnt from: pujitm
PR: unraid/api#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: unraid/api#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.
📚 Learning: 2025-07-28T15:34:24.715Z
Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-28T15:34:24.715Z
Learning: Applies to web/__test__/**/*.{test,spec}.{ts,js} : Mock external dependencies and services in web/__test__

Applied to files:

  • packages/unraid-shared/src/services/socket-config.service.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/store/**/*.ts : Mock external dependencies appropriately in store tests

Applied to files:

  • packages/unraid-shared/src/services/socket-config.service.spec.ts
  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/{components,store,mocks}/**/*.ts : Mock external dependencies and services in tests

Applied to files:

  • packages/unraid-shared/src/services/socket-config.service.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/store/**/*.ts : Verify state changes by updating the store in store tests

Applied to files:

  • packages/unraid-shared/src/services/socket-config.service.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/store/**/*.ts : Test async operations completely in store tests

Applied to files:

  • packages/unraid-shared/src/services/socket-config.service.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/store/**/*.ts : Verify proper error handling in store tests

Applied to files:

  • packages/unraid-shared/src/services/socket-config.service.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/store/**/*.ts : Test getter dependencies are properly mocked in store tests

Applied to files:

  • packages/unraid-shared/src/services/socket-config.service.spec.ts
📚 Learning: 2025-07-28T15:34:24.715Z
Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-28T15:34:24.715Z
Learning: Applies to web/__test__/**/*.{test,spec}.{ts,js} : Test component behavior and output, not implementation details in web/__test__

Applied to files:

  • packages/unraid-shared/src/services/socket-config.service.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/store/**/*.ts : Verify state changes after actions in store tests

Applied to files:

  • packages/unraid-shared/src/services/socket-config.service.spec.ts
📚 Learning: 2025-07-28T15:34:24.715Z
Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-28T15:34:24.715Z
Learning: Applies to web/__test__/**/*store*.{test,spec}.{ts,js} : Use factory functions for module mocks to avoid hoisting issues in store tests

Applied to files:

  • packages/unraid-shared/src/services/socket-config.service.spec.ts
📚 Learning: 2025-07-28T15:34:24.715Z
Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-28T15:34:24.715Z
Learning: Applies to api/**/*.{test,spec}.{ts,js} : Test suite is VITEST, do not use jest in the API package

Applied to files:

  • packages/unraid-shared/src/services/socket-config.service.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/{components,store,mocks}/**/*.ts : Specify return values for component methods with `vi.spyOn()`

Applied to files:

  • packages/unraid-shared/src/services/socket-config.service.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/{components,store,mocks}/**/*.ts : Remember that `vi.mock()` calls are hoisted

Applied to files:

  • packages/unraid-shared/src/services/socket-config.service.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/mocks/**/*.ts : Frequently used mocks are stored under `web/test/mocks`

Applied to files:

  • packages/unraid-shared/src/services/socket-config.service.spec.ts
📚 Learning: 2025-07-21T14:00:06.258Z
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/**/*.{test,spec}.{js,ts,tsx} : Use Vitest as the test suite; do not use Jest

Applied to files:

  • packages/unraid-shared/src/services/socket-config.service.spec.ts
📚 Learning: 2025-07-21T14:01:04.139Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-07-21T14:01:04.139Z
Learning: Applies to web/__test__/{components,store,mocks}/**/*.ts : Use `vi.mock()` for module-level mocks

Applied to files:

  • packages/unraid-shared/src/services/socket-config.service.spec.ts
📚 Learning: 2024-11-05T14:49:07.308Z
Learnt from: elibosley
PR: unraid/api#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-shared/src/services/base-internal-client.service.ts
📚 Learning: 2024-11-15T16:22:03.485Z
Learnt from: elibosley
PR: unraid/api#942
File: api/src/unraid-api/auth/api-key.service.ts:176-188
Timestamp: 2024-11-15T16:22:03.485Z
Learning: Atomic writes are not required for the `saveApiKey` method in `ApiKeyService` (`api/src/unraid-api/auth/api-key.service.ts`) unless specifically needed.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2024-11-06T20:59:25.809Z
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.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-03-12T13:48:14.850Z
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.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2024-11-04T20:44:46.432Z
Learnt from: mdatelle
PR: unraid/api#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.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-03-27T13:34:53.438Z
Learnt from: pujitm
PR: unraid/api#1252
File: api/src/environment.ts:56-56
Timestamp: 2025-03-27T13:34:53.438Z
Learning: For critical components in the Unraid API, such as retrieving version information from package.json, failing fast (allowing crashes) is preferred over graceful degradation with fallback values.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-03-10T18:43:13.016Z
Learnt from: mdatelle
PR: unraid/api#1219
File: api/src/unraid-api/types/fastify.ts:25-30
Timestamp: 2025-03-10T18:43:13.016Z
Learning: In the Unraid API, cookies should be marked as optional (with `?`) in the FastifyRequest type definition since the cookies may not always be present, which is consistent with the authentication flow throughout the codebase.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-01-15T21:34:00.006Z
Learnt from: pujitm
PR: unraid/api#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.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-04-07T14:34:47.255Z
Learnt from: elibosley
PR: unraid/api#1334
File: api/src/unraid-api/graph/resolvers/docker/docker-event.service.ts:63-66
Timestamp: 2025-04-07T14:34:47.255Z
Learning: In DockerEventService, the chokidar file watcher is configured with `ignoreInitial: false` to ensure that existing files (like the Docker socket) are detected and processed at application startup, not just when files change after the watcher is initialized.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-01-30T19:56:42.055Z
Learnt from: pujitm
PR: unraid/api#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.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-07-22T13:29:32.175Z
Learnt from: elibosley
PR: unraid/api#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.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2024-11-27T15:30:02.252Z
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.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-01-28T16:39:50.278Z
Learnt from: elibosley
PR: unraid/api#1078
File: api/src/unraid-api/unraid-file-modifier/modifications/sso.modification.ts:35-36
Timestamp: 2025-01-28T16:39:50.278Z
Learning: When reviewing TypeScript files containing PHP code snippets intended for file modifications (like in `SSOFileModification`), PHP functions that appear undefined should not be flagged as issues since they will be available in the target PHP environment where the code is injected.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-07-21T14:00:25.140Z
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

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2024-12-13T17:28:15.384Z
Learnt from: pujitm
PR: unraid/api#982
File: web/helpers/apollo-cache/index.ts:142-142
Timestamp: 2024-12-13T17:28:15.384Z
Learning: In `web/helpers/apollo-cache/index.ts`, it's acceptable to use `structuredClone` since the project targets Node.js v20 and browsers where `structuredClone` is supported.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2024-12-11T20:39:02.511Z
Learnt from: pujitm
PR: unraid/api#978
File: web/helpers/apollo-cache/index.ts:134-139
Timestamp: 2024-12-11T20:39:02.511Z
Learning: In `web/helpers/apollo-cache/index.ts`, the `cache.readQuery` method returns `null` when it fails to read from the cache, rather than throwing an exception.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2024-12-11T17:20:09.604Z
Learnt from: pujitm
PR: unraid/api#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.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-03-07T19:29:12.540Z
Learnt from: pujitm
PR: unraid/api#1219
File: api/src/unraid-api/main.ts:18-25
Timestamp: 2025-03-07T19:29:12.540Z
Learning: When handling type mismatches between dependencies in TypeScript (such as between NestJS and Fastify), prefer using `ts-expect-error` with a detailed explanation comment instead of `as any` type assertions. This better documents the reason for the type bypass and makes the issue more visible for future maintenance.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-07-28T15:34:24.715Z
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)

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-02-06T17:25:45.397Z
Learnt from: mdatelle
PR: unraid/api#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:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-03-07T17:36:52.790Z
Learnt from: mdatelle
PR: unraid/api#1219
File: api/src/unraid-api/main.ts:57-63
Timestamp: 2025-03-07T17:36:52.790Z
Learning: The CORS configuration in the unraid API is intentionally set to allow all origins (`origin: true`). This was a deliberate architectural decision when removing the previous custom CORS implementation and implementing helmet security headers instead.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2024-11-15T16:22:12.460Z
Learnt from: elibosley
PR: unraid/api#942
File: api/src/unraid-api/auth/auth.module.ts:94-96
Timestamp: 2024-11-15T16:22:12.460Z
Learning: In `CookieService.defaultOpts()` in `api/src/unraid-api/auth/cookie.service.ts`, use `sameSite: 'lax'` for session cookie configuration.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-07-28T15:34:24.715Z
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

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-07-21T14:00:06.258Z
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

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-05-08T19:28:37.034Z
Learnt from: elibosley
PR: unraid/api#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.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-03-04T14:55:00.903Z
Learnt from: elibosley
PR: unraid/api#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:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-03-07T17:35:50.406Z
Learnt from: mdatelle
PR: unraid/api#1219
File: api/src/unraid-api/main.ts:32-55
Timestamp: 2025-03-07T17:35:50.406Z
Learning: Helmet security configuration in the Unraid API is intentionally relaxed (with disabled CSP, CORS policies, and HSTS) to maintain compatibility with existing Unraid plugins. Stricter security settings might break current plugin functionality.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
📚 Learning: 2025-05-23T21:59:29.632Z
Learnt from: elibosley
PR: unraid/api#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.

Applied to files:

  • packages/unraid-shared/src/services/base-internal-client.service.ts
🔇 Additional comments (1)
packages/unraid-shared/src/services/socket-config.service.spec.ts (1)

1-280: Overall: Solid coverage and clear scenarios

Good use of Vitest, thorough edge cases, and integration-like scenarios. After addressing the minor fixes above, this suite will be very robust.

@unraid unraid deleted a comment from claude bot Aug 9, 2025
@unraid unraid deleted a comment from claude bot Aug 9, 2025
@elibosley elibosley force-pushed the fix/local-fetching-using-sockets branch from 9a277b9 to 44e5b92 Compare August 9, 2025 00:08
- Updated BaseInternalClientService to accept SocketConfigService as a constructor parameter, enhancing socket configuration management.
- Modified unit tests to create and inject SocketConfigService instances, ensuring proper initialization and functionality.
- Adjusted test cases to validate the use of injected SocketConfigService, improving test coverage and reliability.
- Introduced a new test suite for WebSocket connections over Unix sockets, validating connection establishment, message exchange, and error handling.
- Implemented tests for multiple concurrent connections and ensured compatibility with the expected URI formats.
- Enhanced cleanup procedures to manage socket files and connections effectively after tests.
- Added afterEach hook to restore all mocks and spies after each test in SocketConfigService test suite.
- Improved test reliability by ensuring a clean state for each test execution.
- Enhanced the afterAll and individual test cleanup processes to ensure all WebSocket clients are closed gracefully.
- Added error handling for socket file cleanup to prevent potential issues during test execution.
- Updated test cases to reflect the new cleanup logic, improving test reliability and resource management.
- Improved cleanup logic in the afterAll hook to ensure all WebSocket clients are closed properly using promises.
- Updated connection tests to use `once` for event listeners, preventing potential memory leaks.
- Enhanced error handling in tests for non-existent Unix sockets, ensuring proper rejection of invalid connections.
- Streamlined test assertions for better clarity and reliability.
…pm-lock.yaml

- Updated the version of @apollo/client from 3.13.8 to 3.13.9 in both package.json and pnpm-lock.yaml to ensure compatibility with the latest features and fixes.
- Removed outdated entries related to the previous version from pnpm-lock.yaml for cleaner dependency management.
- Included `graphql-ws` version 6.0.6 in `package.json` to support WebSocket-based GraphQL subscriptions and enhance real-time capabilities.
…GraphQL client structure

- Deleted the BaseInternalClientService and its associated test file to streamline the codebase.
- Updated the SocketConfigService documentation to clarify its usage across different services.
- Refactored the GraphQL client structure, introducing a new ClientConsumerService that utilizes a factory pattern for better client management and testing.
- Added comprehensive tests for the new ClientConsumerService, ensuring proper API key handling, client lifecycle management, and configuration scenarios.
- Refactored the CliInternalClientService to create clients using a lazy-loaded getApiKey function instead of an immediate API key.
- Updated the InternalGraphQLClientFactory to require a getApiKey function for client creation, enhancing flexibility and security.
- Adjusted related tests to verify the new client creation logic and error handling for API key retrieval.
- Improved SocketConfigService to validate the nginx port configuration, ensuring robustness in service setup.
- Modified the exports in the form component index file to include explicit file extensions for better clarity and compatibility.
- Ensured all component exports now point to their respective index.js files.
…y retrieval

- Enhanced the test for InternalClientService to confirm that the API key is fetched lazily through the new getApiKey function.
- Added assertions to ensure the getApiKey function works correctly and that the API key service is called as expected.
- Improved clarity and reliability of the test suite by validating the client creation process with the updated API key handling.
…aphQLClientFactory interface

- Updated import statements across multiple files to simplify the import paths for the INTERNAL_CLIENT_SERVICE_TOKEN and InternalGraphQLClientFactory.
- Introduced a new InternalGraphQLClientFactory interface in the shared package to standardize client creation across services.
- Removed redundant interface definitions from internal client files, enhancing code clarity and maintainability.
@pujitm pujitm force-pushed the fix/local-fetching-using-sockets branch from 5c83151 to ee4394e Compare August 13, 2025 18:50
@github-actions
Copy link
Contributor

🚀 Storybook has been deployed to staging: https://unraid-ui-storybook-staging.unraid-workers.workers.dev

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: 2

♻️ Duplicate comments (2)
packages/unraid-shared/src/services/socket-config.service.spec.ts (1)

52-65: “PORT not set” scenario now correctly exercises the default

This fixes the earlier issue by returning the provided default only for PORT. Thanks for addressing it.

packages/unraid-shared/src/services/internal-graphql-client-usage.spec.ts (1)

162-167: Avoid asserting exact error messages; use rejects.toThrow() without arguments

Per the testing guidelines, don’t assert exact error strings to keep tests resilient.

Apply this diff:

-            await expect(service.getClient()).rejects.toThrow('API key error');
+            await expect(service.getClient()).rejects.toThrow();
🧹 Nitpick comments (11)
packages/unraid-shared/src/services/socket-config.service.spec.ts (5)

15-18: Remove obvious comment; keep the cleanup

The comment explains an obvious practice; trim it per guidelines to reduce noise.

     afterEach(() => {
-        // Clean up all spies and mocks after each test
         vi.restoreAllMocks();
     });

21-28: Tighten the spy to the expected key for robustness

Currently the spy returns '8080' for any key, which can mask accidental lookups of the wrong key. Restrict it to the nginx key and fall back otherwise.

-            vi.spyOn(configService, 'get').mockReturnValue('8080');
+            vi.spyOn(configService, 'get').mockImplementation((key, defaultValue) =>
+                key === 'store.emhttp.nginx.httpPort' ? '8080' : defaultValue
+            );

83-143: Reduce repetition in getNumericPort tests with parameterized cases

Multiple cases differ only by the input value. Parameterize to make intent clearer and cut duplication.

         it('should handle string ports correctly', () => {
-            vi.spyOn(configService, 'get').mockReturnValue('8080');
-            
-            expect(service.getNumericPort()).toBe(8080);
-        });
-
-        it('should return undefined for non-numeric port values', () => {
-            vi.spyOn(configService, 'get').mockReturnValue('invalid-port');
-            
-            expect(service.getNumericPort()).toBeUndefined();
-        });
-
-        it('should return undefined for empty string port', () => {
-            vi.spyOn(configService, 'get').mockReturnValue('');
-            
-            expect(service.getNumericPort()).toBeUndefined();
-        });
-
-        it('should return undefined for port with mixed characters', () => {
-            vi.spyOn(configService, 'get').mockReturnValue('3000abc');
-            
-            expect(service.getNumericPort()).toBeUndefined();
-        });
-
-        it('should return undefined for port 0', () => {
-            vi.spyOn(configService, 'get').mockReturnValue('0');
-            
-            expect(service.getNumericPort()).toBeUndefined();
-        });
-
-        it('should return undefined for negative port', () => {
-            vi.spyOn(configService, 'get').mockReturnValue('-1');
-            
-            expect(service.getNumericPort()).toBeUndefined();
-        });
-
-        it('should return undefined for port above 65535', () => {
-            vi.spyOn(configService, 'get').mockReturnValue('70000');
-            
-            expect(service.getNumericPort()).toBeUndefined();
-        });
-
-        it('should return valid port 65535', () => {
-            vi.spyOn(configService, 'get').mockReturnValue('65535');
-            
-            expect(service.getNumericPort()).toBe(65535);
-        });
+            vi.spyOn(configService, 'get').mockReturnValue('8080');
+            expect(service.getNumericPort()).toBe(8080);
+        });
+
+        it.each([
+            'invalid-port',
+            '',
+            '3000abc',
+            '0',
+            '-1',
+            '70000',
+        ])('should return undefined for invalid port "%s"', (value) => {
+            vi.spyOn(configService, 'get').mockReturnValue(value);
+            expect(service.getNumericPort()).toBeUndefined();
+        });
+
+        it.each([
+            ['8080', 8080],
+            ['65535', 65535],
+        ])('should parse valid string ports: %s -> %d', (value, expected) => {
+            vi.spyOn(configService, 'get').mockReturnValue(value);
+            expect(service.getNumericPort()).toBe(expected);
+        });

145-173: Add WS + nginx fallback case for getApiAddress

You cover HTTP + nginx fallback but not WS + nginx fallback. Adding it ensures parity across protocols.

         it('should use nginx port when no numeric port configured', () => {
             vi.spyOn(configService, 'get').mockImplementation((key, defaultValue) => {
                 if (key === 'PORT') return '/var/run/unraid-api.sock';
                 if (key === 'store.emhttp.nginx.httpPort') return '8080';
                 return defaultValue;
             });
             
             expect(service.getApiAddress('http')).toBe('http://127.0.0.1:8080/graphql');
         });
+
+        it('should return WS address via nginx port when no numeric port configured', () => {
+            vi.spyOn(configService, 'get').mockImplementation((key, defaultValue) => {
+                if (key === 'PORT') return '/var/run/unraid-api.sock';
+                if (key === 'store.emhttp.nginx.httpPort') return '8080';
+                return defaultValue;
+            });
+            expect(service.getApiAddress('ws')).toBe('ws://127.0.0.1:8080/graphql');
+        });

228-261: Remove duplicated WebSocket TCP test

“should return ws:// URI when running on TCP port” and “should use TCP port for WebSocket when running on TCP port” assert the same observable behavior. Keep one to avoid redundancy.

-        it('should use TCP port for WebSocket when running on TCP port', () => {
-            // Configure to use TCP port instead of Unix socket
-            // This naturally causes isRunningOnSocket() to return false
-            vi.spyOn(configService, 'get').mockImplementation((key, defaultValue) => {
-                if (key === 'PORT') return '3001'; // TCP port, not a socket
-                if (key === 'store.emhttp.nginx.httpPort') return '8080';
-                return defaultValue;
-            });
-            
-            const uri = service.getWebSocketUri(true);
-            
-            // When PORT is numeric, it uses that port directly for WebSocket
-            expect(uri).toBe('ws://127.0.0.1:3001/graphql');
-        });
packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts (2)

43-47: Avoid leaking a client when clearClient() is called mid-creation

If clearClient() is invoked while creation is in-flight, this branch won’t set the cached client (good), but the created client is still returned to the original caller and the service drops its reference. Consider proactively stopping the now-orphaned client to avoid resource leaks.

Apply this diff to safely dispose the orphaned client:

-            if (this.clientCreationPromise === creationPromise) {
-                this.client = client;
-            }
+            if (this.clientCreationPromise === creationPromise) {
+                this.client = client;
+            } else {
+                // clearClient() occurred during creation; prevent leaking the orphaned client
+                client.stop?.();
+            }

68-73: Clear in-flight creation on clearClient() is good; consider logging for observability

Clearing both the cached client and the in-flight promise is correct. Consider logging when an in-flight creation is cleared to aid debugging of races.

packages/unraid-shared/src/services/internal-graphql-client-usage.spec.ts (3)

68-72: Don’t mutate constructor args; set defaults immutably

Mutating the passed options object can surprise callers. Prefer creating a new options object with defaults.

Apply this diff:

-        // Use default origin if not provided
-        if (!this.options.origin) {
-            this.options.origin = 'http://localhost';
-        }
+        // Use default origin if not provided
+        this.options = { origin: 'http://localhost', ...this.options };

16-22: Remove unused undici mock to reduce noise

This spec’s mock factory doesn’t use undici; the mock adds test noise without benefit.

Apply this diff:

-// Mock undici
-vi.mock('undici', () => ({
-    Agent: vi.fn(() => ({
-        connect: { socketPath: '/test/socket.sock' },
-    })),
-    fetch: vi.fn(() => Promise.resolve({ ok: true })),
-}));

41-53: Prefer Partial/satisfies over any for mock client typing

Use Partial<ApolloClient> with satisfies to avoid any.

Apply this diff:

-        const mockClient = {
+        const mockClient = {
             query: vi.fn(),
             mutate: vi.fn(),
             stop: vi.fn(),
             subscribe: vi.fn(),
             watchQuery: vi.fn(),
             readQuery: vi.fn(),
             writeQuery: vi.fn(),
             cache: {
                 reset: vi.fn(),
             },
-        } as any;
+        } satisfies Partial<ApolloClient<any>> as unknown as ApolloClient<any>;
api/src/unraid-api/shared/internal-graphql-client.factory.spec.ts (1)

158-170: Test title vs setup mismatch; ensure wsUri is provided when asserting “even when wsUri is provided”

The test name claims wsUri is provided but the mock returns undefined. Either rename the test or provide a wsUri while keeping enableSubscriptions: false.

Apply this diff to actually provide a wsUri:

-            vi.mocked(socketConfig.getWebSocketUri).mockReturnValue(undefined); // Subscriptions disabled
+            vi.mocked(socketConfig.getWebSocketUri).mockReturnValue('ws://127.0.0.1:3001/graphql');
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 5c83151 and ee4394e.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (30)
  • api/dev/configs/api.json (1 hunks)
  • api/package.json (1 hunks)
  • api/src/unraid-api/cli/cli.module.spec.ts (1 hunks)
  • api/src/unraid-api/cli/internal-client.service.spec.ts (1 hunks)
  • api/src/unraid-api/cli/internal-client.service.ts (2 hunks)
  • api/src/unraid-api/plugin/global-deps.module.ts (3 hunks)
  • api/src/unraid-api/shared/internal-graphql-client.factory.spec.ts (1 hunks)
  • api/src/unraid-api/shared/internal-graphql-client.factory.ts (1 hunks)
  • packages/unraid-api-plugin-connect/package.json (2 hunks)
  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.spec.ts (1 hunks)
  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts (1 hunks)
  • packages/unraid-api-plugin-connect/tsconfig.json (1 hunks)
  • packages/unraid-api-plugin-generator/tsconfig.build.json (1 hunks)
  • packages/unraid-api-plugin-generator/tsconfig.json (1 hunks)
  • packages/unraid-api-plugin-health/tsconfig.json (1 hunks)
  • packages/unraid-shared/package.json (3 hunks)
  • packages/unraid-shared/src/index.ts (1 hunks)
  • packages/unraid-shared/src/jsonforms/__tests__/settings.test.ts (1 hunks)
  • packages/unraid-shared/src/services/__tests__/config-file.test.ts (1 hunks)
  • packages/unraid-shared/src/services/internal-graphql-client-usage.spec.ts (1 hunks)
  • packages/unraid-shared/src/services/socket-config.service.spec.ts (1 hunks)
  • packages/unraid-shared/src/services/socket-config.service.ts (1 hunks)
  • packages/unraid-shared/src/services/ws-unix-socket-test.spec.ts (1 hunks)
  • packages/unraid-shared/src/tokens.ts (1 hunks)
  • packages/unraid-shared/src/types/internal-graphql-client.factory.ts (1 hunks)
  • packages/unraid-shared/src/util/__tests__/config-definition.test.ts (1 hunks)
  • packages/unraid-shared/src/util/__tests__/config-file-handler.test.ts (1 hunks)
  • packages/unraid-shared/src/util/__tests__/key-order.test.ts (1 hunks)
  • unraid-ui/src/components/common/index.ts (1 hunks)
  • unraid-ui/src/components/form/index.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • packages/unraid-shared/src/util/tests/config-file-handler.test.ts
🚧 Files skipped from review as they are similar to previous changes (24)
  • packages/unraid-shared/src/tokens.ts
  • packages/unraid-shared/src/util/tests/config-definition.test.ts
  • api/package.json
  • packages/unraid-shared/src/index.ts
  • unraid-ui/src/components/common/index.ts
  • packages/unraid-api-plugin-generator/tsconfig.json
  • packages/unraid-api-plugin-generator/tsconfig.build.json
  • packages/unraid-shared/src/jsonforms/tests/settings.test.ts
  • api/dev/configs/api.json
  • packages/unraid-shared/src/types/internal-graphql-client.factory.ts
  • packages/unraid-api-plugin-health/tsconfig.json
  • api/src/unraid-api/cli/cli.module.spec.ts
  • packages/unraid-api-plugin-connect/package.json
  • packages/unraid-shared/src/util/tests/key-order.test.ts
  • packages/unraid-shared/src/services/socket-config.service.ts
  • packages/unraid-shared/src/services/ws-unix-socket-test.spec.ts
  • api/src/unraid-api/shared/internal-graphql-client.factory.ts
  • packages/unraid-shared/src/services/tests/config-file.test.ts
  • packages/unraid-api-plugin-connect/tsconfig.json
  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.spec.ts
  • packages/unraid-shared/package.json
  • api/src/unraid-api/cli/internal-client.service.ts
  • unraid-ui/src/components/form/index.ts
  • api/src/unraid-api/plugin/global-deps.module.ts
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (CLAUDE.md)

TypeScript source files must use import specifiers with .js extensions for ESM compatibility

Files:

  • api/src/unraid-api/shared/internal-graphql-client.factory.spec.ts
  • packages/unraid-shared/src/services/internal-graphql-client-usage.spec.ts
  • api/src/unraid-api/cli/internal-client.service.spec.ts
  • packages/unraid-shared/src/services/socket-config.service.spec.ts
  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts
api/src/unraid-api/**

📄 CodeRabbit Inference Engine (CLAUDE.md)

Place new API source files in api/src/unraid-api/ rather than legacy locations

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

Files:

  • api/src/unraid-api/shared/internal-graphql-client.factory.spec.ts
  • api/src/unraid-api/cli/internal-client.service.spec.ts
api/**/*.{test,spec}.{ts,tsx,js,jsx}

📄 CodeRabbit Inference Engine (CLAUDE.md)

Prefer not to mock simple dependencies in API tests

Files:

  • api/src/unraid-api/shared/internal-graphql-client.factory.spec.ts
  • api/src/unraid-api/cli/internal-client.service.spec.ts
**/*.{test,spec}.{js,ts,jsx,tsx}

📄 CodeRabbit Inference Engine (CLAUDE.md)

**/*.{test,spec}.{js,ts,jsx,tsx}: General testing: use .rejects.toThrow() without arguments when asserting thrown errors
General testing: focus on behavior, not exact error message wording
General testing: avoid brittle tests tied to non-essential details (exact messages, log formats)
General testing: use mocks as nouns (objects), not verbs (behavioral overuse)

Files:

  • api/src/unraid-api/shared/internal-graphql-client.factory.spec.ts
  • packages/unraid-shared/src/services/internal-graphql-client-usage.spec.ts
  • api/src/unraid-api/cli/internal-client.service.spec.ts
  • packages/unraid-shared/src/services/socket-config.service.spec.ts
**/*.{ts,tsx,js,jsx,vue}

📄 CodeRabbit Inference Engine (CLAUDE.md)

Avoid unnecessary or obvious comments; only add comments when needed for clarity

Files:

  • api/src/unraid-api/shared/internal-graphql-client.factory.spec.ts
  • packages/unraid-shared/src/services/internal-graphql-client-usage.spec.ts
  • api/src/unraid-api/cli/internal-client.service.spec.ts
  • packages/unraid-shared/src/services/socket-config.service.spec.ts
  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts
api/**/*.{test,spec}.{js,jsx,ts,tsx}

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

api/**/*.{test,spec}.{js,jsx,ts,tsx}: Use Vitest for tests in the api; do not use Jest
Prefer not to mock simple dependencies in tests
For error testing, use .rejects.toThrow() without arguments; avoid asserting exact error messages unless the message format is the subject under test

Files:

  • api/src/unraid-api/shared/internal-graphql-client.factory.spec.ts
  • api/src/unraid-api/cli/internal-client.service.spec.ts
🧠 Learnings (23)
📚 Learning: 2025-08-11T15:10:28.150Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-08-11T15:10:28.150Z
Learning: Applies to {**/*.test.ts,**/__test__/{components,store}/**/*.ts} : Use factory functions in vi.mock calls to define mocks and avoid hoisting pitfalls

Applied to files:

  • api/src/unraid-api/shared/internal-graphql-client.factory.spec.ts
  • packages/unraid-shared/src/services/internal-graphql-client-usage.spec.ts
  • packages/unraid-shared/src/services/socket-config.service.spec.ts
📚 Learning: 2025-08-11T15:10:28.150Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-08-11T15:10:28.150Z
Learning: Applies to **/__test__/components/**/*.ts : Mock external dependencies and services in component tests (e.g., vi.mock for helper modules)

Applied to files:

  • api/src/unraid-api/shared/internal-graphql-client.factory.spec.ts
  • packages/unraid-shared/src/services/internal-graphql-client-usage.spec.ts
  • api/src/unraid-api/cli/internal-client.service.spec.ts
  • packages/unraid-shared/src/services/socket-config.service.spec.ts
📚 Learning: 2025-08-11T15:07:39.222Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/api-rules.mdc:0-0
Timestamp: 2025-08-11T15:07:39.222Z
Learning: Applies to api/**/*.{test,spec}.{js,jsx,ts,tsx} : For error testing, use `.rejects.toThrow()` without arguments; avoid asserting exact error messages unless the message format is the subject under test

Applied to files:

  • packages/unraid-shared/src/services/internal-graphql-client-usage.spec.ts
📚 Learning: 2025-08-11T15:10:28.150Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-08-11T15:10:28.150Z
Learning: Applies to {**/*.test.ts,**/__test__/{components,store}/**/*.ts} : Use .rejects.toThrow() without arguments when asserting that async functions throw; avoid checking exact error message strings unless the message format is explicitly under test

Applied to files:

  • packages/unraid-shared/src/services/internal-graphql-client-usage.spec.ts
📚 Learning: 2024-11-05T14:49:07.308Z
Learnt from: elibosley
PR: unraid/api#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-shared/src/services/internal-graphql-client-usage.spec.ts
📚 Learning: 2025-08-11T15:06:30.877Z
Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-11T15:06:30.877Z
Learning: Applies to **/*.{test,spec}.{js,ts,jsx,tsx} : General testing: use .rejects.toThrow() without arguments when asserting thrown errors

Applied to files:

  • packages/unraid-shared/src/services/internal-graphql-client-usage.spec.ts
📚 Learning: 2025-03-07T19:29:12.540Z
Learnt from: pujitm
PR: unraid/api#1219
File: api/src/unraid-api/main.ts:18-25
Timestamp: 2025-03-07T19:29:12.540Z
Learning: When handling type mismatches between dependencies in TypeScript (such as between NestJS and Fastify), prefer using `ts-expect-error` with a detailed explanation comment instead of `as any` type assertions. This better documents the reason for the type bypass and makes the issue more visible for future maintenance.

Applied to files:

  • packages/unraid-shared/src/services/internal-graphql-client-usage.spec.ts
📚 Learning: 2025-08-11T15:06:30.877Z
Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-11T15:06:30.877Z
Learning: Applies to **/*.{test,spec}.{js,ts,jsx,tsx} : General testing: focus on behavior, not exact error message wording

Applied to files:

  • packages/unraid-shared/src/services/internal-graphql-client-usage.spec.ts
📚 Learning: 2025-08-11T15:06:30.877Z
Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-11T15:06:30.877Z
Learning: Applies to **/*.{test,spec}.{js,ts,jsx,tsx} : General testing: avoid brittle tests tied to non-essential details (exact messages, log formats)

Applied to files:

  • packages/unraid-shared/src/services/internal-graphql-client-usage.spec.ts
📚 Learning: 2025-08-11T15:06:30.877Z
Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-11T15:06:30.877Z
Learning: Applies to web/__test__/**/*.{test,spec}.{ts,js,tsx,jsx} : Web (Vue) component tests should mock external dependencies and services

Applied to files:

  • packages/unraid-shared/src/services/internal-graphql-client-usage.spec.ts
  • packages/unraid-shared/src/services/socket-config.service.spec.ts
📚 Learning: 2025-08-11T15:10:28.150Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-08-11T15:10:28.150Z
Learning: Applies to **/__test__/store/**/*.ts : In store tests, mock external dependencies used by the store and verify interactions with those mocks

Applied to files:

  • packages/unraid-shared/src/services/internal-graphql-client-usage.spec.ts
  • packages/unraid-shared/src/services/socket-config.service.spec.ts
📚 Learning: 2025-08-11T15:07:39.222Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/api-rules.mdc:0-0
Timestamp: 2025-08-11T15:07:39.222Z
Learning: Applies to api/**/*.{test,spec}.{js,jsx,ts,tsx} : Use Vitest for tests in the api; do not use Jest

Applied to files:

  • api/src/unraid-api/cli/internal-client.service.spec.ts
  • packages/unraid-shared/src/services/socket-config.service.spec.ts
📚 Learning: 2025-08-11T15:06:30.877Z
Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-11T15:06:30.877Z
Learning: Applies to api/**/*.{test,spec}.{ts,tsx,js,jsx} : Prefer not to mock simple dependencies in API tests

Applied to files:

  • packages/unraid-shared/src/services/socket-config.service.spec.ts
📚 Learning: 2025-08-11T15:10:28.150Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-08-11T15:10:28.150Z
Learning: Applies to {**/*.test.ts,**/__test__/{components,store}/**/*.ts} : Use vi.spyOn() to specify return values or behavior of methods under test

Applied to files:

  • packages/unraid-shared/src/services/socket-config.service.spec.ts
📚 Learning: 2025-08-11T15:10:28.150Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-08-11T15:10:28.150Z
Learning: Applies to {**/*.test.ts,**/__test__/{components,store}/**/*.ts} : Reset/clear mocks between tests using vi.clearAllMocks() (and vi.resetAllMocks() when appropriate) to ensure isolation

Applied to files:

  • packages/unraid-shared/src/services/socket-config.service.spec.ts
📚 Learning: 2025-08-11T15:10:28.150Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-08-11T15:10:28.150Z
Learning: This Nuxt app is tested with vitest outside the Nuxt runtime; structure tests accordingly

Applied to files:

  • packages/unraid-shared/src/services/socket-config.service.spec.ts
📚 Learning: 2025-08-11T15:10:28.150Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-08-11T15:10:28.150Z
Learning: Applies to {**/*.test.ts,**/__test__/{components,store}/**/*.ts} : Remember that vi.mock calls are hoisted; avoid mixing mock declarations and module mocks incorrectly

Applied to files:

  • packages/unraid-shared/src/services/socket-config.service.spec.ts
📚 Learning: 2025-08-11T15:10:28.150Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-08-11T15:10:28.150Z
Learning: Applies to {**/*.test.ts,**/__test__/{components,store}/**/*.ts} : Place module mock declarations (vi.mock) at the top level of the test file to avoid hoisting issues

Applied to files:

  • packages/unraid-shared/src/services/socket-config.service.spec.ts
📚 Learning: 2025-08-11T15:06:30.877Z
Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-11T15:06:30.877Z
Learning: Applies to web/**/*.{test,spec}.{ts,js,tsx,jsx} : Store testing with Pinia: place all mock declarations at top level; use factory functions for module mocks; clear mocks between tests

Applied to files:

  • packages/unraid-shared/src/services/socket-config.service.spec.ts
📚 Learning: 2025-08-11T15:10:28.150Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-08-11T15:10:28.150Z
Learning: Applies to **/__test__/mocks/**/*.ts : Frequently used mocks can also be placed under __test__/mocks

Applied to files:

  • packages/unraid-shared/src/services/socket-config.service.spec.ts
📚 Learning: 2025-08-09T01:03:29.676Z
Learnt from: elibosley
PR: unraid/api#1575
File: packages/unraid-shared/src/services/socket-config.service.spec.ts:10-13
Timestamp: 2025-08-09T01:03:29.676Z
Learning: Vitest is used for all testing across all repositories in the unraid organization, not Jest. Always use `vi` for mocking utilities, not `jest`.

Applied to files:

  • packages/unraid-shared/src/services/socket-config.service.spec.ts
📚 Learning: 2025-08-11T15:06:30.877Z
Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-11T15:06:30.877Z
Learning: Applies to api/**/jest*.{js,ts,cjs,mjs,json} : In the API package, use Vitest for tests and do not add Jest configuration

Applied to files:

  • packages/unraid-shared/src/services/socket-config.service.spec.ts
📚 Learning: 2025-08-09T01:06:39.314Z
Learnt from: elibosley
PR: unraid/api#1575
File: packages/unraid-shared/src/services/base-internal-client.service.ts:126-132
Timestamp: 2025-08-09T01:06:39.314Z
Learning: The `ws` npm package (version 8.x) natively supports `ws+unix://` URIs for Unix socket connections. The library's URL parser explicitly recognizes the `ws+unix:` protocol alongside `ws:`, `wss:`, `http:`, and `https:` protocols, allowing direct connection to Unix domain sockets using URIs like `ws+unix:///path/to/socket:/endpoint`.

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts
🧬 Code Graph Analysis (5)
api/src/unraid-api/shared/internal-graphql-client.factory.spec.ts (2)
packages/unraid-shared/src/index.ts (2)
  • InternalGraphQLClientFactory (6-6)
  • SocketConfigService (2-2)
web/helpers/create-apollo-client.ts (1)
  • client (93-96)
packages/unraid-shared/src/services/internal-graphql-client-usage.spec.ts (2)
packages/unraid-shared/src/index.ts (1)
  • SocketConfigService (2-2)
web/helpers/create-apollo-client.ts (1)
  • client (93-96)
api/src/unraid-api/cli/internal-client.service.spec.ts (5)
packages/unraid-shared/src/index.ts (1)
  • InternalGraphQLClientFactory (6-6)
packages/unraid-shared/src/types/internal-graphql-client.factory.ts (1)
  • InternalGraphQLClientFactory (7-13)
packages/unraid-shared/src/tokens.ts (1)
  • INTERNAL_CLIENT_SERVICE_TOKEN (5-5)
web/helpers/create-apollo-client.ts (1)
  • client (93-96)
api/src/unraid-api/cli/log.service.ts (1)
  • error (65-69)
packages/unraid-shared/src/services/socket-config.service.spec.ts (1)
packages/unraid-shared/src/index.ts (1)
  • SocketConfigService (2-2)
packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts (4)
packages/unraid-shared/src/tokens.ts (1)
  • INTERNAL_CLIENT_SERVICE_TOKEN (5-5)
packages/unraid-shared/src/index.ts (1)
  • InternalGraphQLClientFactory (6-6)
packages/unraid-shared/src/types/internal-graphql-client.factory.ts (1)
  • InternalGraphQLClientFactory (7-13)
web/helpers/create-apollo-client.ts (1)
  • client (93-96)
🔇 Additional comments (4)
packages/unraid-shared/src/services/socket-config.service.spec.ts (2)

1-5: ESM import specifier and Vitest hooks are correct

Good use of .js extension for ESM compatibility and proper import of afterEach.


219-246: No adapter needed—ws+unix:// URIs are natively supported

The internal‐client factory correctly passes the ws+unix://…:/graphql URI straight through to createClient({ url }) using the Node ws implementation. Per the ws 8.x URL parser (and confirmed by the ws-unix-socket-test.spec.ts integration tests), the library recognizes ws+unix://socketPath:/path as a Unix‐domain‐socket endpoint. No additional parsing or socketPath extraction is required.

• The factory’s subscription block:

const wsLink = new GraphQLWsLink(
  createClient({
    url: wsUri,
    webSocketImpl: WebSocket,})
);

• Under the hood, new WebSocket(ws+unix://…) (from ws v8.x) handles the Unix socket and path.

You can safely ignore the suggestion to add a custom adapter—everything already works as intended.

Likely an incorrect or invalid review comment.

packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts (1)

25-55: Solid DI + concurrency guard; caching logic looks correct

Using an in-flight promise to serialize client creation is correct and prevents duplicate clients under concurrent calls. Caching and finally-block cleanup are handled well.

api/src/unraid-api/shared/internal-graphql-client.factory.spec.ts (1)

4-4: No mismatched ApolloClient imports detected

Both the production factory and its spec file import from the identical path (@apollo/client/core/index.js), so there’s no dual-module issue to address.
• api/src/unraid-api/shared/internal-graphql-client.factory.ts:5
• api/src/unraid-api/shared/internal-graphql-client.factory.spec.ts:4

@github-actions
Copy link
Contributor

🚀 Storybook has been deployed to staging: https://unraid-ui-storybook-staging.unraid-workers.workers.dev

@github-actions
Copy link
Contributor

🚀 Storybook has been deployed to staging: https://unraid-ui-storybook-staging.unraid-workers.workers.dev

@github-actions
Copy link
Contributor

This plugin has been deployed to Cloudflare R2 and is available for testing.
Download it at this URL:

https://preview.dl.unraid.net/unraid-api/tag/PR1575/dynamix.unraid.net.plg

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

🧹 Nitpick comments (6)
api/src/unraid-api/cli/internal-client.service.ts (6)

4-4: Use a type-only import for Apollo types to avoid a runtime dependency

This file only uses ApolloClient and NormalizedCacheObject as types; import them as type-only to avoid emitting a runtime ESM import.

-import { ApolloClient, NormalizedCacheObject } from '@apollo/client/core/index.js';
+import type { ApolloClient, NormalizedCacheObject } from '@apollo/client/core/index.js';

58-61: Simplify creationPromise declaration; drop eslint-disable

You can use const for creationPromise and remove the eslint disable. The closure still captures the correct reference.

-        // Start creating the client with race condition protection
-        let creationPromise!: Promise<ApolloClient<NormalizedCacheObject>>;
-        // eslint-disable-next-line prefer-const
-        creationPromise = (async () => {
+        // Start creating the client with race condition protection
+        const creationPromise: Promise<ApolloClient<NormalizedCacheObject>> = (async () => {

67-73: Remove over-explanatory comments per repo guidelines

These comments are unnecessary and slightly confusing. The code is already guarded via the creatingClient equality checks.

-                // awaiting *before* checking this.creatingClient is important!
-                // by yielding to the event loop, it ensures
-                // `this.creatingClient = creationPromise;` is executed before the next check.
-
-                // This prevents race conditions where the client is assigned to the wrong instance.
-                // Only assign client if this creation is still current
+                // Only assign client if this creation is still current

54-55: Avoid unnecessary await when returning a promise

Returning the promise directly preserves the stack and avoids an extra microtask.

-            return await this.creatingClient;
+            return this.creatingClient;

88-88: Avoid unnecessary await when returning a promise

Return the promise directly.

-        return await creationPromise;
+        return creationPromise;

35-35: Improve error logging: pass stack/message instead of object

Nest Logger.error expects message and optional stack/context. Logging the raw error object can result in “[object Object]”. Include stack or message.

-            this.logger.error('Failed to get admin API key:', error);
+            this.logger.error(
+                'Failed to get admin API key',
+                error instanceof Error ? error.stack : String(error)
+            );
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between ee4394e and 2c94572.

📒 Files selected for processing (5)
  • api/src/unraid-api/cli/internal-client.service.spec.ts (1 hunks)
  • api/src/unraid-api/cli/internal-client.service.ts (2 hunks)
  • api/src/unraid-api/shared/internal-graphql-client.factory.spec.ts (1 hunks)
  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.spec.ts (1 hunks)
  • packages/unraid-shared/src/types/internal-graphql-client.factory.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • api/src/unraid-api/cli/internal-client.service.spec.ts
  • api/src/unraid-api/shared/internal-graphql-client.factory.spec.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (CLAUDE.md)

TypeScript source files must use import specifiers with .js extensions for ESM compatibility

Files:

  • api/src/unraid-api/cli/internal-client.service.ts
  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.spec.ts
  • packages/unraid-shared/src/types/internal-graphql-client.factory.ts
api/src/unraid-api/**

📄 CodeRabbit Inference Engine (CLAUDE.md)

Place new API source files in api/src/unraid-api/ rather than legacy locations

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

Files:

  • api/src/unraid-api/cli/internal-client.service.ts
**/*.{ts,tsx,js,jsx,vue}

📄 CodeRabbit Inference Engine (CLAUDE.md)

Avoid unnecessary or obvious comments; only add comments when needed for clarity

Files:

  • api/src/unraid-api/cli/internal-client.service.ts
  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.spec.ts
  • packages/unraid-shared/src/types/internal-graphql-client.factory.ts
**/*.{test,spec}.{js,ts,jsx,tsx}

📄 CodeRabbit Inference Engine (CLAUDE.md)

**/*.{test,spec}.{js,ts,jsx,tsx}: General testing: use .rejects.toThrow() without arguments when asserting thrown errors
General testing: focus on behavior, not exact error message wording
General testing: avoid brittle tests tied to non-essential details (exact messages, log formats)
General testing: use mocks as nouns (objects), not verbs (behavioral overuse)

Files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.spec.ts
🧠 Learnings (10)
📚 Learning: 2025-08-11T15:10:28.150Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-08-11T15:10:28.150Z
Learning: Applies to {**/*.test.ts,**/__test__/{components,store}/**/*.ts} : Use .rejects.toThrow() without arguments when asserting that async functions throw; avoid checking exact error message strings unless the message format is explicitly under test

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.spec.ts
📚 Learning: 2025-08-11T15:07:39.222Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/api-rules.mdc:0-0
Timestamp: 2025-08-11T15:07:39.222Z
Learning: Applies to api/**/*.{test,spec}.{js,jsx,ts,tsx} : For error testing, use `.rejects.toThrow()` without arguments; avoid asserting exact error messages unless the message format is the subject under test

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.spec.ts
📚 Learning: 2025-08-11T15:06:30.877Z
Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-11T15:06:30.877Z
Learning: Applies to **/*.{test,spec}.{js,ts,jsx,tsx} : General testing: use .rejects.toThrow() without arguments when asserting thrown errors

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.spec.ts
📚 Learning: 2025-08-11T15:06:30.877Z
Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-11T15:06:30.877Z
Learning: Applies to **/*.{test,spec}.{js,ts,jsx,tsx} : General testing: focus on behavior, not exact error message wording

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.spec.ts
📚 Learning: 2025-08-11T15:06:30.877Z
Learnt from: CR
PR: unraid/api#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-11T15:06:30.877Z
Learning: Applies to **/*.{test,spec}.{js,ts,jsx,tsx} : General testing: avoid brittle tests tied to non-essential details (exact messages, log formats)

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.spec.ts
📚 Learning: 2025-08-11T15:10:28.150Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-08-11T15:10:28.150Z
Learning: Applies to **/__test__/components/**/*.ts : Mock external dependencies and services in component tests (e.g., vi.mock for helper modules)

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.spec.ts
📚 Learning: 2025-08-11T15:10:28.150Z
Learnt from: CR
PR: unraid/api#0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-08-11T15:10:28.150Z
Learning: Applies to {**/*.test.ts,**/__test__/{components,store}/**/*.ts} : Use factory functions in vi.mock calls to define mocks and avoid hoisting pitfalls

Applied to files:

  • packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.spec.ts
📚 Learning: 2024-12-13T17:28:15.384Z
Learnt from: pujitm
PR: unraid/api#982
File: web/helpers/apollo-cache/index.ts:142-142
Timestamp: 2024-12-13T17:28:15.384Z
Learning: In `web/helpers/apollo-cache/index.ts`, it's acceptable to use `structuredClone` since the project targets Node.js v20 and browsers where `structuredClone` is supported.

Applied to files:

  • packages/unraid-shared/src/types/internal-graphql-client.factory.ts
📚 Learning: 2024-12-11T17:20:09.604Z
Learnt from: pujitm
PR: unraid/api#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.

Applied to files:

  • packages/unraid-shared/src/types/internal-graphql-client.factory.ts
📚 Learning: 2025-05-23T21:59:29.632Z
Learnt from: elibosley
PR: unraid/api#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.

Applied to files:

  • packages/unraid-shared/src/types/internal-graphql-client.factory.ts
🧬 Code Graph Analysis (3)
api/src/unraid-api/cli/internal-client.service.ts (4)
packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.ts (1)
  • Injectable (13-74)
api/src/unraid-api/shared/internal-graphql-client.factory.ts (1)
  • Injectable (26-168)
packages/unraid-shared/src/tokens.ts (1)
  • INTERNAL_CLIENT_SERVICE_TOKEN (5-5)
packages/unraid-shared/src/types/internal-graphql-client.factory.ts (1)
  • InternalGraphQLClientFactory (7-13)
packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.spec.ts (2)
web/helpers/create-apollo-client.ts (1)
  • client (93-96)
api/src/unraid-api/cli/log.service.ts (1)
  • error (65-69)
packages/unraid-shared/src/types/internal-graphql-client.factory.ts (1)
packages/unraid-shared/src/index.ts (1)
  • InternalGraphQLClientFactory (6-6)
🔇 Additional comments (2)
packages/unraid-shared/src/types/internal-graphql-client.factory.ts (1)

1-13: LGTM: type-only import and ESM-compatible specifier are correct

Interface shape and JSDoc are clear; aligns with the factory implementation and shared token usage. No issues found.

packages/unraid-api-plugin-connect/src/internal-rpc/internal.client.spec.ts (1)

1-274: Tests look solid and follow our guidelines

  • Uses .js extension in imports for ESM.
  • Uses .rejects.toThrow() without message assertions.
  • Concurrency and clearClient behaviors are thoroughly exercised.

Copy link
Member

@pujitm pujitm left a comment

Choose a reason for hiding this comment

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

significant performance improvement in the connect dashboard!

@elibosley elibosley merged commit a2c5d24 into main Aug 13, 2025
15 of 16 checks passed
@elibosley elibosley deleted the fix/local-fetching-using-sockets branch August 13, 2025 20:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

sandbox fails to start if unraid http port is not 80

3 participants