Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Recoverable keys feature for the dashboard #2110

Closed
wants to merge 51 commits into from

Conversation

harshsbhat
Copy link
Contributor

@harshsbhat harshsbhat commented Sep 18, 2024

What does this PR do?

Fixes #2097

This PR introduces the feature to create recoverable keys from the dashboard itself. Adding a toggle recoverable in the dashboard creates keys if enabled encrypts the data and then stores the encrypted data. This also introduces an internal package named @unkey/vault which allows the dashboard to interact with the vault.

https://www.loom.com/share/27f3a1b58e5a4d478b1510a80f679558?sid=e8faf269-7f80-4b8c-9229-7b55c547ebf2

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • Chore (refactoring code, technical debt, workflow improvements)
  • Enhancement (small improvements)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

Checklist

Required

  • Filled out the "How to test" section in this PR
  • Read Contributing Guide
  • Self-reviewed my own code
  • Commented on my code in hard-to-understand areas
  • Ran pnpm build
  • Ran pnpm fmt
  • Checked for warnings, there are none
  • Removed all console.logs
  • Merged the latest changes from main onto my branch with git pull origin main
  • My changes don't cause any responsiveness issues

Appreciated

  • If a UI change was made: Added a screen recording or screenshots to this PR
  • Updated the Unkey Docs if changes were necessary

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Introduced a new option for recoverable keys during key creation, enhancing user control over key management.
    • Added a user-friendly toggle for the recoverEnabled feature in the key creation form, with informative descriptions about security implications.
    • Enhanced encryption and storage options for keys, improving security and recovery options.
    • Integrated a new dependency for better vault management within the application.
    • Added a new Feedback component for user feedback submission, streamlining the feedback process.
  • Bug Fixes

    • Improved error handling and validation related to the new recoverEnabled feature.
    • Updated the sidebar and command menu for better navigation and user experience.

Copy link

changeset-bot bot commented Sep 18, 2024

⚠️ No Changeset found

Latest commit: 5db2d9a

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link

vercel bot commented Sep 18, 2024

@harshsbhat is attempting to deploy a commit to the Unkey Team on Vercel.

A member of the Team first needs to authorize it.

Copy link
Contributor

coderabbitai bot commented Sep 18, 2024

📝 Walkthrough
📝 Walkthrough
📝 Walkthrough
📝 Walkthrough
📝 Walkthrough
📝 Walkthrough

Walkthrough

The changes introduce a new feature in the API key creation process, allowing users to set a recoverEnabled option. This feature is implemented in the CreateKey component, where a toggle switch is added to the UI for users to enable or disable key recovery. Additionally, the backend logic in the createKey procedure is updated to handle this new parameter, facilitating the storage of encrypted keys when recovery is enabled.

Changes

Files Change Summary
apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/new/client.tsx Added recoverEnabled boolean property to the form schema and UI, capturing its state on form submission.
apps/dashboard/lib/trpc/routers/key/create.ts Introduced recoverEnabled parameter in the createKey procedure, modifying logic for key recovery and encryption.
apps/dashboard/CHANGELOG.md Updated changelog for version 0.1.36, reflecting changes in the @unkey/ratelimit dependency.
apps/dashboard/app/(app)/desktop-sidebar.tsx Added Feedback and DiscordIcon components, modified sidebar navigation structure.
apps/dashboard/components/dashboard/command-menu.tsx Replaced Feedback component with FeedbackCommand, simplifying feedback submission logic.
apps/dashboard/components/dashboard/feedback-component.tsx Introduced a new Feedback component for user feedback submission with validation and TRPC integration.
apps/dashboard/package.json Updated version from 0.1.35 to 0.1.36 and added @unkey/vault dependency.
internal/resend/emails/*.tsx Refactored multiple email components to utilize a custom Layout and Signature, improving structure and readability.

Assessment against linked issues

Objective Addressed Explanation
Allow setting recoverable: true when creating keys in the dashboard (Issue #2097)
Update the trpc handler to handle the new recoverEnabled logic (Issue #2097)
Ensure compatibility with externalId for keys created in the dashboard (Issue #2097) The changes do not address the need for externalId compatibility.

Possibly related PRs

  • feat: Show keyId after creating a new key #2107: This PR modifies the CreateKey component, enhancing the user interface for displaying API key information, which is closely related to the overall functionality of key management introduced in the main PR.
  • feat: Added support to sidebar #2119: This PR adds a DiscordIcon component and modifies the NavItem type in the sidebar, which is relevant as it involves changes to the UI components in the same application context as the main PR's UI enhancements for the CreateKey component.

Suggested labels

🕹️ oss.gg, :joystick: 300 points

Suggested reviewers

  • mcstepp
  • chronark
  • perkinsjr

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 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.
    • Generate unit testing code for this file.
    • 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 generate unit testing code for this file.
    • @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 generate unit testing code.
    • @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.

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

Copy link
Contributor

github-actions bot commented Sep 18, 2024

Thank you for following the naming conventions for pull request titles! 🙏

Copy link

vercel bot commented Sep 18, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
dashboard ✅ Ready (Inspect) Visit Preview 💬 Add feedback Oct 6, 2024 6:16am
engineering ❌ Failed (Inspect) Oct 6, 2024 6:16am
planetfall ✅ Ready (Inspect) Visit Preview 💬 Add feedback Oct 6, 2024 6:16am
play ✅ Ready (Inspect) Visit Preview 💬 Add feedback Oct 6, 2024 6:16am
workflows ✅ Ready (Inspect) Visit Preview 💬 Add feedback Oct 6, 2024 6:16am
www ✅ Ready (Inspect) Visit Preview 💬 Add feedback Oct 6, 2024 6:16am

@perkinsjr
Copy link
Collaborator

@harshsbhat this currently has a frozen lock file. Preventing it from building.

ERR_PNPM_OUTDATED_LOCKFILE  Cannot install with "frozen-lockfile" because pnpm-lock.yaml is not up to date with internal/vault/package.json

Just go ahead and delete the pnpm-lock.yaml and run pnpm i and then commit the lock file.

Copy link
Collaborator

@perkinsjr perkinsjr left a comment

Choose a reason for hiding this comment

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

See note

@harshsbhat
Copy link
Contributor Author

See note

Done!

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

Outside diff range and nitpick comments (2)
internal/vault/package.json (1)

1-15: LGTM! The package.json file is well-structured and properly configured.

The file correctly sets up the essential metadata for the @unkey/vault package, including the name, version, main entry point, and type definitions. The use of TypeScript and the inclusion of necessary dev dependencies are appropriate for the package's development.

Consider adding the following enhancements to further improve the package:

  1. Add a scripts section to define common tasks like building, testing, and linting the package. For example:
"scripts": {
  "build": "tsc",
  "test": "jest",
  "lint": "eslint src/**/*.ts"
}
  1. Include a README.md file in the package's root directory to provide documentation and usage instructions for the package. This will help users understand how to integrate and utilize the @unkey/vault package effectively.
apps/dashboard/lib/trpc/routers/key/create.ts (1)

1-1: Remove unused import.

The Api import from @/app/(app)/settings/root-keys/[keyId]/permissions/api is not used in the changed code. Please remove it to keep the code clean.

Apply this diff to remove the unused import:

-import { Api } from "@/app/(app)/settings/root-keys/[keyId]/permissions/api";
 import { db, schema } from "@/lib/db";
Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 5fb8bb9 and c8d08d0.

Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
Files selected for processing (5)
  • apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/new/client.tsx (5 hunks)
  • apps/dashboard/lib/trpc/routers/key/create.ts (3 hunks)
  • apps/dashboard/package.json (1 hunks)
  • internal/vault/package.json (1 hunks)
  • internal/vault/src/index.ts (1 hunks)
Additional comments not posted (13)
internal/vault/src/index.ts (5)

1-27: LGTM!

The type definitions for encryption and decryption requests and responses are well-structured and follow a consistent naming convention. The separation of single and bulk operations enhances clarity and usability.


29-31: LGTM!

The RequestContext type provides a structured way to include a unique requestId with each request, which can be useful for logging, tracing, and debugging purposes.


33-59: LGTM!

The Vault class and its encrypt method are well-implemented. The class encapsulates the encryption functionality and provides a clean interface for other modules to use. The encrypt method follows a standard pattern for making an HTTP request and includes appropriate error handling.


61-81: LGTM!

The encryptBulk method of the Vault class is well-implemented and follows a similar pattern to the encrypt method. It handles bulk encryption requests and includes appropriate error handling.


83-100: LGTM!

The decrypt method of the Vault class is well-implemented and follows a similar pattern to the encrypt and encryptBulk methods. It handles decryption requests and includes appropriate error handling.

apps/dashboard/package.json (1)

56-56: LGTM!

The addition of the @unkey/vault dependency aligns with the stated PR objectives and the feature implementation for recoverable keys. It will facilitate the necessary interaction between the dashboard and the vault.

apps/dashboard/lib/trpc/routers/key/create.ts (2)

21-21: LGTM!

The addition of the optional recoverEnabled parameter to the createKey procedure is a good way to introduce the new recoverable keys feature without breaking existing functionality.


106-121: Excellent work on the key encryption and storage implementation!

The conditional block for handling key encryption and storage when recoverEnabled is true and storeEncryptedKeys is enabled in the key auth is well-structured and follows best practices:

  • Using environment variables for the vault configuration keeps sensitive information secure.
  • Generating a unique request ID for each encryption operation aids in auditing and debugging.
  • Storing the encrypted key and its metadata in a separate encryptedKeys table ensures secure and efficient key recovery when needed.

Great job on this implementation!

apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/new/client.tsx (5)

81-81: LGTM!

The addition of the recoverEnabled property to the form schema is a good way to introduce the key recoverability feature. Setting the default value to false ensures that keys are not recoverable by default, which is a good security practice.


167-167: LGTM!

Adding the recoverEnabled property to the form's default values with a value of false is consistent with the addition of this property to the form schema. It ensures that the form's initial state has recoverEnabled set to false.


208-208: LGTM!

Including the recoverEnabled property in the object passed to key.mutateAsync when the form is submitted ensures that the value of recoverEnabled is passed to the server when creating a new key. This change is consistent with the addition of the recoverEnabled property to the form schema and default values.


297-297: LGTM!

Resetting the recoverEnabled property to false when the user clicks the "Create another key" button ensures that the recoverEnabled property is reset to its default value when creating a new key. This change is consistent with the addition of the recoverEnabled property to the form schema and default values.


812-859: LGTM!

The addition of the key recoverability card component is consistent with the introduction of the key recoverability feature. The toggle switch allows users to easily enable/disable key recoverability. The message displayed when key recoverability is enabled is important to inform users about the security implications of this feature. Including a link to the documentation is helpful for users who want to learn more about key recoverability.

@harshsbhat
Copy link
Contributor Author

See note

After I push the pnpm lock file for some reason it shows 17000+ lines changed. Is that supposed to look like that?

Copy link
Collaborator

@chronark chronark left a comment

Choose a reason for hiding this comment

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

I couldn't test this, I'm just getting unhelpful errors
CleanShot 2024-10-03 at 12 43 47@2x

This is fine to return to the user, but we have 0 error logs on our side.
Could you add console.error statements in all the catch clauses so we have a way to debug issues?

harshsbhat and others added 18 commits October 3, 2024 20:37
* Discord dark mode and fixing the link

* Requested changes

* Requested changes

* Lint

* Adding support to sidebar

* remove unused imports

* change the docs icon to book and remove repeating code

* Consistent size

* Making sure the Feedback is clickable, the feedback can be entered using the arrows and enter key on command

* Revert "Making sure the Feedback is clickable, the feedback can be entered using the arrows and enter key on command"

This reverts commit dc6183f.

* pnpm-lock file

* Fix

---------

Co-authored-by: Andreas Thomas <[email protected]>
* chore(email): update styling, copy, urls

* update urls

* fix class name

* remove unused imports, update date previews

* [autofix.ci] apply automated fixes

* make James the signer of all emails

* add biome ignore to prevent it from breaking an import

* add biome ignore to other component

* Update copy and links to use Dub

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: James Perkins <[email protected]>
Co-authored-by: James P <[email protected]>
* chore: Adds a five minute delay to emails

Adds a five minute delay to our Welcome email

* pnpm lock update

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
* Update to dot com

We has dot com, we should use it.

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
* chore: update legacy_keys_verifyKey.ts

identifiy -> identify

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
* added README.md in /packages/api

* added README.md in the files config in package.json
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
* fix: default ratelimits

* revert

* fix: cache ratelimits on cloudflare correctly

* chore: remove logs

* chore: remove log

* perf: remove unnecessary switch

* fix: track isolate start time

* test: tighten lower ratelimit threshold

* fix: only cache ratelimit blocks

* chore: sync lockfile

* test: improve accuracy of lower limit calculation in rate limit tests

* fix: address rabbit suggestions
@harshsbhat harshsbhat force-pushed the harshbhat/recoverable-keys branch from 5802f2c to 3a5bf01 Compare October 3, 2024 15:41
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.

Caution

Inline review comments failed to post

Actionable comments posted: 23

🧹 Outside diff range and nitpick comments (44)
internal/resend/src/components/signature.tsx (2)

1-3: LGTM! Consider using named import for React.

The imports look good and are appropriate for the component. However, you might want to consider using a named import for React to align with modern practices:

-// biome-ignore lint/style/useImportType: not just the type
-import React from "react";
+import * as React from "react";

This change would eliminate the need for the linting ignore comment and align with TypeScript's preference for named imports.


9-15: LGTM! Consider using React.VFC for stricter typing.

The Signature component is well-implemented and follows React best practices. It correctly uses the SignatureProps interface and renders the content as expected.

A minor suggestion for improvement:

-export const Signature: React.FC<SignatureProps> = ({ signedBy }) => (
+export const Signature: React.VFC<SignatureProps> = ({ signedBy }) => (

Using React.VFC (VoidFunctionComponent) instead of React.FC provides stricter typing by not implicitly including the children prop. This change aligns better with your component's props definition, which doesn't include children.

internal/resend/src/components/layout.tsx (1)

17-37: LGTM: Well-structured Layout component with a minor suggestion.

The Layout component is well-designed and follows best practices for email layouts. It effectively uses @react-email components and Tailwind classes for styling. The inclusion of a social media section is a good touch for user engagement.

Consider extracting the social media links into a separate component or configuration object. This would improve maintainability and make it easier to update the links in the future. For example:

const socialLinks = [
  { name: 'X (formerly Twitter)', url: 'https://x.com/unkeydev' },
  { name: 'Discord', url: 'https://unkey.link/lcMby11' },
  { name: 'GitHub', url: 'https://unkey.link/cfe4hcA' },
];

// Then in the component:
{socialLinks.map((link, index) => (
  <React.Fragment key={link.name}>
    {index > 0 && ' | '}
    <Link href={link.url}>{link.name}</Link>
  </React.Fragment>
))}

This approach would make the component more flexible and easier to maintain.

internal/resend/tailwind.config.ts (1)

58-72: LGTM: Comprehensive gray palette added.

The addition of a custom gray color palette is a great improvement. It provides a wide range of shades that can be used consistently across the application. The progression from light to dark is logical and includes an extra dark 950 shade, which is useful for high-contrast designs or dark mode implementations.

Consider adding a comment above the colors object to explain the purpose of this custom palette and how it should be used in the project. This can help other developers understand the design system better. For example:

// Custom gray palette for consistent use across the application.
// Use these colors instead of default gray shades for better design consistency.
colors: {
  // ... (rest of the colors object)
}
packages/api/README.md (4)

11-15: Consider adding installation instructions for other package managers

While the npm installation instructions are clear, consider adding instructions for other popular package managers like yarn or pnpm. This would make the README more inclusive for users with different preferences or project setups.

Example addition:

## Installation

npm:
```bash
npm install @unkey/api

yarn:

yarn add @unkey/api

pnpm:

pnpm add @unkey/api

---

`17-29`: **Enhance the quickstart section with more context**

The quickstart guide is informative and includes crucial security advice. To make it even more helpful for new users:

1. Provide a brief explanation of what a root key is and its importance.
2. Include a link or more specific instructions on where to find the settings for creating a new Unkey Root Key.

Example addition:
```markdown
## Quickstart

1. Create a new Unkey Root Key in the [settings](https://unkey.dev/app/settings). A root key is a secure credential that grants access to manage your Unkey account programmatically.
2. Use the root key to initialize the client:

...

**Important:** Always keep your root key safe and reset it if you suspect it has been compromised. The root key provides full access to your Unkey account, so it should be treated with the utmost security.

30-53: Consider adding more usage examples

The current usage section provides a clear example of verifying a key, which is helpful. To make the README more comprehensive, consider adding examples of other common operations that users might perform with the SDK. This could include:

  1. Creating a new key
  2. Revoking a key
  3. Updating key permissions
  4. Listing keys

Example addition:

### Creating a New Key

```ts
const { result, error } = await unkey.keys.create({
  apiId: "api_123",
  prefix: "prefix",
  byteLength: 16,
  ownerId: "user_123",
});

if (error) {
  console.error(error.message);
  return;
}

console.log("New key created:", result.key);

(Add more examples as needed)


This will give users a broader understanding of the SDK's capabilities right from the README.

---

`78-118`: **Add information about telemetry data collection**

The configuration options section is comprehensive and well-explained. To improve transparency and user trust, consider adding a brief explanation of what telemetry data is collected when not disabled. This information helps users make an informed decision about whether to opt out.

Example addition:
```markdown
### Disable Telemetry

By default, we collect anonymous usage data to help improve our services. This data includes [list specific data points collected]. To opt out of anonymous telemetry data collection:

```ts
const unkey = new Unkey({
  rootKey: "<UNKEY_ROOT_KEY>",
  disableTelemetry: true,
});

For more information about our data collection practices, please see our Privacy Policy.


This addition provides users with more context about the telemetry feature and directs them to more detailed information if needed.

</blockquote></details>
<details>
<summary>apps/dashboard/CHANGELOG.md (1)</summary><blockquote>

`3-7`: **LGTM! Consider adding more details to the changelog entry.**

The new version entry and dependency update are correctly formatted and placed. Good job on keeping the changelog up-to-date.



Consider adding a brief description of the changes or improvements introduced by the @unkey/ratelimit update to version 0.4.5. This would provide more context for users and developers reading the changelog.

For example:

```markdown
## 0.1.36

### Patch Changes

- Updated @unkey/ratelimit to version 0.4.5
  - [Brief description of changes or improvements in this update]
packages/hono/CHANGELOG.md (2)

3-8: LGTM! Consider adding a brief description of the change.

The new changelog entry for version 1.4.7 is well-formatted and consistent with previous entries. It clearly indicates the patch change and the updated dependency.

To enhance the changelog's informativeness, consider adding a brief description of the change or the reason for the dependency update. For example:

## 1.4.7

### Patch Changes

- Updated dependencies [82f5b5d]
  - @unkey/api@0.26.2
  - Brief description: [Insert reason for the update or any notable changes]

This additional context can help users and developers understand the significance of each update.


1-9: Well-maintained changelog. Consider adding a "Highlights" section for major versions.

The changelog is consistently formatted and effectively documents the version history and dependency updates of the @unkey/hono package. It's particularly noteworthy how diligently the @unkey/api dependency updates are tracked.

To further enhance the changelog's usefulness, consider adding a "Highlights" or "Summary of Changes" section for major version updates. This could provide a quick overview of significant changes or new features introduced in each major release, making it easier for users to understand the package's evolution at a glance.

packages/nextjs/CHANGELOG.md (1)

3-8: LGTM! Consider adding a brief description of the changes.

The new changelog entry for version 0.17.7 is correctly formatted and consistent with previous entries. It accurately reflects the patch update to the @unkey/api dependency.

To improve clarity, consider adding a brief description of any notable changes or fixes introduced by this dependency update. For example:

## 0.17.7

### Patch Changes

- Updated dependencies [82f5b5d]
  - @unkey/api@0.26.2
  - Brief description: [Insert any relevant information about the changes in @unkey/api@0.26.2]

This addition would provide more context for users and developers about the nature of the update.

internal/resend/src/client.tsx (3)

27-28: LGTM! Consider extracting common email configuration.

The changes to the from and replyTo fields improve email formatting and maintain consistency. Good job on standardizing these across all methods.

Consider extracting these common email configuration fields into a separate method or constant to reduce repetition across all email sending methods. For example:

private getCommonEmailConfig() {
  return {
    from: "James from Unkey <[email protected]>",
    replyTo: this.replyTo
  };
}

// Usage in methods:
const result = await this.client.emails.send({
  ...this.getCommonEmailConfig(),
  to: req.email,
  subject: "Your Unkey trial has ended",
  html,
});

66-67: LGTM! Consider parameterizing the email delay.

The addition of the scheduledAt parameter is a good feature for controlling when the welcome email is sent. The email field changes are consistent with other methods, which is great.

Consider parameterizing the delay for the welcome email to make it more flexible:

public async sendWelcomeEmail(req: { email: string, delayMinutes?: number }) {
  const delayMs = (req.delayMinutes ?? 5) * 60 * 1000;
  const scheduledAt = new Date(Date.now() + delayMs).toISOString();

  // ... rest of the method ...
}

This allows the caller to specify a custom delay if needed, while still defaulting to 5 minutes if not specified.

Also applies to: 72-73, 76-76


121-122: LGTM! Consider improving error logging.

The changes to the from and replyTo fields are consistent with the updates made to other methods, which is good for maintaining uniformity across the class.

Consider improving the error logging in the catch block. Instead of just logging the error, it would be more informative to include context about the operation that failed:

} catch (error) {
  console.error('Error sending leaked key email:', error);
  // Optionally, you could also log the email address (but be cautious with PII)
  // console.error('Failed to send leaked key email to:', email);
}

This will make debugging easier if issues arise in production.

apps/api/src/pkg/middleware/metrics.ts (2)

Line range hint 126-126: Consider performance impact of request body logging

The redacted requestBody is now being used in the analytics.insertApiRequest call, which enhances security by preventing sensitive data from being logged. However, for large requests, this could potentially impact performance.

Consider the following recommendations:

  1. Implement a size limit for the logged request body to prevent performance issues with very large requests.
  2. For large requests exceeding the limit, consider logging only a summary or the first N characters.

Example implementation:

const MAX_BODY_SIZE = 1024 * 10; // 10 KB limit
let truncatedRequestBody = requestBody.length > MAX_BODY_SIZE 
  ? requestBody.slice(0, MAX_BODY_SIZE) + '... (truncated)'
  : requestBody;

// Use truncatedRequestBody in analytics.insertApiRequest

This approach balances security needs with performance considerations.

🧰 Tools
🪛 Biome

[error] 10-11: Expected a statement but instead found '<<<<<<< HEAD'.

Expected a statement here.

(parse)


[error] 13-15: Expected a statement but instead found '=======

7ffdbc4 (fix: cf cache ratelimits (#2112))'.

Expected a statement here.

(parse)


[error] 15-15: numbers cannot be followed by identifiers directly after

an identifier cannot appear here

(parse)


Line range hint 1-138: Summary of changes and recommendations

The changes to this middleware enhance security by redacting sensitive information in the request body before logging. This is a positive improvement that aligns with best practices for handling potentially sensitive data.

Key points and recommendations:

  1. Resolve the merge conflict, ensuring that both the new security feature and the Cloudflare cache rate limits fix are correctly implemented.
  2. Consider implementing a size limit for logged request bodies to mitigate potential performance issues with large requests.
  3. After resolving the merge conflict, thoroughly test the middleware to ensure it functions as expected, particularly focusing on the interaction between the new request body handling and any changes from the Cloudflare cache rate limits fix.
  4. Update any relevant documentation to reflect the new behavior of redacting sensitive information in logged request bodies.

As a final step, consider adding a configuration option to enable/disable request body logging or adjust the maximum logged body size. This would provide flexibility for different environments (e.g., development vs. production) and use cases.

🧰 Tools
🪛 Biome

[error] 10-11: Expected a statement but instead found '<<<<<<< HEAD'.

Expected a statement here.

(parse)


[error] 13-15: Expected a statement but instead found '=======

7ffdbc4 (fix: cf cache ratelimits (#2112))'.

Expected a statement here.

(parse)


[error] 15-15: numbers cannot be followed by identifiers directly after

an identifier cannot appear here

(parse)

apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/keys.tsx (3)

101-101: LGTM! Consider applying the gap consistently.

The addition of the gap-2 class improves the spacing between grid items, which is a good UI enhancement. This change aligns with the overall improvements for the new recoverable keys feature.

For consistency, consider applying this gap to other similar grid layouts in the component, if any exist. This will ensure a uniform appearance throughout the UI.


Line range hint 1-143: Consider adding a recoverable key indicator

While this component doesn't directly implement the new recoverable keys feature, it might be beneficial to add a visual indicator for recoverable keys in the key list. This would provide users with at-a-glance information about which keys are recoverable.

You could add a new badge or icon next to recoverable keys. For example:

<div className="flex items-center col-span-3 gap-2">
  {/* Existing badges */}
  {k.isRecoverable && (
    <Badge variant="secondary">
      <KeyIcon className="w-4 h-4 mr-1" />
      Recoverable
    </Badge>
  )}
</div>

This assumes that the key object includes an isRecoverable property. If it doesn't, you may need to modify the data fetching logic to include this information.


Line range hint 1-143: Enhance security awareness for recoverable keys

With the introduction of recoverable keys, it's crucial to ensure users understand the security implications. Consider adding visual cues or informational tooltips to highlight the special nature of recoverable keys.

Suggestions:

  1. Add a tooltip or info icon next to recoverable keys explaining their nature and security considerations.
  2. Include a warning message in the key creation/editing process about the security implications of recoverable keys.
  3. Consider implementing an audit log for actions related to recoverable keys, if not already present.

Example tooltip implementation:

import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { InfoIcon } from "lucide-react";

// Inside the key list item
{k.isRecoverable && (
  <Tooltip>
    <TooltipTrigger>
      <InfoIcon className="w-4 h-4 text-warning" />
    </TooltipTrigger>
    <TooltipContent>
      This is a recoverable key. Ensure you understand the security implications and handle it with extra care.
    </TooltipContent>
  </Tooltip>
)}

These additions will help maintain a high level of security awareness among users interacting with recoverable keys.

apps/api/src/routes/legacy_keys_verifyKey.ts (1)

Deprecated route legacy_keys_verifyKey is still referenced in the codebase.

  • apps/api/src/worker.ts imports registerLegacyKeysVerifyKey from the deprecated route.
  • apps/api/src/routes/legacy_keys_verifyKey.test.ts still references the deprecated route.
🔗 Analysis chain

Line range hint 6-6: Verify deprecation status in relation to new recoverable keys feature.

This route is marked as deprecated. Given that the PR introduces a new feature for recoverable keys, it's important to ensure that this deprecation aligns with the overall API evolution strategy.

Let's check if there are any references to this deprecated route in relation to the new feature:

Consider documenting the migration path from this deprecated route to the new recoverable keys feature in the API documentation or changelog.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for references to this deprecated route in files related to the new recoverable keys feature
rg -i "legacy_keys_verifyKey" $(fd -t f -e ts -e tsx recoverable)

Length of output: 250

apps/dashboard/app/(app)/desktop-sidebar.tsx (2)

50-65: LGTM. Consider making the icon size configurable.

The DiscordIcon component is well-implemented as an SVG. To enhance its reusability, consider making the icon size configurable through props. This would allow for easy adjustment in different contexts without modifying the component itself.

Example:

const DiscordIcon = ({ size = 4 }: { size?: number }) => (
  <svg
    className={`w-${size} h-${size} fill-current`}
    // ... rest of the SVG code
  >
    {/* ... */}
  </svg>
);

143-153: LGTM. Consider using a custom icon for the "Docs" item as well.

The changes to the resourcesNavigation array are good. The Discord item has been added correctly, and the icon for the "Docs" item has been updated. For consistency, you might want to consider creating a custom icon for the "Docs" item as well, similar to the DiscordIcon.

const DocsIcon = () => (
  // Custom SVG implementation
);

// Then in resourcesNavigation
{
  icon: DocsIcon,
  href: "https://unkey.dev/docs",
  external: true,
  label: "Docs",
},

This would provide a consistent look and feel across all resource navigation items.

apps/dashboard/lib/trpc/routers/key/create.ts (3)

Line range hint 92-195: Resolve merge conflict and approve transaction handling changes.

There's a merge conflict in this section that needs to be resolved. The new version (after ">>>>>>> ca22641") with the updated transaction handling is the correct one to keep. This change improves the atomicity of the key creation and encryption process.

Please resolve the merge conflict by keeping the new version and removing the conflict markers.


226-255: LGTM: Key creation and encryption logic for recoverable keys.

The implementation of the encryption logic for recoverable keys is well-structured and includes proper error handling. The condition input.recoverEnabled && keyAuth?.storeEncryptedKeys ensures that encryption only occurs when necessary.

One minor suggestion:

Consider updating the error message on line 252 for clarity:

-                "We are unable to store encrypt the key. Please contact support using [email protected]",
+                "We are unable to store the encrypted key. Please contact support using [email protected]",

Line range hint 258-272: Consider handling potential audit logging failures.

Moving the audit logging outside the transaction is good for performance, but it introduces a potential inconsistency if the transaction succeeds and the audit logging fails.

Consider wrapping the ingestAuditLogs call in a try-catch block to handle potential failures gracefully:

try {
  await ingestAuditLogs({
    // ... existing code ...
  });
} catch (error) {
  console.error("Failed to ingest audit logs:", error);
  // Consider implementing a retry mechanism or alerting system
}

This ensures that any audit logging failures are caught and logged, allowing for easier debugging and potentially implementing a retry mechanism in the future.

🧰 Tools
🪛 Biome

[error] 173-173: Expected a statement but instead found ')'.

Expected a statement here.

(parse)


[error] 190-190: Expected an expression but instead found '==='.

Expected an expression here.

(parse)


[error] 190-190: Expected an expression but instead found '='.

Expected an expression here.

(parse)


[error] 188-190: Invalid assignment to await db .insert(schema.encryptedKeys) ======

This expression cannot be assigned to

(parse)


[error] 194-194: Expected an expression but instead found '>>>'.

Expected an expression here.

(parse)


[error] 194-194: Expected an expression but instead found '>'.

Expected an expression here.

(parse)


[error] 194-194: expected , but instead found transaction

Remove transaction

(parse)


[error] 182-182: This variable is unused.

Unused variables usually are result of incomplete refactoring, typos and other source of bugs.
Unsafe fix: If this is intentional, prepend vaultRes with an underscore.

(lint/correctness/noUnusedVariables)

apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/new/client.tsx (2)

941-946: Fix the documentation link for recoverable keys.

The link to the documentation about recoverable keys is incorrectly formatted.

Please update the link as follows:

-<Link
-  className="font-semibold"
-  href={"unkey.com/docs/security/recovering-keys"}
->
-  unkey.com/docs/security/recovering-keys.
+<Link
+  target="_blank"
+  className="font-semibold"
+  href="/docs/security/recovering-keys"
+>
+  unkey.com/docs/security/recovering-keys
</Link>

This change ensures that:

  1. The link is relative to the current domain.
  2. It opens in a new tab (consistent with the first Card).
  3. The trailing period is removed from the link text.

Line range hint 1-967: Overall implementation of recoverable keys feature is good, with room for improvement.

The implementation of the recoverable keys feature is generally correct and functional. However, there are a few areas that could be improved:

  1. Remove the duplicate "Recoverable" Card component as mentioned earlier.
  2. Ensure consistent formatting and naming conventions throughout the file.
  3. Consider extracting some of the larger Card components into separate, reusable components to improve readability and maintainability.

These improvements will enhance the overall quality and maintainability of the code.

To further improve the code structure, consider:

  1. Creating a separate component for the "Recoverable" Card.
  2. Using a custom hook to manage the form logic, which would help separate concerns and make the component more focused on rendering.
  3. Implementing a more robust error handling system for form submissions and API calls.
internal/resend/emails/secret_scanning_key_detected.tsx (3)

24-24: Correct the spelling of 'Github' to 'GitHub'

The correct spelling is "GitHub" with a capital 'H'.

Apply this diff:

-      <Text>Github found that one of your keys has been leaked. Details are as follows:</Text>
+      <Text>GitHub found that one of your keys has been leaked. Details are as follows:</Text>

52-55: Enhance link text for accessibility

For better accessibility, consider using more descriptive link text instead of displaying the email address directly.

Apply this diff:

       <Text>
-        Need help? Please reach out to{" "}
-        <Link href="mailto:[email protected]">[email protected]</Link> or just reply to this email.
+        Need help? Please reach out to our{" "}
+        <Link href="mailto:[email protected]">support team</Link> or just reply to this email.
       </Text>

61-64: Use HTTPS instead of HTTP for URLs

For security and user trust, it's recommended to use HTTPS in URLs.

Apply this diff:

 SecretScanningKeyDetected.PreviewProps = {
   date: "Tue Oct 01 2024", // Date().toDateString
   source: "commit",
-  url: "http://unkey.com",
+  url: "https://unkey.com",
 } satisfies Props;
internal/resend/emails/welcome_email.tsx (3)

31-31: Remove unnecessary whitespace strings.

On lines 31 and 41, there's an unnecessary string containing a space (" "). This extra space is not needed and can be removed to clean up the code.

Apply this diff to remove the unnecessary spaces:

-                {" "}

Also applies to: 41-41


32-33: Ensure consistent URL formatting in links.

The href attributes in your Link components use different URL formats:

  • https://go.unkey.com
  • https://www.unkey.com
  • https://unkey.com

For consistency and to prevent any potential confusion or redirection issues, consider standardizing the URLs to use a uniform format (e.g., always include or exclude www).

Also applies to: 36-38, 42-43


62-63: Manage user expectations in the postscript message.

The statement "I read and reply to every single one" may become unsustainable as the user base grows. Consider adjusting the message to set realistic expectations:

- P.S. - if you have any questions or feedback, reply to this email. I read and reply to every single one.
+ P.S. - if you have any questions or feedback, feel free to reply to this email. I'll do my best to get back to you.
internal/resend/emails/trial_ended.tsx (5)

26-27: Minor grammatical improvement in the message

The phrase "for your workspace" is repeated and could be rephrased for better readability.

Consider updating the text:

-        We hope you've enjoyed your two-week Unkey Pro trial for your workspace{" "}
-        <strong>{workspaceName}</strong>.
+        We hope you've enjoyed your two-week Unkey Pro trial of{" "}
+        <strong>{workspaceName}</strong>.

30-31: Enhance the clarity of the sentence

The sentence can be made more engaging by emphasizing the continuation of Pro features.

Consider rephrasing:

-        Since your trial ended, please add a payment method to keep all features of the Pro plan.
+        Now that your trial has ended, add a payment method to continue enjoying all the Pro plan features.

39-52: Clean up unnecessary JSX whitespace

The use of {" "} in lines 39, 44, and 49 is unnecessary as JSX handles spacing appropriately.

Apply this diff to remove redundant whitespace:

-          <li className="pb-4">
-            {" "}
-            1M monthly active keys included{" "}
+          <li className="pb-4">
+            1M monthly active keys included
             <span className="italic text-sm">(free users only get 1k total)</span>
           </li>
-          <li className="pb-4">
-            {" "}
-            150k monthly verifications included{" "}
+          <li className="pb-4">
+            150k monthly verifications included
             <span className="italic text-sm">(free users only get 2.5k per month)</span>
           </li>
-          <li className="pb-4">
-            {" "}
-            2.5M monthly ratelimits included{" "}
+          <li className="pb-4">
+            2.5M monthly ratelimits included
             <span className="italic text-sm">(free users only get 100k per month)</span>
           </li>

58-58: Consistency in list item formatting

In line 58, consider adding a period at the end of the sentence for consistency with other list items.

-            Unlimited seats at no additional cost so you can invite your whole team
+            Unlimited seats at no additional cost so you can invite your whole team.

75-75: Optional: Add spacing around the horizontal rule for better visual separation

Consider adding vertical spacing around the <Hr /> component to improve the email's readability.

       </Section>

+      <div className="py-4">
         <Hr />
+      </div>
apps/dashboard/components/dashboard/feedback-component.tsx (3)

28-29: Use camelCase for Prop Names

The prop FeedbackOpen is not following the camelCase convention commonly used in JavaScript and TypeScript. Renaming it to feedbackOpen improves consistency and readability.

Apply this diff to rename the prop:

interface FeedbackProps {
  variant?: FeedbackVariant;
-  FeedbackOpen?: boolean;
+  feedbackOpen?: boolean;
}

Update its usage accordingly:

-export const Feedback: React.FC<FeedbackProps> = ({ variant, FeedbackOpen }) => {
+export const Feedback: React.FC<FeedbackProps> = ({ variant, feedbackOpen }) => {

  const [open, setOpen] = useState(false);

  useEffect(() => {
-    if (FeedbackOpen) {
+    if (feedbackOpen) {
      setOpen(true);
    } else {
      setOpen(false);
    }
-  }, [FeedbackOpen]);
+  }, [feedbackOpen]);

44-47: Clarify the Comment for Better Understanding

The comment explaining the necessity of the code could be more specific to help future maintainers understand why this implementation is required.

Consider updating the comment for clarity:

/**
- * This was necessary cause otherwise the dialog would not close when you're clicking outside of it
+ * Ensuring the `open` prop and `onOpenChange` handler are set allows the dialog to close when clicking outside of it.
 */

83-85: Add Accessible Label to the Feedback Button

The feedback button lacks an aria-label, which can hinder accessibility for users relying on screen readers. Adding an aria-label will improve the user experience for all users.

Apply this diff to include an accessible label:

<button
  type="button"
  onClick={() => setOpen(true)}
  className={`flex items-center w-full ${cursorClasses}`}
+ aria-label="Open feedback form"
>
  <MessagesSquare className="w-4 h-4 mr-2" />
  Feedback
</button>
internal/resend/emails/payment_issue.tsx (2)

24-25: Improve date formatting for better readability

The date displayed using Date().toDateString results in a format like "Tue Oct 01 2024", which may not be the most user-friendly. Consider formatting the date to a more readable format, such as "October 1, 2024", using a date formatting library like date-fns or Moment.js.


43-43: Consider providing a full name or title in the signature

Using only "James" in the signature may be too informal. Consider providing a full name or including the individual's role or department to make it more professional.

🛑 Comments failed to post (23)
apps/api/src/routes/v1_ratelimit_limit.accuracy.test.ts (1)

99-103: ⚠️ Potential issue

Resolve merge conflict and remove conflict markers

There's an unresolved merge conflict in this section. Please resolve the conflict by keeping the new calculation for upperLimit and removing the conflict markers.

Apply this diff to resolve the merge conflict:

-<<<<<<< HEAD
-    const upperLimit = Math.round(exactLimit * 2.5);
-=======
     const upperLimit = Math.round(exactLimit * 1.5);
->>>>>>> 7ffdbc4f (fix: cf cache ratelimits (#2112))
📝 Committable suggestion

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

    const upperLimit = Math.round(exactLimit * 1.5);
🧰 Tools
🪛 Biome

[error] 100-101: Expected a statement but instead found '======='.

Expected a statement here.

(parse)


[error] 102-103: Expected a statement but instead found '>>>>>>> 7ffdbc4 (fix: cf cache ratelimits (#2112))'.

Expected a statement here.

(parse)


[error] 103-103: numbers cannot be followed by identifiers directly after

an identifier cannot appear here

(parse)


[error] 102-102: Shouldn't redeclare 'upperLimit'. Consider to delete it or rename it.

'upperLimit' is defined here:

(lint/suspicious/noRedeclare)


[error] 100-100: This variable is unused.

Unused variables usually are result of incomplete refactoring, typos and other source of bugs.
Unsafe fix: If this is intentional, prepend upperLimit with an underscore.

(lint/correctness/noUnusedVariables)

internal/resend/src/client.tsx (1)

42-42: ⚠️ Potential issue

Address the TODO comment regarding method deprecation.

The TODO comment suggests that this method might be unused and should be deprecated.

If this method is indeed unused, consider removing it entirely to keep the codebase clean. If there's a reason to keep it, please add a comment explaining why, and consider marking it as deprecated using a proper TypeScript decorator:

/** @deprecated This method is no longer in use and will be removed in the next major version. */
@deprecated("This method is no longer in use and will be removed in the next major version.")
public async sendSubscriptionEnded(req: { email: string; name: string; }): Promise<void> {
  // ...
}

Also, ensure to update any documentation or API references if you decide to deprecate or remove this method.

apps/api/src/routes/legacy_keys_verifyKey.ts (1)

51-51: 💡 Codebase verification

Found remaining instances of typo "identifiy" in multiple files

The typo "identifiy" was found in the following locations:

  • packages/api/src/openapi.d.ts:

    • * @description The name of the key, give keys a name to easily identifiy their purpose
    • * @description The name of the key, give keys a name to easily identifiy their purpose
  • apps/api/src/routes/v1_keys_verifyKey.ts:

    • "The name of the key, give keys a name to easily identifiy their purpose",

Please correct these instances by replacing "identifiy" with "identify" to ensure documentation clarity.

🔗 Analysis chain

LGTM: Typo correction improves documentation clarity.

The correction from "identifiy" to "identify" in the description of the 'name' field enhances the clarity of the API documentation. This change, while minor, contributes to maintaining high-quality documentation.

Let's check if there are any other occurrences of this typo in the file:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any remaining instances of "identifiy" in the file
rg "identifiy" "apps/api/src/routes/legacy_keys_verifyKey.ts"

Length of output: 59


Script:

#!/bin/bash
# Recursively search for any instances of "identifiy" in the entire codebase
rg "identifiy" --ignore-dir=node_modules --ignore-dir=dist --ignore-dir=build

Length of output: 520


Script:

#!/bin/bash
# Recursively search for any instances of "identifiy" in the entire codebase
rg "identifiy"

Length of output: 402

apps/dashboard/lib/trpc/routers/key/create.ts (1)

3-6: ⚠️ Potential issue

Remove duplicate import of 'env'.

There's a duplicate import of the 'env' module.

Please remove the duplicate import on line 6:

import { env } from "@/lib/env";
import { ingestAuditLogsTinybird } from "@/lib/tinybird";
import { rateLimitedProcedure, ratelimit } from "@/lib/trpc/ratelimitProcedure";
- import { env } from "@/lib/env";
import { TRPCError } from "@trpc/server";

This will resolve the "noRedeclare" warning from the static analysis tool.

📝 Committable suggestion

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

import { env } from "@/lib/env";
import { ingestAuditLogsTinybird } from "@/lib/tinybird";
import { rateLimitedProcedure, ratelimit } from "@/lib/trpc/ratelimitProcedure";
import { TRPCError } from "@trpc/server";
🧰 Tools
🪛 Biome

[error] 6-6: Shouldn't redeclare 'env'. Consider to delete it or rename it.

'env' is defined here:

(lint/suspicious/noRedeclare)

apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/new/client.tsx (1)

850-949: ⚠️ Potential issue

Remove duplicate "Recoverable" Card component.

There are two nearly identical Card components for the "Recoverable" feature. This duplication is likely unintended and could lead to confusion or inconsistent behavior.

To resolve this, please remove one of the Card components. The first one (lines 850-901) seems more appropriate as it's conditionally rendered based on checkStoreEncryptedKeys. Here's a suggested fix:

  1. Keep the Card component at lines 850-901.
  2. Remove the duplicate Card component at lines 902-949.
  3. Ensure that the remaining Card component correctly handles all necessary logic and UI elements for the recoverable feature.

This will eliminate redundancy and potential inconsistencies in the UI.

internal/resend/emails/subscription_ended.tsx (1)

17-17: ⚠️ Potential issue

Correct text-semibold to font-semibold

The class text-semibold is not a valid Tailwind CSS class. Replace it with font-semibold to properly apply the font weight.

Apply this diff to fix the class name:

-      <Heading className="font-sans text-3xl text-semibold text-center">
+      <Heading className="font-sans text-3xl font-semibold text-center">
📝 Committable suggestion

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

      <Heading className="font-sans text-3xl font-semibold text-center">
internal/resend/emails/secret_scanning_key_detected.tsx (3)

27-37: 🛠️ Refactor suggestion

Remove unnecessary whitespace elements

The {" "} elements inside the <li> tags are unnecessary and can be removed to clean up the code.

Apply this diff:

         <li className="pt-4">
-          {" "}
           <strong>Source:</strong> {source}
-          {" "}
         </li>
         <li className="pt-4">
-          {" "}
           <strong>Date:</strong> {date}
-          {" "}
         </li>
         <li className="pt-4">
-          {" "}
           <strong>URL:</strong> {url}
         </li>
📝 Committable suggestion

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

          <strong>Source:</strong> {source}
        </li>
        <li className="pt-4">
          <strong>Date:</strong> {date}
        </li>
        <li className="pt-4">
          <strong>URL:</strong> {url}
        </li>

20-20: ⚠️ Potential issue

Use correct Tailwind CSS class: replace 'text-semibold' with 'font-semibold'

The class name "text-semibold" in the <Heading> component is not a valid Tailwind CSS class. To make the text semibold, you should use "font-semibold" instead.

Apply this diff to fix the class name:

-      <Heading className="font-sans text-3xl text-semibold text-center">
+      <Heading className="font-sans text-3xl font-semibold text-center">
📝 Committable suggestion

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

      <Heading className="font-sans text-3xl font-semibold text-center">

46-51: 🛠️ Refactor suggestion

Use meaningful link text instead of raw URLs

Displaying raw URLs can be less user-friendly. Consider using descriptive text for the link to improve readability.

Apply this diff:

       <Text>
-        You can disable the Root Key in your dashboard by following our docs available at{" "}
-        <Link href="https://www.unkey.com/docs/security/root-keys">
-          https://www.unkey.com/docs/security/root-keys
-        </Link>
-        .
+        You can disable the Root Key in your dashboard by following our{" "}
+        <Link href="https://www.unkey.com/docs/security/root-keys">
+          documentation on Root Keys
+        </Link>
+        .
       </Text>
📝 Committable suggestion

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

        You can disable the Root Key in your dashboard by following our{" "}
        <Link href="https://www.unkey.com/docs/security/root-keys">
          documentation on Root Keys
        </Link>
        .
      </Text>
internal/resend/emails/welcome_email.tsx (2)

59-59: 🛠️ Refactor suggestion

Enhance the feedback request about how users heard about Unkey.

Currently, you're asking users how they heard about Unkey without providing a way to respond directly. Consider adding a link to a quick survey or prompting them to reply to the email:

- <Text>Also, just curious - how did you hear about Unkey?</Text>
+ <Text>Also, just curious - how did you hear about Unkey? Reply to this email and let us know!</Text>

Committable suggestion was skipped due to low confidence.


20-24: 🛠️ Refactor suggestion

Personalize the greeting message with the user's name.

Currently, the greeting says "Hi there!". If you have access to the user's name, consider personalizing the greeting for a more engaging experience:

- <Text>Hi there!</Text>
+ <Text>Hi {username}!</Text>

Committable suggestion was skipped due to low confidence.

internal/resend/emails/trial_ended.tsx (2)

67-73: 🛠️ Refactor suggestion

Button styling best practices

Ensure the button's class names align with Tailwind CSS best practices, and consider extracting styles into a reusable component if used frequently.


20-20: ⚠️ Potential issue

Incorrect Tailwind CSS class: Replace text-semibold with font-semibold

In line 20, the class text-semibold is not a valid Tailwind CSS class for font weight. The correct class is font-semibold.

Apply this diff to fix the class name:

-      <Heading className="font-sans text-3xl text-semibold text-center">
+      <Heading className="font-sans text-3xl font-semibold text-center">
📝 Committable suggestion

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

      <Heading className="font-sans text-3xl font-semibold text-center">
apps/dashboard/components/dashboard/command-menu.tsx (1)

90-94: ⚠️ Potential issue

Render FeedbackComponent outside CommandItem to manage visibility properly

Currently, FeedbackComponent is rendered inside CommandItem, which may lead to unexpected behavior since CommandItem is intended to display menu items, not render modal components. To properly manage the visibility of FeedbackComponent, render it outside of CommandItem and control its visibility using the open state.

Here is a suggested refactor:

 const FeedbackCommand: React.FC = () => {
   const [open, setOpen] = useState(false);
-  return (
-    <CommandItem onSelect={() => setOpen(true)}>
-      <FeedbackComponent variant="command" FeedbackOpen={open} />
-    </CommandItem>
-  );
+  return (
+    <>
+      <CommandItem onSelect={() => setOpen(true)}>
+        <FeedbackIcon className="w-4 h-4 mr-2" />
+        <span>Send Feedback</span>
+      </CommandItem>
+      <FeedbackComponent
+        variant="command"
+        FeedbackOpen={open}
+        onClose={() => setOpen(false)}
+      />
+    </>
+  );
 };

Note: Ensure to import an appropriate icon for FeedbackIcon, or replace it with suitable text to maintain consistency with other command items.

📝 Committable suggestion

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

const FeedbackCommand: React.FC = () => {
  const [open, setOpen] = useState(false);
  return (
    <>
      <CommandItem onSelect={() => setOpen(true)}>
        <FeedbackIcon className="w-4 h-4 mr-2" />
        <span>Send Feedback</span>
      </CommandItem>
      <FeedbackComponent
        variant="command"
        FeedbackOpen={open}
        onClose={() => setOpen(false)}
      />
    </>
  );
};
apps/dashboard/components/dashboard/feedback-component.tsx (5)

33-33: ⚠️ Potential issue

Typo in Variable Name cursorClasess

There's a typo in the variable name cursorClasess. It should be cursorClasses to maintain consistent spelling and prevent potential confusion.

Apply this diff to correct the typo:

-const cursorClasess = variant === "command" ? "cursor-default" : "cursor-pointer";
+const cursorClasses = variant === "command" ? "cursor-default" : "cursor-pointer";

...

<button
  type="button"
  onClick={() => setOpen(true)}
-  className={`flex items-center w-full ${cursorClasess}`}
+  className={`flex items-center w-full ${cursorClasses}`}
>

Also applies to: 81-81


88-89: ⚠️ Potential issue

Handle Form Submission with onSubmit

Currently, the form submission logic is attached to the submit button's onClick handler. It's best practice to handle form submission using the form's onSubmit event to ensure proper handling of form submission events, including when the user presses Enter.

Apply this diff to move the submission logic to the form:

<Form {...form}>
-  <form>
+  <form
+    onSubmit={form.handleSubmit((data) => {
+      create.mutate(data);
+    })}
+  >
    <DialogContent>
      {/* ... */}
    </DialogContent>
  </form>
</Form>

Then, remove the onClick handler from the submit button:

<Button
  type="submit"
  disabled={create.isLoading}
-  onClick={form.handleSubmit((data) => {
-    create.mutate(data);
-  })}
>
  {create.isLoading ? <Loading /> : "Send"}
</Button>

Also applies to: 168-172


24-24: 🛠️ Refactor suggestion

Unnecessary Type Alias FeedbackOpen

The type alias FeedbackOpen = boolean is unnecessary since it's simply a boolean. You can directly use boolean in your FeedbackProps interface for clarity.

Apply this diff to simplify the type:

-type FeedbackOpen = boolean;

interface FeedbackProps {
  variant?: FeedbackVariant;
-  FeedbackOpen?: FeedbackOpen;
+  feedbackOpen?: boolean;
}

Committable suggestion was skipped due to low confidence.


76-76: 🛠️ Refactor suggestion

Simplify Complex className String

The className string is quite long and can be hard to read and maintain. Consider extracting it into a constant or using a utility like clsx or classnames to improve readability.

Example using a constant:

+const containerClassName = `w-full transition-all duration-150 group flex gap-x-2 rounded-md text-sm font-normal leading-6 items-center border border-transparent hover:bg-background-subtle hover:text-content justify-between ${commandClasses}`;

return (
  <div
-    className={`w-full transition-all duration-150 group flex gap-x-2 rounded-md text-sm font-normal leading-6 items-center border border-transparent hover:bg-background-subtle hover:text-content justify-between ${commandClasses}`}
+    className={containerClassName}
  >
    {/* ... */}
  </div>
);

Or using clsx for better management:

+import clsx from "clsx";

return (
  <div
    className={clsx(
      "w-full transition-all duration-150 group flex gap-x-2 rounded-md text-sm font-normal leading-6 items-center border border-transparent hover:bg-background-subtle hover:text-content justify-between",
      commandClasses
    )}
  >
    {/* ... */}
  </div>
);

Committable suggestion was skipped due to low confidence.


63-72: 💡 Codebase verification

⚠️ Potential issue

Multiple Instances of toast.error Require Proper Error Handling

Several usages of toast.error across the codebase do not provide a default error message, which could lead to potential runtime errors if err.message is undefined. Please update these instances to include a fallback message.

  • Example:
    toast.error(err.message || "An unexpected error occurred.");
🔗 Analysis chain

Ensure Proper Error Handling in Mutation

The error handling in the create mutation assumes that err.message exists. To prevent potential runtime errors, ensure that err.message is always available or provide a default message.

Apply this diff to safeguard against undefined error messages:

onError(err) {
  console.error(err);
-  toast.error(err.message);
+  toast.error(err.message || "An unexpected error occurred.");
},

Run the following script to check all usages of toast.error to ensure they handle errors appropriately:


Action Required: Re-run Verification with Corrected Script

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that all uses of 'toast.error' handle error messages safely.

# Search for 'toast.error' usages in TypeScript and TSX files
rg --type-add 'typescript: *.ts' --type-add 'tsx: *.tsx' --type {typescript,tsx} 'toast\.error\('

# Expected: All usages should handle cases where 'err.message' might be undefined.

Length of output: 152


Script:

#!/bin/bash
# Description: Verify that all uses of 'toast.error' handle error messages safely.

# Define custom types for TypeScript and TSX
rg --type-add 'typescript:*.ts' --type-add 'tsx:*.tsx' --type typescript --type tsx 'toast\.error\('

Length of output: 7747

apps/api/src/pkg/ratelimit/client.ts (2)

39-43: ⚠️ Potential issue

Unresolved merge conflict markers found between lines 39-43

Merge conflict markers (<<<<<<<, =======, >>>>>>>) are present in the code between lines 39 and 43. This indicates that a merge conflict has not been properly resolved. Please resolve these conflicts to ensure the code compiles and functions correctly.

🧰 Tools
🪛 Biome

[error] 40-42: Expected a statement but instead found '=======
private setCache(id: string, current: number, reset: number, blocked: boolean)'.

Expected a statement here.

(parse)


[error] 42-43: Expected a statement but instead found '>>>>>>> 7ffdbc4 (fix: cf cache ratelimits (#2112))'.

Expected a statement here.

(parse)


[error] 43-43: numbers cannot be followed by identifiers directly after

an identifier cannot appear here

(parse)


62-70: ⚠️ Potential issue

Unresolved merge conflict markers found between lines 62-70

There are merge conflict markers (<<<<<<<, =======, >>>>>>>) present between lines 62 and 70. These need to be resolved by selecting the correct code and removing the conflict markers.

🧰 Tools
🪛 Biome

[error] 67-69: Expected a statement but instead found '=======
this.cache.set(id,'.

Expected a statement here.

(parse)


[error] 69-69: Expected a statement but instead found ')'.

Expected a statement here.

(parse)


[error] 69-70: Expected a statement but instead found '>>>>>>> 7ffdbc4 (fix: cf cache ratelimits (#2112))'.

Expected a statement here.

(parse)


[error] 70-70: numbers cannot be followed by identifiers directly after

an identifier cannot appear here

(parse)

internal/resend/emails/payment_issue.tsx (2)

19-19: ⚠️ Potential issue

Fix incorrect Tailwind CSS class name in Heading component

In the className for the Heading component, text-semibold is not a valid Tailwind CSS class. The correct class for setting font weight to semi-bold is font-semibold.

Apply this diff to fix the class name:

-<Heading className="font-sans text-3xl text-semibold text-center">
+<Heading className="font-sans text-3xl font-semibold text-center">
📝 Committable suggestion

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

      <Heading className="font-sans text-3xl font-semibold text-center">

28-35: ⚠️ Potential issue

Ensure button styles render correctly in email clients

Email clients often have limited support for CSS classes and external stylesheets. To ensure the button renders consistently across different email clients, consider inlining the styles instead of relying on Tailwind CSS classes.

Apply this diff to inline the styles for the Button component:

-<Button
-  href="https://app.unkey.com/settings/billing/stripe"
-  className="bg-gray-900 text-gray-50 rounded-lg p-3 w-2/3"
->
+<Button
+  href="https://app.unkey.com/settings/billing/stripe"
+  style="
+    background-color: #1f2937; /* bg-gray-900 */
+    color: #f9fafb; /* text-gray-50 */
+    border-radius: 0.5rem; /* rounded-lg */
+    padding: 12px; /* p-3 */
+    width: 66.6667%; /* w-2/3 */
+    text-align: center;
+    text-decoration: none;
+    display: inline-block;
+  "
+>
📝 Committable suggestion

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

      <Section className="text-center py-3">
        <Button
          href="https://app.unkey.com/settings/billing/stripe"
          style="
            background-color: #1f2937; /* bg-gray-900 */
            color: #f9fafb; /* text-gray-50 */
            border-radius: 0.5rem; /* rounded-lg */
            padding: 12px; /* p-3 */
            width: 66.6667%; /* w-2/3 */
            text-align: center;
            text-decoration: none;
            display: inline-block;
          "
        >
          Update payment information
        </Button>
      </Section>

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.

Allow setting recoverable: true when creating keys in the dashboard
7 participants