Skip to content

Migrate and Move Schemas#31

Merged
FindMalek merged 5 commits intomainfrom
fin-107
Oct 21, 2025
Merged

Migrate and Move Schemas#31
FindMalek merged 5 commits intomainfrom
fin-107

Conversation

@FindMalek
Copy link
Owner

@FindMalek FindMalek commented Oct 21, 2025

Summary by CodeRabbit

  • Refactor

    • Centralized and standardized validation schemas across auth, credential, container, user, and utility flows to ensure consistent form validation and API contracts.
  • New Features

    • Added new input/output schemas for credential key-value operations, QR code forms, environment validation, enums (activity/entity), and default container handling.
    • New auth form schemas for login and sign-up; expanded waitlist and roadmap subscription schemas.
  • Documentation

    • Updated docs to position the schemas folder as the single source of truth and clarified import/migration guidance.

@FindMalek FindMalek self-assigned this Oct 21, 2025
@linear
Copy link

linear bot commented Oct 21, 2025

@vercel
Copy link
Contributor

vercel bot commented Oct 21, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
zero-locker Ready Ready Preview Comment Oct 21, 2025 8:38pm

@coderabbitai
Copy link

coderabbitai bot commented Oct 21, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Centralizes Zod schemas into a new schemas folder, adding many input/output/utility schemas and types, updating components and ORPC routers to consume the centralized schemas, and removing the legacy exported schemas from config/schema.ts.

Changes

Cohort / File(s) Summary
Schema Consolidation (auth, user, utils)
schemas/user/user/input.ts, schemas/user/waitlist/output.ts, schemas/utils/utils.ts, schemas/utils/index.ts, schemas/utils/container/...
Adds centralized authentication schemas (loginInputSchema, signUpInputSchema), waitlist/user output schema, utility schemas (qrCodeFormInputSchema, emptyInputSchema), container input/output schemas, and re-exports/types.
Credential Schema Layer
schemas/credential/input.ts, schemas/credential/key-value/input.ts, schemas/credential/key-value/output.ts, schemas/credential/output.ts
Introduces credential input schemas (moveCredentialInputSchema, deleteCredentialConfirmationInputSchema, credentialFormWithIdInputSchema, key-value inputs) and output schemas (security settings, key-value pairs, password, update results) plus corresponding types.
Component form migrations
components/app/auth-login-form.tsx, components/app/auth-register-form.tsx, components/app/dashboard-credential-delete-dialog.tsx, components/app/dashboard-credential-move-dialog.tsx, components/app/dashboard-qr-code-dialog.tsx, components/app/marketing-roadmap-subscription.tsx, components/app/marketing-waitlist-form.tsx
Replace local/legacy Zod schemas and inferred types with centralized schema imports and types; update useForm generics, zodResolver calls, and handler parameter types to the new schema types.
ORPC router schema usage
orpc/routers/container.ts, orpc/routers/credential.ts, orpc/routers/user.ts
Swap inline input/output Zod definitions for centralized schemas (e.g., emptyInputSchema, getDefaultContainerForEntityInputSchema, credential output schemas); update procedure signatures and exported types to use the new schemas.
Legacy removals
config/schema.ts
Removes exported Zod schemas and inferred public types previously defined here (loginSchema, signUpSchema, WaitlistUserDtoSchema, UserDto, UserRo, and their types).

Sequence Diagram(s)

sequenceDiagram
  participant UI as Component (Form)
  participant Schema as Schema Module
  participant ORPC as ORPC Router
  participant DB as Database / Service

  UI->>Schema: import <inputSchema>, zodResolver
  UI->>Schema: validate form (zodResolver)
  UI->>ORPC: call procedure with validated data
  ORPC->>Schema: import <input/output schemas> (validate/shape)
  ORPC->>DB: perform operation (use typed input)
  DB-->>ORPC: result
  ORPC->>Schema: format result with output schema
  ORPC-->>UI: response (typed via schema)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Possibly related PRs

Poem

🐰 I hopped from config to a tidy fold,
Brought schemas home — both new and old.
Forms now lean on one true nest,
Types snug in zod's gentle rest. 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "Migrate and Move Schemas" directly aligns with the primary objective of this pull request. The changeset demonstrates a systematic consolidation of schema definitions from a scattered configuration file (config/schema.ts) into a centralized schemas folder structure, along with corresponding updates to component and router imports throughout the codebase. The terms "Migrate" and "Move" accurately capture the core activities: relocating schema definitions and updating references across the application. The title is specific and meaningful—developers reviewing the commit history would immediately understand that this involves schema reorganization and refactoring.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 699f523 and 259d7c9.

📒 Files selected for processing (2)
  • .cursor/rules/schemas-folder.mdc (10 hunks)
  • schemas/utils/utils.ts (2 hunks)

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

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

⚠️ Outside diff range comments (3)
orpc/routers/credential.ts (3)

523-566: Critical: missing server-side plan check on container change enables plan bypass.

When updating a credential, restricted users (canOnlyAccessDefaultContainers) can connect to a non-default container. Mirror the create flow’s check to enforce plan constraints.

   .handler(async ({ input, context }): Promise<CredentialSimpleOutput> => {
     const { id, ...updateData } = input
@@
     if (!existingCredential) {
       throw new ORPCError("NOT_FOUND")
     }
 
+    // Enforce plan: restricted users may only use default containers
+    if (
+      context.permissions?.canOnlyAccessDefaultContainers &&
+      updateData.containerId
+    ) {
+      const allowedContainer = await database.container.findFirst({
+        where: {
+          id: updateData.containerId,
+          userId: context.user.id,
+          isDefault: true,
+        },
+      })
+      if (!allowedContainer) {
+        throw new ORPCError("FORBIDDEN", {
+          message: "Your plan only allows using default containers.",
+        })
+      }
+    }

As per orpc/routers guidelines on auth/permissions.


687-777: Apply the same plan enforcement in updateCredentialWithSecuritySettings.

Replicate the default-container restriction before updates.

   .handler(async ({ input, context }): Promise<CredentialSimpleOutput> => {
     const {
       id,
       passwordProtection,
       twoFactorAuth,
       accessLogging,
       ...updateData
     } = input
@@
     if (!existingCredential) {
       throw new ORPCError("NOT_FOUND")
     }
 
+    // Enforce plan: restricted users may only use default containers
+    if (
+      context.permissions?.canOnlyAccessDefaultContainers &&
+      updateData.containerId
+    ) {
+      const allowedContainer = await database.container.findFirst({
+        where: {
+          id: updateData.containerId,
+          userId: context.user.id,
+          isDefault: true,
+        },
+      })
+      if (!allowedContainer) {
+        throw new ORPCError("FORBIDDEN", {
+          message: "Your plan only allows using default containers.",
+        })
+      }
+    }

1-46: Remove unused imports to comply with coding guidelines.

Verification confirms these three imports are unused in orpc/routers/credential.ts:

  • credentialFormInputSchema (line 14) — defined in schemas but not referenced in this router
  • credentialKeyValuePairOutputSchema (line 16) — never used in this file
  • keyValueWithValueOutputSchema (line 47) — only appears in the import statement

Remove them as suggested to satisfy the "Disallow unused imports" guideline.

-import {
-  createCredentialInputSchema,
-  credentialFormInputSchema,
+import {
+  createCredentialInputSchema,
   credentialFormWithIdInputSchema,
-  credentialKeyValuePairOutputSchema,
   credentialKeyValuePairsArrayOutputSchema,
   credentialKeyValuePairsWithValueArrayOutputSchema,
   credentialKeyValuePairValueOutputSchema,
-  credentialKeyValuePairWithValueOutputSchema,
   credentialPasswordOutputSchema,
   credentialSecuritySettingsOutputSchema,
@@
-} from "@/schemas/credential"
-import { keyValueWithValueOutputSchema } from "@/schemas/credential/key-value"
+} from "@/schemas/credential"
🧹 Nitpick comments (17)
schemas/user/user/input.ts (2)

48-51: Complete list input: add search/filters and integer constraints.

Lists should include pagination, search, and filtering with bounds; also mark numbers as integers.

-export const listUsersInputSchema = z.object({
-  page: z.number().min(1).default(1),
-  limit: z.number().min(1).max(100).default(10),
-})
+export const listUsersInputSchema = z.object({
+  page: z.number().int().min(1).default(1),
+  limit: z.number().int().min(1).max(100).default(10),
+  search: z.string().trim().max(100).default(""),
+  filters: z.record(z.string(), z.any()).default({}).optional(),
+})

Based on learnings.


9-10: Tighten string bounds and normalize emails.

Add sensible max lengths and normalize emails; cap password length to avoid DoS/BCrypt cost spikes.

-  name: z.string().min(2, "Name must be at least 2 characters"),
-  email: z.string().email("Please enter a valid email address"),
+  name: z.string().min(2, "Name must be at least 2 characters").max(100, "Name must be at most 100 characters"),
+  email: z.string().email("Please enter a valid email address").max(320).transform(v => v.toLowerCase()),
 export const loginInputSchema = z.object({
-  email: z.string().email("Please enter a valid email address"),
-  password: z.string().min(8, "Password must be at least 8 characters"),
+  email: z.string().email("Please enter a valid email address").max(320).transform(v => v.toLowerCase()),
+  password: z.string().min(8, "Password must be at least 8 characters").max(128, "Password must be at most 128 characters"),
   rememberMe: z.boolean(),
 })
 export const signUpInputSchema = z.object({
-  name: z.string().min(2, "Name must be at least 2 characters"),
-  email: z.string().email("Please enter a valid email address"),
-  password: z.string().min(8, "Password must be at least 8 characters"),
+  name: z.string().min(2, "Name must be at least 2 characters").max(100, "Name must be at most 100 characters"),
+  email: z.string().email("Please enter a valid email address").max(320).transform(v => v.toLowerCase()),
+  password: z.string().min(8, "Password must be at least 8 characters").max(128, "Password must be at most 128 characters"),
   image: z.string().url("Please enter a valid image URL").optional(),
 })

Based on learnings.

Also applies to: 59-63, 67-73

components/app/auth-register-form.tsx (2)

36-41: Align schema/UI: image default is unused at submit.

You set a default image but override with a computed URL. Either remove image from defaultValues or add a field if you want user-provided images.

-  defaultValues: {
-    email: "",
-    name: "",
-    password: "",
-    image: "https://avatar.vercel.sh/default",
-  },
+  defaultValues: {
+    email: "",
+    name: "",
+    password: "",
+  },

Also applies to: 52-52


145-150: Adopt disabled opacity pattern on buttons.

Match button loading/disabled UX convention.

-<Button type="submit" className="w-full" disabled={isLoading}>
+<Button type="submit" className="w-full disabled:opacity-50" disabled={isLoading}>

Based on learnings.

components/app/marketing-roadmap-subscription.tsx (2)

40-52: Clean up nested timeout to avoid state updates after unmount.

The inner setTimeout isn’t cleared; track and clear both timers.

 useEffect(() => {
   if (showSuccess) {
-    const timer = setTimeout(() => {
+    const timer = setTimeout(() => {
       setIsTransitioning(true)
-      setTimeout(() => {
+      const fadeTimer = setTimeout(() => {
         setShowSuccess(false)
         setIsTransitioning(false)
-      }, 300) // Wait for fade out animation
+      }, 300) // Wait for fade out animation
     }, 3000) // Show success for 3 seconds

-    return () => clearTimeout(timer)
+    return () => {
+      clearTimeout(timer)
+      // Clear any in-flight fade timer
+      try { clearTimeout((fadeTimer as unknown as number)) } catch {}
+    }
   }
 }, [showSuccess])

115-124: Adopt disabled opacity pattern on buttons.

Enhance disabled-state affordance.

-<Button
-  type="submit"
-  className="w-full sm:w-auto"
-  disabled={subscribeToRoadmapMutation.isPending}
->
+<Button
+  type="submit"
+  className="w-full sm:w-auto disabled:opacity-50"
+  disabled={subscribeToRoadmapMutation.isPending}
+>

Based on learnings.

components/app/auth-login-form.tsx (1)

138-143: Adopt disabled opacity pattern on buttons.

Match loading button UX across the app.

-<Button type="submit" className="w-full" disabled={isLoading}>
+<Button type="submit" className="w-full disabled:opacity-50" disabled={isLoading}>

Based on learnings.

components/app/dashboard-qr-code-dialog.tsx (1)

47-58: Address TODO comments for QR code functionality.

There are three TODO items for QR code download, copy, and generation. These represent incomplete core functionality for this dialog.

Would you like me to generate implementations for:

  1. QR code generation using a library like qrcode or qr-code-styling
  2. Download functionality to save QR code as PNG/SVG
  3. Copy to clipboard functionality using the Clipboard API

Or should I open an issue to track these tasks?

schemas/utils/container/input.ts (1)

5-5: Use specific file import to avoid potential circular dependencies.

The import from "../utils" should reference the specific file to prevent barrel export cycles.

Based on coding guidelines for schemas, apply this diff:

-import { EntityTypeSchema } from "../utils"
+import { EntityTypeSchema } from "@/schemas/utils/utils"

This follows the guideline: "Avoid circular dependencies by importing from specific schema files, not barrel exports."

components/app/dashboard-credential-move-dialog.tsx (2)

193-201: Add explicit type to buttons for accessibility/spec compliance.

Set explicit type to avoid default behavior ambiguity and to satisfy the “Always include a type attribute for button elements” rule.

-          <Button
+          <Button
+            type="button"
             variant="outline"
             onClick={() => handleOpenChange(false)}
             disabled={isMoving}
             className="order-2 sm:order-1"
           >
             Cancel
           </Button>
...
-          <Button
+          <Button
+            type="button"
             onClick={form.handleSubmit(handleMove)}
             disabled={
               isMoving ||
               selectedContainerId === credential.containerId ||
               !canMoveCredential
             }
             className="order-1 disabled:opacity-50 sm:order-2"
           >

As per coding guidelines.

Also applies to: 201-213


105-108: Avoid console in production paths; use a structured logger.

Replace console.error with your logging utility (e.g., server logger) to comply with “Don't use console”.

-      console.error("Failed to move credential:", error)
+      // TODO: route through structured logger
+      // logger.error({ err: error, credentialId: credential.id }, "Failed to move credential")
schemas/credential/input.ts (1)

117-123: Harden validation: trim and reject empty strings.

containerId and confirmation text should reject whitespace-only strings.

 export const moveCredentialInputSchema = z.object({
-  containerId: z.string().optional(),
+  containerId: z.string().trim().min(1, "Container ID cannot be empty").optional(),
 })
 
 export type MoveCredentialInput = z.infer<typeof moveCredentialInputSchema>
 
 // Delete credential confirmation
 export const deleteCredentialConfirmationInputSchema = z.object({
-  confirmationText: z.string().min(1, "Confirmation text is required"),
+  confirmationText: z.string().trim().min(1, "Confirmation text is required"),
 })

Based on learnings.

components/app/dashboard-credential-delete-dialog.tsx (1)

194-203: Add explicit type to the destructive button.

Set type to comply with button-type rule and avoid accidental submits if DOM structure changes.

-          <Button
+          <Button
+            type="button"
             onClick={form.handleSubmit(handleDelete)}
             disabled={!isConfirmationValid || isDeleting}
             variant="destructive"
             className="order-1 disabled:opacity-50 sm:order-2"
           >
schemas/credential/key-value/input.ts (1)

55-60: Tighten key and ID validators (trim/min).

Prevent empty/whitespace keys and IDs; maintain optional value for update semantics.

 export const credentialKeyValuePairInputSchema = z.object({
   id: z.string().optional(),
-  key: z.string(),
+  key: z.string().trim().min(1, "Key is required"),
   value: z.string().optional(),
 })
...
 export const updateCredentialKeyValuePairsInputSchema = z.object({
-  credentialId: z.string(),
+  credentialId: z.string().trim().min(1, "Credential ID is required"),
   keyValuePairs: z.array(credentialKeyValuePairInputSchema),
 })
...
 export const getCredentialKeyValuePairValueInputSchema = z.object({
-  credentialId: z.string(),
-  keyValuePairId: z.string(),
+  credentialId: z.string().trim().min(1, "Credential ID is required"),
+  keyValuePairId: z.string().trim().min(1, "Key-value pair ID is required"),
 })

Based on learnings.

Also applies to: 65-68, 74-77

schemas/credential/key-value/output.ts (1)

109-123: Deduplicate repeated “success: boolean” outputs.

You define both updateCredentialPasswordOutputSchema and updateCredentialKeyValuePairsOutputSchema with identical shapes. Consider a shared successOnlyOutputSchema to DRY.

+export const successOnlyOutputSchema = z.object({ success: z.boolean() })
...
-export const updateCredentialPasswordOutputSchema = z.object({
-  success: z.boolean(),
-})
+export const updateCredentialPasswordOutputSchema = successOnlyOutputSchema
...
-export const updateCredentialKeyValuePairsOutputSchema = z.object({
-  success: z.boolean(),
-})
+export const updateCredentialKeyValuePairsOutputSchema = successOnlyOutputSchema
orpc/routers/credential.ts (2)

125-133: Performance: avoid fetching heavy includes when returning key-only data.

Use a narrower select for metadata.keyValuePairs (id,key,createdAt,updatedAt) to reduce payload/CPU.

-    const credential = await database.credential.findFirst({
-      where: { id: input.id, userId: context.user.id },
-      include: CredentialQuery.getInclude(), // full include
-    })
+    const credential = await database.credential.findFirst({
+      where: { id: input.id, userId: context.user.id },
+      include: {
+        metadata: {
+          include: {
+            keyValuePairs: true, // or select only necessary fields
+          },
+        },
+      },
+    })

Also applies to: 160-170


214-222: Replace console.error with structured logging.

Follow the “Don't use console” rule; use your logger and include contextual fields (credential/kvPair ids).

-          console.error(`Failed to decrypt key-value pair ${kvPair.id}:`, error)
+          // logger.error({ err: error, kvPairId: kvPair.id }, "Failed to decrypt key-value pair")
...
-      console.error("Failed to decrypt password:", error)
+      // logger.error({ err: error, credentialId: input.id }, "Failed to decrypt password")
...
-      console.error("Error creating credential with metadata:", error)
+      // logger.error({ err: error }, "Error creating credential with metadata")

Also applies to: 332-337, 1015-1016

Comment on lines +8 to +13
export const userInputSchema = z.object({
name: z.string().min(2, "Name must be at least 2 characters"),
email: z.string().email("Please enter a valid email address"),
emailVerified: z.boolean().default(false),
image: z.string().url().optional(),
})
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Block external control of emailVerified in create/update.

Allowing clients to set emailVerified enables bypassing verification. Omit it from public create/update inputs.

Apply this minimal, safe diff:

 export const userInputSchema = z.object({
   name: z.string().min(2, "Name must be at least 2 characters"),
   email: z.string().email("Please enter a valid email address"),
-  emailVerified: z.boolean().default(false),
+  emailVerified: z.boolean().default(false), // server-managed only; excluded below
   image: z.string().url().optional(),
 })

 // Create
-export const createUserInputSchema = userInputSchema
+export const createUserInputSchema = userInputSchema.omit({ emailVerified: true })

 // Update
-export const updateUserInputSchema = userInputSchema.partial().extend({
-  id: z.string().min(1, "User ID is required"),
-})
+export const updateUserInputSchema = userInputSchema
+  .omit({ emailVerified: true })
+  .partial()
+  .extend({
+    id: z.string().min(1, "User ID is required"),
+  })

Based on learnings.

Also applies to: 22-25, 34-36

🤖 Prompt for AI Agents
In schemas/user/user/input.ts around lines 8-13 (and similarly at 22-25 and
34-36), the public input schema includes emailVerified which lets clients set
verification status; remove emailVerified from the public create/update schemas
so it cannot be supplied by clients, and ensure server-side code sets or updates
emailVerified internally (e.g., default false or set true only after
verification) rather than accepting it from the request payload.

@FindMalek FindMalek merged commit a509839 into main Oct 21, 2025
1 of 3 checks passed
@FindMalek FindMalek deleted the fin-107 branch October 21, 2025 20:37
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.

1 participant