Skip to content

Conversation

@elibosley
Copy link
Member

@elibosley elibosley commented May 23, 2025

Summary by CodeRabbit

  • New Features

    • Added a full-featured API key management UI, including creation, listing, and deletion of API keys with customizable roles and permissions.
    • Introduced a new page for API key management.
    • Accordion UI components are now available for enhanced interface interactions.
    • API now provides queries for possible API key roles and permissions.
  • Improvements

    • API key-related mutations are now grouped under a single field, improving organization and usability.
    • Permissions can be assigned directly to API keys, not just roles.
  • Bug Fixes

    • Validation updated to require at least one role or permission when creating an API key.
  • Documentation

    • Updated and added rules and configuration documentation for code generation and testing.
  • Tests

    • Added and updated tests for new API key mutation logic; removed obsolete tests for deprecated mutations.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented May 23, 2025

Warning

Rate limit exceeded

@elibosley has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 4 minutes and 31 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 2815f82 and 88f898d.

📒 Files selected for processing (3)
  • api/src/unraid-api/graph/resolvers/api-key/api-key.mutation.spec.ts (1 hunks)
  • api/src/unraid-api/graph/resolvers/mutation/mutation.resolver.ts (2 hunks)
  • web/pages/apikey.vue (1 hunks)

Walkthrough

This update introduces a comprehensive API key management system, including backend GraphQL schema restructuring, new resolvers, and test coverage. It adds Vue components and pages for creating, listing, and deleting API keys with role and permission assignment. Supporting codegen rules, configuration tweaks, and UI utilities (accordion components and Tailwind animations) are also included.

Changes

File(s) / Path(s) Change Summary
.cursor/rules/web-graphql.mdc, .cursor/rules/api-rules.mdc Added/updated rules for GraphQL codegen and API testing commands.
api/generated-schema.graphql, api/src/unraid-api/graph/resolvers/api-key/api-key.model.ts, ... Refactored GraphQL schema: grouped API key mutations under ApiKeyMutations, added new input types, queries for possible roles/permissions, and updated mutation structure.
api/src/unraid-api/graph/resolvers/api-key/api-key.mutation.ts, api-key.mutation.spec.ts Added new mutation resolver for API key operations with corresponding tests.
api/src/unraid-api/graph/resolvers/api-key/api-key.resolver.ts, api-key.resolver.spec.ts Removed legacy API key mutations and related tests; added new queries for possible roles and permissions.
api/src/unraid-api/graph/resolvers/api-key/api-key.module.ts, api/src/unraid-api/graph/resolvers/resolvers.module.ts Updated module imports/exports to integrate new resolvers and modules.
api/src/unraid-api/graph/resolvers/mutation/mutation.model.ts, mutation.resolver.ts Added ApiKeyMutations object type and resolver to root mutation structure.
api/src/unraid-api/auth/api-key.service.ts, api-key.service.spec.ts Adjusted validation in API key creation to accept either roles or permissions; updated test assertions accordingly.
api/dev/states/myservers.cfg Bumped configuration version from 4.7.0 to 4.8.0.
unraid-ui/package.json Updated reka-ui dependency version.
unraid-ui/src/components/common/accordion/Accordion.vue, ... (and related accordion components and index) Added new Accordion UI components and index for simplified imports.
unraid-ui/src/index.ts Exported new accordion components.
unraid-ui/tailwind.config.ts Added Tailwind keyframes and animations for accordion transitions.
web/components/ApiKey/ApiKeyCreate.vue, ApiKeyManager.vue, apikey.query.ts Added Vue components for API key creation and management, with supporting GraphQL operations.
web/composables/gql/gql.ts, graphql.ts Added typed support for new API key-related GraphQL operations.
web/pages/apikey.vue Added new page for API key management.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant ApiKeyManager (Vue)
    participant ApiKeyCreate (Vue)
    participant Backend GraphQL API
    participant Database

    User->>ApiKeyManager: Opens API Key page
    ApiKeyManager->>Backend GraphQL API: Query apiKeys, apiKeyPossibleRoles, apiKeyPossiblePermissions
    Backend GraphQL API->>Database: Fetch API keys, roles, permissions
    Database-->>Backend GraphQL API: Return data
    Backend GraphQL API-->>ApiKeyManager: Return keys, roles, permissions
    User->>ApiKeyManager: Clicks "Create API Key"
    ApiKeyManager->>ApiKeyCreate: Show creation form
    User->>ApiKeyCreate: Fills form, submits
    ApiKeyCreate->>Backend GraphQL API: Mutation apiKey.create(input)
    Backend GraphQL API->>Database: Create API key, assign roles/permissions
    Database-->>Backend GraphQL API: Return new API key
    Backend GraphQL API-->>ApiKeyCreate: Return created API key
    ApiKeyCreate->>ApiKeyManager: Emit 'created' event
    ApiKeyManager->>Backend GraphQL API: Refetch apiKeys
    Backend GraphQL API-->>ApiKeyManager: Return updated keys
Loading

Possibly related PRs

  • refactor: permissions system rewrite #942: Implements a permissions system rewrite with role-based access control for API keys, closely related to the grouped mutations and permission assignment in this PR.
  • feat: session issues #1087: Modifies API key creation logic, including the addition of an overwrite flag, which aligns with the changes to API key creation and input types here.
  • feat: code first graphql #1347: Migrates to a code-first GraphQL approach, which is directly continued and expanded in this PR with new models and resolvers.

Suggested reviewers

  • mdatelle
  • zackspear

Poem

🎉
Keys to the kingdom, roles at your hand,
Permissions assigned with a click or command.
Accordions open, transitions so neat,
GraphQL mutations now grouped and complete.
Create, delete, and manage with glee—
Your API keys, secure as can be!
🔑✨

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 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.
    • Explain this complex logic.
    • 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. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • 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 src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

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

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

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

Documentation and Community

  • 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.

@elibosley elibosley force-pushed the feat/api-key-creation branch from 3c11f7f to 54f30b4 Compare May 23, 2025 13:23
@elibosley elibosley changed the title fix: node_modules dir removed on plugin update feat: API key management May 23, 2025
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: 7

♻️ Duplicate comments (1)
api/dev/Unraid.net/myservers.cfg (1)

11-11: Avoid committing actual API keys to source control
Same concern as in api/dev/states/myservers.cfg: this field contains a real API key. Use environment variables or a secrets manager to keep it out of version control.

🧹 Nitpick comments (9)
api/src/unraid-api/graph/resolvers/resolvers.module.ts (1)

5-5: Potentially unused import

The ApiKeyResolver import appears to be unused now that you're importing the entire ApiKeyModule. Consider removing this import to keep the code clean.

 import { AuthModule } from '@app/unraid-api/auth/auth.module.js';
 import { ApiKeyModule } from '@app/unraid-api/graph/resolvers/api-key/api-key.module.js';
-import { ApiKeyResolver } from '@app/unraid-api/graph/resolvers/api-key/api-key.resolver.js';
 import { ArrayModule } from '@app/unraid-api/graph/resolvers/array/array.module.js';
unraid-ui/src/components/common/accordion/AccordionTrigger.vue (1)

25-27: Consider adding accessibility attributes to the icon

While the implementation works well, the chevron icon could benefit from additional accessibility attributes.

-        <ChevronDown class="h-4 w-4 shrink-0 transition-transform duration-200" />
+        <ChevronDown class="h-4 w-4 shrink-0 transition-transform duration-200" aria-hidden="true" />
api/src/unraid-api/graph/resolvers/api-key/api-key.mutation.ts (1)

69-79: Enhance delete method with more informative return values

The delete method returns a hardcoded true regardless of the actual deletion result. Consider returning more meaningful information about the operation's success.

@ResolveField(() => Boolean, { description: 'Delete one or more API keys' })
async delete(@Args('input') input: DeleteApiKeyInput): Promise<boolean> {
    const validatedInput = await validateObject(DeleteApiKeyInput, input);
-   await this.apiKeyService.deleteApiKeys(validatedInput.ids);
-   return true;
+   const result = await this.apiKeyService.deleteApiKeys(validatedInput.ids);
+   return result.success;
}

This assumes the service method would be updated to return an object with operation status information.

web/components/ApiKey/ApiKeyManager.vue (2)

104-107: Improve accessibility for the show/hide key toggle

The eye icon button lacks appropriate accessibility attributes.

-          <button type="button" class="focus:outline-none" @click="toggleShowKey">
-            <component :is="showKey ? EyeSlashIcon : EyeIcon" class="w-5 h-5 text-gray-500" />
+          <button 
+            type="button" 
+            class="focus:outline-none" 
+            @click="toggleShowKey"
+            :aria-label="showKey ? 'Hide API key' : 'Show API key'"
+          >
+            <component :is="showKey ? EyeSlashIcon : EyeIcon" class="w-5 h-5 text-gray-500" aria-hidden="true" />

101-109: Add copy-to-clipboard functionality for the API key

Users often need to copy API keys. Consider adding a copy button next to the key.

<div v-if="createdKey" class="mt-4">
  <div class="flex items-center gap-2">
    <span>API Key created:</span>
    <b>{{ showKey ? createdKey.key : '••••••••••••••••••••••••••••••••' }}</b>
    <button type="button" class="focus:outline-none" @click="toggleShowKey">
      <component :is="showKey ? EyeSlashIcon : EyeIcon" class="w-5 h-5 text-gray-500" />
    </button>
+   <button 
+     v-if="showKey" 
+     type="button" 
+     class="focus:outline-none" 
+     @click="navigator.clipboard.writeText(createdKey.key)"
+     aria-label="Copy API key to clipboard"
+   >
+     <ClipboardIcon class="w-5 h-5 text-gray-500" aria-hidden="true" />
+   </button>
  </div>
</div>

Don't forget to import the ClipboardIcon:

import { EyeIcon, EyeSlashIcon } from '@heroicons/vue/24/solid';
+import { ClipboardIcon } from '@heroicons/vue/24/solid';
web/components/ApiKey/ApiKeyCreate.vue (3)

110-129: Missing form field validation indicators.

The form lacks visual cues for required fields and validation feedback.

Consider adding required indicators and validation feedback:

    <div class="mb-2">
-      <Label for="api-key-name">Name</Label>
+      <Label for="api-key-name">Name *</Label>
      <Input id="api-key-name" v-model="newKeyName" placeholder="Name" class="mt-1" />
+      <span v-if="!newKeyName.trim() && formSubmitted" class="text-red-500 text-xs mt-1">Name is required</span>
    </div>

156-156: Potential event type safety issue.

The event handling for the checkbox change doesn't use a more specific event type.

Consider using a more specific event type for better type safety:

-      @change="(e: Event) => togglePermission(perm.resource, action, (e.target as HTMLInputElement)?.checked)"
+      @change="(e: Event & { target: HTMLInputElement }) => togglePermission(perm.resource, action, e.target.checked)"

168-169: Add disabled state to the Create button.

The Create button should be disabled when the form is invalid or during submission.

Enhance the button with loading and validation states:

-      <Button variant="primary" @click="createKey">Create</Button>
+      <Button 
+        variant="primary" 
+        @click="createKey" 
+        :disabled="loading || !newKeyName.trim()"
+      >
+        {{ loading ? 'Creating...' : 'Create' }}
+      </Button>
      <Button variant="secondary" @click="$emit('cancel')">Cancel</Button>
api/generated-schema.graphql (1)

901-911: Consider adding description validation to CreateApiKeyInput.

While the input type is well-structured, consider adding validation constraints for the description field if there are specific requirements.

Consider using directives to document validation constraints if applicable:

input CreateApiKeyInput {
  name: String!
-  description: String
+  description: String @constraint(maxLength: 500)
  roles: [Role!]
  permissions: [AddPermissionInput!]

  """
  This will replace the existing key if one already exists with the same name, otherwise returns the existing key
  """
  overwrite: Boolean
}
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 7b005cb and 54f30b4.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (26)
  • .cursor/rules/web-graphql.mdc (1 hunks)
  • api/dev/Unraid.net/myservers.cfg (2 hunks)
  • api/dev/states/myservers.cfg (2 hunks)
  • api/generated-schema.graphql (3 hunks)
  • api/src/unraid-api/auth/api-key.service.ts (1 hunks)
  • api/src/unraid-api/graph/resolvers/api-key/api-key.model.ts (1 hunks)
  • api/src/unraid-api/graph/resolvers/api-key/api-key.module.ts (1 hunks)
  • api/src/unraid-api/graph/resolvers/api-key/api-key.mutation.ts (1 hunks)
  • api/src/unraid-api/graph/resolvers/api-key/api-key.resolver.ts (2 hunks)
  • api/src/unraid-api/graph/resolvers/mutation/mutation.model.ts (2 hunks)
  • api/src/unraid-api/graph/resolvers/mutation/mutation.resolver.ts (2 hunks)
  • api/src/unraid-api/graph/resolvers/resolvers.module.ts (3 hunks)
  • unraid-ui/package.json (1 hunks)
  • unraid-ui/src/components/common/accordion/Accordion.vue (1 hunks)
  • unraid-ui/src/components/common/accordion/AccordionContent.vue (1 hunks)
  • unraid-ui/src/components/common/accordion/AccordionItem.vue (1 hunks)
  • unraid-ui/src/components/common/accordion/AccordionTrigger.vue (1 hunks)
  • unraid-ui/src/components/common/accordion/index.ts (1 hunks)
  • unraid-ui/src/index.ts (2 hunks)
  • unraid-ui/tailwind.config.ts (2 hunks)
  • web/components/ApiKey/ApiKeyCreate.vue (1 hunks)
  • web/components/ApiKey/ApiKeyManager.vue (1 hunks)
  • web/components/ApiKey/apikey.query.ts (1 hunks)
  • web/composables/gql/gql.ts (3 hunks)
  • web/composables/gql/graphql.ts (6 hunks)
  • web/pages/apikey.vue (1 hunks)
🧰 Additional context used
🧠 Learnings (3)
api/src/unraid-api/auth/api-key.service.ts (1)
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.
api/src/unraid-api/graph/resolvers/api-key/api-key.resolver.ts (2)
Learnt from: mdatelle
PR: unraid/api#942
File: api/src/unraid-api/graph/resolvers/auth/auth.resolver.ts:111-113
Timestamp: 2024-11-06T20:59:25.809Z
Learning: In the Unraid API project, error handling for mutations is handled at the service level rather than in the GraphQL resolvers. Specifically, in `api/src/unraid-api/graph/resolvers/auth/auth.resolver.ts`, methods like `removeRoleFromApiKey` rely on service-level error handling.
Learnt from: 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.
api/src/unraid-api/graph/resolvers/api-key/api-key.mutation.ts (1)
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.
🧬 Code Graph Analysis (4)
api/src/unraid-api/graph/resolvers/mutation/mutation.resolver.ts (1)
web/composables/gql/graphql.ts (2)
  • Mutation (892-921)
  • ApiKeyMutations (135-145)
api/src/unraid-api/graph/resolvers/api-key/api-key.module.ts (1)
api/src/unraid-api/graph/resolvers/resolvers.module.ts (1)
  • Module (37-65)
web/components/ApiKey/apikey.query.ts (1)
web/composables/gql/gql.ts (1)
  • graphql (222-224)
web/composables/gql/gql.ts (1)
api/src/graphql/generated/client/gql.ts (1)
  • graphql (54-56)
🪛 GitHub Actions: CI - Main (API)
unraid-ui/tailwind.config.ts

[error] 1-1: Cannot find module '@/theme/preset'. This module is required by tailwind.config.ts but was not found, causing the build to fail.

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (42)
api/dev/states/myservers.cfg (1)

2-2: Version bump to 4.8.0 is correctly applied
The API version under [api] has been updated from “4.7.0” to “4.8.0” to match the new schema changes. No further actions needed.

unraid-ui/package.json (1)

56-56: Dependency reka-ui updated to ^2.1.1
The minor bump aligns with the new accordion components. Please ensure that version ^2.1.1 is available on the registry and that no breaking changes affect existing functionality.

api/dev/Unraid.net/myservers.cfg (1)

2-2: Version bump to 4.8.0 is correctly applied
This matches the update in api/dev/states/myservers.cfg.

unraid-ui/src/index.ts (2)

12-17: Re-export new accordion components
Imports for Accordion, AccordionContent, AccordionItem, and AccordionTrigger look correct and match the component paths.


89-93: Expose accordion components in public API
The export block correctly includes the newly added accordion components. Ensure the order is consistent with other components for readability.

web/pages/apikey.vue (1)

1-6: Add new API Key management page
The template and script setup correctly import and render ApiKeyManager. Ensure this page is registered in the router configuration (e.g., web/router/index.ts) so it’s accessible via its intended path.

api/src/unraid-api/graph/resolvers/api-key/api-key.model.ts (1)

136-142: Well-structured input type for API key deletion

The new DeleteApiKeyInput class is properly designed with appropriate validation decorators to ensure the input contains an array of string IDs. This addition supports the bulk deletion operation within the new ApiKeyMutations group.

unraid-ui/src/components/common/accordion/index.ts (1)

1-4: Clean barrel exports for accordion components

This is a good implementation of a barrel file that centralizes exports for the accordion component set. The approach simplifies imports in other parts of the codebase and follows common component library patterns.

api/src/unraid-api/graph/resolvers/mutation/mutation.resolver.ts (2)

4-4: Import matches implementation

The import of ApiKeyMutations correctly aligns with its usage in the new mutation resolver method.


34-37: Consistent mutation resolver pattern

The new apiKey() resolver method follows the established pattern in the codebase - it's properly decorated with @Mutation and returns a new instance of the mutations class. This implementation aligns with the GraphQL schema restructuring that groups API key operations.

api/src/unraid-api/auth/api-key.service.ts (2)

133-134: Improved validation for API key creation

The validation logic now correctly checks for either roles or permissions, which is more flexible and aligns with the expanded functionality of the API key system.


137-137: Added null safety with optional chaining

Good addition of optional chaining to prevent potential TypeErrors when roles might be undefined. This defensive programming approach makes the code more robust.

api/src/unraid-api/graph/resolvers/api-key/api-key.module.ts (1)

4-4: Well-structured module with proper dependency integration

The changes to ApiKeyModule correctly introduce the AuthModule import and integrate the new ApiKeyMutationsResolver. This modularization aligns with NestJS best practices by:

  1. Importing the required AuthModule
  2. Adding ApiKeyMutationsResolver to providers
  3. Exporting ApiKeyService alongside ApiKeyResolver

This structure supports the new API key management feature and follows good separation of concerns by grouping related functionality.

Also applies to: 6-6, 10-12

api/src/unraid-api/graph/resolvers/resolvers.module.ts (1)

4-4: Good modularization of ApiKey functionality

The changes properly integrate ApiKeyModule into the ResolversModule by importing and exporting it. This is a good refactoring that improves code organization.

Also applies to: 38-38, 63-63

.cursor/rules/web-graphql.mdc (1)

1-9: Good GraphQL code organization rules

These rules establish clear conventions for GraphQL code in the web directory, which will help maintain consistency as the codebase grows. The separation of queries and mutations into distinct file types is a good practice that makes the code more discoverable and maintainable.

unraid-ui/src/components/common/accordion/Accordion.vue (1)

1-19: Well-structured Vue component with proper prop forwarding

This Accordion component follows Vue 3 best practices:

  • Uses the composition API with <script setup> syntax
  • Properly types props and emits using TypeScript
  • Efficiently forwards props and emits to the underlying component
  • Provides a slot for flexible content injection

The component serves as a clean wrapper around the reka-ui AccordionRoot component, maintaining a consistent interface while leveraging the third-party functionality.

api/src/unraid-api/graph/resolvers/mutation/mutation.model.ts (2)

12-15: Clean implementation of API key mutations.

The new ApiKeyMutations class follows the existing pattern for organizing GraphQL mutations in the codebase, with proper use of decorators and descriptions.


33-34: Correctly integrates API key mutations into root schema.

The addition of the apiKey field to RootMutations is properly implemented with appropriate typing and description, making API key operations accessible through the GraphQL API.

unraid-ui/src/components/common/accordion/AccordionContent.vue (1)

1-22: Well-structured accordion content component.

This component follows Vue best practices with:

  • Proper TypeScript typing
  • Clean prop forwarding using reactiveOmit
  • Appropriate animation classes for accordion transitions
  • Slot content handling with class merging via cn utility

The animation classes reference will work in conjunction with the keyframes defined in the Tailwind config.

unraid-ui/src/components/common/accordion/AccordionItem.vue (1)

1-19: Well-implemented accordion item component.

This component:

  • Correctly extends the base component from reka-ui
  • Properly handles prop forwarding with useForwardProps and reactiveOmit
  • Applies consistent styling with border-bottom
  • Maintains the slot pattern for content injection

The implementation follows the same pattern as the AccordionContent component, creating a cohesive accordion system.

unraid-ui/tailwind.config.ts (1)

91-112: Good addition of accordion animation keyframes.

The keyframes and animation definitions are well-structured and provide smooth transitions for the accordion components. The values are appropriate for a good user experience:

  • Using CSS variables for dynamic height calculation
  • Reasonable animation duration (0.2s)
  • Appropriate easing function (ease-out)
unraid-ui/src/components/common/accordion/AccordionTrigger.vue (2)

1-11: Good implementation of the AccordionTrigger component

The component setup looks solid with proper use of TypeScript and Vue 3's script setup syntax. Using reactiveOmit to handle prop forwarding is a good practice to avoid passing the class prop directly.


13-30: Well-structured template with proper styling

The template implementation is clean and follows best practices:

  • Wrapping with AccordionHeader
  • Using slots for content customization
  • Providing a default icon with animation
  • Using the cn utility for class merging

The animation effect on the chevron icon (rotate 180 degrees when open) provides good visual feedback to users.

api/src/unraid-api/graph/resolvers/api-key/api-key.mutation.ts (4)

21-26: Well-structured resolver with proper dependency injection

The resolver class follows NestJS best practices with constructor-based dependency injection for services.


28-45: Create method properly validates input and handles permissions

The create method:

  • Uses appropriate permission decorators
  • Validates input before processing
  • Correctly converts optional values to undefined when needed
  • Properly syncs roles after creation

This implementation aligns with best practices for GraphQL resolvers.


47-56: Good implementation of addRole method

The method correctly:

  • Applies permission checks
  • Validates input
  • Converts the role string to the appropriate enum value
  • Returns the result from the service

This follows the established pattern in the project.


58-67: Consistent implementation of removeRole method

This method follows the same pattern as addRole, ensuring consistency across the codebase.

web/components/ApiKey/apikey.query.ts (4)

1-18: Well-structured API keys query

The GET_API_KEYS query is properly structured and fetches all necessary fields for displaying API keys, including nested permissions.


20-37: Comprehensive CREATE_API_KEY mutation

The mutation correctly uses the new hierarchical structure with apiKey { create } and returns all relevant fields, including the secret key which is essential for first-time display to users.


39-45: Simple and effective DELETE_API_KEY mutation

The mutation is correctly structured to match the new schema pattern.


47-55: Useful metadata query for API key management

The GET_API_KEY_META query retrieves essential information for the UI to display role and permission options.

web/components/ApiKey/ApiKeyManager.vue (2)

15-22: Well-defined interface for API keys

The interface includes all necessary properties for displaying and managing API keys.


24-37: Effective state management with Apollo composables

The component properly handles state with Apollo composables and Vue reactivity. Using watchEffect is a good approach to keep the local state in sync with query results.

api/src/unraid-api/graph/resolvers/api-key/api-key.resolver.ts (1)

57-66: Avoid rebuilding a static matrix on every request

resources.map(...) allocates a brand-new array each time the resolver is hit.
Because Resource and AuthActionVerb are compile-time enums, the result never changes, so we can compute it once:

+// move to module scope (above the @Resolver class)
+const ALL_PERMISSION_COMBOS: Permission[] = Object.values(Resource).map(
+  (resource) => ({
+    resource,
+    actions: Object.values(AuthActionVerb).map(String), // ensure string[]
+  }),
+);

 ...

-    const resources = Object.values(Resource);
-    const actions = Object.values(AuthActionVerb);
-    return resources.map((resource) => ({
-        resource,
-        actions,
-    }));
+    return ALL_PERMISSION_COMBOS;

This shaves off repeated allocations and keeps the resolver pure.
Also note the explicit map(String) cast – without it actions is typed as AuthActionVerb[], whereas the GraphQL schema expects string[].
[ suggest_optional_refactor ]

web/composables/gql/gql.ts (1)

1-2045: Generated file – no manual changes required

All additions are code-gen output and look consistent with the updated schema.
No action needed.

web/composables/gql/graphql.ts (1)

134-170: Schema types update acknowledged

The new ApiKeyMutations, DeleteApiKeyInput, and query fields are correctly reflected.
Nothing to flag here.

web/components/ApiKey/ApiKeyCreate.vue (2)

1-24: Well-structured component setup with appropriate props and events.

The component correctly uses Vue 3's Composition API with TypeScript and properly defines props and emits. The component accepts possible roles and permissions as props and emits events when a key is created or the operation is canceled.


35-51: Well-implemented permission toggle function.

The togglePermission function correctly handles adding and removing permissions with proper checks for existing resources and actions.

api/generated-schema.graphql (4)

886-899: Good design choice grouping API key mutations.

Organizing related API key mutations under a dedicated ApiKeyMutations type improves API structure and maintainability. This approach follows GraphQL best practices for organizing related operations.


928-930: Good design for bulk delete operation.

The DeleteApiKeyInput type efficiently supports bulk deletion by accepting an array of IDs, which is more efficient than making multiple single-delete requests.


1529-1536: Well-designed query endpoints for API key management.

The addition of dedicated queries for API keys, including metadata queries for possible roles and permissions, provides a complete and cohesive API for managing API keys.


1574-1574: Consistent integration with root mutation type.

The API key mutations are correctly integrated into the root Mutation type using a single field, maintaining a clean and organized schema structure.

@elibosley
Copy link
Member Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented May 23, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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

🧹 Nitpick comments (2)
api/src/unraid-api/graph/resolvers/api-key/api-key.mutation.spec.ts (2)

59-60: Test the permissions functionality

The test creates a mock with empty permissions array but doesn't test any permission-related functionality. If the resolver supports permission management, consider adding tests that verify this functionality works correctly.


76-86: Expand delete test cases

The current test only verifies the happy path for deleting API keys. Consider adding tests for:

  1. Deleting multiple API keys at once
  2. Error handling when deletion fails
  3. Edge cases like empty ID array or non-existent IDs

This will ensure robust error handling and functionality.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between ca0e9c2 and 2815f82.

📒 Files selected for processing (6)
  • .cursor/rules/api-rules.mdc (1 hunks)
  • api/dev/states/myservers.cfg (1 hunks)
  • api/src/unraid-api/auth/api-key.service.spec.ts (1 hunks)
  • api/src/unraid-api/graph/resolvers/api-key/api-key.mutation.spec.ts (1 hunks)
  • api/src/unraid-api/graph/resolvers/api-key/api-key.resolver.spec.ts (0 hunks)
  • api/src/unraid-api/graph/resolvers/api-key/api-key.resolver.ts (3 hunks)
💤 Files with no reviewable changes (1)
  • api/src/unraid-api/graph/resolvers/api-key/api-key.resolver.spec.ts
✅ Files skipped from review due to trivial changes (1)
  • .cursor/rules/api-rules.mdc
🚧 Files skipped from review as they are similar to previous changes (2)
  • api/dev/states/myservers.cfg
  • api/src/unraid-api/graph/resolvers/api-key/api-key.resolver.ts
🧰 Additional context used
🧬 Code Graph Analysis (1)
api/src/unraid-api/graph/resolvers/api-key/api-key.mutation.spec.ts (1)
web/composables/gql/graphql.ts (2)
  • ApiKey (124-132)
  • ApiKeyWithSecret (177-186)
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: Test API
  • GitHub Check: Build Web App
  • GitHub Check: Build API
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (2)
api/src/unraid-api/auth/api-key.service.spec.ts (1)

188-188: Test updated to reflect new validation requirements

The error message has been updated to reflect that API keys now require either roles OR permissions to be specified, rather than just roles. This aligns with the enhanced API key management system that now supports both role-based and permission-based access control.

api/src/unraid-api/graph/resolvers/api-key/api-key.mutation.spec.ts (1)

64-73: Verify correct parameters in create test

The test verifies that apiKeyService.create is called with the expected parameters, but there's a discrepancy: mockApiKeyWithSecret is returned by the service, but mockApiKey (without the secret) is used in the expectations for syncApiKeyRoles. Ensure this is intentional and accurately reflects the resolver's behavior.

@mdatelle
Copy link
Contributor

Just a couple nits.

@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/PR1407/dynamix.unraid.net.plg

@elibosley elibosley merged commit d37dc3b into main May 23, 2025
12 checks passed
@elibosley elibosley deleted the feat/api-key-creation branch May 23, 2025 17:12
pujitm pushed a commit that referenced this pull request Jul 8, 2025
🤖 I have created a release *beep* *boop*
---


## [4.9.0](v4.8.0...v4.9.0)
(2025-07-08)


### Features

* add graphql resource for API plugins
([#1420](#1420))
([642a220](642a220))
* add management page for API keys
([#1408](#1408))
([0788756](0788756))
* add rclone ([#1362](#1362))
([5517e75](5517e75))
* API key management
([#1407](#1407))
([d37dc3b](d37dc3b))
* api plugin management via CLI
([#1416](#1416))
([3dcbfbe](3dcbfbe))
* build out docker components
([#1427](#1427))
([711cc9a](711cc9a))
* docker and info resolver issues
([#1423](#1423))
([9901039](9901039))
* fix shading in UPC to be less severe
([#1438](#1438))
([b7c2407](b7c2407))
* info resolver cleanup
([#1425](#1425))
([1b279bb](1b279bb))
* initial codeql setup
([#1390](#1390))
([2ade7eb](2ade7eb))
* initialize claude code in codebse
([#1418](#1418))
([b6c4ee6](b6c4ee6))
* move api key fetching to use api key service
([#1439](#1439))
([86bea56](86bea56))
* move to cron v4 ([#1428](#1428))
([b8035c2](b8035c2))
* move to iframe for changelog
([#1388](#1388))
([fcd6fbc](fcd6fbc))
* native slackware package
([#1381](#1381))
([4f63b4c](4f63b4c))
* send active unraid theme to docs
([#1400](#1400))
([f71943b](f71943b))
* slightly better watch mode
([#1398](#1398))
([881f1e0](881f1e0))
* upgrade nuxt-custom-elements
([#1461](#1461))
([345e83b](345e83b))
* use bigint instead of long
([#1403](#1403))
([574d572](574d572))


### Bug Fixes

* activation indicator removed
([5edfd82](5edfd82))
* alignment of settings on ManagementAccess settings page
([#1421](#1421))
([70c790f](70c790f))
* allow rclone to fail to initialize
([#1453](#1453))
([7c6f02a](7c6f02a))
* always download 7.1 versioned files for patching
([edc0d15](edc0d15))
* api `pnpm type-check`
([#1442](#1442))
([3122bdb](3122bdb))
* **api:** connect config `email` validation
([#1454](#1454))
([b9a1b9b](b9a1b9b))
* backport
unraid/webgui[#2269](https://github.com/unraid/api/issues/2269) rc.nginx
update ([#1436](#1436))
([a7ef06e](a7ef06e))
* bigint
([e54d27a](e54d27a))
* config migration from `myservers.cfg`
([#1440](#1440))
([c4c9984](c4c9984))
* **connect:** fatal race-condition in websocket disposal
([#1462](#1462))
([0ec0de9](0ec0de9))
* **connect:** mothership connection
([#1464](#1464))
([7be8bc8](7be8bc8))
* console hidden
([9b85e00](9b85e00))
* debounce is too long
([#1426](#1426))
([f12d231](f12d231))
* delete legacy connect keys and ensure description
([22fe91c](22fe91c))
* **deps:** pin dependencies
([#1465](#1465))
([ba75a40](ba75a40))
* **deps:** pin dependencies
([#1470](#1470))
([412b329](412b329))
* **deps:** storybook v9
([#1476](#1476))
([45bb49b](45bb49b))
* **deps:** update all non-major dependencies
([#1366](#1366))
([291ee47](291ee47))
* **deps:** update all non-major dependencies
([#1379](#1379))
([8f70326](8f70326))
* **deps:** update all non-major dependencies
([#1389](#1389))
([cb43f95](cb43f95))
* **deps:** update all non-major dependencies
([#1399](#1399))
([68df344](68df344))
* **deps:** update dependency @types/diff to v8
([#1393](#1393))
([00da27d](00da27d))
* **deps:** update dependency cache-manager to v7
([#1413](#1413))
([9492c2a](9492c2a))
* **deps:** update dependency commander to v14
([#1394](#1394))
([106ea09](106ea09))
* **deps:** update dependency diff to v8
([#1386](#1386))
([e580f64](e580f64))
* **deps:** update dependency dotenv to v17
([#1474](#1474))
([d613bfa](d613bfa))
* **deps:** update dependency lucide-vue-next to ^0.509.0
([#1383](#1383))
([469333a](469333a))
* **deps:** update dependency marked to v16
([#1444](#1444))
([453a5b2](453a5b2))
* **deps:** update dependency shadcn-vue to v2
([#1302](#1302))
([26ecf77](26ecf77))
* **deps:** update dependency vue-sonner to v2
([#1401](#1401))
([53ca414](53ca414))
* disable file changes on Unraid 7.2
([#1382](#1382))
([02de89d](02de89d))
* do not start API with doinst.sh
([7d88b33](7d88b33))
* do not uninstall fully on 7.2
([#1484](#1484))
([2263881](2263881))
* drop console with terser
([a87d455](a87d455))
* error logs from `cloud` query when connect is not installed
([#1450](#1450))
([719f460](719f460))
* flash backup integration with Unraid Connect config
([#1448](#1448))
([038c582](038c582))
* header padding regression
([#1477](#1477))
([e791cc6](e791cc6))
* incorrect state merging in redux store
([#1437](#1437))
([17b7428](17b7428))
* lanip copy button not present
([#1459](#1459))
([a280786](a280786))
* move to bigint scalar
([b625227](b625227))
* node_modules dir removed on plugin update
([#1406](#1406))
([7b005cb](7b005cb))
* omit Connect actions in UPC when plugin is not installed
([#1417](#1417))
([8c8a527](8c8a527))
* parsing of `ssoEnabled` in state.php
([#1455](#1455))
([f542c8e](f542c8e))
* pin ranges ([#1460](#1460))
([f88400e](f88400e))
* pr plugin promotion workflow
([#1456](#1456))
([13bd9bb](13bd9bb))
* proper fallback if missing paths config modules
([7067e9e](7067e9e))
* rc.unraid-api now cleans up older dependencies
([#1404](#1404))
([83076bb](83076bb))
* remote access lifecycle during boot & shutdown
([#1422](#1422))
([7bc583b](7bc583b))
* sign out correctly on error
([#1452](#1452))
([d08fc94](d08fc94))
* simplify usb listing
([#1402](#1402))
([5355115](5355115))
* theme issues when sent from graph
([#1424](#1424))
([75ad838](75ad838))
* **ui:** notifications positioning regression
([#1445](#1445))
([f73e5e0](f73e5e0))
* use some instead of every for connect detection
([9ce2fee](9ce2fee))


### Reverts

* revert package.json dependency updates from commit 711cc9a for api and
packages/*
([94420e4](94420e4))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants