Improve FAQ & video rendering, tighten homepage and CSS

This commit is contained in:
Andras Schmelczer 2026-05-04 22:07:30 +01:00
parent 05a1f316e1
commit c69bb0d614
48 changed files with 4689 additions and 1077 deletions

97
video/src/auth.ts Normal file
View file

@ -0,0 +1,97 @@
import { chromium } from 'playwright';
import { writeFileSync } from 'node:fs';
import { APP_URL, AUTH_STATE_PATH } from './config.js';
/**
* Auth setup. Two modes:
*
* 1. Programmatic (preferred for CI / non-interactive runs): set
* PB_URL, PB_EMAIL, PB_PASSWORD env vars. We hit the PocketBase REST
* auth-with-password endpoint, then hand-write a Playwright storageState
* file with the resulting token in localStorage["pb_auth"]. The PocketBase
* JS SDK reads that key on boot and treats us as logged in bit-equivalent
* to a real UI login.
*
* 2. Interactive: no env vars, we open a headed browser, you log in by hand,
* press Enter, and we serialize the resulting cookies + localStorage.
* Works on a developer laptop; doesn't work in headless environments.
*/
interface PbAuthResponse {
token: string;
record: Record<string, unknown>;
}
async function programmatic() {
const email = process.env.PB_EMAIL!;
const password = process.env.PB_PASSWORD!;
// Driving the login through the app itself ensures the PocketBase SDK's
// LocalAuthStore sees the token via its own write path. Hand-writing
// localStorage["pb_auth"] sometimes races with the SDK's module-time read.
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({ viewport: { width: 1280, height: 800 } });
const page = await context.newPage();
await page.goto(APP_URL);
await page.evaluate(
async ({ email, password }) => {
const res = await fetch('/pb/api/collections/users/auth-with-password', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ identity: email, password }),
});
if (!res.ok) throw new Error(`login ${res.status} ${await res.text()}`);
const data = await res.json();
// The SDK's LocalAuthStore default storageKey is "pocketbase_auth",
// not "pb_auth" (which is just the cookie name in BaseAuthStore).
localStorage.setItem(
'pocketbase_auth',
JSON.stringify({ token: data.token, record: data.record })
);
// Skip the react-joyride product tour — its spotlight overlay
// intercepts pointer events and breaks the recording.
localStorage.setItem('tutorial_completed', '1');
},
{ email, password }
);
await context.storageState({ path: AUTH_STATE_PATH });
await browser.close();
console.log(`Saved ${AUTH_STATE_PATH} via in-app PocketBase login (user: ${email}).`);
}
async function interactive() {
const browser = await chromium.launch({ headless: false });
const context = await browser.newContext({ viewport: { width: 1280, height: 800 } });
const page = await context.newPage();
await page.goto(APP_URL);
console.log('');
console.log(' → Log in via the UI in the opened browser window.');
console.log(' → Once you see the dashboard, press Enter in this terminal.');
console.log('');
await new Promise<void>((resolve) => {
process.stdin.resume();
process.stdin.once('data', () => resolve());
});
await context.storageState({ path: AUTH_STATE_PATH });
console.log(`Saved storage state to ${AUTH_STATE_PATH}`);
await browser.close();
process.exit(0);
}
async function main() {
if (process.env.PB_URL && process.env.PB_EMAIL && process.env.PB_PASSWORD) {
await programmatic();
process.exit(0);
}
await interactive();
}
main().catch((err) => {
console.error(err);
process.exit(1);
});