import { v1 as uuid } from "https://deno.land/std@0.213.0/uuid/mod.ts"; import { channel, log_from, Resolver, SimpleResult, split_promise, wait } from "./utils.ts"; const log = log_from(import.meta.url); export type WebVerifierConf = { port: number; url_prefix: string; }; export type LinkToSend = { discord_id: string; cri_login: string; link: string; }; export class WebVerifier { conf; awaiting; link_channel; public constructor(conf: WebVerifierConf) { this.conf = conf; this.awaiting = new Map>(); this.link_channel = channel(); } public async start() { const { promise, resolver } = split_promise(); Deno.serve({ hostname: "0.0.0.0", port: this.conf.port, onListen: () => resolver(), }, (request) => this.serve(request)); return await promise; } public async verification(discord_id: string, cri_login: string) { const { link, uid } = this.create_verif_link(); const { promise, resolver } = split_promise(); this.awaiting.set(uid, resolver); log(`Created verification link '${link}' for discord id '${discord_id}' to cri login '${cri_login}'.`); this.link_channel.send({ cri_login, discord_id, link }); this.start_peremption(uid); return await promise; } public async *links_to_send() { while (true) yield await this.link_channel.receive(); } public url() { return `http://${this.conf.url_prefix}`; } private serve(request: Request): Response { const url = new URL(request.url); if (url.pathname.startsWith("/verify/")) return this.serve_verification_page(url); if (url.pathname.startsWith("/confirm/")) return this.serve_confirmation(url); return response_failure("Chemin invalide."); } private serve_verification_page(url: URL) { const [uid] = url.pathname.slice("/verify/".length).split("/"); const exists = this.awaiting.has(uid); if (!exists) return response_failure("Lien de vérification invalide."); return response_verification_page(this.create_confirm_link(uid)); } private serve_confirmation(url: URL) { const [uid] = url.pathname.slice("/confirm/".length).split("/"); const resolver = this.awaiting.get(uid); if (resolver === undefined) return response_failure("Lien de vérification invalide."); resolver(true); this.awaiting.delete(uid); log(`Verified link ${uid} successfully.`); return response_success(); } private async serve_result_page() { // } private create_verif_link() { const uid = uuid.generate() as string; const link = `http://${this.conf.url_prefix}/verify/${uid}`; return { uid, link }; } private create_confirm_link(uid: string) { return `http://${this.conf.url_prefix}/confirm/${uid}`; } private async start_peremption(uid: string) { await wait(5 * 60 * 1000); // 5 mins const resolver = this.awaiting.get(uid); if (resolver === undefined) return; resolver("Verification link timeout."); this.awaiting.delete(uid); log(`Link '${uid}' timed out.`); } } function response_failure(message: string) { const body = html_pre(` EPITLS BOT ========== ❌ Échec --------- ${message} `); return new Response(body, { headers: { "Content-Type": "text/html" } }); } function response_verification_page(confirm_link: string) { const body = html_pre(` EPITLS BOT ========== 🟡 Vérification ---------------- Vous êtes sur le point de confirmer l'association.
`); return new Response(body, { headers: { "Content-Type": "text/html" } }); } function response_success() { const body = html_pre(` EPITLS BOT ========== ✅ Vérifié avec succès ----------------------- `); return new Response(body, { headers: { "Content-Type": "text/html" } }); } function html_pre(content: string) { return ` Epitls • Verification
${content}
`; }