diff --git a/app/api/auth/[...nextauth]/options.ts b/app/api/auth/[...nextauth]/options.ts
new file mode 100644
index 00000000..7bf5f674
--- /dev/null
+++ b/app/api/auth/[...nextauth]/options.ts
@@ -0,0 +1,80 @@
+import { createClient } from "falkordb";
+import CredentialsProvider from "next-auth/providers/credentials"
+import { AuthOptions } from "next-auth"
+
+const authOptions : AuthOptions = {
+ providers: [
+ CredentialsProvider({
+ // The name to display on the sign in form (e.g. 'Sign in with...')
+ name: 'Credentials',
+ // The credentials is used to generate a suitable form on the sign in page.
+ // You can specify whatever fields you are expecting to be submitted.
+ // e.g. domain, username, password, 2FA token, etc.
+ // You can pass any HTML attribute to the tag through the object.
+ credentials: {
+ host: { label: "Host", type: "text", placeholder: "localhost" },
+ port: { label: "Port", type: "number", placeholder: "6379" },
+ username: { label: "Username", type: "text" },
+ password: { label: "Password", type: "password" }
+ },
+ async authorize(credentials, req) {
+
+ if (!credentials) {
+ return null
+ }
+
+ try {
+ let client = await createClient({
+ socket: {
+ host: credentials.host ?? "localhost",
+ port: credentials.port ? parseInt(credentials.port) : 6379,
+ reconnectStrategy: false
+ },
+ password: credentials.password ?? undefined,
+ username: credentials.username ?? undefined
+ })
+ .on('error', err => console.log('FalkorDB Client Error', err))
+ .connect();
+
+ let res : any = {
+ host: credentials.host,
+ port: credentials.port,
+ password: credentials.password,
+ username: credentials.username,
+ }
+ return res
+ } catch (err) {
+ console.log(err)
+ return null;
+ }
+ }
+
+ })
+ ],
+ callbacks: {
+ async jwt({ token, user }) {
+ if (user) {
+ token.host = user.host;
+ token.port = user.port;
+ token.username = user.username;
+ token.password = user.password;
+ }
+
+ return token;
+ },
+ async session({ session, token, user }) {
+ if (session.user) {
+ session.user.host = token.host as string;
+ session.user.port = parseInt(token.port as string);
+ session.user.username = token.username as string;
+ session.user.password = token.password as string;
+
+ }
+ return session;
+ }
+ }
+}
+
+
+
+export default authOptions
\ No newline at end of file
diff --git a/app/api/auth/[...nextauth]/route.ts b/app/api/auth/[...nextauth]/route.ts
new file mode 100644
index 00000000..27ead0b0
--- /dev/null
+++ b/app/api/auth/[...nextauth]/route.ts
@@ -0,0 +1,7 @@
+import NextAuth from "next-auth"
+import authOptions from "./options";
+
+
+const handler = NextAuth(authOptions)
+
+export { handler as GET, handler as POST }
\ No newline at end of file
diff --git a/app/api/graph/route.ts b/app/api/graph/route.ts
index 15d43ac3..2f718e05 100644
--- a/app/api/graph/route.ts
+++ b/app/api/graph/route.ts
@@ -1,11 +1,17 @@
import { NextRequest, NextResponse } from "next/server";
import { Graph, RedisClientType, RedisDefaultModules, createClient } from 'falkordb';
+import { getServerSession } from "next-auth/next";
+import authOptions from "../auth/[...nextauth]/options";
+import { cookies } from 'next/headers'
const client = createClient();
client.connect()
export async function GET(request: NextRequest) {
+ const session = await getServerSession(authOptions)
+ console.log(JSON.stringify( session))
+
const graphID = request.nextUrl.searchParams.get("graph");
try {
if (graphID) {
diff --git a/app/layout.tsx b/app/layout.tsx
index 307db4fc..5673253f 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -3,6 +3,8 @@ import { Inter } from 'next/font/google'
import './globals.css'
import Navbar from '@/components/custom/navbar'
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '@/components/ui/resizable'
+import NextAuth from 'next-auth'
+import { NextAuthProvider } from './providers'
const inter = Inter({ subsets: ['latin'] })
@@ -20,13 +22,7 @@ export default function RootLayout({
-
-
-
-
-
- {children}
-
+ {children}
diff --git a/app/login/page.tsx b/app/login/page.tsx
index cc903711..530dfe2d 100644
--- a/app/login/page.tsx
+++ b/app/login/page.tsx
@@ -1,46 +1,54 @@
+'use client'
+
import { CardTitle, CardDescription, CardHeader, CardContent, CardFooter, Card } from "@/components/ui/card"
import { Label } from "@/components/ui/label"
import { Input } from "@/components/ui/input"
import { Button } from "@/components/ui/button"
+import { FormEvent } from "react"
export default function Page() {
+ function handleSubmit(event: FormEvent): void {
+ event.preventDefault();
+
+ // Read the form data
+ const form: any = event.target;
+ const formData = new FormData(form);
+ console.log(formData);
+ }
+
return (
-
-
+
)
}
\ No newline at end of file
diff --git a/app/page.tsx b/app/page.tsx
index c97bd1b3..06ae8dd7 100644
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -1,3 +1,7 @@
+"use client";
+
+import { Button } from "@/components/ui/button";
+import { signIn } from "next-auth/react";
import Link from "next/link";
@@ -7,7 +11,7 @@ export default function Home() {
Welcome to{' '}
-
+ signIn("Credentials", { callbackUrl: '/graph' })} href="/">
FalkorDB Browser
diff --git a/app/providers.tsx b/app/providers.tsx
new file mode 100644
index 00000000..0dbd3fd8
--- /dev/null
+++ b/app/providers.tsx
@@ -0,0 +1,24 @@
+"use client";
+
+import Navbar from "@/components/custom/navbar";
+import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from "@/components/ui/resizable";
+import { SessionProvider } from "next-auth/react";
+// import Navigation from "./components/navigation";
+
+type Props = {
+ children?: React.ReactNode;
+};
+
+export const NextAuthProvider = ({ children }: Props) => {
+ return (
+
+
+
+
+
+
+ {children}
+
+
+ )
+};
\ No newline at end of file
diff --git a/components/custom/navbar.tsx b/components/custom/navbar.tsx
index e4298486..68ba91e0 100644
--- a/components/custom/navbar.tsx
+++ b/components/custom/navbar.tsx
@@ -1,4 +1,6 @@
import { AirVentIcon } from "lucide-react";
+import { signOut } from "next-auth/react";
+import Link from "next/link";
export default function Navbar() {
return (
@@ -33,7 +35,9 @@ export default function Navbar() {
- Setting
+ signOut({ callbackUrl: '/' })} href="/">
+ Sign Out
+
diff --git a/package-lock.json b/package-lock.json
index 882f08b1..bd31e4a2 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -18,6 +18,7 @@
"falkordb": "^5.0.1",
"lucide-react": "^0.301.0",
"next": "14.0.4",
+ "next-auth": "^4.24.5",
"react": "^18",
"react-cytoscapejs": "^2.0.0",
"react-dom": "^18",
@@ -479,6 +480,14 @@
"node": ">= 8"
}
},
+ "node_modules/@panva/hkdf": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.1.1.tgz",
+ "integrity": "sha512-dhPeilub1NuIG0X5Kvhh9lH4iW3ZsHlnzwgwbOlgwQ2wG1IqFzsgHqmKPk3WzsdWAeaxKJxgM0+W433RmN45GA==",
+ "funding": {
+ "url": "https://github.com/sponsors/panva"
+ }
+ },
"node_modules/@pkgjs/parseargs": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
@@ -1800,6 +1809,14 @@
"url": "https://github.com/chalk/supports-color?sponsor=1"
}
},
+ "node_modules/cookie": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
+ "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -3443,6 +3460,14 @@
"jiti": "bin/jiti.js"
}
},
+ "node_modules/jose": {
+ "version": "4.15.4",
+ "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.4.tgz",
+ "integrity": "sha512-W+oqK4H+r5sITxfxpSU+MMdr/YSWGvgZMQDIsNoBDGGy4i7GBPTtvFKibQzW06n3U3TqHjhvBJsirShsEJ6eeQ==",
+ "funding": {
+ "url": "https://github.com/sponsors/panva"
+ }
+ },
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -3599,7 +3624,6 @@
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
- "dev": true,
"dependencies": {
"yallist": "^4.0.0"
},
@@ -3749,6 +3773,33 @@
}
}
},
+ "node_modules/next-auth": {
+ "version": "4.24.5",
+ "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.24.5.tgz",
+ "integrity": "sha512-3RafV3XbfIKk6rF6GlLE4/KxjTcuMCifqrmD+98ejFq73SRoj2rmzoca8u764977lH/Q7jo6Xu6yM+Re1Mz/Og==",
+ "dependencies": {
+ "@babel/runtime": "^7.20.13",
+ "@panva/hkdf": "^1.0.2",
+ "cookie": "^0.5.0",
+ "jose": "^4.11.4",
+ "oauth": "^0.9.15",
+ "openid-client": "^5.4.0",
+ "preact": "^10.6.3",
+ "preact-render-to-string": "^5.1.19",
+ "uuid": "^8.3.2"
+ },
+ "peerDependencies": {
+ "next": "^12.2.5 || ^13 || ^14",
+ "nodemailer": "^6.6.5",
+ "react": "^17.0.2 || ^18",
+ "react-dom": "^17.0.2 || ^18"
+ },
+ "peerDependenciesMeta": {
+ "nodemailer": {
+ "optional": true
+ }
+ }
+ },
"node_modules/next/node_modules/postcss": {
"version": "8.4.31",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
@@ -3799,6 +3850,11 @@
"node": ">=0.10.0"
}
},
+ "node_modules/oauth": {
+ "version": "0.9.15",
+ "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz",
+ "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA=="
+ },
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -3924,6 +3980,14 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/oidc-token-hash": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.3.tgz",
+ "integrity": "sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==",
+ "engines": {
+ "node": "^10.13.0 || >=12.0.0"
+ }
+ },
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -3933,6 +3997,28 @@
"wrappy": "1"
}
},
+ "node_modules/openid-client": {
+ "version": "5.6.4",
+ "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.6.4.tgz",
+ "integrity": "sha512-T1h3B10BRPKfcObdBklX639tVz+xh34O7GjofqrqiAQdm7eHsQ00ih18x6wuJ/E6FxdtS2u3FmUGPDeEcMwzNA==",
+ "dependencies": {
+ "jose": "^4.15.4",
+ "lru-cache": "^6.0.0",
+ "object-hash": "^2.2.0",
+ "oidc-token-hash": "^5.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/panva"
+ }
+ },
+ "node_modules/openid-client/node_modules/object-hash": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz",
+ "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/optionator": {
"version": "0.9.3",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz",
@@ -4225,6 +4311,26 @@
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
},
+ "node_modules/preact": {
+ "version": "10.19.3",
+ "resolved": "https://registry.npmjs.org/preact/-/preact-10.19.3.tgz",
+ "integrity": "sha512-nHHTeFVBTHRGxJXKkKu5hT8C/YWBkPso4/Gad6xuj5dbptt9iF9NZr9pHbPhBrnT2klheu7mHTxTZ/LjwJiEiQ==",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/preact"
+ }
+ },
+ "node_modules/preact-render-to-string": {
+ "version": "5.2.6",
+ "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz",
+ "integrity": "sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==",
+ "dependencies": {
+ "pretty-format": "^3.8.0"
+ },
+ "peerDependencies": {
+ "preact": ">=10"
+ }
+ },
"node_modules/prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@@ -4234,6 +4340,11 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/pretty-format": {
+ "version": "3.8.0",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz",
+ "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew=="
+ },
"node_modules/prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
@@ -5237,6 +5348,14 @@
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
+ "node_modules/uuid": {
+ "version": "8.3.2",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
"node_modules/watchpack": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
diff --git a/package.json b/package.json
index ebbf61ab..3d15322e 100644
--- a/package.json
+++ b/package.json
@@ -21,6 +21,7 @@
"falkordb": "^5.0.1",
"lucide-react": "^0.301.0",
"next": "14.0.4",
+ "next-auth": "^4.24.5",
"react": "^18",
"react-cytoscapejs": "^2.0.0",
"react-dom": "^18",
diff --git a/tsconfig.json b/tsconfig.json
index c7146963..75a4570d 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -22,6 +22,6 @@
"@/*": ["./*"]
}
},
- "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "app/api/auth/[...nextauth]/route.ts"],
"exclude": ["node_modules"]
}
diff --git a/types/next-auth.d.ts b/types/next-auth.d.ts
new file mode 100644
index 00000000..e3f6ac21
--- /dev/null
+++ b/types/next-auth.d.ts
@@ -0,0 +1,15 @@
+import NextAuth, { DefaultUser } from "next-auth";
+
+declare module "next-auth" {
+
+ interface User extends DefaultUser {
+ host: string;
+ port: number;
+ username: string;
+ password: string;
+ }
+
+ interface Session {
+ user: User;
+ }
+}
\ No newline at end of file