diff --git a/package.json b/package.json index afa335b..537ac98 100644 --- a/package.json +++ b/package.json @@ -1,75 +1,75 @@ { - "name": "rdamn", - "private": true, - "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", - "lint": "next lint", - "buildDocs": "npx typedoc" - }, - "dependencies": { - "@auth0/nextjs-auth0": "^1.7.0", - "@aws-sdk/client-ec2": "^3.54.0", - "@aws-sdk/client-ecs": "^3.54.0", - "@aws-sdk/client-s3": "^3.54.0", - "@aws-sdk/s3-request-presigner": "^3.54.0", - "@headlessui/react": "^1.5.0", - "@monaco-editor/react": "^4.3.1", - "@szhsin/react-menu": "^3.0.0", - "ajv": "^8.10.0", - "daisyui": "^2.8.0", - "file-extension-icon-js": "^1.1.3", - "ioredis": "^4.28.5", - "monaco-editor": "^0.33.0", - "mongoose": "^6.2.6", - "nanoid": "^3.3.1", - "next": "^12.1.0", - "nextjs-progressbar": "^0.0.14", - "node-abort-controller": "^3.0.1", - "object-path": "^0.11.8", - "object-path-immutable": "^4.1.2", - "random-word-slugs": "^0.1.6", - "react": "17.0.2", - "react-dom": "17.0.2", - "react-hot-toast": "^2.2.0", - "react-reflex": "^4.0.6", - "react-use-websocket": "^3.0.0", - "reconnecting-websocket": "^4.4.0", - "swr": "^1.2.2", - "tailwind-scrollbar-hide": "^1.1.7", - "thenby": "^1.3.4", - "xterm": "^4.18.0", - "xterm-addon-fit": "^0.5.0", - "xterm-addon-web-links": "^0.5.1" - }, - "devDependencies": { - "@next/eslint-plugin-next": "^12.1.0", - "@types/ioredis": "^4.28.8", - "@types/node": "^17.0.21", - "@types/object-path": "^0.11.1", - "@types/react": "^17.0.40", - "@typescript-eslint/eslint-plugin": "^5.15.0", - "@typescript-eslint/parser": "^5.15.0", - "autoprefixer": "^10.4.2", - "eslint": "^8.11.0", - "eslint-config-next": "^12.1.0", - "eslint-config-prettier": "^8.5.0", - "eslint-plugin-prettier": "^4.0.0", - "eslint-plugin-react": "^7.29.4", - "import-sort-style-module": "^6.0.0", - "postcss": "^8.4.8", - "prettier": "^2.5.1", - "prettier-plugin-import-sort": "^0.0.7", - "tailwindcss": "^3.0.23", - "typedoc": "^0.22.13", - "typedoc-theme-hierarchy": "^1.0.14", - "typescript": "^4.6.2" - }, - "importSort": { - ".js, .jsx, .ts, .tsx": { - "style": "module", - "parser": "typescript" - } - } -} + "name": "rdamn", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint", + "buildDocs": "npx typedoc" + }, + "dependencies": { + "@auth0/nextjs-auth0": "^1.7.0", + "@aws-sdk/client-ec2": "^3.54.0", + "@aws-sdk/client-ecs": "^3.54.0", + "@aws-sdk/client-s3": "^3.54.0", + "@aws-sdk/s3-request-presigner": "^3.54.0", + "@headlessui/react": "^1.5.0", + "@monaco-editor/react": "^4.3.1", + "@szhsin/react-menu": "^3.0.0", + "ajv": "^8.10.0", + "daisyui": "^2.8.0", + "file-extension-icon-js": "^1.1.3", + "ioredis": "^4.28.5", + "monaco-editor": "^0.33.0", + "mongoose": "^6.2.6", + "nanoid": "^3.3.1", + "next": "^13.4.18", + "nextjs-progressbar": "^0.0.14", + "node-abort-controller": "^3.0.1", + "object-path": "^0.11.8", + "object-path-immutable": "^4.1.2", + "random-word-slugs": "^0.1.6", + "react": "17.0.2", + "react-dom": "17.0.2", + "react-hot-toast": "^2.2.0", + "react-reflex": "^4.0.6", + "react-use-websocket": "^3.0.0", + "reconnecting-websocket": "^4.4.0", + "swr": "^1.2.2", + "tailwind-scrollbar-hide": "^1.1.7", + "thenby": "^1.3.4", + "xterm": "^4.18.0", + "xterm-addon-fit": "^0.5.0", + "xterm-addon-web-links": "^0.5.1" + }, + "devDependencies": { + "@next/eslint-plugin-next": "^12.1.0", + "@types/ioredis": "^4.28.8", + "@types/node": "^17.0.21", + "@types/object-path": "^0.11.1", + "@types/react": "^17.0.40", + "@typescript-eslint/eslint-plugin": "^5.15.0", + "@typescript-eslint/parser": "^5.15.0", + "autoprefixer": "^10.4.2", + "eslint": "^8.11.0", + "eslint-config-next": "^12.1.0", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-prettier": "^4.0.0", + "eslint-plugin-react": "^7.29.4", + "import-sort-style-module": "^6.0.0", + "postcss": "^8.4.8", + "prettier": "^2.5.1", + "prettier-plugin-import-sort": "^0.0.7", + "tailwindcss": "^3.0.23", + "typedoc": "^0.22.13", + "typedoc-theme-hierarchy": "^1.0.14", + "typescript": "^4.6.2" + }, + "importSort": { + ".js, .jsx, .ts, .tsx": { + "style": "module", + "parser": "typescript" + } + } +} \ No newline at end of file diff --git a/src/components/AuthCheck/index.tsx b/src/components/AuthCheck/index.tsx index d4dc59a..56af7b6 100644 --- a/src/components/AuthCheck/index.tsx +++ b/src/components/AuthCheck/index.tsx @@ -1,8 +1,9 @@ + +import { useRouter } from "next/navigation"; /* Copyright (c) rishabhrao (https://github.com/rishabhrao) */ -import { UserProfile } from "@auth0/nextjs-auth0" -import Spinner from "@components/Spinner" -import { useRouter } from "next/router" +import { UserProfile } from "@auth0/nextjs-auth0"; +import Spinner from "@components/Spinner"; /** * Shape of properties provided to the AuthCheck component @@ -10,25 +11,25 @@ import { useRouter } from "next/router" * @export */ export type AuthCheckPropsType = { - /** - * JSX element to be rendered if AuthCheck passes - * - * @type {JSX.Element} - */ - children: JSX.Element - /** - * Auth0 User object - * - * @type {UserProfile | undefined} - */ - authUser: UserProfile | undefined - /** - * Whether Auth0 is loading - * - * @type {boolean} - */ - isAuthLoading: boolean -} + /** + * JSX element to be rendered if AuthCheck passes + * + * @type {JSX.Element} + */ + children: JSX.Element; + /** + * Auth0 User object + * + * @type {UserProfile | undefined} + */ + authUser: UserProfile | undefined; + /** + * Whether Auth0 is loading + * + * @type {boolean} + */ + isAuthLoading: boolean; +}; /** * An Auth Check component to allow only the users who are logged in to access a page or view an element @@ -44,17 +45,20 @@ export type AuthCheckPropsType = { * ``` */ const AuthCheck = (props: AuthCheckPropsType): JSX.Element => { - const { children, authUser, isAuthLoading } = props - const router = useRouter() + const { children, authUser, isAuthLoading } = props; + const router = useRouter(); + + if (isAuthLoading) + return ; - if (isAuthLoading) return + if (!authUser) { + const router = useRouter(); + void router.push("/api/auth/login?returnTo=/playgrounds"); + return ; + } - if (!authUser) { - void router.push("/api/auth/login?returnTo=/playgrounds") - return - } + return children; +}; - return children -} +export default AuthCheck; -export default AuthCheck diff --git a/src/components/FileExplorer/index.tsx b/src/components/FileExplorer/index.tsx index 684c791..8fa0062 100644 --- a/src/components/FileExplorer/index.tsx +++ b/src/components/FileExplorer/index.tsx @@ -5,7 +5,7 @@ import "@szhsin/react-menu/dist/theme-dark.css" import { ControlledMenu, MenuItem, useMenuState } from "@szhsin/react-menu" import AjvJtd, { JTDParser, JTDSchemaType } from "ajv/dist/jtd" -import Image from "next/image" +import LegacyImage from "next/legacy/image" import objectPath from "object-path" import * as objectPathImmutable from "object-path-immutable" import { Dispatch, Fragment, SetStateAction, useEffect, useMemo, useState } from "react" @@ -492,7 +492,7 @@ const FileExplorer = (props: FileExplorerPropsType): JSX.Element => {
sendCrudSocketMessage(serializeCrudClientToServerEvent({ command: "readFile", filePath: item.path }))}> - {item.path} +

{item.name}

@@ -520,7 +520,7 @@ const FileExplorer = (props: FileExplorerPropsType): JSX.Element => { }} >
sendCrudSocketMessage(serializeCrudClientToServerEvent({ command: "readFile", filePath: item.path }))}> - {item.path} +

{ - const router = useRouter() + const router = useRouter(); - useEffect(() => { - void router.push("/") - }, [router]) + useEffect(() => { + void router.push("/"); + }, [router]); + + return (); +}; + +export default Home; - return ( - - 404 - rdamn - - ) -} -export default Home diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index b859be8..a9a311c 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -1,7 +1,7 @@ /* Copyright (c) rishabhrao (https://github.com/rishabhrao) */ -import "tailwindcss/tailwind.css" import "@styles/globals.css" +import "tailwindcss/tailwind.css" import { UserProvider } from "@auth0/nextjs-auth0" import type { AppProps } from "next/app" diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 668fdfb..eed590a 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,144 +1,153 @@ +import { Metadata } from "next"; /* Copyright (c) rishabhrao (https://github.com/rishabhrao) */ -import LogoIcon from "@public/logoWhite.png" -import type { NextPage } from "next" -import Head from "next/head" -import Image from "next/image" -import Link from "next/link" +import LogoIcon from "@public/logoWhite.png"; +import type { NextPage } from "next"; +import Head from "next/head"; +import LegacyImage from "next/legacy/image"; +import Link from "next/link"; +export const metadata: Metadata = { + title: `rdamn - Online Playground IDEs`, + description: "Online Playground IDEs", + themeColor: [{ + color: "#000000", + }], + openGraph: { + title: "rdamn", + description: "Online Playground IDEs", + url: "https://rdamn.cloud", + type: "website", + }, +}; const Home: NextPage = () => { - return ( - <> - - rdamn - Online Playground IDEs - - - - - - - - - - - - - - - - -

-
-
-
-
-
- - rdamn Logo - -
- -
-

rdamn

- -

Online Playground IDEs

-
- -
-
- - - Take me to the Playgrounds! - - - -

You will have to login to access the Playgrounds

-
-
-
-
- -
-
-
-
- - - - - - - - -
- -
- {/* eslint-disable-next-line @next/next/no-img-element */} - rdamn Playground -
-
-
-
- -
- -
- - -
- - ) -} - -export default Home + return (<> + + {/* title was removed */} + + {/* meta was removed */} + + {/* meta was removed */} + + {/* meta was removed */} + {/* meta was removed */} + + + + + + {/* meta was removed */} + {/* meta was removed */} + +
+
+
+
+
+
+ + + +
+ +
+

rdamn

+ +

Online Playground IDEs

+
+ +
+
+ + + Take me to the Playgrounds! + + + +

You will have to login to access the Playgrounds

+
+
+
+
+ +
+
+
+
+ + + + + + + + +
+ +
+ {/* eslint-disable-next-line @next/next/no-img-element */} + rdamn Playground +
+
+
+
+ +
+ +
+ + +
+ ); +}; + +export default Home; + diff --git a/src/pages/playground/[playgroundId].tsx b/src/pages/playground/[playgroundId].tsx index e0ae8db..834a7eb 100644 --- a/src/pages/playground/[playgroundId].tsx +++ b/src/pages/playground/[playgroundId].tsx @@ -17,7 +17,7 @@ import type * as monaco from "monaco-editor/esm/vs/editor/editor.api" import type { GetServerSideProps, InferGetServerSidePropsType } from "next" import dynamic from "next/dynamic" import Head from "next/head" -import Image from "next/image" +import LegacyImage from "next/legacy/image" import Link from "next/link" import { useEffect, useState } from "react" import toast, { Toaster } from "react-hot-toast" @@ -25,6 +25,9 @@ import { ReflexContainer, ReflexElement, ReflexSplitter } from "react-reflex" import ReconnectingWebSocket from "reconnecting-websocket" import useSWR from "swr" +// TODO: implement this function +async function getPlayground() {} + const { getMaterialFileIcon } = await import("file-extension-icon-js") const Terminal = dynamic(() => import("@components/Terminal"), { @@ -258,8 +261,8 @@ const Playground = ({ playground }: InferGetServerSidePropsType - <> + ( + <> {playground.playgroundName} - rdamn Playgrounds @@ -290,7 +293,7 @@ const Playground = ({ playground }: InferGetServerSidePropsType - rdamn + @@ -522,7 +531,7 @@ const Playground = ({ playground }: InferGetServerSidePropsType
setActiveTab(openFile)}> - {openFile.filePath} +

setActiveTab(openFile)}>{openFile.filePath.split("/").splice(-1)}

) : (
- rdamn + {!isPlaygroundUp && (
@@ -632,8 +641,8 @@ const Playground = ({ playground }: InferGetServerSidePropsType - - ) + ) + ); } export default Playground diff --git a/src/pages/playgrounds/index.tsx b/src/pages/playgrounds/index.tsx index 4e97b80..b07e32d 100644 --- a/src/pages/playgrounds/index.tsx +++ b/src/pages/playgrounds/index.tsx @@ -1,119 +1,127 @@ +import { Metadata } from "next"; + +import { useRouter } from "next/navigation"; /* Copyright (c) rishabhrao (https://github.com/rishabhrao) */ -import { getSession, useUser } from "@auth0/nextjs-auth0" -import Alert, { AlertTypes } from "@components/Alert" -import AuthCheck from "@components/AuthCheck" -import { nextPublicBaseUrl } from "@constants/nextPublicBaseUrl" -import { Listbox } from "@headlessui/react" -import { connectToDatabase } from "@lib/connectToDatabase" -import { PlaygroundModel, PlaygroundType } from "@models/PlaygroundModel" -import LogoIcon from "@public/logoWhite.png" -import ProfileIconWhite from "@public/ProfileIconWhite.png" -import type { GetServerSideProps, InferGetServerSidePropsType } from "next" -import Head from "next/head" -import Image from "next/image" -import Link from "next/link" -import { useRouter } from "next/router" -import { Fragment, useState } from "react" -import toast, { Toaster } from "react-hot-toast" - -export const getServerSideProps: GetServerSideProps<{ playgrounds: PlaygroundType[] }> = async ({ req, res }) => { - const authSession = getSession(req, res) - - if (!authSession) { - return { - redirect: { - destination: "/api/auth/login?returnTo=/playgrounds", - permanent: false, - }, - } - } - - const userId = authSession.user.sub as string - - await connectToDatabase() - - const playgrounds = ( - await PlaygroundModel.find({ userId }, null, { - sort: { - createdAt: -1, // Sort by createdAt Desc - }, - }) - ).map(playground => playground.toJSON()) - - return { - props: { playgrounds }, - } -} +import { getSession, useUser } from "@auth0/nextjs-auth0"; +import Alert, { AlertTypes } from "@components/Alert"; +import AuthCheck from "@components/AuthCheck"; +import { nextPublicBaseUrl } from "@constants/nextPublicBaseUrl"; +import { Listbox } from "@headlessui/react"; +import { connectToDatabase } from "@lib/connectToDatabase"; +import { PlaygroundModel, PlaygroundType } from "@models/PlaygroundModel"; +import LogoIcon from "@public/logoWhite.png"; +import ProfileIconWhite from "@public/ProfileIconWhite.png"; +import type { GetServerSideProps, InferGetServerSidePropsType } from "next"; +import LegacyImage from "next/legacy/image"; +import Link from "next/link"; +import { Fragment, useState } from "react"; +import toast, { Toaster } from "react-hot-toast"; +export const metadata: Metadata = { + title: `Playgrounds - rdamn`, + openGraph: { + title: "Playgrounds - rdamn", + }, +}; + +// TODO: implement this function +async function getPlaygrounds() { } + +export const getServerSideProps: GetServerSideProps<{ + playgrounds: PlaygroundType[]; +}> = async ({ req, res }) => { + const authSession = getSession(req, res); + + if (!authSession) { + return { + redirect: { + destination: "/api/auth/login?returnTo=/playgrounds", + permanent: false + } + }; + } + + const userId = authSession.user.sub as string; + + await connectToDatabase(); + + const playgrounds = (await PlaygroundModel.find({ userId }, null, { + sort: { + createdAt: -1 // Sort by createdAt Desc + } + })).map(playground => playground.toJSON()); + + return { + props: { playgrounds } + }; +}; const Playgrounds = ({ playgrounds: ssrPlaygrounds }: InferGetServerSidePropsType) => { - const { user: authUser, isLoading: isAuthLoading } = useUser() - - const router = useRouter() - - const [playgrounds, setPlaygrounds] = useState(ssrPlaygrounds) - - const [isCreatePlaygroundModalOpen, setisCreatePlaygroundModalOpen] = useState(false) - const [isCreatePlaygroundLoading, setIsCreatePlaygroundLoading] = useState(false) - const [newPlaygroundName, setNewPlaygroundName] = useState("") - const [newPlaygroundTemplate, setNewPlaygroundTemplate] = useState("") - - const createNewPlayground = async () => { - setIsCreatePlaygroundLoading(true) - - await fetch(`${nextPublicBaseUrl}/api/playgrounds/create`, { - method: "POST", - body: JSON.stringify({ - newPlaygroundName, - newPlaygroundTemplate, - }), - headers: { "Content-Type": "application/json" }, - }) - .then(async res => { - type serverResponseType = { - success: boolean - message: string - newPlayground?: PlaygroundType - } - - const responseBody = (await res.json()) as serverResponseType - - if (res.ok && responseBody.success && responseBody.newPlayground) { - setPlaygrounds([responseBody.newPlayground, ...playgrounds]) - - toast.custom(, { position: "bottom-center", duration: 5000, id: "success" }) - - await router.push(`/playground/${responseBody.newPlayground.playgroundId}`) - } else { - throw responseBody.message - } - }) - .catch((error: string) => { - toast.custom(, { position: "bottom-center", duration: 5000, id: "error" }) - }) - - setIsCreatePlaygroundLoading(false) - setisCreatePlaygroundModalOpen(false) - } - - return ( - - <> - - Playgrounds - rdamn - - + const { user: authUser, isLoading: isAuthLoading } = useUser(); + + const router = useRouter(); + + const [playgrounds, setPlaygrounds] = useState(ssrPlaygrounds); + + const [isCreatePlaygroundModalOpen, setisCreatePlaygroundModalOpen] = useState(false); + const [isCreatePlaygroundLoading, setIsCreatePlaygroundLoading] = useState(false); + const [newPlaygroundName, setNewPlaygroundName] = useState(""); + const [newPlaygroundTemplate, setNewPlaygroundTemplate] = useState(""); + + const createNewPlayground = async () => { + const router = useRouter(); + setIsCreatePlaygroundLoading(true); + + await fetch(`${nextPublicBaseUrl}/api/playgrounds/create`, { + method: "POST", + body: JSON.stringify({ + newPlaygroundName, + newPlaygroundTemplate + }), + headers: { "Content-Type": "application/json" } + }) + .then(async (res) => { + type serverResponseType = { + success: boolean; + message: string; + newPlayground?: PlaygroundType; + }; + + const responseBody = (await res.json()) as serverResponseType; + + if (res.ok && responseBody.success && responseBody.newPlayground) { + setPlaygrounds([responseBody.newPlayground, ...playgrounds]); + + toast.custom(, { position: "bottom-center", duration: 5000, id: "success" }); + + await router.push(`/playground/${responseBody.newPlayground.playgroundId}`); + } + else { + throw responseBody.message; + } + }) + .catch((error: string) => { + toast.custom(, { position: "bottom-center", duration: 5000, id: "error" }); + }); + + setIsCreatePlaygroundLoading(false); + setisCreatePlaygroundModalOpen(false); + }; + + return (( + <> +
- - - rdamn - - + + + + +
rdamn @@ -126,7 +134,7 @@ const Playgrounds = ({ playgrounds: ssrPlaygrounds }: InferGetServerSidePropsTyp
- User Options +
    @@ -136,18 +144,14 @@ const Playgrounds = ({ playgrounds: ssrPlaygrounds }: InferGetServerSidePropsTyp

    {authUser?.email}

- - - - - - Logout - - + + + + + + Logout + +
@@ -158,22 +162,18 @@ const Playgrounds = ({ playgrounds: ssrPlaygrounds }: InferGetServerSidePropsTyp
-
+
Playgrounds - @@ -181,32 +181,15 @@ const Playgrounds = ({ playgrounds: ssrPlaygrounds }: InferGetServerSidePropsTyp
    - {playgrounds.map(playground => ( -
  • + {playgrounds.map(playground => (
  • - {playground.playgroundTemplate === "html" ? ( - - - - ) : playground.playgroundTemplate === "nextjs" ? ( - - - - ) : ( - - - - )} + {playground.playgroundTemplate === "html" ? ( + + ) : playground.playgroundTemplate === "nextjs" ? ( + + ) : ( + + )}

    {playground.playgroundName}

    @@ -226,19 +209,17 @@ const Playgrounds = ({ playgrounds: ssrPlaygrounds }: InferGetServerSidePropsTyp -
  • - ))} + ))}
{playgrounds.length === 0 &&

No Playgrounds yet... Create one!

} @@ -257,10 +238,9 @@ const Playgrounds = ({ playgrounds: ssrPlaygrounds }: InferGetServerSidePropsTyp
- {isCreatePlaygroundModalOpen && ( -
+ {isCreatePlaygroundModalOpen && (
-
+
{/* This element is to trick the browser into centering the modal contents. */}
@@ -308,29 +278,13 @@ const Playgrounds = ({ playgrounds: ssrPlaygrounds }: InferGetServerSidePropsTyp
- {newPlaygroundTemplate === "html" ? ( - - - - ) : newPlaygroundTemplate === "nextjs" ? ( - - - - ) : ( - - - - )} + {newPlaygroundTemplate === "html" ? ( + + ) : newPlaygroundTemplate === "nextjs" ? ( + + ) : ( + + )} {newPlaygroundTemplate === "html" ? "HTML" : newPlaygroundTemplate === "nextjs" ? "Next.js" : "Select Playground Template"} @@ -338,81 +292,46 @@ const Playgrounds = ({ playgrounds: ssrPlaygrounds }: InferGetServerSidePropsTyp - + - - {({ selected, active }) => ( - + + {({ selected, active }) => (
- + HTML
- {selected ? ( - + {selected ? ( - + - - ) : null} -
- )} + ) : null} +
)}
- - {({ selected, active }) => ( - + + {({ selected, active }) => (
- + Next.js
- {selected ? ( - + {selected ? ( - + - - ) : null} -
- )} + ) : null} +
)}
@@ -420,46 +339,15 @@ const Playgrounds = ({ playgrounds: ssrPlaygrounds }: InferGetServerSidePropsTyp
-
-
- )} +
)} - - ) -} + )); +}; + +export default Playgrounds; + -export default Playgrounds