many changes idek

main
Ethan Niser 2023-09-17 20:16:01 -05:00
parent f96e9cdd0b
commit 039a4f500c
15 changed files with 273 additions and 270 deletions

Binary file not shown.

Binary file not shown.

@ -11,7 +11,8 @@
"db:seed": "bun run src/model/store/seed.ts",
"uno": "bunx unocss",
"uno:dev": "bunx unocss --watch",
"typecheck": "bunx --bun tsc"
"typecheck": "bunx --bun tsc",
"cli": "bunx beth-stack foo bar"
},
"devDependencies": {
"@kitajs/ts-html-plugin": "^1.0.1",
@ -31,11 +32,10 @@
"@t3-oss/env-core": "^0.6.1",
"drizzle-orm": "^0.28.6",
"drizzle-typebox": "^0.1.1",
"elysia": "link:elysia",
"elysia-autoroutes": "^0.2.2",
"elysia": "./elysia-0.7.0-exp.0.tgz",
"lucia": "^2.6.0",
"pino-pretty": "^10.2.0",
"zod": "^3.22.2",
"beth-jsx": "link:beth-jsx"
"beth-stack": "0.0.3"
}
}

@ -13,19 +13,19 @@ function dispatch() {
const app = new Elysia()
.ws("/ws", {
open(ws) {
console.log("open");
// console.log("open");
wsConnections.add(ws);
},
close(ws) {
console.log("close");
// console.log("close");
wsConnections.delete(ws);
},
message(ws, message) {
console.log("message", message);
// console.log("message", message);
},
})
.get("/restart", () => {
console.log("recieved restart");
// console.log("recieved restart");
dispatch();
})
.listen(3001);

@ -1,4 +1,4 @@
import { type PropsWithChildren } from "beth-jsx";
import { type PropsWithChildren } from "beth-stack/jsx";
import { config } from "../config";
export const BaseHtml = ({ children }: PropsWithChildren) => (

@ -43,7 +43,7 @@ export function TodoForm() {
_="on submit target.reset()"
>
<select name="content" class="border border-black">
<option value="" disabled="true" selected="true">
<option value="" disabled={true} selected="true">
Select a Todo
</option>
<option value="beth">Learn the BETH stack</option>

@ -3,8 +3,8 @@ import { Elysia } from "elysia";
// import pretty from "pino-pretty";
import { config } from "../config";
import { client, db } from "../db";
import "beth-jsx/register";
import "beth-jsx/htmx";
import "beth-stack/jsx/register";
import { bethStack } from "beth-stack/elysia";
import { auth } from "../auth";
// import { cron } from "@elysiajs/cron";
@ -14,11 +14,13 @@ import { auth } from "../auth";
export const ctx = new Elysia({
name: "@app/ctx",
cookie: {
secrets: config.env.COOKIE_SECRET,
sign: "session",
},
})
.use(
bethStack({
log: true,
returnStaleWhileRevalidate: false,
})
)
// .use(
// logger({
// level: config.env.LOG_LEVEL,
@ -42,14 +44,7 @@ export const ctx = new Elysia({
// )
.decorate("db", db)
.decorate("config", config)
.decorate("auth", auth)
.decorate(
"html",
(html: string) =>
new Response(html, {
headers: { "Content-Type": "text/html; charset=utf8" },
})
);
.decorate("auth", auth);
// .onStart(({ log }) => log.info("Server starting"))
// .onStop(({ log }) => log.info("Server stopping"))
// .onRequest(({ log, request }) => {

@ -1,9 +1,8 @@
import Elysia from "elysia";
import { todosController } from "./todos";
import { authController } from "./auth";
// import { authController } from "./auth";
export const api = new Elysia({
prefix: "/api",
})
.use(todosController)
.use(authController);
}).use(todosController);
// .use(authController);

@ -1,129 +1,128 @@
import { Elysia, t } from "elysia";
import { ctx } from "../context";
import { set } from "zod";
import { LuciaError } from "lucia";
// import { Elysia, t } from "elysia";
// import { ctx } from "../context";
// import { set } from "zod";
// import { LuciaError } from "lucia";
class DuplicateEmailError extends Error {
constructor() {
super("Duplicate email");
}
}
// class DuplicateEmailError extends Error {
// constructor() {
// super("Duplicate email");
// }
// }
export const authController = new Elysia({
prefix: "/auth",
})
.use(ctx)
.post(
"/signup",
async ({ body: { email, password }, auth, set, cookie }) => {
const user = await auth
.createUser({
key: {
providerId: "email", // auth method
providerUserId: email.toLowerCase(), // unique id when using "email" auth method
password, // hashed by Lucia
},
attributes: {
email,
},
})
.catch((err) => {
if (err.code === "SQLITE_CONSTRAINT") {
throw new DuplicateEmailError();
} else {
throw err;
}
});
const session = await auth.createSession({
userId: user.userId,
attributes: {},
});
// export const authController = new Elysia({
// prefix: "/auth",
// })
// .use(ctx)
// .post(
// "/signup",
// async ({ body: { email, password }, auth, set }) => {
// const user = await auth
// .createUser({
// key: {
// providerId: "email", // auth method
// providerUserId: email.toLowerCase(), // unique id when using "email" auth method
// password, // hashed by Lucia
// },
// attributes: {
// email,
// },
// })
// .catch((err) => {
// if (err.code === "SQLITE_CONSTRAINT") {
// throw new DuplicateEmailError();
// } else {
// throw err;
// }
// });
// const session = await auth.createSession({
// userId: user.userId,
// attributes: {},
// });
const sessionCookie = auth.createSessionCookie(session);
// const sessionCookie = auth.createSessionCookie(session);
// cookie.session?.set(sessionCookie);
// // cookie.session?.set(sessionCookie);
set.headers["Set-Cookie"] = sessionCookie.serialize();
set.headers["HX-Location"] = "/profile";
},
{
body: t.Object({
email: t.String({
minLength: 5,
maxLength: 30,
}),
password: t.String({
minLength: 6,
maxLength: 255,
}),
}),
error({ code, error, set }) {
if (code === "VALIDATION") {
console.log("sign up validation error");
console.log(error);
set.status = 400;
return "Invalid email or password";
} else if (error instanceof DuplicateEmailError) {
console.log("sign up duplicate email error");
console.log(error);
set.status = 400;
return "Email already exists";
} else {
console.log("sign up error");
console.log(error);
set.status = 500;
return "Internal server error";
}
},
}
)
.post(
"/signin",
async ({ body: { email, password }, auth, set, cookie }) => {
const user = await auth.useKey("email", email.toLowerCase(), password);
// set.headers["Set-Cookie"] = sessionCookie.serialize();
// set.headers["HX-Location"] = "/profile";
// },
// {
// body: t.Object({
// email: t.String({
// minLength: 5,
// maxLength: 30,
// }),
// password: t.String({
// minLength: 6,
// maxLength: 255,
// }),
// }),
// error({ code, error, set }) {
// if (code === "VALIDATION") {
// console.log("sign up validation error");
// console.log(error);
// set.status = 400;
// return "Invalid email or password";
// } else if (error instanceof DuplicateEmailError) {
// console.log("sign up duplicate email error");
// console.log(error);
// set.status = 400;
// return "Email already exists";
// } else {
// console.log("sign up error");
// console.log(error);
// set.status = 500;
// return "Internal server error";
// }
// },
// }
// )
// .post(
// "/signin",
// async ({ body: { email, password }, auth, set }) => {
// const user = await auth.useKey("email", email.toLowerCase(), password);
const session = await auth.createSession({
userId: user.userId,
attributes: {},
});
const sessionCookie = auth.createSessionCookie(session);
// const session = await auth.createSession({
// userId: user.userId,
// attributes: {},
// });
// const sessionCookie = auth.createSessionCookie(session);
// cookie.sesion?.set(sessionCookie);
set.headers["Set-Cookie"] = sessionCookie.serialize();
set.headers["HX-Location"] = "/profile";
},
{
body: t.Object({
email: t.String({
minLength: 5,
maxLength: 30,
}),
password: t.String({
minLength: 6,
maxLength: 255,
}),
}),
error({ code, error, set }) {
if (code === "VALIDATION") {
console.log("sign up validation error");
console.log(error);
set.status = 400;
return "Invalid email or password";
} else if (
error instanceof LuciaError &&
(error.message === "AUTH_INVALID_KEY_ID" ||
error.message === "AUTH_INVALID_PASSWORD")
) {
console.log("sign in invalid email or password error");
console.log(error);
set.status = 400;
return "Invalid email or password";
} else {
console.log("sign up error");
console.log(error);
set.status = 500;
return "Internal server error";
}
},
}
);
// set.headers["Set-Cookie"] = sessionCookie.serialize();
// set.headers["HX-Location"] = "/profile";
// },
// {
// body: t.Object({
// email: t.String({
// minLength: 5,
// maxLength: 30,
// }),
// password: t.String({
// minLength: 6,
// maxLength: 255,
// }),
// }),
// error({ code, error, set }) {
// if (code === "VALIDATION") {
// console.log("sign up validation error");
// console.log(error);
// set.status = 400;
// return "Invalid email or password";
// } else if (
// error instanceof LuciaError &&
// (error.message === "AUTH_INVALID_KEY_ID" ||
// error.message === "AUTH_INVALID_PASSWORD")
// ) {
// console.log("sign in invalid email or password error");
// console.log(error);
// set.status = 400;
// return "Invalid email or password";
// } else {
// console.log("sign up error");
// console.log(error);
// set.status = 500;
// return "Internal server error";
// }
// },
// }
// );

@ -1,6 +1,8 @@
import Elysia from "elysia";
import { signup } from "./signup";
// import { signup } from "./signup";
import { profile } from "./profile";
import { signin } from "./signin";
// import { signin } from "./signin";
export const authGroup = new Elysia().use(signup).use(signin).use(profile);
export const authGroup = new Elysia()
// .use(signup).use(signin)
.use(profile);

@ -8,7 +8,7 @@ export const profile = new Elysia()
const authRequest = auth.handleRequest(request);
const session = await authRequest.validate();
return html(
return html(() =>
session ? <div>Hello {session.user.email}</div> : <div>Not logged in</div>
);
});

@ -1,54 +1,54 @@
import Elysia from "elysia";
import { BaseHtml } from "../../components/base";
import { ctx } from "../../context";
// import Elysia from "elysia";
// import { BaseHtml } from "../../components/base";
// import { ctx } from "../../context";
export const signin = new Elysia().use(ctx).get("/signin", ({ html }) =>
html(
<BaseHtml>
<div class="flex w-full h-screen bg-gray-200 justify-center items-center">
<form
hx-post="/api/auth/signin"
hx-swap="afterend"
class="bg-white p-8 rounded-lg shadow-md w-96"
>
<div class="mb-4">
<label
for="email"
class="block text-sm font-medium text-gray-600 mb-2"
>
Email
</label>
<input
type="text"
name="email"
id="email"
placeholder="Enter your email"
class="w-full p-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-400 focus:border-transparent"
/>
</div>
<div class="mb-4">
<label
for="password"
class="block text-sm font-medium text-gray-600 mb-2"
>
Password
</label>
<input
type="password"
name="password"
id="password"
placeholder="Enter your password"
class="w-full p-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-400 focus:border-transparent"
/>
</div>
<button
type="submit"
class="w-full bg-indigo-600 text-white p-2 rounded-md hover:bg-indigo-500 focus:outline-none focus:ring-2 focus:ring-indigo-400 focus:ring-opacity-50"
>
Sign In
</button>
</form>
</div>
</BaseHtml>
)
);
// export const signin = new Elysia().use(ctx).get("/signin", ({ html }) =>
// html(
// <BaseHtml>
// <div class="flex w-full h-screen bg-gray-200 justify-center items-center">
// <form
// hx-post="/api/auth/signin"
// hx-swap="afterend"
// class="bg-white p-8 rounded-lg shadow-md w-96"
// >
// <div class="mb-4">
// <label
// for="email"
// class="block text-sm font-medium text-gray-600 mb-2"
// >
// Email
// </label>
// <input
// type="text"
// name="email"
// id="email"
// placeholder="Enter your email"
// class="w-full p-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-400 focus:border-transparent"
// />
// </div>
// <div class="mb-4">
// <label
// for="password"
// class="block text-sm font-medium text-gray-600 mb-2"
// >
// Password
// </label>
// <input
// type="password"
// name="password"
// id="password"
// placeholder="Enter your password"
// class="w-full p-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-400 focus:border-transparent"
// />
// </div>
// <button
// type="submit"
// class="w-full bg-indigo-600 text-white p-2 rounded-md hover:bg-indigo-500 focus:outline-none focus:ring-2 focus:ring-indigo-400 focus:ring-opacity-50"
// >
// Sign In
// </button>
// </form>
// </div>
// </BaseHtml>
// )
// );

@ -1,54 +1,54 @@
import Elysia from "elysia";
import { BaseHtml } from "../../components/base";
import { ctx } from "../../context";
// import Elysia from "elysia";
// import { BaseHtml } from "../../components/base";
// import { ctx } from "../../context";
export const signup = new Elysia().use(ctx).get("/signup", ({ html }) =>
html(
<BaseHtml>
<div class="flex w-full h-screen bg-gray-200 justify-center items-center">
<form
hx-post="/api/auth/signup"
hx-swap="afterend"
class="bg-white p-8 rounded-lg shadow-md w-96"
>
<div class="mb-4">
<label
for="email"
class="block text-sm font-medium text-gray-600 mb-2"
>
Email
</label>
<input
type="text"
name="email"
id="email"
placeholder="Enter your email"
class="w-full p-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-400 focus:border-transparent"
/>
</div>
<div class="mb-4">
<label
for="password"
class="block text-sm font-medium text-gray-600 mb-2"
>
Password
</label>
<input
type="password"
name="password"
id="password"
placeholder="Enter your password"
class="w-full p-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-400 focus:border-transparent"
/>
</div>
<button
type="submit"
class="w-full bg-indigo-600 text-white p-2 rounded-md hover:bg-indigo-500 focus:outline-none focus:ring-2 focus:ring-indigo-400 focus:ring-opacity-50"
>
Sign Up
</button>
</form>
</div>
</BaseHtml>
)
);
// export const signup = new Elysia().use(ctx).get("/signup", ({ html }) =>
// html(
// <BaseHtml>
// <div class="flex w-full h-screen bg-gray-200 justify-center items-center">
// <form
// hx-post="/api/auth/signup"
// hx-swap="afterend"
// class="bg-white p-8 rounded-lg shadow-md w-96"
// >
// <div class="mb-4">
// <label
// for="email"
// class="block text-sm font-medium text-gray-600 mb-2"
// >
// Email
// </label>
// <input
// type="text"
// name="email"
// id="email"
// placeholder="Enter your email"
// class="w-full p-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-400 focus:border-transparent"
// />
// </div>
// <div class="mb-4">
// <label
// for="password"
// class="block text-sm font-medium text-gray-600 mb-2"
// >
// Password
// </label>
// <input
// type="password"
// name="password"
// id="password"
// placeholder="Enter your password"
// class="w-full p-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-400 focus:border-transparent"
// />
// </div>
// <button
// type="submit"
// class="w-full bg-indigo-600 text-white p-2 rounded-md hover:bg-indigo-500 focus:outline-none focus:ring-2 focus:ring-indigo-400 focus:ring-opacity-50"
// >
// Sign Up
// </button>
// </form>
// </div>
// </BaseHtml>
// )
// );

@ -1,8 +1,8 @@
import Elysia from "elysia";
import { Elysia } from "elysia";
import { BaseHtml } from "../components/base";
import { ctx } from "../context";
import { Suspense, renderToStream, renderToString } from "beth-jsx";
import { persistedCache, revalidateTag } from "beth-jsx";
import { Suspense, renderToStream, renderToString } from "beth-stack/jsx";
import { persistedCache, revalidateTag } from "beth-stack/cache";
const start = Date.now();
@ -24,18 +24,19 @@ export const index = new Elysia()
});
}
})
.get("/test", async () => {
.get("/test", async ({ html }) => {
const time = await cachedGetTime();
return renderToString(() => <p>{time}</p>);
return html(() => <p>{time}</p>);
})
.get("/", async ({ set }) => {
return renderToString(() => (
.get("/", async ({ html }) => {
return html(() => (
<BaseHtml>
<h1>cache revalidates on two second interval</h1>
<h1>cache revalidates every two seconds</h1>
<button hx-get="/test" hx-target="#foo" hx-swap="beforeend">
click me to get time since start (cached)
</button>
<br />
<div>hot reload</div>
<br />
<button
hx-get="/test"

@ -9,7 +9,6 @@
"noEmit": true,
"composite": true,
"strict": true,
// "noUncheckedIndexedAccess": true,
"downlevelIteration": true,
"skipLibCheck": true,
"jsx": "react",
@ -21,6 +20,14 @@
"types": [
"bun-types" // add Bun global
],
"plugins": [{ "name": "@kitajs/ts-html-plugin" }]
// non bun init
"plugins": [{ "name": "@kitajs/ts-html-plugin" }],
"noUncheckedIndexedAccess": true,
// "noUnusedLocals": true,
// "noUnusedParameters": true,
"exactOptionalPropertyTypes": true,
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true
// "noImplicitReturns": true
}
}