11"use client" ;
22
3- import { deletePasswordItem , getPasswords } from "@/app/actions" ;
4- import { Button } from "@/components/ui/button" ;
5- import { Input } from "@/components/ui/input" ;
6- import { ScrollArea } from "@/components/ui/scroll-area" ;
7- import { Sheet , SheetContent } from "@/components/ui/sheet" ;
8- import { CreatePasswordDialog } from "@/components/vault/dialogs/create-password-dialog" ;
9- import { EditPasswordDialog } from "@/components/vault/dialogs/edit-password-dialog" ;
10- import { cn } from "@/lib/utils" ;
11- import { decrypt } from "@/utils/encryption" ;
12- import { useUser } from "@clerk/nextjs" ;
13- import { Prisma } from "@prisma/client" ;
14- import { Plus , SquareArrowOutUpRight , Trash , User } from "lucide-react" ;
3+ import { deletePasswordItem , getPasswords } from "@/app/actions" ;
4+ import { Button } from "@/components/ui/button" ;
5+ import { Input } from "@/components/ui/input" ;
6+ import { ScrollArea } from "@/components/ui/scroll-area" ;
7+ import { Sheet , SheetContent } from "@/components/ui/sheet" ;
8+ import { CreatePasswordDialog } from "@/components/vault/dialogs/create-password-dialog" ;
9+ import { EditPasswordDialog } from "@/components/vault/dialogs/edit-password-dialog" ;
10+ import { cn } from "@/lib/utils" ;
11+ import { decrypt , generateAndStoreKey , retrieveKey } from "@/utils/encryption" ;
12+ import { useUser } from "@clerk/nextjs" ;
13+ import { Prisma } from "@prisma/client" ;
14+ import { Plus , SquareArrowOutUpRight , Trash , User } from "lucide-react" ;
1515import Image from "next/image" ;
16- import { useRouter } from "next/navigation" ;
17- import { useEffect , useState } from "react" ;
16+ import { useRouter } from "next/navigation" ;
17+ import { useEffect , useState } from "react" ;
1818import toast from "react-hot-toast" ;
1919import {
2020 ContextMenu ,
@@ -23,16 +23,17 @@ import {
2323 ContextMenuLabel ,
2424 ContextMenuTrigger ,
2525} from "../ui/context-menu" ;
26- import { EmptyState } from "./empty-state" ;
27- import { PasswordDetails } from "./password-details" ;
28- import { Sidebar } from "./sidebar" ;
26+ import { EmptyState } from "./empty-state" ;
27+ import { PasswordDetails } from "./password-details" ;
28+ import { Sidebar } from "./sidebar" ;
2929
3030interface PasswordEntry {
3131 id : string ;
3232 name : string ;
3333 username : string ;
3434 website : string ;
3535 password : string ;
36+ iv : string ;
3637 updatedAt : string ;
3738 lastAccess : string ;
3839 created : string ;
@@ -63,30 +64,61 @@ export const VaultPage: React.FC<VaultPageProps> = ({ user }) => {
6364 const [ searchQuery , setSearchQuery ] = useState ( "" ) ;
6465 const [ filteredEntries , setFilteredEntries ] = useState < PasswordEntry [ ] > ( [ ] ) ;
6566 const [ passwords , setPasswords ] = useState < PasswordEntry [ ] > ( [ ] ) ;
66- const [ passwordItems , setPasswordItems ] = useState ( user ?. passwordItems )
67+ const [ passwordItems , setPasswordItems ] = useState ( user ?. passwordItems ) ;
6768
6869 useEffect ( ( ) => {
69- if ( ! clerkUser ) return ;
70+ const ensureEncryptionKey = async ( ) => {
71+ if ( ! clerkUser ) return ;
7072
71- if ( ! user ?. passwordItems || ! passwordItems ) return ;
73+ const userId = clerkUser . id ;
7274
73- const decryptedPasswords = passwordItems
74- . map ( ( item ) => ( {
75- id : item . id ,
76- name : decrypt ( item . username , clerkUser ) ,
77- username : decrypt ( item . username , clerkUser ) ,
78- website : decrypt ( item . website , clerkUser ) ,
79- password : decrypt ( item . password , clerkUser ) ,
80- updatedAt : item . updatedAt . toISOString ( ) ,
81- lastAccess : item . updatedAt . toISOString ( ) ,
82- created : item . createdAt . toISOString ( ) ,
83- } ) )
84- . sort (
85- ( a , b ) => new Date ( b . created ) . getTime ( ) - new Date ( a . created ) . getTime ( )
86- ) ;
75+ try {
76+ await retrieveKey ( userId ) ;
77+ toast . success ( "Encryption key found" ) ;
78+ } catch {
79+ toast . success ( "Generating encryption key..." ) ;
80+ await generateAndStoreKey ( userId ) ;
81+ }
82+ } ;
83+
84+ ensureEncryptionKey ( ) ;
85+ } , [ clerkUser ] ) ;
8786
88- setPasswords ( decryptedPasswords ) ;
87+ useEffect ( ( ) => {
88+ if ( ! clerkUser ) return ;
89+
90+ if ( ! user ?. passwordItems || ! passwordItems ) return ;
91+
92+ const decryptPasswords = async ( ) => {
93+ const decryptedPasswords = await Promise . all (
94+ passwordItems . map ( async ( item ) => {
95+ try {
96+ const decryptedItem = {
97+ id : item . id ,
98+ name : await decrypt ( item . username , item . usernameIV , clerkUser . id ) ,
99+ username : await decrypt ( item . username , item . usernameIV , clerkUser . id ) ,
100+ website : await decrypt ( item . website , item . websiteIV , clerkUser . id ) ,
101+ password : await decrypt ( item . password , item . passwordIV , clerkUser . id ) ,
102+ updatedAt : item . updatedAt . toISOString ( ) ,
103+ lastAccess : item . updatedAt . toISOString ( ) ,
104+ created : item . createdAt . toISOString ( ) ,
105+ } ;
106+ return decryptedItem ;
107+ } catch ( error ) {
108+ console . error ( `Error decrypting item ID: ${ item . id } ` , error ) ;
109+ return null ;
110+ }
111+ } )
112+ ) ;
113+
114+ setPasswords (
115+ decryptedPasswords . filter ( ( item ) : item is PasswordEntry => item !== null )
116+ ) ;
117+ } ;
118+
119+ decryptPasswords ( ) ;
89120 } , [ user ?. passwordItems , clerkUser , passwordItems ] ) ;
121+
90122
91123 const handleSearchChange = ( e : React . ChangeEvent < HTMLInputElement > ) => {
92124 setSearchQuery ( e . target . value ) ;
@@ -212,8 +244,10 @@ export const VaultPage: React.FC<VaultPageProps> = ({ user }) => {
212244 onClick = { async ( ) => {
213245 try {
214246 await deletePasswordItem ( password . id ) ;
215- const updatedItems = await getPasswords ( user ?. id as string )
216- setPasswordItems ( updatedItems ?. passwordItems ) ;
247+ const updatedItems = await getPasswords (
248+ user ?. id as string
249+ ) ;
250+ setPasswordItems ( updatedItems ?. passwordItems ) ;
217251 if ( selectedEntry ?. id === password . id ) {
218252 setSelectedEntry ( null ) ;
219253 }
@@ -285,8 +319,8 @@ export const VaultPage: React.FC<VaultPageProps> = ({ user }) => {
285319 onClose = { async ( ) => {
286320 setIsCreateDialogOpen ( false ) ;
287321 setSelectedEntry ( null ) ;
288- const userWithPasswords = await getPasswords ( user ?. id as string )
289- setPasswordItems ( userWithPasswords ?. passwordItems )
322+ const userWithPasswords = await getPasswords ( user ?. id as string ) ;
323+ setPasswordItems ( userWithPasswords ?. passwordItems ) ;
290324 } }
291325 />
292326 </ div >
0 commit comments