Vals using lodash-es
SSR React Mini & SQLite Todo App
This Todo App is server rendered and client-hydrated React. This architecture is a lightweight alternative to NextJS, RemixJS, or other React metaframeworks with no compile or build step. The data is saved server-side in Val Town SQLite.
SSR React Mini Framework
This "framework" is currently 44 lines of code, so it's obviously not a true replacement for NextJS or Remix.
The trick is client-side importing the React component that you're server rendering. Val Town is uniquely suited for this trick because it both runs your code server-side and exposes vals as modules importable by the browser.
The tricky part is making sure that server-only code doesn't run on the client and vice-versa. For example, because this val colocates the server-side loader
and action
with the React component we have to be careful to do all server-only imports (ie sqlite) dynamically inside the loader
and action
, so they only run server-side.
SSR React Mini & SQLite Todo App
This Todo App is server rendered and client-hydrated React. This architecture is a lightweight alternative to NextJS, RemixJS, or other React metaframeworks with no compile or build step. The data is saved server-side in Val Town SQLite.
SSR React Mini Framework
This "framework" is currently 44 lines of code, so it's obviously not a true replacement for NextJS or Remix.
The trick is client-side importing the React component that you're server rendering. Val Town is uniquely suited for this trick because it both runs your code server-side and exposes vals as modules importable by the browser.
The tricky part is making sure that server-only code doesn't run on the client and vice-versa. For example, because this val colocates the server-side loader
and action
with the React component we have to be careful to do all server-only imports (ie sqlite) dynamically inside the loader
and action
, so they only run server-side.
Password Auth Middleware
Protect your vals behind a password. Use session cookies to persist authentication.
Demo
See @pomdtr/password_auth_test
Usage
If you want to use an api token to authenticate:
Create valimport { passwordAuth } from "https://esm.town/v/pomdtr/password_auth";
export default passwordAuth(() => {
return new Response("OK");
});
Or if you prefer to use a string:
Create valimport { passwordAuth } from "https://esm.town/v/pomdtr/password_auth";
export default passwordAuth(() => {
return new Response("OK");
}, { password: Deno.env.get("VAL_PASSWORD") });
You can also set multiple ones
Create valimport { passwordAuth } from "https://esm.town/v/pomdtr/password_auth";
export default passwordAuth(() => {
return new Response("OK");
}, { password: [Deno.env.get("VAL_PASSWORD"), Deno.env.get("STEVE_PASSWORD")] });
Note that authenticating using your api token remain an option even after setting a password.
TODO
- allow to authenticate using a val town token
- add a way to send an email to ask a password from the val owner
- automatically extend the session
- automatically remove expired sessions
FAQ
How to sign out ?
Navigate to <your-site>/signout
.
Email Auth for Val Town
โ ๏ธ Require a pro account (needed to send email to users)
Usage
Create an http server, and wrap it in the emailAuth middleware.
Create valimport { emailAuth } from "https://www.val.town/v/pomdtr/email_auth"
export default emailAuth((req, ctx) => {
return new Response(`your mail is ${ctx.email}`);
});
When an user access the val, he will need to input his mail, then confirm it through a confirmation code.
You can limit how can access your vals through an allowList:
Create valimport { emailAuth } from "https://www.val.town/v/pomdtr/email_auth"
export default emailAuth((req, ctx) => {
return new Response(`your mail is ${ctx.email}`);
}, {
allowList: ["steve@val.town"]
});
If someone tries to access your val but is not in the allowlist, he will be blocked.
If you want to allow user to request for access, you can mix allowList
with allowSignup
:
Create valimport { emailAuth } from "https://www.val.town/v/pomdtr/email_auth"
export default emailAuth((req, ctx) => {
return new Response(`your mail is ${ctx.email}`);
}, {
allowList: ["steve@val.town"],
allowSignup: true
});
Each time a new user not present in the allowList try to login to a val, you will receive an email containing:
- the email of the user trying to log in
- the name of the val the he want to access
You can then just add the user to your whitelist to allow him in (and the user will not need to confirm his email again) !
Tips
If you don't want to put your email in clear text, you can just use an env variable:
Create valimport { emailAuth } from "https://www.val.town/v/pomdtr/email_auth"
export default emailAuth((req, ctx) => {
return new Response(`your mail is ${ctx.email}`);
}, {
allowList: [Deno.env.get("email")]
});
Or just setup a forward val (see @pomdtr/inbox):
Create valimport { emailAuth } from "https://www.val.town/v/pomdtr/email_auth"
export default emailAuth((req, ctx) => {
return new Response(`your mail is ${ctx.email}`);
}, {
allowList: ["pomdtr.inbox@valtown.email"]
});
TODO
- Add expiration for verification codes and session tokens
- use links instead of code for verification
- improve errors pages
Password Auth Middleware
Protect your vals behind a password. Use session cookies to persist authentication.
Demo
See @pomdtr/password_auth_test
Usage
If you want to use an api token to authenticate:
Create valimport { passwordAuth } from "https://esm.town/v/pomdtr/password_auth";
export default passwordAuth(() => {
return new Response("OK");
});
Or if you prefer to use a string:
Create valimport { passwordAuth } from "https://esm.town/v/pomdtr/password_auth";
export default passwordAuth(() => {
return new Response("OK");
}, { password: Deno.env.get("MY_PASSWORD") });
Or if you want to share your val with someone without sharing your main password, you can set multiple ones
Create valimport { passwordAuth } from "https://esm.town/v/pomdtr/password_auth";
export default passwordAuth(() => {
return new Response("OK");
}, { password: [Deno.env.get("MY_PASSWORD"), Deno.env.get("STEVE_PASSWORD")] });
Note that authenticating using your api token is always an option.
TODO
- allow to authenticate using a val town token
- add a way to send an email to ask a password from the val owner
- automatically extend the session
- automatically remove expired sessions
FAQ
How to sign out ?
Navigate to <your-site>/signout
.
Password Auth Middleware
Protect your vals behind a password. Use session cookies to persist authentication.
Demo
See @pomdtr/password_auth_test
Usage
If you want to use an api token to authenticate:
Create valimport { passwordAuth } from "https://esm.town/v/pomdtr/password_auth";
export default passwordAuth(() => {
return new Response("OK");
});
Or if you prefer to use a string:
Create valimport { passwordAuth } from "https://esm.town/v/pomdtr/password_auth";
export default passwordAuth(() => {
return new Response("OK");
}, { password: Deno.env.get("VAL_PASSWORD") });
You can also set multiple ones
Create valimport { passwordAuth } from "https://esm.town/v/pomdtr/password_auth";
export default passwordAuth(() => {
return new Response("OK");
}, { password: [Deno.env.get("VAL_PASSWORD"), Deno.env.get("STEVE_PASSWORD")] });
Note that authenticating using your api token remain an option even after setting a password.
TODO
- allow to authenticate using a val town token
- add a way to send an email to ask a password from the val owner
- automatically extend the session
- automatically remove expired sessions
FAQ
How to sign out ?
Navigate to <your-site>/signout
.
Lucia Adapter for val.town
Usage
Create valimport { ValTownAdapter } from "https://esm.town/v/pomdtr/lucia_adapter";
import { Lucia } from "npm:lucia@3.0.1";
const adapter = new ValTownAdapter({
user: "user",
session: "session",
});
const lucia = new Lucia(adapter)
SQL Template Tag
Port of blakeembrey/sql-template-tag for usage in val.town.
Usage
import { sqlite } from "https://esm.town/v/std/sqlite"
import { sql, zip } from "https://esm.town/v/pomdtr/sql"
const query = sql`SELECT * FROM books WHERE author = ${author}`;
console.log(query.sql) // => "SELECT * FROM books WHERE author = ?"
console.log(query.args) // => [author]
const res = await sqlite.execute(query)
console.table(zip(res))
For advanced usage (ex: nesting queries), refer to the project readme.