From 0b93d9759d7f94378cc36c4d9838b2ade9ceb732 Mon Sep 17 00:00:00 2001 From: Shahar Talmi Date: Fri, 25 Oct 2024 22:33:55 +0200 Subject: [PATCH] added server source code --- src/server/deno.js | 3 ++ src/server/deno.json | 10 +++++ src/server/deploy.sh | 3 ++ src/server/worker.js | 95 ++++++++++++++++++++++++++++++++++++++++ src/server/wrangler.toml | 8 ++++ src/utils/urls.js | 1 + 6 files changed, 120 insertions(+) create mode 100644 src/server/deno.js create mode 100644 src/server/deno.json create mode 100755 src/server/deploy.sh create mode 100644 src/server/worker.js create mode 100644 src/server/wrangler.toml diff --git a/src/server/deno.js b/src/server/deno.js new file mode 100644 index 00000000..5c4729ae --- /dev/null +++ b/src/server/deno.js @@ -0,0 +1,3 @@ +import worker from './worker.js'; + +Deno.serve(worker.fetch); diff --git a/src/server/deno.json b/src/server/deno.json new file mode 100644 index 00000000..d64c4f5b --- /dev/null +++ b/src/server/deno.json @@ -0,0 +1,10 @@ +{ + "deploy": { + "project": "652c6edb-dacd-440a-845e-8ea175eddd31", + "exclude": [ + "**/node_modules" + ], + "include": [], + "entrypoint": "deno.js" + } +} \ No newline at end of file diff --git a/src/server/deploy.sh b/src/server/deploy.sh new file mode 100755 index 00000000..e457b44d --- /dev/null +++ b/src/server/deploy.sh @@ -0,0 +1,3 @@ +#!/bin/bash +deployctl deploy --prod +npx wrangler deploy \ No newline at end of file diff --git a/src/server/worker.js b/src/server/worker.js new file mode 100644 index 00000000..871a99b3 --- /dev/null +++ b/src/server/worker.js @@ -0,0 +1,95 @@ +function cleanError(s) { + return s.match(/

(.*)<\/h1>/)?.[1] || s; +} + +function cleanAnswer(s) { + return s.match(/

([^.]*\.)/)?.[1] || s; +} + +function cleanQuestion(s) { + return s.match(/(?<=

Your puzzle answer was ).*?(?=<\/code>)/g) || []; +} + +async function downloadContent(url, session, postPayload) { + const headers = { Cookie: `session=${session}` }; + const options = { headers }; + if (postPayload) { + Object.assign(options, { + body: postPayload, + method: 'POST', + }); + Object.assign(options.headers, { + 'content-type': 'application/x-www-form-urlencoded', + }); + } + const response = await fetch(url, options); + if (response.status >= 400) { + throw new Error( + [ + `Failed to download from ${url} (${response.status})`, + `Description: ${cleanError(await response.text())}`, + ].join('\n'), + ); + } + return await response.text(); +} + +async function getDayAnswer(year, day, session) { + const url = `https://adventofcode.com/${+year}/day/${+day}`; + return cleanQuestion(await downloadContent(url, session)); +} + +async function getDayInput(year, day, session) { + const url = `https://adventofcode.com/${+year}/day/${+day}/input`; + return await downloadContent(url, session); +} + +async function submitDayAnswer(year, day, session, level, answer) { + const url = `https://adventofcode.com/${+year}/day/${+day}/answer`; + const postPayload = `level=${level}&answer=${encodeURIComponent(answer)}`; + return cleanAnswer(await downloadContent(url, session, postPayload)); +} + +async function respond(fn) { + try { + const body = await fn(); + return new Response( + typeof body === 'string' ? body : JSON.stringify(body), + { + status: 200, + headers: { 'Access-Control-Allow-Origin': '*' }, + }, + ); + } catch (e) { + return new Response(e.toString(), { + status: 500, + headers: { 'Access-Control-Allow-Origin': '*' }, + }); + } +} + +export default { + async fetch(req) { + let match; + const session = new URL(req.url).searchParams.get('session'); + match = new URLPattern({ pathname: '/input/:year/:day' }).exec(req.url); + if (req.method === 'GET' && match) { + const { year, day } = match.pathname.groups; + return respond(() => getDayInput(year, day, session)); + } + match = new URLPattern({ pathname: '/question/:year/:day' }).exec(req.url); + if (req.method === 'GET' && match) { + const { year, day } = match.pathname.groups; + return respond(() => getDayAnswer(year, day, session)); + } + match = new URLPattern({ pathname: '/answer/:year/:day' }).exec(req.url); + if (req.method === 'POST' && match) { + const { year, day } = match.pathname.groups; + const formData = await req.formData(); + const level = formData.get('level'); + const answer = formData.get('answer'); + return respond(() => submitDayAnswer(year, day, session, level, answer)); + } + return new Response('Not found', { status: 404 }); + }, +}; diff --git a/src/server/wrangler.toml b/src/server/wrangler.toml new file mode 100644 index 00000000..fe05d40e --- /dev/null +++ b/src/server/wrangler.toml @@ -0,0 +1,8 @@ +#:schema node_modules/wrangler/config-schema.json +name = "aoc" +main = "worker.js" +compatibility_date = "2024-10-22" +compatibility_flags = ["nodejs_compat"] + +[observability] +enabled = true diff --git a/src/utils/urls.js b/src/utils/urls.js index 28d51dcf..71bc9181 100644 --- a/src/utils/urls.js +++ b/src/utils/urls.js @@ -23,4 +23,5 @@ export const imports = { }; // export const aocSolverServer = 'https://www.wix.com/_serverless/adventofcode'; +// export const aocSolverServer = 'https://aoc.shahar-talmi.workers.dev'; export const aocSolverServer = 'https://aoc.deno.dev';