minor refactors to the adapter code

This commit is contained in:
2026-02-09 00:55:48 +05:30
parent 624b4d35e0
commit 4927be7206
7 changed files with 193 additions and 75 deletions

1
.gitignore vendored
View File

@@ -12,3 +12,4 @@ count.txt
.vinxi .vinxi
todos.json todos.json
data.db data.db
public/uploads

View File

@@ -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 { export default {
connection: { connection: {
url: "data.db", url: "data.db",
}, },
options:{ options: {},
config: {
} data: schema.toJSON(),
} satisfies NextjsBkndConfig; auth: { enabled: true },
media: {
enabled: true,
adapter: local({
path: "./public/uploads",
}),
},
},
} satisfies BkndConfig;

View File

@@ -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 { getApp as getBkndApp } from "bknd/adapter/nextjs";
import config from "../bknd.config"; import config from "../bknd.config";
// import { headers } from "next/headers";
export async function getApi({ export async function getApi({
headers, headers,
verify, verify,
@@ -20,5 +20,3 @@ export async function getApi({
return app.getApi(); return app.getApi();
} }
export { config };

View File

@@ -1,33 +1,34 @@
import { HeadContent, Scripts, createRootRoute } from '@tanstack/react-router' import { HeadContent, Scripts, createRootRoute } from "@tanstack/react-router";
import { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools' import { TanStackRouterDevtoolsPanel } from "@tanstack/react-router-devtools";
import { TanStackDevtools } from '@tanstack/react-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({ export const Route = createRootRoute({
head: () => ({ head: () => ({
meta: [ meta: [
{ {
charSet: 'utf-8', charSet: "utf-8",
}, },
{ {
name: 'viewport', name: "viewport",
content: 'width=device-width, initial-scale=1', content: "width=device-width, initial-scale=1",
}, },
{ {
title: 'TanStack Start Starter', title: "TanStack Start Starter",
}, },
], ],
links: [ links: [
{ {
rel: 'stylesheet', rel: "stylesheet",
href: appCss, href: appCss,
}, },
], ],
}), }),
shellComponent: RootDocument, shellComponent: RootDocument,
}) });
function RootDocument({ children }: { children: React.ReactNode }) { function RootDocument({ children }: { children: React.ReactNode }) {
return ( return (
@@ -36,14 +37,16 @@ function RootDocument({ children }: { children: React.ReactNode }) {
<HeadContent /> <HeadContent />
</head> </head>
<body> <body>
<ClientProvider verbose baseUrl="http://localhost:3000">
{children} {children}
</ClientProvider>
<TanStackDevtools <TanStackDevtools
config={{ config={{
position: 'bottom-right', position: "bottom-right",
}} }}
plugins={[ plugins={[
{ {
name: 'Tanstack Router', name: "Tanstack Router",
render: <TanStackRouterDevtoolsPanel />, render: <TanStackRouterDevtoolsPanel />,
}, },
]} ]}
@@ -51,5 +54,5 @@ function RootDocument({ children }: { children: React.ReactNode }) {
<Scripts /> <Scripts />
</body> </body>
</html> </html>
) );
} }

View File

@@ -1,41 +1,21 @@
import { getApi } from "@/bknd";
import { createFileRoute } from "@tanstack/react-router"; import { createFileRoute } from "@tanstack/react-router";
import { createServerFn } from "@tanstack/react-start"; import { useAuth } from "bknd/client";
import { getRequestHeaders } from "@tanstack/react-start/server";
import "bknd/dist/styles.css"; import "bknd/dist/styles.css";
import { Admin } from "bknd/ui"; 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/$")({ export const Route = createFileRoute("/admin/$")({
component: RouteComponent, component: RouteComponent,
loader: async () => {
const user = await getUser();
return { user };
},
}); });
function RouteComponent() { function RouteComponent() {
const { user } = Route.useLoaderData(); const { user } = useAuth();
console.log(user);
return ( return (
<Admin <Admin
withProvider={{ withProvider={{ user: user }}
user: {
email: "ada@example.com",
id: "",
strategy: "",
},
}}
config={{ config={{
basepath: "/admin", basepath: "/admin",
logo_return_path: "/../", logo_return_path: "/../",
theme: "system",
}} }}
baseUrl="http://localhost:3000" baseUrl="http://localhost:3000"
/> />

View File

@@ -1,25 +1,17 @@
import { createFileRoute } from "@tanstack/react-router"; import { createFileRoute } from "@tanstack/react-router";
import { config } from "@/bknd"; import config from "../../bknd.config";
// Works fine
import { serve } from "bknd/adapter/nextjs"; import { serve } from "bknd/adapter/nextjs";
const handler = serve({ const handler = serve({
...config, ...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/$")({ export const Route = createFileRoute("/api/$")({
server: { server: {
handlers: { handlers: {
ANY: async ({ request }) => { ANY: async ({ request }) => await handler(request),
const res = await handler(request);
// console.log("[API] ", res);
return res;
},
}, },
}, },
}); });

View File

@@ -1,27 +1,142 @@
import { createFileRoute, Link } from "@tanstack/react-router"; import { createFileRoute, Link } from "@tanstack/react-router";
import "../App.css"; import "../App.css";
import { useAuth } from "bknd/client";
import { useState } from "react";
export const Route = createFileRoute("/")({ component: App }); export const Route = createFileRoute("/")({ component: App });
function 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 ( return (
<div className="App"> <div>
<header className="App-header"> <main style={{ padding: 20 }} className="App-header">
<header>
<img <img
src="/tanstack-circle-logo.png" src="/tanstack-circle-logo.png"
alt="TanStack Logo" alt="TanStack Logo"
style={{ style={{ width: "100px", height: "100px" }}
width: "100px",
height: "100px",
}}
/> />
<Link
className="App-link"
to="/admin"
>
Admin Dashboard
</Link>
</header> </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> </div>
); );
} }