shared user restrictions + cleanup console logs

This commit is contained in:
Ishaan Dey 2024-05-07 22:40:59 -07:00
parent 9d288f580d
commit 12e8051673
12 changed files with 194 additions and 174 deletions

View File

@ -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 {

View File

@ -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()

View File

@ -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
}

View File

@ -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}</>;
}

View File

@ -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 (
<div className="relative h-9 w-44 flex items-center justify-start">
@ -32,5 +31,5 @@ export default function DashboardNavbarSearch() {
className="pl-8"
/>
</div>
)
);
}

View File

@ -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<monaco.editor.IStandaloneCodeEditor | null>(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,

View File

@ -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 (
<Dialog open={open} onOpenChange={setOpen}>
<DialogContent>
<DialogHeader>
<DialogTitle>Live Collaboration Disabled</DialogTitle>
</DialogHeader>
<div className="text-sm text-muted-foreground">{message}</div>
<div className="text-sm text-muted-foreground space-y-2">
<div>{message}</div>
<div className="flex items-center">
<Loader2 className="w-4 h-4 animate-spin mr-2" />
Redirecting you to dashboard...
</div>
</div>
</DialogContent>
</Dialog>
);

View File

@ -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<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
@ -62,24 +62,22 @@ export default function EditSandboxModal({
name: data.name,
visibility: data.visibility,
},
})
});
async function onSubmit(values: z.infer<typeof formSchema>) {
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({
</Button>
</DialogContent>
</Dialog>
)
);
}

View File

@ -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<HTMLInputElement>(null)
const [pendingDelete, setPendingDelete] = useState(false)
const [imgSrc, setImgSrc] = useState(`/icons/${getIconForFile(data.name)}`);
const [editing, setEditing] = useState(false);
const inputRef = useRef<HTMLInputElement>(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
}
setEditing(false)
inputRef.current.value = data.name;
}
setEditing(false);
};
return (
<ContextMenu>
<ContextMenuTrigger
disabled={pendingDelete}
onClick={() => {
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({
) : (
<form
onSubmit={(e) => {
e.preventDefault()
renameFile()
e.preventDefault();
renameFile();
}}
>
<input
@ -102,8 +99,8 @@ export default function SidebarFile({
<ContextMenuContent>
<ContextMenuItem
onClick={() => {
console.log("rename")
setEditing(true)
console.log("rename");
setEditing(true);
}}
>
<Pencil className="w-4 h-4 mr-2" />
@ -112,9 +109,9 @@ export default function SidebarFile({
<ContextMenuItem
disabled={pendingDelete}
onClick={() => {
console.log("delete")
setPendingDelete(true)
handleDeleteFile(data)
console.log("delete");
setPendingDelete(true);
handleDeleteFile(data);
}}
>
<Trash2 className="w-4 h-4 mr-2" />
@ -122,5 +119,5 @@ export default function SidebarFile({
</ContextMenuItem>
</ContextMenuContent>
</ContextMenu>
)
);
}

View File

@ -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<HTMLInputElement>(null)
const [editing, setEditing] = useState(false);
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
if (editing) {
inputRef.current?.focus()
inputRef.current?.focus();
}
}, [editing])
}, [editing]);
return (
<ContextMenu>
@ -60,9 +60,8 @@ export default function SidebarFolder({
/>
<form
onSubmit={(e) => {
e.preventDefault()
console.log("file renamed")
setEditing(false)
e.preventDefault();
setEditing(false);
}}
>
<input
@ -73,8 +72,7 @@ export default function SidebarFolder({
disabled={!editing}
defaultValue={data.name}
onBlur={() => {
console.log("file renamed")
setEditing(false)
setEditing(false);
}}
/>
</form>
@ -82,8 +80,7 @@ export default function SidebarFolder({
<ContextMenuContent>
<ContextMenuItem
onClick={() => {
console.log("rename")
setEditing(true)
setEditing(true);
}}
>
<Pencil className="w-4 h-4 mr-2" />
@ -92,7 +89,7 @@ export default function SidebarFolder({
<ContextMenuItem
// disabled={pendingDelete}
onClick={() => {
console.log("delete")
console.log("delete");
// setPendingDelete(true)
// handleDeleteFile(data)
}}
@ -129,5 +126,5 @@ export default function SidebarFolder({
</div>
) : null}
</ContextMenu>
)
);
}

View File

@ -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<React.SetStateAction<boolean>>
) => boolean;
handleDeleteFile: (file: TFile) => void;
handleDeleteFolder: (folder: TFolder) => void;
socket: Socket;
addNew: (name: string, type: "file" | "folder") => void;
ai: boolean;
setAi: React.Dispatch<React.SetStateAction<boolean>>;
}) {
const [creatingNew, setCreatingNew] = useState<"file" | "folder" | null>(null)
const [creatingNew, setCreatingNew] = useState<"file" | "folder" | null>(
null
);
return (
<div className="h-full w-56 select-none flex flex-col text-sm items-start justify-between p-2">
@ -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({
<Switch checked={ai} onCheckedChange={setAi} />
</div>
</div>
)
);
}

View File

@ -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);
});