Compare commits

...

6 commits

Author SHA1 Message Date
Erin 6243a7d80d the redirects are not meant to be permanent 2023-09-08 07:44:04 -04:00
Erin cb5778377e update readme with config instructions and stuff 2023-09-05 22:12:28 -04:00
Erin 81a472f6e4 npm run fmt 2023-09-05 21:42:20 -04:00
Erin d62a32e427 add dprint 2023-09-05 21:42:15 -04:00
Erin 124b3cb365 update license identifier in package.json 2023-09-05 21:40:39 -04:00
Erin f2f51b28fe rewrite without express 2023-09-05 21:40:09 -04:00
5 changed files with 216 additions and 1042 deletions

View file

@ -2,9 +2,19 @@
## run it ## run it
npm i no external dependencies! no need to `npm i`! woo!
cp webring.txt.sample webring.txt && $EDITOR webring.txt
npm run start cp webring.txt.sample webring.txt && $EDITOR webring.txt
npm run start
## configuration
the server listens on port `$PORT` (default 80) and loads the list of
participating sites from the file `$SITES_FILE` (default `webring.txt`).
the sites file is a newline-separated list of URLs; leading and trailing
whitespace, empty lines, and lines starting with `#` are ignored. you can test
out the format using [this regexr link](https://regexr.com/?expression=/^\s*([^%23\s].*)\s*$/gm&text=%23%20sample%20SITES_FILE%0A%0A%23%20my%20friend%20alice%20who%20is%20cool%0Ahttps%3A%2F%2Falices.awesome.website%0A%0A%23%20my%20other%20friend%20bob%20who%20is%20neat%20as%20well%0Ahttp%3A%2F%2Fsome.shared.domain%2F~bob%0A&tool=list&input=your.webring.server/next?from=$1\n).
## link to webring sites ## link to webring sites

29
dprint.json Normal file
View file

@ -0,0 +1,29 @@
{
"typescript": {
"lineWidth": 80,
"useTabs": true,
"quoteStyle": "alwaysSingle",
"quoteProps": "consistent",
"useBraces": "always",
"arrowFunction.useParentheses": "preferNone",
"enumDeclaration.memberSpacing": "newLine",
"spaceSurroundingProperties": false,
"exportDeclaration.spaceSurroundingNamedExports": false,
"importDeclaration.spaceSurroundingNamedImports": false,
"constructor.spaceBeforeParentheses": true,
"functionDeclaration.spaceBeforeParentheses": true,
"functionExpression.spaceBeforeParentheses": true,
"getAccessor.spaceBeforeParentheses": true,
"setAccessor.spaceBeforeParentheses": true,
"method.spaceBeforeParentheses": true
},
"markdown": {},
"excludes": [
"**/node_modules",
"**/*-lock.json"
],
"plugins": [
"https://plugins.dprint.dev/typescript-0.86.1.wasm",
"https://plugins.dprint.dev/markdown-0.15.3.wasm"
]
}

View file

@ -1,30 +1,30 @@
import * as fs from 'node:fs'; import {readFileSync} from 'node:fs';
import express from 'express'; import {createServer} from 'node:http';
import {URL} from 'node:url';
const sites = fs.readFileSync('./webring.txt', 'utf-8') const data = readFileSync(process.env.SITES_FILE || 'webring.txt', 'utf-8');
// split lines const sites = [...data.matchAll(/^\s*([^#\s].*)\s*$/gm)].map(match => match[1]);
.split(/\r?\n/)
// trim whitespace from each line
.map(site => site.trim())
// exclude empty lines and lines that start with # because why not
.filter(site => site && !site.startsWith('#'));
// modulo that does the right thing with negatives const text = (response, body, code = 200) => {
// https://stackoverflow.com/a/4467559 response.writeHead(code, {'Content-Type': 'text/plain'}).write(body);
const mod = (n, m) => ((n % m) + m) % m; response.end();
};
const redirect = (res, url) => res.writeHead(302, {Location: url}).end();
function handle (offset, request, response) { const toSite = (response, from, offset) => {
const from = request.query.from;
const index = sites.findIndex(site => site === from); const index = sites.findIndex(site => site === from);
if (!from || index === -1) { if (!from || index === -1) {
response.status(400).send('the provided site is not in the webring'); return text(response, 'the provided site is not in the webring', 400);
return;
} }
response.redirect(301, sites[mod(index + offset, sites.length)]); redirect(response, sites[(index + offset + sites.length) % sites.length]);
} };
express() const pathHandlers = {
.get('/prev', (request, response) => handle(-1, request, response)) '/prev': (response, query) => toSite(response, query.get('from'), -1),
.get('/next', (request, response) => handle(+1, request, response)) '/next': (response, query) => toSite(response, query.get('from'), +1),
.get('/list', (_, response) => response.type('txt').send(sites.join('\n'))) '/list': response => text(response, sites.join('\n')),
.listen(process.env.PORT || 8080); };
createServer((request, response) => {
const url = new URL(request.url, `http://${request.headers.host}`);
pathHandlers[url.pathname]?.(response, url.searchParams);
}).listen(process.env.PORT || 80);

1158
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -4,12 +4,13 @@
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"start": "node index.mjs" "start": "node index.mjs",
"fmt": "dprint fmt"
}, },
"keywords": [], "keywords": [],
"author": "", "author": "",
"license": "ISC", "license": "WTFPL",
"dependencies": { "devDependencies": {
"express": "^4.18.2" "dprint": "^0.40.2"
} }
} }