diff --git a/backend/database/src/index.ts b/backend/database/src/index.ts index b115fb5..604b093 100644 --- a/backend/database/src/index.ts +++ b/backend/database/src/index.ts @@ -94,7 +94,6 @@ export default { const initStorageRes = await env.STORAGE.fetch(initStorageRequest); const initStorage = await initStorageRes.text(); - console.log("initStorage: ", initStorage); return new Response(sb.id, { status: 200 }); } else { diff --git a/backend/server/src/index.ts b/backend/server/src/index.ts index dde30a1..a13d39c 100644 --- a/backend/server/src/index.ts +++ b/backend/server/src/index.ts @@ -39,6 +39,7 @@ const io = new Server(httpServer, { }) let inactivityTimeout: NodeJS.Timeout | null = null; +let isOwnerConnected = false; const terminals: { [id: string]: { terminal: IPty; onData: IDisposable; onExit: IDisposable } @@ -58,7 +59,7 @@ io.use(async (socket, next) => { const parseQuery = handshakeSchema.safeParse(q) if (!parseQuery.success) { - console.log("Invalid request.") + ("Invalid request.") next(new Error("Invalid request.")) return } @@ -68,7 +69,6 @@ io.use(async (socket, next) => { const dbUserJSON = (await dbUser.json()) as User if (!dbUserJSON) { - console.log("DB error.") next(new Error("DB error.")) return } @@ -79,7 +79,6 @@ io.use(async (socket, next) => { ) if (!sandbox && !sharedSandboxes) { - console.log("Invalid credentials.") next(new Error("Invalid credentials.")) return } @@ -103,6 +102,15 @@ io.on("connection", async (socket) => { isOwner: boolean } + if (data.isOwner) { + isOwnerConnected = true + } else { + if (!isOwnerConnected) { + socket.emit("disableAccess", "The sandbox owner is not connected.") + return + } + } + const sandboxFiles = await getSandboxFiles(data.sandboxId) sandboxFiles.fileData.forEach((file) => { const filePath = path.join(dirName, file.id) @@ -224,12 +232,7 @@ io.on("connection", async (socket) => { socket.on("createTerminal", (id: string, callback) => { console.log("creating terminal", id) - if (terminals[id]) { - console.log("Terminal already exists.") - return - } - if (Object.keys(terminals).length >= 4) { - console.log("Too many terminals.") + if (terminals[id] || Object.keys(terminals).length >= 4) { return } @@ -240,9 +243,7 @@ io.on("connection", async (socket) => { }) const onData = pty.onData((data) => { - console.log("ondata") socket.emit("terminalResponse", { - // data: Buffer.from(data, "utf-8").toString("base64"), id, data, }) @@ -263,7 +264,6 @@ io.on("connection", async (socket) => { socket.on("terminalData", (id: string, data: string) => { if (!terminals[id]) { - console.log("terminals", terminals) return } @@ -276,7 +276,6 @@ io.on("connection", async (socket) => { socket.on("closeTerminal", (id: string, callback) => { if (!terminals[id]) { - console.log("tried to close, but term does not exist. terminals", terminals) return } @@ -332,11 +331,7 @@ io.on("connection", async (socket) => { delete terminals[t[0]] }) - // console.log("The owner disconnected.") - socket.broadcast.emit("ownerDisconnected") - } - else { - // console.log("A shared user disconnected.") + socket.broadcast.emit("disableAccess", "The sandbox owner has disconnected.") } const sockets = await io.fetchSockets() diff --git a/backend/server/src/utils.ts b/backend/server/src/utils.ts index e07b69c..eb660c6 100644 --- a/backend/server/src/utils.ts +++ b/backend/server/src/utils.ts @@ -16,7 +16,6 @@ export const getSandboxFiles = async (id: string) => { const paths = sandboxData.objects.map((obj) => obj.key) const processedFiles = await processFiles(paths, id) - // console.log("processedFiles.fileData:", processedFiles.fileData) return processedFiles } @@ -27,7 +26,6 @@ const processFiles = async (paths: string[], id: string) => { paths.forEach((path) => { const allParts = path.split("/") if (allParts[1] !== id) { - console.log("invalid path!!!!") return } diff --git a/frontend/app/(app)/layout.tsx b/frontend/app/(app)/layout.tsx index 973ec9e..dad9bc4 100644 --- a/frontend/app/(app)/layout.tsx +++ b/frontend/app/(app)/layout.tsx @@ -1,22 +1,22 @@ -import { User } from "@/lib/types" -import { currentUser } from "@clerk/nextjs" -import { redirect } from "next/navigation" +import { User } from "@/lib/types"; +import { currentUser } from "@clerk/nextjs"; +import { redirect } from "next/navigation"; export default async function AppAuthLayout({ children, }: { - children: React.ReactNode + children: React.ReactNode; }) { - const user = await currentUser() + const user = await currentUser(); if (!user) { - redirect("/") + redirect("/"); } const dbUser = await fetch( `https://database.ishaan1013.workers.dev/api/user?id=${user.id}` - ) - const dbUserJSON = (await dbUser.json()) as User + ); + const dbUserJSON = (await dbUser.json()) as User; if (!dbUserJSON.id) { const res = await fetch( @@ -32,12 +32,8 @@ export default async function AppAuthLayout({ email: user.emailAddresses[0].emailAddress, }), } - ) - - console.log(res) - } else { - // user already exists in db + ); } - return <>{children} + return <>{children}; } diff --git a/frontend/components/dashboard/navbar/search.tsx b/frontend/components/dashboard/navbar/search.tsx index a223610..65e731f 100644 --- a/frontend/components/dashboard/navbar/search.tsx +++ b/frontend/components/dashboard/navbar/search.tsx @@ -1,26 +1,25 @@ -"use client" +"use client"; -import { Input } from "../../ui/input" -import { Search } from "lucide-react" -import { useEffect, useState } from "react" -import { useRouter } from "next/navigation" +import { Input } from "../../ui/input"; +import { Search } from "lucide-react"; +import { useEffect, useState } from "react"; +import { useRouter } from "next/navigation"; export default function DashboardNavbarSearch() { - const [search, setSearch] = useState("") - const router = useRouter() + const [search, setSearch] = useState(""); + const router = useRouter(); useEffect(() => { const delayDebounceFn = setTimeout(() => { - console.log("SEARCH", search) if (search) { - router.push(`/dashboard?q=${search}`) + router.push(`/dashboard?q=${search}`); } else { - router.push(`/dashboard`) + router.push(`/dashboard`); } - }, 300) + }, 300); - return () => clearTimeout(delayDebounceFn) - }, [search]) + return () => clearTimeout(delayDebounceFn); + }, [search]); return (
@@ -32,5 +31,5 @@ export default function DashboardNavbarSearch() { className="pl-8" />
- ) + ); } diff --git a/frontend/components/editor/index.tsx b/frontend/components/editor/index.tsx index 195f75b..85b34e9 100644 --- a/frontend/components/editor/index.tsx +++ b/frontend/components/editor/index.tsx @@ -88,7 +88,6 @@ export default function CodeEditor({ const clerk = useClerk(); const room = useRoom(); const activeTerminal = terminals.find((t) => t.id === activeTerminalId); - console.log("activeTerminal", activeTerminal ? activeTerminal.id : "none"); // const editorRef = useRef(null) const [editorRef, setEditorRef] = @@ -270,9 +269,6 @@ export default function CodeEditor({ if (e.key === "s" && (e.metaKey || e.ctrlKey)) { e.preventDefault(); - // const activeTab = tabs.find((t) => t.id === activeFileId) - // console.log("saving:", activeTab?.name, editorRef?.getValue()) - setTabs((prev) => prev.map((tab) => tab.id === activeFileId ? { ...tab, saved: true } : tab @@ -367,7 +363,11 @@ export default function CodeEditor({ createTerminal(); }; - const onDisconnect = () => {}; + const onDisconnect = () => { + console.log("disconnected"); + + closeAllTerminals(); + }; const onLoadedEvent = (files: (TFolder | TFile)[]) => { setFiles(files); @@ -385,11 +385,19 @@ export default function CodeEditor({ if (term && term.terminal) term.terminal.write(res); }; + const onDisableAccess = (message: string) => { + setDisableAccess({ + isDisabled: true, + message, + }); + }; + socket.on("connect", onConnect); socket.on("disconnect", onDisconnect); socket.on("loaded", onLoadedEvent); socket.on("rateLimit", onRateLimit); socket.on("terminalResponse", onTerminalResponse); + socket.on("disableAccess", onDisableAccess); return () => { socket.off("connect", onConnect); @@ -397,6 +405,7 @@ export default function CodeEditor({ socket.off("loaded", onLoadedEvent); socket.off("rateLimit", onRateLimit); socket.off("terminalResponse", onTerminalResponse); + socket.off("disableAccess", onDisableAccess); }; }, []); @@ -492,6 +501,13 @@ export default function CodeEditor({ }); }; + const closeAllTerminals = () => { + terminals.forEach((term) => { + socket.emit("closeTerminal", term.id, () => {}); // no need to wait for response here + setTerminals((prev) => prev.filter((t) => t.id !== term.id)); + }); + }; + const handleRename = ( id: string, newName: string, diff --git a/frontend/components/editor/live/disableModal.tsx b/frontend/components/editor/live/disableModal.tsx index b66d369..1056b42 100644 --- a/frontend/components/editor/live/disableModal.tsx +++ b/frontend/components/editor/live/disableModal.tsx @@ -9,7 +9,15 @@ import { DialogTrigger, } from "@/components/ui/dialog"; -import { ChevronRight, FileStack, Globe, TextCursor } from "lucide-react"; +import { + ChevronRight, + FileStack, + Globe, + Loader2, + TextCursor, +} from "lucide-react"; +import { useRouter } from "next/navigation"; +import { useEffect } from "react"; export default function DisableAccessModal({ open, @@ -20,13 +28,30 @@ export default function DisableAccessModal({ setOpen: (open: boolean) => void; message: string; }) { + const router = useRouter(); + + useEffect(() => { + if (open) { + const timeout = setTimeout(() => { + router.push("/dashboard"); + }, 5000); + return () => clearTimeout(timeout); + } + }, []); + return ( Live Collaboration Disabled -
{message}
+
+
{message}
+
+ + Redirecting you to dashboard... +
+
); diff --git a/frontend/components/editor/navbar/edit.tsx b/frontend/components/editor/navbar/edit.tsx index c5316fb..17575d7 100644 --- a/frontend/components/editor/navbar/edit.tsx +++ b/frontend/components/editor/navbar/edit.tsx @@ -1,4 +1,4 @@ -"use client" +"use client"; import { Dialog, @@ -7,10 +7,10 @@ import { DialogHeader, DialogTitle, DialogTrigger, -} from "@/components/ui/dialog" -import { z } from "zod" -import { zodResolver } from "@hookform/resolvers/zod" -import { useForm } from "react-hook-form" +} from "@/components/ui/dialog"; +import { z } from "zod"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useForm } from "react-hook-form"; import { Form, @@ -20,41 +20,41 @@ import { FormItem, FormLabel, FormMessage, -} from "@/components/ui/form" -import { Input } from "@/components/ui/input" +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, -} from "@/components/ui/select" -import { Loader2 } from "lucide-react" -import { useState } from "react" -import { Sandbox } from "@/lib/types" -import { Button } from "@/components/ui/button" -import { deleteSandbox, updateSandbox } from "@/lib/actions" -import { useRouter } from "next/navigation" -import { toast } from "sonner" +} from "@/components/ui/select"; +import { Loader2 } from "lucide-react"; +import { useState } from "react"; +import { Sandbox } from "@/lib/types"; +import { Button } from "@/components/ui/button"; +import { deleteSandbox, updateSandbox } from "@/lib/actions"; +import { useRouter } from "next/navigation"; +import { toast } from "sonner"; const formSchema = z.object({ name: z.string().min(1).max(16), visibility: z.enum(["public", "private"]), -}) +}); export default function EditSandboxModal({ open, setOpen, data, }: { - open: boolean - setOpen: (open: boolean) => void - data: Sandbox + open: boolean; + setOpen: (open: boolean) => void; + data: Sandbox; }) { - const [loading, setLoading] = useState(false) - const [loadingDelete, setLoadingDelete] = useState(false) + const [loading, setLoading] = useState(false); + const [loadingDelete, setLoadingDelete] = useState(false); - const router = useRouter() + const router = useRouter(); const form = useForm>({ resolver: zodResolver(formSchema), @@ -62,24 +62,22 @@ export default function EditSandboxModal({ name: data.name, visibility: data.visibility, }, - }) + }); async function onSubmit(values: z.infer) { - console.log(values) + setLoading(true); + await updateSandbox({ id: data.id, ...values }); - setLoading(true) - await updateSandbox({ id: data.id, ...values }) + toast.success("Sandbox updated successfully"); - toast.success("Sandbox updated successfully") - - setLoading(false) + setLoading(false); } async function onDelete() { - setLoadingDelete(true) - await deleteSandbox(data.id) + setLoadingDelete(true); + await deleteSandbox(data.id); - router.push("/dashboard") + router.push("/dashboard"); } return ( @@ -155,5 +153,5 @@ export default function EditSandboxModal({ - ) + ); } diff --git a/frontend/components/editor/sidebar/file.tsx b/frontend/components/editor/sidebar/file.tsx index e483033..1f4db2e 100644 --- a/frontend/components/editor/sidebar/file.tsx +++ b/frontend/components/editor/sidebar/file.tsx @@ -1,16 +1,16 @@ -"use client" +"use client"; -import Image from "next/image" -import { getIconForFile } from "vscode-icons-js" -import { TFile, TTab } from "@/lib/types" -import { useEffect, useRef, useState } from "react" +import Image from "next/image"; +import { getIconForFile } from "vscode-icons-js"; +import { TFile, TTab } from "@/lib/types"; +import { useEffect, useRef, useState } from "react"; import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuTrigger, -} from "@/components/ui/context-menu" -import { Loader2, Pencil, Trash2 } from "lucide-react" +} from "@/components/ui/context-menu"; +import { Loader2, Pencil, Trash2 } from "lucide-react"; export default function SidebarFile({ data, @@ -18,29 +18,26 @@ export default function SidebarFile({ handleRename, handleDeleteFile, }: { - data: TFile - selectFile: (file: TTab) => void + data: TFile; + selectFile: (file: TTab) => void; handleRename: ( id: string, newName: string, oldName: string, type: "file" | "folder" - ) => boolean - handleDeleteFile: (file: TFile) => void + ) => boolean; + handleDeleteFile: (file: TFile) => void; }) { - const [imgSrc, setImgSrc] = useState(`/icons/${getIconForFile(data.name)}`) - const [editing, setEditing] = useState(false) - const inputRef = useRef(null) - const [pendingDelete, setPendingDelete] = useState(false) + const [imgSrc, setImgSrc] = useState(`/icons/${getIconForFile(data.name)}`); + const [editing, setEditing] = useState(false); + const inputRef = useRef(null); + const [pendingDelete, setPendingDelete] = useState(false); useEffect(() => { if (editing) { - setTimeout(() => inputRef.current?.focus(), 0) + setTimeout(() => inputRef.current?.focus(), 0); } - if (!inputRef.current) { - console.log("no input ref") - } - }, [editing, inputRef.current]) + }, [editing, inputRef.current]); const renameFile = () => { const renamed = handleRename( @@ -48,19 +45,19 @@ export default function SidebarFile({ inputRef.current?.value ?? data.name, data.name, "file" - ) + ); if (!renamed && inputRef.current) { - inputRef.current.value = data.name + inputRef.current.value = data.name; } - setEditing(false) - } + setEditing(false); + }; return ( { - if (!editing && !pendingDelete) selectFile({ ...data, saved: true }) + if (!editing && !pendingDelete) selectFile({ ...data, saved: true }); }} // onDoubleClick={() => { // setEditing(true) @@ -83,8 +80,8 @@ export default function SidebarFile({ ) : (
{ - e.preventDefault() - renameFile() + e.preventDefault(); + renameFile(); }} > { - console.log("rename") - setEditing(true) + console.log("rename"); + setEditing(true); }} > @@ -112,9 +109,9 @@ export default function SidebarFile({ { - console.log("delete") - setPendingDelete(true) - handleDeleteFile(data) + console.log("delete"); + setPendingDelete(true); + handleDeleteFile(data); }} > @@ -122,5 +119,5 @@ export default function SidebarFile({ - ) + ); } diff --git a/frontend/components/editor/sidebar/folder.tsx b/frontend/components/editor/sidebar/folder.tsx index 7c47059..6a1d148 100644 --- a/frontend/components/editor/sidebar/folder.tsx +++ b/frontend/components/editor/sidebar/folder.tsx @@ -1,17 +1,17 @@ -"use client" +"use client"; -import Image from "next/image" -import { useEffect, useRef, useState } from "react" -import { getIconForFolder, getIconForOpenFolder } from "vscode-icons-js" -import { TFile, TFolder, TTab } from "@/lib/types" -import SidebarFile from "./file" +import Image from "next/image"; +import { useEffect, useRef, useState } from "react"; +import { getIconForFolder, getIconForOpenFolder } from "vscode-icons-js"; +import { TFile, TFolder, TTab } from "@/lib/types"; +import SidebarFile from "./file"; import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuTrigger, -} from "@/components/ui/context-menu" -import { Pencil, Trash2 } from "lucide-react" +} from "@/components/ui/context-menu"; +import { Pencil, Trash2 } from "lucide-react"; export default function SidebarFolder({ data, @@ -20,30 +20,30 @@ export default function SidebarFolder({ handleDeleteFile, handleDeleteFolder, }: { - data: TFolder - selectFile: (file: TTab) => void + data: TFolder; + selectFile: (file: TTab) => void; handleRename: ( id: string, newName: string, oldName: string, type: "file" | "folder" - ) => boolean - handleDeleteFile: (file: TFile) => void - handleDeleteFolder: (folder: TFolder) => void + ) => boolean; + handleDeleteFile: (file: TFile) => void; + handleDeleteFolder: (folder: TFolder) => void; }) { - const [isOpen, setIsOpen] = useState(false) + const [isOpen, setIsOpen] = useState(false); const folder = isOpen ? getIconForOpenFolder(data.name) - : getIconForFolder(data.name) + : getIconForFolder(data.name); - const [editing, setEditing] = useState(false) - const inputRef = useRef(null) + const [editing, setEditing] = useState(false); + const inputRef = useRef(null); useEffect(() => { if (editing) { - inputRef.current?.focus() + inputRef.current?.focus(); } - }, [editing]) + }, [editing]); return ( @@ -60,9 +60,8 @@ export default function SidebarFolder({ /> { - e.preventDefault() - console.log("file renamed") - setEditing(false) + e.preventDefault(); + setEditing(false); }} > { - console.log("file renamed") - setEditing(false) + setEditing(false); }} /> @@ -82,8 +80,7 @@ export default function SidebarFolder({ { - console.log("rename") - setEditing(true) + setEditing(true); }} > @@ -92,7 +89,7 @@ export default function SidebarFolder({ { - console.log("delete") + console.log("delete"); // setPendingDelete(true) // handleDeleteFile(data) }} @@ -129,5 +126,5 @@ export default function SidebarFolder({ ) : null} - ) + ); } diff --git a/frontend/components/editor/sidebar/index.tsx b/frontend/components/editor/sidebar/index.tsx index acb3df3..0c725dd 100644 --- a/frontend/components/editor/sidebar/index.tsx +++ b/frontend/components/editor/sidebar/index.tsx @@ -1,14 +1,14 @@ -"use client" +"use client"; -import { FilePlus, FolderPlus, Loader2, Search, Sparkles } from "lucide-react" -import SidebarFile from "./file" -import SidebarFolder from "./folder" -import { TFile, TFolder, TTab } from "@/lib/types" -import { useState } from "react" -import New from "./new" -import { Socket } from "socket.io-client" -import Button from "@/components/ui/customButton" -import { Switch } from "@/components/ui/switch" +import { FilePlus, FolderPlus, Loader2, Search, Sparkles } from "lucide-react"; +import SidebarFile from "./file"; +import SidebarFolder from "./folder"; +import { TFile, TFolder, TTab } from "@/lib/types"; +import { useState } from "react"; +import New from "./new"; +import { Socket } from "socket.io-client"; +import Button from "@/components/ui/customButton"; +import { Switch } from "@/components/ui/switch"; export default function Sidebar({ files, @@ -21,22 +21,24 @@ export default function Sidebar({ ai, setAi, }: { - files: (TFile | TFolder)[] - selectFile: (tab: TTab) => void + files: (TFile | TFolder)[]; + selectFile: (tab: TTab) => void; handleRename: ( id: string, newName: string, oldName: string, type: "file" | "folder" - ) => boolean - handleDeleteFile: (file: TFile) => void - handleDeleteFolder: (folder: TFolder) => void - socket: Socket - addNew: (name: string, type: "file" | "folder") => void - ai: boolean - setAi: React.Dispatch> + ) => boolean; + handleDeleteFile: (file: TFile) => void; + handleDeleteFolder: (folder: TFolder) => void; + socket: Socket; + addNew: (name: string, type: "file" | "folder") => void; + ai: boolean; + setAi: React.Dispatch>; }) { - const [creatingNew, setCreatingNew] = useState<"file" | "folder" | null>(null) + const [creatingNew, setCreatingNew] = useState<"file" | "folder" | null>( + null + ); return (
@@ -94,8 +96,7 @@ export default function Sidebar({ socket={socket} type={creatingNew} stopEditing={() => { - console.log("stopped editing") - setCreatingNew(null) + setCreatingNew(null); }} addNew={addNew} /> @@ -119,5 +120,5 @@ export default function Sidebar({
- ) + ); } diff --git a/frontend/components/editor/terminal/index.tsx b/frontend/components/editor/terminal/index.tsx index 73fb3d9..81831d7 100644 --- a/frontend/components/editor/terminal/index.tsx +++ b/frontend/components/editor/terminal/index.tsx @@ -51,7 +51,6 @@ export default function EditorTerminal({ setTerm(term); } const disposable = term.onData((data) => { - console.log("sending data", data); socket.emit("terminalData", id, data); });