155 lines
3.9 KiB
TypeScript
155 lines
3.9 KiB
TypeScript
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<string, Resolver<SimpleResult>>();
|
|
this.link_channel = channel<LinkToSend>();
|
|
}
|
|
|
|
public async start() {
|
|
const { promise, resolver } = split_promise<void>();
|
|
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<SimpleResult>();
|
|
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.
|
|
<form action="${confirm_link}">
|
|
<input type="submit" value="Confirmer">
|
|
</form>
|
|
`);
|
|
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 `<!DOCTYPE html>
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>Epitls • Verification</title>
|
|
</head>
|
|
<body>
|
|
<pre>
|
|
${content}
|
|
</pre>
|
|
</body>
|
|
</html>
|
|
`;
|
|
}
|