mirror of
https://github.com/shishantbiswas/bknd-examples.git
synced 2026-03-15 18:01:06 +00:00
cleanups/refactor of the entire main branch
All checks were successful
Build and Push to Gitea Registry / build-and-push (push) Successful in 6m40s
All checks were successful
Build and Push to Gitea Registry / build-and-push (push) Successful in 6m40s
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -11,4 +11,5 @@ count.txt
|
|||||||
.output
|
.output
|
||||||
.vinxi
|
.vinxi
|
||||||
todos.json
|
todos.json
|
||||||
public/uploads
|
public/uploads
|
||||||
|
data
|
||||||
27
README.md
27
README.md
@@ -16,6 +16,33 @@ bun run dev
|
|||||||
3. **`src/routes/index.tsx`** - Using `getApp()` to fetch data in loader
|
3. **`src/routes/index.tsx`** - Using `getApp()` to fetch data in loader
|
||||||
3. **`src/routes/ssr.tsx`** - Server Side example with `getApp()` to fetch data on server
|
3. **`src/routes/ssr.tsx`** - Server Side example with `getApp()` to fetch data on server
|
||||||
|
|
||||||
|
## Directory Structure
|
||||||
|
|
||||||
|
```txt
|
||||||
|
├── bknd.config.ts
|
||||||
|
├── bun.lock
|
||||||
|
├── docker-compose.yml
|
||||||
|
├── Dockerfile
|
||||||
|
├── package.json
|
||||||
|
├── public
|
||||||
|
├── README.md
|
||||||
|
├── src
|
||||||
|
│ ├── bknd.ts # bknd singleton initialization
|
||||||
|
│ ├── logo.svg
|
||||||
|
│ ├── router.tsx
|
||||||
|
│ ├── routes
|
||||||
|
│ │ ├── api.$.ts # bknd API handler
|
||||||
|
│ │ ├── admin.$.tsx # Admin Dashboard
|
||||||
|
│ │ ├── index.tsx # Client Side Rendering example
|
||||||
|
│ │ └── ssr.tsx # Server Side Rendering example
|
||||||
|
│ │ ├── __root.tsx
|
||||||
|
│ ├── routeTree.gen.ts
|
||||||
|
│ └── styles.css
|
||||||
|
├── tsconfig.json
|
||||||
|
└── vite.config.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## API Endpoints
|
## API Endpoints
|
||||||
|
|
||||||
- `GET /admin` - for Admin Dashboard
|
- `GET /admin` - for Admin Dashboard
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { em, entity, text, boolean, libsql } from "bknd";
|
import { em, entity, text, boolean, libsql, type Connection } from "bknd";
|
||||||
|
|
||||||
import type { TanstackStartConfig } from "bknd/adapter/tanstack-start";
|
import type { TanstackStartConfig } from "bknd/adapter/tanstack-start";
|
||||||
import { registerLocalMediaAdapter } from "bknd/adapter/node";
|
import { registerLocalMediaAdapter } from "bknd/adapter/node";
|
||||||
@@ -19,10 +19,17 @@ declare module "bknd" {
|
|||||||
interface DB extends Database { }
|
interface DB extends Database { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getDBConnection(): Connection | { url: ":memory:" | (string & {}) } {
|
||||||
|
if (process.env.NODE_ENV !== "production") return { url: ":memory:" };
|
||||||
|
|
||||||
|
return libsql({
|
||||||
|
url: process.env.DATABASE_URL,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
connection: libsql({
|
connection: getDBConnection(),
|
||||||
url: process.env.DATABASE_URL || "http://localhost:8080",
|
|
||||||
}),
|
|
||||||
options: {
|
options: {
|
||||||
// the seed option is only executed if the database was empty
|
// the seed option is only executed if the database was empty
|
||||||
seed: async (ctx) => {
|
seed: async (ctx) => {
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
build:
|
image: gitea.bsws.in/shishantbiswas/bknd-examples:tanstack-start
|
||||||
context: .
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
deploy:
|
deploy:
|
||||||
resources:
|
resources:
|
||||||
limits:
|
limits:
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { App } from "bknd";
|
import { App } from "bknd";
|
||||||
import config from "../bknd.config";
|
import config from "../../bknd.config";
|
||||||
import { getApp as getBkndApp } from "bknd/adapter/tanstack-start";
|
import { getApp as getBkndApp } from "bknd/adapter/tanstack-start";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
@@ -4,7 +4,7 @@ import {
|
|||||||
useRouter,
|
useRouter,
|
||||||
useRouterState,
|
useRouterState,
|
||||||
} from "@tanstack/react-router";
|
} from "@tanstack/react-router";
|
||||||
import { getApi } from "@/bknd";
|
import { getApi } from "@/lib/bknd";
|
||||||
import { createServerFn, useServerFn } from "@tanstack/react-start";
|
import { createServerFn, useServerFn } from "@tanstack/react-start";
|
||||||
|
|
||||||
export const completeTodo = createServerFn({ method: "POST" })
|
export const completeTodo = createServerFn({ method: "POST" })
|
||||||
@@ -57,7 +57,6 @@ export const getTodo = createServerFn({ method: "POST" }).handler(async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const Route = createFileRoute("/")({
|
export const Route = createFileRoute("/")({
|
||||||
ssr:false,
|
|
||||||
component: App,
|
component: App,
|
||||||
loader: async () => {
|
loader: async () => {
|
||||||
return await getTodo();
|
return await getTodo();
|
||||||
@@ -73,11 +72,11 @@ function App() {
|
|||||||
const addTodo = useServerFn(createTodo);
|
const addTodo = useServerFn(createTodo);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
|
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 ">
|
||||||
<main className="flex flex-col gap-8 row-start-2 items-center sm:items-start">
|
<main className="flex flex-col gap-8 row-start-2 items-center sm:items-start">
|
||||||
<div className="flex flex-row items-center ">
|
<div className="flex flex-row items-center ">
|
||||||
<img
|
<img
|
||||||
className="dark:invert size-18"
|
className="size-18"
|
||||||
src="/tanstack-circle-logo.png"
|
src="/tanstack-circle-logo.png"
|
||||||
alt="Next.js logo"
|
alt="Next.js logo"
|
||||||
/>
|
/>
|
||||||
@@ -102,7 +101,7 @@ function App() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="flex flex-col gap-3">
|
<div className="flex flex-col gap-3">
|
||||||
{todos.reverse().map((todo) => (
|
{todos.map((todo) => (
|
||||||
<div className="flex flex-row" key={String(todo.id)}>
|
<div className="flex flex-row" key={String(todo.id)}>
|
||||||
<div className="flex flex-row flex-grow items-center gap-3 ml-1">
|
<div className="flex flex-row flex-grow items-center gap-3 ml-1">
|
||||||
<input
|
<input
|
||||||
@@ -230,141 +229,3 @@ export function Footer() {
|
|||||||
</footer>
|
</footer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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>
|
|
||||||
// <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>
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { createFileRoute, useRouterState } from "@tanstack/react-router";
|
import { createFileRoute, useRouterState } from "@tanstack/react-router";
|
||||||
import { getRequest } from "@tanstack/react-start/server";
|
import { getRequest } from "@tanstack/react-start/server";
|
||||||
|
import { getApi } from "@/lib/bknd";
|
||||||
|
import { createServerFn } from "@tanstack/react-start";
|
||||||
|
import { Link } from "@tanstack/react-router";
|
||||||
|
|
||||||
export const getTodo = createServerFn({ method: "POST" }).handler(async () => {
|
export const getTodo = createServerFn({ method: "POST" }).handler(async () => {
|
||||||
const api = await getApi({});
|
const api = await getApi({});
|
||||||
@@ -23,21 +26,18 @@ export const Route = createFileRoute("/ssr")({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
import { getApi } from "@/bknd";
|
|
||||||
import { createServerFn } from "@tanstack/react-start";
|
|
||||||
import { Link } from "@tanstack/react-router";
|
|
||||||
|
|
||||||
function RouteComponent() {
|
function RouteComponent() {
|
||||||
const { todos, user } = Route.useLoaderData();
|
const { todos, user } = Route.useLoaderData();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
|
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 ">
|
||||||
<main className="flex flex-col gap-8 row-start-2 items-center sm:items-start">
|
<main className="flex flex-col gap-8 row-start-2 items-center sm:items-start">
|
||||||
<div className="flex flex-row items-center ">
|
<div className="flex flex-row items-center ">
|
||||||
<img
|
<img
|
||||||
className="dark:invert size-18"
|
className="size-18"
|
||||||
src="/tanstack-circle-logo.png"
|
src="/tanstack-circle-logo.png"
|
||||||
alt="Next.js logo"
|
alt="tanstack logo"
|
||||||
/>
|
/>
|
||||||
<div className="ml-3.5 mr-2 font-mono opacity-70">&</div>
|
<div className="ml-3.5 mr-2 font-mono opacity-70">&</div>
|
||||||
<img
|
<img
|
||||||
@@ -157,7 +157,7 @@ function Buttons() {
|
|||||||
return (
|
return (
|
||||||
<div className="flex gap-4 items-center flex-col sm:flex-row">
|
<div className="flex gap-4 items-center flex-col sm:flex-row">
|
||||||
<a
|
<a
|
||||||
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground gap-2 text-white hover:bg-[#383838] dark:hover:bg-[#ccc] text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5"
|
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground gap-2 text-white hover:bg-[#383838] dark:hover:bg-[#ccc] dark:text-black text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5"
|
||||||
href="https://bknd.io/"
|
href="https://bknd.io/"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
@@ -173,7 +173,7 @@ function Buttons() {
|
|||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44"
|
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44"
|
||||||
href="https://docs.bknd.io/integration/nextjs"
|
href="https://docs.bknd.io/integration/tanstack-start"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,8 +1,16 @@
|
|||||||
|
@import url('https://fonts.googleapis.com/css2?family=Geist+Mono:wght@100..900&display=swap');
|
||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
|
|
||||||
|
.geist-mono-100 {
|
||||||
|
font-optical-sizing: auto;
|
||||||
|
font-weight: 100;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--background: #ffffff;
|
--background: #ffffff;
|
||||||
--foreground: #171717;
|
--foreground: #171717;
|
||||||
|
font-family: "Geist Mono", monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
@@ -13,13 +21,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@theme {
|
@theme {
|
||||||
--font-sans: var(--font-geist-sans);
|
--font-sans: var(--font-geist-mono-100);
|
||||||
--font-mono: var(--font-geist-mono);
|
--font-mono: var(--font-geist-mono-100);
|
||||||
--color-background: var(--background);
|
--color-background: var(--background);
|
||||||
--color-foreground: var(--foreground);
|
--color-foreground: var(--foreground);
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@apply bg-background text-foreground;
|
@apply bg-background text-foreground;
|
||||||
font-family: Arial, Helvetica, sans-serif;
|
font-family: var(--font-geist-mono-100)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user