@farm.js/create-app 0.1.0-beta.4 → 0.1.0-beta.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -4
- package/bin/create-farm-app.js +8 -2
- package/dist/index.js +3244 -19
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3239 -18
- package/dist/index.mjs.map +1 -1
- package/dist/utils.js +9 -6
- package/dist/utils.js.map +1 -1
- package/dist/utils.mjs +9 -6
- package/dist/utils.mjs.map +1 -1
- package/package.json +2 -2
- package/templates/_integrations/better-auth/preact/farm.config.ts +17 -0
- package/templates/_integrations/better-auth/preact/src/app/dashboard/page.tsx +73 -0
- package/templates/_integrations/better-auth/preact/src/app/layout.tsx +14 -0
- package/templates/_integrations/better-auth/preact/src/app/page.tsx +36 -0
- package/templates/_integrations/better-auth/preact/src/app/sign-in/page.tsx +10 -0
- package/templates/_integrations/better-auth/preact/src/app/sign-up/page.tsx +10 -0
- package/templates/_integrations/better-auth/preact/src/components/auth-form.tsx +92 -0
- package/templates/_integrations/better-auth/preact/src/components/auth-shell.tsx +16 -0
- package/templates/_integrations/better-auth/preact/src/lib/auth-client.ts +5 -0
- package/templates/_integrations/better-auth/solid/farm.config.ts +17 -0
- package/templates/_integrations/better-auth/solid/src/app/dashboard/page.tsx +67 -0
- package/templates/_integrations/better-auth/solid/src/app/layout.tsx +14 -0
- package/templates/_integrations/better-auth/solid/src/app/page.tsx +36 -0
- package/templates/_integrations/better-auth/solid/src/app/sign-in/page.tsx +10 -0
- package/templates/_integrations/better-auth/solid/src/app/sign-up/page.tsx +10 -0
- package/templates/_integrations/better-auth/solid/src/components/auth-form.tsx +91 -0
- package/templates/_integrations/better-auth/solid/src/components/auth-shell.tsx +16 -0
- package/templates/_integrations/better-auth/solid/src/lib/auth-client.ts +5 -0
- package/templates/_integrations/better-auth/svelte/farm.config.ts +17 -0
- package/templates/_integrations/better-auth/svelte/src/app/dashboard/page.svelte +62 -0
- package/templates/_integrations/better-auth/svelte/src/app/layout.svelte +20 -0
- package/templates/_integrations/better-auth/svelte/src/app/page.svelte +32 -0
- package/templates/_integrations/better-auth/svelte/src/app/sign-in/page.svelte +10 -0
- package/templates/_integrations/better-auth/svelte/src/app/sign-up/page.svelte +10 -0
- package/templates/_integrations/better-auth/svelte/src/components/auth-form.svelte +83 -0
- package/templates/_integrations/better-auth/svelte/src/components/auth-shell.svelte +16 -0
- package/templates/_integrations/better-auth/svelte/src/components/resource-links.svelte +47 -0
- package/templates/_integrations/better-auth/svelte/src/lib/auth-client.ts +5 -0
- package/templates/_integrations/better-auth/vue/farm.config.ts +17 -0
- package/templates/_integrations/better-auth/vue/src/app/dashboard/page.vue +62 -0
- package/templates/_integrations/better-auth/vue/src/app/layout.vue +20 -0
- package/templates/_integrations/better-auth/vue/src/app/page.vue +34 -0
- package/templates/_integrations/better-auth/vue/src/app/sign-in/page.vue +12 -0
- package/templates/_integrations/better-auth/vue/src/app/sign-up/page.vue +12 -0
- package/templates/_integrations/better-auth/vue/src/components/auth-form.vue +86 -0
- package/templates/_integrations/better-auth/vue/src/components/auth-shell.vue +15 -0
- package/templates/_integrations/better-auth/vue/src/components/resource-links.vue +43 -0
- package/templates/_integrations/better-auth/vue/src/lib/auth-client.ts +5 -0
- package/templates/_renderers/preact/farm.config.ts +12 -0
- package/templates/_renderers/preact/package.json +27 -0
- package/templates/_renderers/preact/src/app/api/greeting/route.ts +15 -0
- package/templates/_renderers/preact/src/app/layout.tsx +16 -0
- package/templates/_renderers/preact/src/app/page.tsx +66 -0
- package/templates/_renderers/preact/src/app/preact.css +26 -0
- package/templates/_renderers/preact/src/components/resource-links.tsx +71 -0
- package/templates/_renderers/preact/src/lib/api-client.ts +4 -0
- package/templates/_renderers/preact/src/lib/api.generated.ts +7 -0
- package/templates/_renderers/preact/tsconfig.json +25 -0
- package/templates/_renderers/solid/farm.config.ts +12 -0
- package/templates/_renderers/solid/package.json +26 -0
- package/templates/_renderers/solid/src/app/api/greeting/route.ts +15 -0
- package/templates/_renderers/solid/src/app/layout.tsx +15 -0
- package/templates/_renderers/solid/src/app/page.tsx +56 -0
- package/templates/_renderers/solid/src/app/solid.css +22 -0
- package/templates/_renderers/solid/src/components/resource-links.tsx +71 -0
- package/templates/_renderers/solid/src/lib/api-client.ts +4 -0
- package/templates/_renderers/solid/src/lib/api.generated.ts +7 -0
- package/templates/_renderers/solid/tsconfig.json +25 -0
- package/templates/_renderers/svelte/farm.config.ts +9 -0
- package/templates/_renderers/svelte/package.json +28 -0
- package/templates/_renderers/svelte/src/app/api/greeting/route.ts +15 -0
- package/templates/_renderers/svelte/src/app/layout.svelte +21 -0
- package/templates/_renderers/svelte/src/app/page.svelte +64 -0
- package/templates/_renderers/svelte/src/app/svelte.css +26 -0
- package/templates/_renderers/svelte/src/components/resource-links.svelte +31 -0
- package/templates/_renderers/svelte/src/lib/api-client.ts +4 -0
- package/templates/_renderers/svelte/src/lib/api.generated.ts +7 -0
- package/templates/_renderers/svelte/tsconfig.json +23 -0
- package/templates/_renderers/vue/farm.config.ts +12 -0
- package/templates/_renderers/vue/package.json +27 -0
- package/templates/_renderers/vue/src/app/api/greeting/route.ts +15 -0
- package/templates/_renderers/vue/src/app/layout.vue +21 -0
- package/templates/_renderers/vue/src/app/page.vue +69 -0
- package/templates/_renderers/vue/src/app/vue.css +26 -0
- package/templates/_renderers/vue/src/components/resource-links.vue +33 -0
- package/templates/_renderers/vue/src/env.d.ts +7 -0
- package/templates/_renderers/vue/src/lib/api-client.ts +4 -0
- package/templates/_renderers/vue/src/lib/api.generated.ts +7 -0
- package/templates/_renderers/vue/tsconfig.json +23 -0
- package/templates/auth/.env.example +6 -0
- package/templates/auth/README.md +95 -0
- package/templates/auth/farm.config.ts +14 -0
- package/templates/auth/gitignore +10 -0
- package/templates/auth/package.json +32 -0
- package/templates/auth/pnpm-workspace.yaml +13 -0
- package/templates/auth/src/app/dashboard/middleware.ts +13 -0
- package/templates/auth/src/app/dashboard/page.tsx +140 -0
- package/templates/auth/src/app/error.tsx +25 -0
- package/templates/auth/src/app/globals.css +1064 -0
- package/templates/auth/src/app/layout.tsx +11 -0
- package/templates/auth/src/app/loading.tsx +15 -0
- package/templates/auth/src/app/not-found.tsx +26 -0
- package/templates/auth/src/app/page.tsx +46 -0
- package/templates/auth/src/app/sign-in/page.tsx +16 -0
- package/templates/auth/src/app/sign-up/page.tsx +16 -0
- package/templates/auth/src/components/auth-form.tsx +120 -0
- package/templates/auth/src/components/auth-shell.tsx +21 -0
- package/templates/auth/src/components/resource-links.tsx +78 -0
- package/templates/auth/src/components/sign-out-button.tsx +45 -0
- package/templates/auth/src/components/site-header.tsx +46 -0
- package/templates/auth/tsconfig.json +20 -0
- package/templates/basic/farm.config.ts +3 -0
- package/templates/basic/gitignore +23 -0
- package/templates/basic/package.json +4 -2
- package/templates/basic/pnpm-workspace.yaml +8 -0
- package/templates/basic/public/favicon.svg +13 -0
- package/templates/basic/src/app/globals.css +241 -0
- package/templates/basic/src/app/layout.tsx +6 -5
- package/templates/basic/src/app/page.tsx +20 -61
- package/templates/basic/src/components/resource-links.tsx +78 -0
- package/templates/basic/tsconfig.json +4 -0
- package/templates/better-auth/.env.example +3 -0
- package/templates/better-auth/README.md +89 -0
- package/templates/better-auth/farm.config.ts +32 -0
- package/templates/better-auth/gitignore +10 -0
- package/templates/better-auth/package.json +34 -0
- package/templates/better-auth/pnpm-workspace.yaml +13 -0
- package/templates/better-auth/src/app/dashboard/middleware.ts +15 -0
- package/templates/better-auth/src/app/dashboard/page.tsx +188 -0
- package/templates/better-auth/src/app/error.tsx +25 -0
- package/templates/better-auth/src/app/globals.css +1064 -0
- package/templates/better-auth/src/app/layout.tsx +11 -0
- package/templates/better-auth/src/app/loading.tsx +15 -0
- package/templates/better-auth/src/app/not-found.tsx +26 -0
- package/templates/better-auth/src/app/page.tsx +50 -0
- package/templates/better-auth/src/app/sign-in/page.tsx +16 -0
- package/templates/better-auth/src/app/sign-up/page.tsx +16 -0
- package/templates/better-auth/src/components/auth-form.tsx +120 -0
- package/templates/better-auth/src/components/auth-shell.tsx +21 -0
- package/templates/better-auth/src/components/resource-links.tsx +78 -0
- package/templates/better-auth/src/components/sign-out-button.tsx +45 -0
- package/templates/better-auth/src/components/site-header.tsx +46 -0
- package/templates/better-auth/src/lib/auth-client.ts +5 -0
- package/templates/better-auth/src/lib/auth.ts +50 -0
- package/templates/better-auth/src/lib/session.ts +8 -0
- package/templates/better-auth/tsconfig.json +20 -0
- package/templates/react-compiler/README.md +62 -0
- package/templates/react-compiler/farm.config.ts +23 -0
- package/templates/react-compiler/gitignore +23 -0
- package/templates/react-compiler/package.json +31 -0
- package/templates/react-compiler/pnpm-workspace.yaml +8 -0
- package/templates/react-compiler/public/favicon.svg +5 -0
- package/templates/react-compiler/scripts/verify-experiment.mjs +113 -0
- package/templates/react-compiler/src/app/globals.css +470 -0
- package/templates/react-compiler/src/app/layout.tsx +22 -0
- package/templates/react-compiler/src/app/page.tsx +47 -0
- package/templates/react-compiler/src/components/compiler-comparison.tsx +93 -0
- package/templates/react-compiler/src/components/resource-links.tsx +78 -0
- package/templates/react-compiler/src/farm.d.ts +73 -0
- package/templates/react-compiler/src/lib/api.generated.ts +10 -0
- package/templates/react-compiler/tsconfig.json +24 -0
- package/templates/basic/src/app/about/page.tsx +0 -32
- package/templates/basic/src/farm-images.d.ts +0 -59
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { betterAuth } from "@farm.js/better-auth";
|
|
2
|
+
import { defineConfig } from "@farm.js/core/config";
|
|
3
|
+
import { preact } from "@farm.js/preact";
|
|
4
|
+
import { auth } from "./src/lib/auth.ts";
|
|
5
|
+
|
|
6
|
+
export default defineConfig({
|
|
7
|
+
renderer: preact(),
|
|
8
|
+
theme: {
|
|
9
|
+
default: "dark",
|
|
10
|
+
},
|
|
11
|
+
integrations: {
|
|
12
|
+
auth: betterAuth({ instance: auth }),
|
|
13
|
+
},
|
|
14
|
+
deploy: {
|
|
15
|
+
target: "vercel",
|
|
16
|
+
},
|
|
17
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState } from "preact/hooks";
|
|
4
|
+
import { authClient } from "../../lib/auth-client";
|
|
5
|
+
|
|
6
|
+
type SessionData = NonNullable<Awaited<ReturnType<typeof authClient.getSession>>["data"]>;
|
|
7
|
+
|
|
8
|
+
export default function DashboardPage() {
|
|
9
|
+
const [session, setSession] = useState<SessionData | null>(null);
|
|
10
|
+
const [pending, setPending] = useState(true);
|
|
11
|
+
const [error, setError] = useState<string | null>(null);
|
|
12
|
+
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
let active = true;
|
|
15
|
+
authClient
|
|
16
|
+
.getSession()
|
|
17
|
+
.then((response) => {
|
|
18
|
+
if (!active) return;
|
|
19
|
+
if (response.error || !response.data?.user) {
|
|
20
|
+
window.location.replace("/sign-in");
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
setSession(response.data);
|
|
24
|
+
setPending(false);
|
|
25
|
+
})
|
|
26
|
+
.catch(() => window.location.replace("/sign-in"));
|
|
27
|
+
return () => {
|
|
28
|
+
active = false;
|
|
29
|
+
};
|
|
30
|
+
}, []);
|
|
31
|
+
|
|
32
|
+
async function signOut() {
|
|
33
|
+
setPending(true);
|
|
34
|
+
setError(null);
|
|
35
|
+
const response = await authClient.signOut();
|
|
36
|
+
if (response.error) {
|
|
37
|
+
setError(response.error.message ?? "Could not sign out.");
|
|
38
|
+
setPending(false);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
window.location.assign("/");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (!session) {
|
|
45
|
+
return (
|
|
46
|
+
<main className="loading-shell" aria-live="polite">
|
|
47
|
+
<div className="loading-block">
|
|
48
|
+
<span className="loading-label">Verifying protected session</span>
|
|
49
|
+
<div className="loading-line wide" />
|
|
50
|
+
<div className="loading-line" />
|
|
51
|
+
</div>
|
|
52
|
+
</main>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<main className="dashboard-shell">
|
|
58
|
+
<div className="dashboard-heading">
|
|
59
|
+
<div>
|
|
60
|
+
<span className="section-index">PROTECTED / MIDDLEWARE VERIFIED</span>
|
|
61
|
+
<h1>Welcome back, {session.user.name}.</h1>
|
|
62
|
+
<p>{session.user.email}</p>
|
|
63
|
+
</div>
|
|
64
|
+
<div className="sign-out-control">
|
|
65
|
+
<button className="button button-secondary" disabled={pending} onClick={signOut}>
|
|
66
|
+
{pending ? "Signing out…" : "Sign out"}
|
|
67
|
+
</button>
|
|
68
|
+
{error ? <span className="inline-error">{error}</span> : null}
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
</main>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { LayoutProps, Metadata } from "@farm.js/core";
|
|
2
|
+
import "./globals.css";
|
|
3
|
+
|
|
4
|
+
export const metadata: Metadata = {
|
|
5
|
+
title: "FARMJS Preact Better Auth Starter",
|
|
6
|
+
description: "Better Auth with FARMJS and Preact",
|
|
7
|
+
icons: {
|
|
8
|
+
icon: [{ url: "/favicon.svg", type: "image/svg+xml", sizes: "any" }],
|
|
9
|
+
},
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default function RootLayout({ children }: LayoutProps) {
|
|
13
|
+
return <>{children}</>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ResourceLinks } from "../components/resource-links";
|
|
2
|
+
|
|
3
|
+
export default function HomePage() {
|
|
4
|
+
return (
|
|
5
|
+
<main className="landing-main">
|
|
6
|
+
<section className="hero-section">
|
|
7
|
+
<div className="hero-copy">
|
|
8
|
+
<div className="eyebrow-row">
|
|
9
|
+
<span>00</span>
|
|
10
|
+
<span>FARMJS / Preact + Better Auth</span>
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
<h1>
|
|
14
|
+
Connect Postgres. Start at <code>/sign-up</code>.
|
|
15
|
+
</h1>
|
|
16
|
+
|
|
17
|
+
<div className="command-list" aria-label="Development commands">
|
|
18
|
+
<div className="command-row">
|
|
19
|
+
<span>01</span>
|
|
20
|
+
<code>cp .env.example .env.local</code>
|
|
21
|
+
</div>
|
|
22
|
+
<div className="command-row">
|
|
23
|
+
<span>02</span>
|
|
24
|
+
<code>pnpm dev</code>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<ResourceLinks
|
|
29
|
+
className="resource-links"
|
|
30
|
+
primary={{ href: "/sign-up", label: "Get started" }}
|
|
31
|
+
/>
|
|
32
|
+
</div>
|
|
33
|
+
</section>
|
|
34
|
+
</main>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { JSX } from "preact";
|
|
4
|
+
import { useState } from "preact/hooks";
|
|
5
|
+
import { authClient } from "../lib/auth-client";
|
|
6
|
+
|
|
7
|
+
export function AuthForm({ mode }: { mode: "sign-in" | "sign-up" }) {
|
|
8
|
+
const isSignUp = mode === "sign-up";
|
|
9
|
+
const [pending, setPending] = useState(false);
|
|
10
|
+
const [error, setError] = useState<string | null>(null);
|
|
11
|
+
|
|
12
|
+
async function submit(event: JSX.TargetedSubmitEvent<HTMLFormElement>) {
|
|
13
|
+
event.preventDefault();
|
|
14
|
+
setError(null);
|
|
15
|
+
setPending(true);
|
|
16
|
+
|
|
17
|
+
const formData = new FormData(event.currentTarget);
|
|
18
|
+
const email = String(formData.get("email") ?? "").trim();
|
|
19
|
+
const password = String(formData.get("password") ?? "");
|
|
20
|
+
const name = String(formData.get("name") ?? "").trim();
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
const response = isSignUp
|
|
24
|
+
? await authClient.signUp.email({ email, name, password })
|
|
25
|
+
: await authClient.signIn.email({ email, password });
|
|
26
|
+
if (response.error) {
|
|
27
|
+
setError(response.error.message ?? "We could not complete that request.");
|
|
28
|
+
setPending(false);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
window.location.assign("/dashboard");
|
|
32
|
+
} catch {
|
|
33
|
+
setError("The authentication service could not be reached. Please try again.");
|
|
34
|
+
setPending(false);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<div className="auth-form-panel">
|
|
40
|
+
<div className="auth-form-heading">
|
|
41
|
+
<span className="section-index">{isSignUp ? "01 / SIGN UP" : "01 / SIGN IN"}</span>
|
|
42
|
+
<h1>{isSignUp ? "Create account." : "Welcome back."}</h1>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<form className="auth-form" onSubmit={submit}>
|
|
46
|
+
{isSignUp ? (
|
|
47
|
+
<label className="field">
|
|
48
|
+
<span>
|
|
49
|
+
<small>01</small> Name
|
|
50
|
+
</span>
|
|
51
|
+
<input autoComplete="name" minLength={2} name="name" required type="text" />
|
|
52
|
+
</label>
|
|
53
|
+
) : null}
|
|
54
|
+
<label className="field">
|
|
55
|
+
<span>
|
|
56
|
+
<small>{isSignUp ? "02" : "01"}</small> Email
|
|
57
|
+
</span>
|
|
58
|
+
<input autoComplete="email" name="email" required type="email" />
|
|
59
|
+
</label>
|
|
60
|
+
<label className="field">
|
|
61
|
+
<span>
|
|
62
|
+
<small>{isSignUp ? "03" : "02"}</small> Password
|
|
63
|
+
</span>
|
|
64
|
+
<input
|
|
65
|
+
autoComplete={isSignUp ? "new-password" : "current-password"}
|
|
66
|
+
minLength={8}
|
|
67
|
+
name="password"
|
|
68
|
+
required
|
|
69
|
+
type="password"
|
|
70
|
+
/>
|
|
71
|
+
</label>
|
|
72
|
+
|
|
73
|
+
{error ? (
|
|
74
|
+
<div className="form-error" role="alert">
|
|
75
|
+
<strong>Authentication failed</strong>
|
|
76
|
+
<span>{error}</span>
|
|
77
|
+
</div>
|
|
78
|
+
) : null}
|
|
79
|
+
|
|
80
|
+
<button className="button button-primary auth-submit" disabled={pending} type="submit">
|
|
81
|
+
<span>{pending ? "Please wait…" : isSignUp ? "Create account" : "Sign in"}</span>
|
|
82
|
+
<span aria-hidden="true">{pending ? "···" : "→"}</span>
|
|
83
|
+
</button>
|
|
84
|
+
</form>
|
|
85
|
+
|
|
86
|
+
<p className="auth-switch">
|
|
87
|
+
{isSignUp ? "Have an account?" : "Need an account?"}{" "}
|
|
88
|
+
<a href={isSignUp ? "/sign-in" : "/sign-up"}>{isSignUp ? "Sign in →" : "Sign up →"}</a>
|
|
89
|
+
</p>
|
|
90
|
+
</div>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ComponentChildren } from "preact";
|
|
2
|
+
import { ResourceLinks } from "./resource-links";
|
|
3
|
+
|
|
4
|
+
export function AuthShell({ children }: { children: ComponentChildren }) {
|
|
5
|
+
return (
|
|
6
|
+
<main className="auth-shell">
|
|
7
|
+
<a className="auth-home-link" href="/" aria-label="Back to starter home">
|
|
8
|
+
<span>00</span>
|
|
9
|
+
<span>FARMJS / Preact + Better Auth</span>
|
|
10
|
+
</a>
|
|
11
|
+
|
|
12
|
+
<section className="auth-form-side">{children}</section>
|
|
13
|
+
<ResourceLinks className="auth-resource-links" />
|
|
14
|
+
</main>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { betterAuth } from "@farm.js/better-auth";
|
|
2
|
+
import { defineConfig } from "@farm.js/core/config";
|
|
3
|
+
import { solid } from "@farm.js/solid";
|
|
4
|
+
import { auth } from "./src/lib/auth.ts";
|
|
5
|
+
|
|
6
|
+
export default defineConfig({
|
|
7
|
+
renderer: solid(),
|
|
8
|
+
theme: {
|
|
9
|
+
default: "dark",
|
|
10
|
+
},
|
|
11
|
+
integrations: {
|
|
12
|
+
auth: betterAuth({ instance: auth }),
|
|
13
|
+
},
|
|
14
|
+
deploy: {
|
|
15
|
+
target: "vercel",
|
|
16
|
+
},
|
|
17
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { createSignal, onMount, Show } from "solid-js";
|
|
4
|
+
import { authClient } from "../../lib/auth-client";
|
|
5
|
+
|
|
6
|
+
type SessionData = NonNullable<Awaited<ReturnType<typeof authClient.getSession>>["data"]>;
|
|
7
|
+
|
|
8
|
+
export default function DashboardPage() {
|
|
9
|
+
const [session, setSession] = createSignal<SessionData | null>(null);
|
|
10
|
+
const [pending, setPending] = createSignal(true);
|
|
11
|
+
const [error, setError] = createSignal<string | null>(null);
|
|
12
|
+
|
|
13
|
+
onMount(async () => {
|
|
14
|
+
const response = await authClient.getSession();
|
|
15
|
+
if (response.error || !response.data?.user) {
|
|
16
|
+
window.location.replace("/sign-in");
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
setSession(response.data);
|
|
20
|
+
setPending(false);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
async function signOut() {
|
|
24
|
+
setPending(true);
|
|
25
|
+
setError(null);
|
|
26
|
+
const response = await authClient.signOut();
|
|
27
|
+
if (response.error) {
|
|
28
|
+
setError(response.error.message ?? "Could not sign out.");
|
|
29
|
+
setPending(false);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
window.location.assign("/");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<Show
|
|
37
|
+
when={session()}
|
|
38
|
+
fallback={
|
|
39
|
+
<main class="loading-shell" aria-live="polite">
|
|
40
|
+
<div class="loading-block">
|
|
41
|
+
<span class="loading-label">Verifying protected session</span>
|
|
42
|
+
<div class="loading-line wide" />
|
|
43
|
+
<div class="loading-line" />
|
|
44
|
+
</div>
|
|
45
|
+
</main>
|
|
46
|
+
}
|
|
47
|
+
>
|
|
48
|
+
{(current) => (
|
|
49
|
+
<main class="dashboard-shell">
|
|
50
|
+
<div class="dashboard-heading">
|
|
51
|
+
<div>
|
|
52
|
+
<span class="section-index">PROTECTED / MIDDLEWARE VERIFIED</span>
|
|
53
|
+
<h1>Welcome back, {current().user.name}.</h1>
|
|
54
|
+
<p>{current().user.email}</p>
|
|
55
|
+
</div>
|
|
56
|
+
<div class="sign-out-control">
|
|
57
|
+
<button class="button button-secondary" disabled={pending()} onClick={signOut}>
|
|
58
|
+
{pending() ? "Signing out…" : "Sign out"}
|
|
59
|
+
</button>
|
|
60
|
+
{error() ? <span class="inline-error">{error()}</span> : null}
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
</main>
|
|
64
|
+
)}
|
|
65
|
+
</Show>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { LayoutProps, Metadata } from "@farm.js/core";
|
|
2
|
+
import "./globals.css";
|
|
3
|
+
|
|
4
|
+
export const metadata: Metadata = {
|
|
5
|
+
title: "FARMJS Solid Better Auth Starter",
|
|
6
|
+
description: "Better Auth with FARMJS and Solid",
|
|
7
|
+
icons: {
|
|
8
|
+
icon: [{ url: "/favicon.svg", type: "image/svg+xml", sizes: "any" }],
|
|
9
|
+
},
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default function RootLayout(props: LayoutProps) {
|
|
13
|
+
return <>{props.children}</>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ResourceLinks } from "../components/resource-links";
|
|
2
|
+
|
|
3
|
+
export default function HomePage() {
|
|
4
|
+
return (
|
|
5
|
+
<main class="landing-main">
|
|
6
|
+
<section class="hero-section">
|
|
7
|
+
<div class="hero-copy">
|
|
8
|
+
<div class="eyebrow-row">
|
|
9
|
+
<span>00</span>
|
|
10
|
+
<span>FARMJS / Solid + Better Auth</span>
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
<h1>
|
|
14
|
+
Connect Postgres. Start at <code>/sign-up</code>.
|
|
15
|
+
</h1>
|
|
16
|
+
|
|
17
|
+
<div class="command-list" aria-label="Development commands">
|
|
18
|
+
<div class="command-row">
|
|
19
|
+
<span>01</span>
|
|
20
|
+
<code>cp .env.example .env.local</code>
|
|
21
|
+
</div>
|
|
22
|
+
<div class="command-row">
|
|
23
|
+
<span>02</span>
|
|
24
|
+
<code>pnpm dev</code>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<ResourceLinks
|
|
29
|
+
className="resource-links"
|
|
30
|
+
primary={{ href: "/sign-up", label: "Get started" }}
|
|
31
|
+
/>
|
|
32
|
+
</div>
|
|
33
|
+
</section>
|
|
34
|
+
</main>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { createSignal } from "solid-js";
|
|
4
|
+
import { authClient } from "../lib/auth-client";
|
|
5
|
+
|
|
6
|
+
export function AuthForm(props: { mode: "sign-in" | "sign-up" }) {
|
|
7
|
+
const isSignUp = () => props.mode === "sign-up";
|
|
8
|
+
const [pending, setPending] = createSignal(false);
|
|
9
|
+
const [error, setError] = createSignal<string | null>(null);
|
|
10
|
+
|
|
11
|
+
async function submit(event: SubmitEvent) {
|
|
12
|
+
event.preventDefault();
|
|
13
|
+
setError(null);
|
|
14
|
+
setPending(true);
|
|
15
|
+
|
|
16
|
+
const formData = new FormData(event.currentTarget as HTMLFormElement);
|
|
17
|
+
const email = String(formData.get("email") ?? "").trim();
|
|
18
|
+
const password = String(formData.get("password") ?? "");
|
|
19
|
+
const name = String(formData.get("name") ?? "").trim();
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
const response = isSignUp()
|
|
23
|
+
? await authClient.signUp.email({ email, name, password })
|
|
24
|
+
: await authClient.signIn.email({ email, password });
|
|
25
|
+
if (response.error) {
|
|
26
|
+
setError(response.error.message ?? "We could not complete that request.");
|
|
27
|
+
setPending(false);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
window.location.assign("/dashboard");
|
|
31
|
+
} catch {
|
|
32
|
+
setError("The authentication service could not be reached. Please try again.");
|
|
33
|
+
setPending(false);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<div class="auth-form-panel">
|
|
39
|
+
<div class="auth-form-heading">
|
|
40
|
+
<span class="section-index">{isSignUp() ? "01 / SIGN UP" : "01 / SIGN IN"}</span>
|
|
41
|
+
<h1>{isSignUp() ? "Create account." : "Welcome back."}</h1>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<form class="auth-form" onSubmit={submit}>
|
|
45
|
+
{isSignUp() ? (
|
|
46
|
+
<label class="field">
|
|
47
|
+
<span>
|
|
48
|
+
<small>01</small> Name
|
|
49
|
+
</span>
|
|
50
|
+
<input autocomplete="name" minlength={2} name="name" required type="text" />
|
|
51
|
+
</label>
|
|
52
|
+
) : null}
|
|
53
|
+
<label class="field">
|
|
54
|
+
<span>
|
|
55
|
+
<small>{isSignUp() ? "02" : "01"}</small> Email
|
|
56
|
+
</span>
|
|
57
|
+
<input autocomplete="email" name="email" required type="email" />
|
|
58
|
+
</label>
|
|
59
|
+
<label class="field">
|
|
60
|
+
<span>
|
|
61
|
+
<small>{isSignUp() ? "03" : "02"}</small> Password
|
|
62
|
+
</span>
|
|
63
|
+
<input
|
|
64
|
+
autocomplete={isSignUp() ? "new-password" : "current-password"}
|
|
65
|
+
minlength={8}
|
|
66
|
+
name="password"
|
|
67
|
+
required
|
|
68
|
+
type="password"
|
|
69
|
+
/>
|
|
70
|
+
</label>
|
|
71
|
+
|
|
72
|
+
{error() ? (
|
|
73
|
+
<div class="form-error" role="alert">
|
|
74
|
+
<strong>Authentication failed</strong>
|
|
75
|
+
<span>{error()}</span>
|
|
76
|
+
</div>
|
|
77
|
+
) : null}
|
|
78
|
+
|
|
79
|
+
<button class="button button-primary auth-submit" disabled={pending()} type="submit">
|
|
80
|
+
<span>{pending() ? "Please wait…" : isSignUp() ? "Create account" : "Sign in"}</span>
|
|
81
|
+
<span aria-hidden="true">{pending() ? "···" : "→"}</span>
|
|
82
|
+
</button>
|
|
83
|
+
</form>
|
|
84
|
+
|
|
85
|
+
<p class="auth-switch">
|
|
86
|
+
{isSignUp() ? "Have an account?" : "Need an account?"}{" "}
|
|
87
|
+
<a href={isSignUp() ? "/sign-in" : "/sign-up"}>{isSignUp() ? "Sign in →" : "Sign up →"}</a>
|
|
88
|
+
</p>
|
|
89
|
+
</div>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { JSX } from "solid-js";
|
|
2
|
+
import { ResourceLinks } from "./resource-links";
|
|
3
|
+
|
|
4
|
+
export function AuthShell(props: { children: JSX.Element }) {
|
|
5
|
+
return (
|
|
6
|
+
<main class="auth-shell">
|
|
7
|
+
<a class="auth-home-link" href="/" aria-label="Back to starter home">
|
|
8
|
+
<span>00</span>
|
|
9
|
+
<span>FARMJS / Solid + Better Auth</span>
|
|
10
|
+
</a>
|
|
11
|
+
|
|
12
|
+
<section class="auth-form-side">{props.children}</section>
|
|
13
|
+
<ResourceLinks className="auth-resource-links" />
|
|
14
|
+
</main>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { betterAuth } from "@farm.js/better-auth";
|
|
2
|
+
import { defineConfig } from "@farm.js/core/config";
|
|
3
|
+
import { svelte } from "@farm.js/svelte";
|
|
4
|
+
import { auth } from "./src/lib/auth.ts";
|
|
5
|
+
|
|
6
|
+
export default defineConfig({
|
|
7
|
+
renderer: svelte(),
|
|
8
|
+
theme: {
|
|
9
|
+
default: "dark",
|
|
10
|
+
},
|
|
11
|
+
integrations: {
|
|
12
|
+
auth: betterAuth({ instance: auth }),
|
|
13
|
+
},
|
|
14
|
+
deploy: {
|
|
15
|
+
target: "vercel",
|
|
16
|
+
},
|
|
17
|
+
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
export const hydrate = true;
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<script lang="ts">
|
|
6
|
+
import { onMount } from "svelte";
|
|
7
|
+
import { authClient } from "../../lib/auth-client";
|
|
8
|
+
|
|
9
|
+
type SessionData = NonNullable<Awaited<ReturnType<typeof authClient.getSession>>["data"]>;
|
|
10
|
+
|
|
11
|
+
let session = $state<SessionData | null>(null);
|
|
12
|
+
let pending = $state(true);
|
|
13
|
+
let error = $state<string | null>(null);
|
|
14
|
+
|
|
15
|
+
onMount(async () => {
|
|
16
|
+
const response = await authClient.getSession();
|
|
17
|
+
if (response.error || !response.data?.user) {
|
|
18
|
+
window.location.replace("/sign-in");
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
session = response.data;
|
|
22
|
+
pending = false;
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
async function signOut() {
|
|
26
|
+
pending = true;
|
|
27
|
+
error = null;
|
|
28
|
+
const response = await authClient.signOut();
|
|
29
|
+
if (response.error) {
|
|
30
|
+
error = response.error.message ?? "Could not sign out.";
|
|
31
|
+
pending = false;
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
window.location.assign("/");
|
|
35
|
+
}
|
|
36
|
+
</script>
|
|
37
|
+
|
|
38
|
+
{#if !session}
|
|
39
|
+
<main class="loading-shell" aria-live="polite">
|
|
40
|
+
<div class="loading-block">
|
|
41
|
+
<span class="loading-label">Verifying protected session</span>
|
|
42
|
+
<div class="loading-line wide"></div>
|
|
43
|
+
<div class="loading-line"></div>
|
|
44
|
+
</div>
|
|
45
|
+
</main>
|
|
46
|
+
{:else}
|
|
47
|
+
<main class="dashboard-shell">
|
|
48
|
+
<div class="dashboard-heading">
|
|
49
|
+
<div>
|
|
50
|
+
<span class="section-index">PROTECTED / MIDDLEWARE VERIFIED</span>
|
|
51
|
+
<h1>Welcome back, {session.user.name}.</h1>
|
|
52
|
+
<p>{session.user.email}</p>
|
|
53
|
+
</div>
|
|
54
|
+
<div class="sign-out-control">
|
|
55
|
+
<button class="button button-secondary" disabled={pending} onclick={signOut}>
|
|
56
|
+
{pending ? "Signing out…" : "Sign out"}
|
|
57
|
+
</button>
|
|
58
|
+
{#if error}<span class="inline-error">{error}</span>{/if}
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
</main>
|
|
62
|
+
{/if}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import type { Metadata } from "@farm.js/core";
|
|
3
|
+
import "./globals.css";
|
|
4
|
+
|
|
5
|
+
export const metadata: Metadata = {
|
|
6
|
+
title: "FARMJS Svelte Better Auth Starter",
|
|
7
|
+
description: "Better Auth with FARMJS and Svelte",
|
|
8
|
+
icons: {
|
|
9
|
+
icon: [{ url: "/favicon.svg", type: "image/svg+xml", sizes: "any" }],
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<script lang="ts">
|
|
15
|
+
import type { Snippet } from "svelte";
|
|
16
|
+
|
|
17
|
+
let { children }: { children?: Snippet } = $props();
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
{@render children?.()}
|