From de3f40508f7385918014398cc1990a14bb97a5aa Mon Sep 17 00:00:00 2001 From: Jason Lengstorf Date: Sat, 9 Aug 2025 10:47:24 -0700 Subject: [PATCH] wip: add password-protected pages making and sharing slide decks sucks, so this is an attempt to make and share "landing page" style presentations instead very WIP, bordering on thought experiment lol --- apps/website/src/actions/index.ts | 29 ++ .../src/components/header-simple.astro | 277 ++++++++++++++++++ .../src/components/password-form.astro | 49 ++++ apps/website/src/pages/decks/secret.astro | 40 +++ 4 files changed, 395 insertions(+) create mode 100644 apps/website/src/components/header-simple.astro create mode 100644 apps/website/src/components/password-form.astro create mode 100644 apps/website/src/pages/decks/secret.astro diff --git a/apps/website/src/actions/index.ts b/apps/website/src/actions/index.ts index 7d2ac4c..22ced4b 100644 --- a/apps/website/src/actions/index.ts +++ b/apps/website/src/actions/index.ts @@ -182,4 +182,33 @@ export const server = { }, }), }, + decks: { + unlock: defineAction({ + accept: 'form', + input: z.object({ + password: z.string(), + deckId: z.string(), + }), + async handler(input, context) { + // TODO move the passwords into a DB or CMS + const passwords: Record = { + secret: 'sneaky', + }; + + if (input.password === passwords[input.deckId]) { + // TODO should probably figure out something slightly less guessable + // (to be clear, though, nothing in here will be SECRET secret) + context.cookies.set(`codetv:deck:${input.deckId}`, '1', { + httpOnly: true, + secure: true, + sameSite: 'strict', + }); + + return { hasAccess: true }; + } else { + return { hasAccess: false }; + } + }, + }), + }, }; diff --git a/apps/website/src/components/header-simple.astro b/apps/website/src/components/header-simple.astro new file mode 100644 index 0000000..09160ce --- /dev/null +++ b/apps/website/src/components/header-simple.astro @@ -0,0 +1,277 @@ +--- +import CodeTVLogo from '../assets/codetv-logo.svg'; +import UserButton from './user-button.astro'; + +const nav = [{ path: '/', label: '← back to CodeTV' }]; +--- + +
+ + + + + + + +
+ + + + diff --git a/apps/website/src/components/password-form.astro b/apps/website/src/components/password-form.astro new file mode 100644 index 0000000..f6e6dab --- /dev/null +++ b/apps/website/src/components/password-form.astro @@ -0,0 +1,49 @@ +--- +import { actions } from 'astro:actions'; +import '../styles/forms.css'; + +export interface Props { + deckId: string; +} + +const result = Astro.getActionResult(actions.decks.unlock); + +const { deckId } = Astro.props; +--- + +
+
+
+
+

This page is a secret

+ +

Enter the password to access it.

+
+ +
+ +

+ You can find the password in the email that contained the link to this + page. +

+ +
+ + { + result && result.data && !result.data.hasAccess ? ( +

The password you entered is invalid.

+ ) : null + } + + + + +
+
+
diff --git a/apps/website/src/pages/decks/secret.astro b/apps/website/src/pages/decks/secret.astro new file mode 100644 index 0000000..e06feae --- /dev/null +++ b/apps/website/src/pages/decks/secret.astro @@ -0,0 +1,40 @@ +--- +import Layout from '../../layouts/default.astro'; +import PasswordForm from '../../components/password-form.astro'; +import '../../styles/forms.css'; + +// TODO put the password and content into either a CMS or DB +const hasAccess = Astro.cookies.get('codetv:deck:secret')?.value === '1'; +--- + + +
+ {hasAccess ? ( +
+

Your Secret Deck

+
+ ) : ( + + )} +
+
+ +