cleanups/refactor of the entire main branch
All checks were successful
Build and Push to Gitea Registry / build-and-push (push) Successful in 6m40s

This commit is contained in:
2026-03-15 23:29:10 +05:30
parent 994c7eab86
commit 95a9a62704
8 changed files with 65 additions and 163 deletions

3
.gitignore vendored
View File

@@ -11,4 +11,5 @@ count.txt
.output .output
.vinxi .vinxi
todos.json todos.json
public/uploads public/uploads
data

View File

@@ -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

View File

@@ -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) => {

View File

@@ -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:

View File

@@ -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 {

View File

@@ -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>
// );
// }

View File

@@ -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">&amp;</div> <div className="ml-3.5 mr-2 font-mono opacity-70">&amp;</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"
> >

View File

@@ -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)
} }