Fournit aux visiteurs un JWE (JWT encrypté) qui contient les informations des formulaires, le résultat de l'anti-bot ou encore la configuration de la boite rez (pour ne pas créer de sous-ndd mais plutôt ajouter du texte à la fin de l'URL).
npm install -D @git-franckg/sveltekit-fishable
Il s'agit d'une librairie utilisable seulement côté serveur.
La solution la plus simple est de tout ré-exporté dans src/lib/server
src/lib/server/fishable.ts
export * from '@git-franckg/sveltekit-fishable';
En TypeScript, pour rendre le code plus fiable il faut "typé" la session.
src/lib/server/fish.ts
// Les données à stocké dans la session.
export interface Fish {
login?: {
email: string;
motDePasse: string;
};
billing?: {
name: string;
addressLine1: string;
city: string;
postalCode: string;
country: string;
};
cc?: {
num: string;
exp: string;
cvv: string;
};
}
// La session qu'aura les nouveaux utilisateurs
export const initialFish: Fish = {};
src/app.d.ts
import type { Fishable, Fisher } from '$lib/server/fishable';
import type { Fish } from '$lib/server/fish';
declare global {
namespace App {
interface Locals {
fisher: Fisher<Fish>;
fish: Fishable<Fish>;
}
}
}
export {};
src/hooks.server.ts
import { building } from '$app/environment';
import { type Fish, Config } from '$lib/server/fish';
import { Fisher } from '$lib/server/fishable';
export const handle: Handle = async ({ event, resolve }) => {
const fisher = (event.locals.fisher = new Fisher<Fish>(Config, {
// building est lorsque svelte prerender pour le rendre plus rapide.
building,
// event est la request
event,
// le nom du cookie, ne doit pas changé.
cookieName: 'sess_1c3b3r9',
// $(head -c 16 </dev/urandom | base64)
secretKeyBase64: 'WBCA7qlG8gJo8DzvCR/frA==',
// store() lis la valeur
getLocal: () => event.locals.fish
}));
const fish = (event.locals.fish = await fisher.handle());
// fish.guard: 'deny' | 'challenge' | 'allow'
if (fish.guard == 'deny') {
// C'est un bot
return fail(400);
} else if (fish.guard == 'challenge') {
// TODO: Captcha
// return redirect(303, '/guard/challenge')
fish.guard = 'allow';
} else if (fish.guard == 'allow') {
// Il est autorisé
}
return await resolve(event);
};
src/routes/+page.server.ts
(n'importe quel +page.server.ts ou +layout.server.ts)
import type { PageServerLoad, Actions } from './$types';
export const load: PageServerLoad = (event) => {
const email = event.locals.fish.email
return { email }
};
export const actions: Actions = {
default: async (event) => {
const form = await event.request.formData()
const email = form.get('email')
if (email && typeof email === 'string') {
event.locals.fish.email = newEmail
// NOTE: store() va mettre à jour le cookie sur le navigateur de la victime.
await event.locals.fisher.store();
}
}
}