mirror of
https://github.com/shishantbiswas/bknd-examples.git
synced 2026-02-27 12:01:16 +00:00
minor refactors to the adapter code
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -12,3 +12,4 @@ count.txt
|
||||
.vinxi
|
||||
todos.json
|
||||
data.db
|
||||
public/uploads
|
||||
@@ -1,10 +1,39 @@
|
||||
import type { NextjsBkndConfig } from "bknd/adapter/nextjs";
|
||||
import { type BkndConfig, em, entity, text, boolean } from "bknd";
|
||||
|
||||
// Unrelated to framework adapters
|
||||
import { registerLocalMediaAdapter } from "bknd/adapter/node";
|
||||
|
||||
const local = registerLocalMediaAdapter();
|
||||
|
||||
|
||||
// --------------------- SCHEMA -----------------------
|
||||
// this just for testing
|
||||
const schema = em({
|
||||
todos: entity("todos", {
|
||||
title: text(),
|
||||
done: boolean(),
|
||||
}),
|
||||
post:entity("posts",{
|
||||
title: text(),
|
||||
content: text(),
|
||||
})
|
||||
});
|
||||
|
||||
// --------------------- SCHEMA END -----------------------
|
||||
|
||||
export default {
|
||||
connection: {
|
||||
url: "data.db",
|
||||
},
|
||||
options:{
|
||||
|
||||
}
|
||||
} satisfies NextjsBkndConfig;
|
||||
options: {},
|
||||
config: {
|
||||
data: schema.toJSON(),
|
||||
auth: { enabled: true },
|
||||
media: {
|
||||
enabled: true,
|
||||
adapter: local({
|
||||
path: "./public/uploads",
|
||||
}),
|
||||
},
|
||||
},
|
||||
} satisfies BkndConfig;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Both work just fine
|
||||
// import { getApp as getBkndApp } from "bknd/adapter/react-router";
|
||||
import { getApp as getBkndApp } from "bknd/adapter/nextjs";
|
||||
import config from "../bknd.config";
|
||||
|
||||
// import { headers } from "next/headers";
|
||||
|
||||
export async function getApi({
|
||||
headers,
|
||||
verify,
|
||||
@@ -20,5 +20,3 @@ export async function getApi({
|
||||
|
||||
return app.getApi();
|
||||
}
|
||||
|
||||
export { config };
|
||||
|
||||
@@ -1,33 +1,34 @@
|
||||
import { HeadContent, Scripts, createRootRoute } from '@tanstack/react-router'
|
||||
import { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools'
|
||||
import { TanStackDevtools } from '@tanstack/react-devtools'
|
||||
import { HeadContent, Scripts, createRootRoute } from "@tanstack/react-router";
|
||||
import { TanStackRouterDevtoolsPanel } from "@tanstack/react-router-devtools";
|
||||
import { TanStackDevtools } from "@tanstack/react-devtools";
|
||||
import { ClientProvider } from "bknd/client";
|
||||
|
||||
import appCss from '../styles.css?url'
|
||||
import appCss from "../styles.css?url";
|
||||
|
||||
export const Route = createRootRoute({
|
||||
head: () => ({
|
||||
meta: [
|
||||
{
|
||||
charSet: 'utf-8',
|
||||
charSet: "utf-8",
|
||||
},
|
||||
{
|
||||
name: 'viewport',
|
||||
content: 'width=device-width, initial-scale=1',
|
||||
name: "viewport",
|
||||
content: "width=device-width, initial-scale=1",
|
||||
},
|
||||
{
|
||||
title: 'TanStack Start Starter',
|
||||
title: "TanStack Start Starter",
|
||||
},
|
||||
],
|
||||
links: [
|
||||
{
|
||||
rel: 'stylesheet',
|
||||
rel: "stylesheet",
|
||||
href: appCss,
|
||||
},
|
||||
],
|
||||
}),
|
||||
|
||||
shellComponent: RootDocument,
|
||||
})
|
||||
});
|
||||
|
||||
function RootDocument({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
@@ -36,14 +37,16 @@ function RootDocument({ children }: { children: React.ReactNode }) {
|
||||
<HeadContent />
|
||||
</head>
|
||||
<body>
|
||||
{children}
|
||||
<ClientProvider verbose baseUrl="http://localhost:3000">
|
||||
{children}
|
||||
</ClientProvider>
|
||||
<TanStackDevtools
|
||||
config={{
|
||||
position: 'bottom-right',
|
||||
position: "bottom-right",
|
||||
}}
|
||||
plugins={[
|
||||
{
|
||||
name: 'Tanstack Router',
|
||||
name: "Tanstack Router",
|
||||
render: <TanStackRouterDevtoolsPanel />,
|
||||
},
|
||||
]}
|
||||
@@ -51,5 +54,5 @@ function RootDocument({ children }: { children: React.ReactNode }) {
|
||||
<Scripts />
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,41 +1,21 @@
|
||||
import { getApi } from "@/bknd";
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { createServerFn } from "@tanstack/react-start";
|
||||
import { getRequestHeaders } from "@tanstack/react-start/server";
|
||||
import { useAuth } from "bknd/client";
|
||||
import "bknd/dist/styles.css";
|
||||
import { Admin } from "bknd/ui";
|
||||
|
||||
export const getUser = createServerFn({ method: "GET" }).handler(async () => {
|
||||
const headers = getRequestHeaders();
|
||||
const api = await getApi({ verify: true, headers });
|
||||
return { user: api.getUser() };
|
||||
});
|
||||
|
||||
export const Route = createFileRoute("/admin/$")({
|
||||
component: RouteComponent,
|
||||
loader: async () => {
|
||||
const user = await getUser();
|
||||
return { user };
|
||||
},
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
const { user } = Route.useLoaderData();
|
||||
console.log(user);
|
||||
|
||||
const { user } = useAuth();
|
||||
return (
|
||||
<Admin
|
||||
withProvider={{
|
||||
user: {
|
||||
email: "ada@example.com",
|
||||
id: "",
|
||||
strategy: "",
|
||||
},
|
||||
}}
|
||||
withProvider={{ user: user }}
|
||||
config={{
|
||||
basepath: "/admin",
|
||||
logo_return_path: "/../",
|
||||
theme: "system",
|
||||
}}
|
||||
baseUrl="http://localhost:3000"
|
||||
/>
|
||||
|
||||
@@ -1,25 +1,17 @@
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { config } from "@/bknd";
|
||||
import config from "../../bknd.config";
|
||||
|
||||
// Works fine
|
||||
import { serve } from "bknd/adapter/nextjs";
|
||||
|
||||
const handler = serve({
|
||||
...config,
|
||||
// cleanRequest: {
|
||||
// depending on what name you used for the catch-all route,
|
||||
// you need to change this to clean it from the request.
|
||||
// searchParams: ["$"],
|
||||
// },
|
||||
});
|
||||
|
||||
export const Route = createFileRoute("/api/$")({
|
||||
server: {
|
||||
handlers: {
|
||||
ANY: async ({ request }) => {
|
||||
const res = await handler(request);
|
||||
// console.log("[API] ", res);
|
||||
|
||||
return res;
|
||||
},
|
||||
ANY: async ({ request }) => await handler(request),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,27 +1,142 @@
|
||||
import { createFileRoute, Link } from "@tanstack/react-router";
|
||||
import "../App.css";
|
||||
import { useAuth } from "bknd/client";
|
||||
import { useState } from "react";
|
||||
|
||||
export const Route = createFileRoute("/")({ component: App });
|
||||
|
||||
function App() {
|
||||
const { user, verified, register, logout, login } = useAuth();
|
||||
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [mode, setMode] = useState<"login" | "register">("login");
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
async function handleSubmit(e: React.SubmitEvent) {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
if (mode === "login") {
|
||||
// attempt login
|
||||
await login({ email, password } as any);
|
||||
} else {
|
||||
// attempt register
|
||||
await register({ email, password } as any);
|
||||
}
|
||||
setEmail("");
|
||||
setPassword("");
|
||||
} catch (err: any) {
|
||||
setError(err?.message ?? String(err));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleLogout() {
|
||||
setLoading(true);
|
||||
try {
|
||||
await logout();
|
||||
} catch (err: any) {
|
||||
setError(err?.message ?? String(err));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="App">
|
||||
<header className="App-header">
|
||||
<img
|
||||
src="/tanstack-circle-logo.png"
|
||||
alt="TanStack Logo"
|
||||
style={{
|
||||
width: "100px",
|
||||
height: "100px",
|
||||
}}
|
||||
/>
|
||||
<Link
|
||||
className="App-link"
|
||||
to="/admin"
|
||||
>
|
||||
Admin Dashboard
|
||||
</Link>
|
||||
</header>
|
||||
<div>
|
||||
<main style={{ padding: 20 }} className="App-header">
|
||||
<header>
|
||||
<img
|
||||
src="/tanstack-circle-logo.png"
|
||||
alt="TanStack Logo"
|
||||
style={{ width: "100px", height: "100px" }}
|
||||
/>
|
||||
</header>
|
||||
<section>
|
||||
|
||||
</section>
|
||||
|
||||
<section style={{ maxWidth: 420, margin: "0 auto" }}>
|
||||
<h2 style={{margin:"24px 0 "}}>Account</h2>
|
||||
{user ? (
|
||||
<div style={{ gap: 8, display: "flex", flexDirection: "column" }}>
|
||||
<div>
|
||||
<strong>Signed in as:</strong> {user?.email ?? "Unknown"}
|
||||
</div>
|
||||
<div>
|
||||
<strong>Verified:</strong> {verified ? "Yes" : "No"}
|
||||
</div>
|
||||
<div style={{ display: "flex", gap: 8 }}>
|
||||
<button onClick={handleLogout} disabled={loading}>
|
||||
{loading ? "Signing out..." : "Sign out"}
|
||||
</button>
|
||||
<Link to={"/admin" as string}>
|
||||
<button>Go to Admin</button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<form onSubmit={handleSubmit} style={{ display: "grid", gap: 8 }}>
|
||||
<div style={{ display: "flex", gap: 8 }}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setMode("login")}
|
||||
style={{ textDecoration: mode === "login" ? "underline" : "none" }}
|
||||
>
|
||||
Log in
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setMode("register")}
|
||||
style={{ textDecoration: mode === "register" ? "underline" : "none" }}
|
||||
>
|
||||
Register
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<input
|
||||
placeholder="Email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
required
|
||||
style={{
|
||||
border: "1px solid white",
|
||||
padding: "4px",
|
||||
borderRadius: "4px"
|
||||
}}
|
||||
type="email"
|
||||
/>
|
||||
|
||||
<input
|
||||
placeholder="Password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
required
|
||||
style={{
|
||||
border: "1px solid white",
|
||||
padding: "4px",
|
||||
borderRadius: "4px"
|
||||
}}
|
||||
type="password"
|
||||
/>
|
||||
|
||||
{error ? <div style={{ color: "#ff6b6b" }}>{error}</div> : null}
|
||||
|
||||
<button type="submit" disabled={loading}>
|
||||
{loading
|
||||
? "Please wait..."
|
||||
: mode === "login"
|
||||
? "Log in"
|
||||
: "Create account"}
|
||||
</button>
|
||||
</form>
|
||||
)}
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user