Skip to content

Commit

Permalink
Merge branch 'MitulSonagara:master' into name
Browse files Browse the repository at this point in the history
  • Loading branch information
akash70629 authored Oct 25, 2024
2 parents 1caa51f + 168a371 commit 052542d
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 58 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 11 additions & 12 deletions src/app/(app)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,22 +31,21 @@ const Page = async () => {
</div>

{/* Content */}

<div className="relative z-20">
<h1 className="mt-10 text-4xl font-bold mb-6 bg-clip-text text-transparent bg-gradient-to-r from-red-500 to-red-800">
Secure Communication in a Dangerous World
</h1>
<p className="text-xl text-gray-600 dark:text-gray-300 mb-8 max-w-2xl mx-auto">
Experience unbreakable encryption and total anonymity. Truth
Tunnel: Where your secrets remain hidden.
<h1 className="text-5xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-red-500 to-red-800 p-3">
Secure Communication in a Dangerous World
</h1>
<div className=" flex items-center justify-center mt-2 ">
<p className="rounded-lg py-2 px-2 border-2 border-red-500 font-bold text-red-900 font-sans text-lg bg-red-100 shadow-md shadow-red-400 border-none">
Where your secrets
remain hidden.
</p>
</div>
<div className="relative flex z-20 items-center w-10/12 mt-10 ">
<div className="relative flex z-20 items-center lg:w-10/12 mt-10 ">
<div className="w-full ml-8 ">
<p className="text-3xl text-[#A34343] dark:text-gray-300 mb-7 font-bold ">
Welcome to Truth Tunnel!
</p>
<p className="text-lg text-[#df6b59] dark:text-gray-300 font-semibold mb-10 w-10/12 leading-relaxed">
<p className="text-lg px-2 text-[#df6b59] dark:text-gray-300 font-semibold mb-10 lg:w-10/12 leading-relaxed">
Experience unparalleled privacy
and security in your communications. Join a community where
anonymity is prioritized, and your identity remains hidden.
Expand Down Expand Up @@ -154,8 +153,8 @@ const Page = async () => {
</section>
{/* Footer */}
{/* Footer */}

<footer className="relative z-10 border-t border-gray-200 dark:border-gray-800 py-8 text-gray-600 dark:text-gray-400 mr-16">
<footer className="relative z-10 border-t border-gray-200 dark:border-gray-800 py-8 text-gray-600 dark:text-gray-400 mr-16">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 flex flex-col sm:flex-row justify-between items-center">
<div className="text-left mb-4 sm:mb-0">
<p>&copy; 2024 Truth Tunnel. All rights reserved. Secured by quantum encryption.</p>
Expand Down
32 changes: 29 additions & 3 deletions src/components/Messages.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
import { Trash2 } from "lucide-react";
import React from "react";
import React, { useState } from "react"; // Import useState
import { Button } from "./ui/button";
import { Message } from "@prisma/client";
import moment from "moment";
import { deleteMessage } from "@/lib/queries";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { toast } from "sonner";
import { useDecryptedMessages } from "@/hooks/use-decrypt-message";
import { Loader2 } from "lucide-react";


export default function Messages({
messages,
}: {
messages: Message[] | undefined;
}) {
const queryClient = useQueryClient();

// State to track loading for each message
const [loadingMessageId, setLoadingMessageId] = useState<string | null>(null);

// Mutation to delete a message
const deleteMessageMutation = useMutation({
mutationFn: deleteMessage,
Expand All @@ -22,17 +28,32 @@ export default function Messages({
queryKey: ["messages"],
});
toast.success("Message deleted successfully");
setLoadingMessageId(null); // Reset loading state
},
onError: (error: any) => {
toast.error("Error", {
description: error.response?.data.message || "Failed to delete message",
});
setLoadingMessageId(null); // Reset loading state
},
});

const handleDeleteMessages = (messageId: string) => {
deleteMessageMutation.mutate(messageId);
setLoadingMessageId(messageId); // Set loading state for the message being deleted
deleteMessageMutation.mutate(messageId, {
onSuccess: () => {
// Remove the deleted message from the local messages array
const updatedMessages = messages?.filter(message => message.id !== messageId);
queryClient.setQueryData(["messages"], updatedMessages); // Update the cached messages
},
onError: (error: any) => {
toast.error("Error", {
description: error.response?.data.message || "Failed to delete message",
});
},
});
};


const messageContents = messages?.map((message) => message.content) || [];
const { decryptedMessages, loading } = useDecryptedMessages(messageContents); // Use the custom hook
Expand Down Expand Up @@ -63,8 +84,13 @@ export default function Messages({
variant="destructive"
className="rounded-full"
onClick={() => handleDeleteMessages(id)}
disabled={loadingMessageId === id} // Disable button if this message is being deleted
>
<Trash2 className="h-5 w-5" />
{loadingMessageId === id ? ( // Show loader while deleting
<Loader2 className="h-5 w-5 animate-spin" />
) : (
<Trash2 className="h-5 w-5" />
)}
</Button>
</li>
);
Expand Down
2 changes: 1 addition & 1 deletion src/components/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ const Navbar = () => {
</DropdownMenu>
) : (
<Link href="/sign-in">
<Button className="text-xs mr-2 px-3 py-1 rounded-md bg-red-600 hover:bg-red-700 text-white transition-all duration-200">
<Button className="text-xs mr-6 px-3 py-1 rounded-md bg-red-600 hover:bg-red-700 text-white transition-all duration-200">
Sign In
</Button>
</Link>
Expand Down
10 changes: 6 additions & 4 deletions src/components/modals/EncryptionKeyModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,11 @@ export default function EncryptionKeyModal() {

const handleCopyPrivateKey = () => {
if (privateKey) {
navigator.clipboard.writeText(privateKey);
toast.success("Private Key copied to clipboard!", {
description: "Keep it very save.",
});
navigator.clipboard.writeText(privateKey).then(() => {
toast.success("Private Key copied to clipboard!", {
description: "Keep it very safe.",
});
});
}
};

Expand All @@ -87,6 +88,7 @@ export default function EncryptionKeyModal() {
const newWindow = window.open();
if (newWindow) {
newWindow.document.write(`<pre>${privateKey}</pre>`);
newWindow.document.close(); // Close the document for rendering
newWindow.print();
}
}
Expand Down
23 changes: 13 additions & 10 deletions src/hooks/use-decrypt-message.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,41 @@
'use client'
'use client';
import { decryptMessage } from '@/lib/crypto';
import { getPrivateKey } from '@/lib/indexedDB';
import { useEffect, useState } from 'react';

export const useDecryptedMessages = (messages: string[]) => {
const [decryptedMessages, setDecryptedMessages] = useState<string[]>([]);
const [decryptedMessages, setDecryptedMessages] = useState<(string | null)[]>([]);
const [loading, setLoading] = useState<boolean[]>(new Array(messages.length).fill(true)); // Initial loading states

useEffect(() => {

const decryptAllMessages = async () => {
const privateKey = await getPrivateKey()
const privateKey = await getPrivateKey();
const newLoading = new Array(messages.length).fill(true); // Create a new loading array

const decrypted = await Promise.all(messages.map(async (message, index) => {
if (privateKey) {
try {
const decryptedMessage = decryptMessage(privateKey, message);
loading[index] = false; // Set loading to false for this index
newLoading[index] = false; // Set loading to false for this index
return decryptedMessage;
} catch {
loading[index] = false; // Set loading to false even if there's an error
newLoading[index] = false; // Set loading to false even if there's an error
return "Error decrypting message.";
}
} else {
loading[index] = false; // Set loading to false if no private key
newLoading[index] = false; // Set loading to false if no private key
return "Message is encrypted.";
}
}));

setDecryptedMessages(decrypted);
setLoading(loading);
setLoading(newLoading); // Update state with the new loading array
};

decryptAllMessages();
}, [messages, loading]);
decryptAllMessages(); // Call the function

}, []); // Empty dependency array to run only on mount

return { decryptedMessages, loading };
};

26 changes: 14 additions & 12 deletions src/lib/crypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,22 @@ export function generateKeyPair(): { publicKey: string; privateKey: string } {

// Encrypt message using the recipient's public key
export function encryptMessage(publicKeyPem: string, message: string): string {
const publicKey = forge.pki.publicKeyFromPem(publicKeyPem);
const encryptedMessage = publicKey.encrypt(message, 'RSA-OAEP');
return forge.util.encode64(encryptedMessage); // Base64 encode the result
const publicKey = forge.pki.publicKeyFromPem(publicKeyPem);
const encryptedMessage = publicKey.encrypt(message, 'RSA-OAEP');
return forge.util.encode64(encryptedMessage); // Base64 encode the result
}

// Decrypt message using the recipient's private key
export function decryptMessage(privateKeyPem: string, encryptedMessage: string): string {
try {
export function decryptMessage(privateKeyPem: string, encryptedMessage: string): string | null {
try {
const privateKey = forge.pki.privateKeyFromPem(privateKeyPem);
const decodedMessage = forge.util.decode64(encryptedMessage); // Ensure proper decoding
console.log("Decoded Message Length: ", decodedMessage.length); // Debug: log length of the decoded message

const privateKey = forge.pki.privateKeyFromPem(privateKeyPem);
const decodedMessage = forge.util.decode64(encryptedMessage);
return privateKey.decrypt(decodedMessage, 'RSA-OAEP');
} catch (error) {
console.log("MESSAGE IN NOT ENCRYPTED");
return encryptedMessage.substring(0, 25);
const decryptedMessage = privateKey.decrypt(decodedMessage, 'RSA-OAEP');
return decryptedMessage; // Return the decrypted message
} catch (error) {
console.log("Decryption failed: ", error);
return encryptedMessage.substring(0, 25);
}
}
}
55 changes: 40 additions & 15 deletions src/lib/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,58 @@ import { ApiResponse } from "@/types/ApiResponse";
import { Message, User } from "@prisma/client";

// Fetch all messages
export const fetchMessages = async (): Promise<Message[] | undefined> => {
const { data } = await axios.get<ApiResponse>(`/api/messages`);
return data.messages;
export const fetchMessages = async (): Promise<Message[]> => {
try {
const { data } = await axios.get<ApiResponse>(`/api/messages`);
return data.messages || []; // Return an empty array if messages are undefined
} catch (error) {
console.error("Error fetching messages:", error);
return []; // Return an empty array on error
}
};

// Fetch accept messages status
export const fetchAcceptMessages = async (): Promise<boolean | undefined> => {
const { data } = await axios.get<ApiResponse>(`/api/messages/accept`);
return data.isAcceptingMessages;
export const fetchAcceptMessages = async (): Promise<boolean> => {
try {
const { data } = await axios.get<ApiResponse>(`/api/messages/accept`);
return data.isAcceptingMessages ?? false; // Default to false if undefined
} catch (error) {
console.error("Error fetching accept messages status:", error);
return false; // Default to false on error
}
};

// Toggle accept messages status
export const toggleAcceptMessages = async (acceptMessages: boolean): Promise<ApiResponse> => {
const { data } = await axios.post<ApiResponse>(`/api/messages/accept`, {
acceptMessages,
});
return data;
try {
const { data } = await axios.post<ApiResponse>(`/api/messages/accept`, {
acceptMessages,
});
return data;
} catch (error) {
console.error("Error toggling accept messages:", error);
throw error; // Re-throw the error for handling in the caller
}
};

// Delete a message by ID
export const deleteMessage = async (messageId: string): Promise<ApiResponse> => {
const { data } = await axios.delete<ApiResponse>(`/api/messages/delete/${messageId}`);
return data;
try {
const { data } = await axios.delete<ApiResponse>(`/api/messages/delete/${messageId}`);
return data;
} catch (error) {
console.error(`Error deleting message with ID ${messageId}:`, error);
throw error; // Re-throw the error for handling in the caller
}
};


// Fetch suggested users
export const suggestedUsers = async (): Promise<User[]> => {
const { data } = await axios.get<ApiResponse>(`/api/search`);
return data.users;
try {
const { data } = await axios.get<ApiResponse>(`/api/search`);
return data.users || []; // Return an empty array if users are undefined
} catch (error) {
console.error("Error fetching suggested users:", error);
return []; // Return an empty array on error
}
};

0 comments on commit 052542d

Please sign in to comment.