import express from "express"; import { config as dotenvConfig } from "dotenv"; import path from "path"; import SQLite from 'better-sqlite3'; import { Kysely, SqliteDialect } from 'kysely'; import { nanoid } from "nanoid"; import { JSDOM } from "jsdom"; dotenvConfig(); const __dirname = import.meta.dirname; const rawDB = new SQLite('abtmtr.db'); const db = new Kysely({ dialect: new SqliteDialect({ database: rawDB, }) }); rawDB.exec(`CREATE TABLE IF NOT EXISTS blurbs( 'id' TEXT, 'site' TEXT, 'blurb' TEXT, 'verified' INTEGER, 'time' INTEGER );`); const app = express(); app.set('view engine', 'ejs'); app.use('/assets', express.static('assets')); app.set('views', path.join(__dirname, "views", "pages")); // app.use(async (req, res, next) => { // const buttonsJson = await fetch("https://cdn.abtmtr.link/site_content/buttons.json") // .catch(() => res.status(500).send()) // .then((res) => res.json()) // .catch(() => res.status(500).send()); // const followingJson = await fetch("https://cdn.abtmtr.link/site_content/following.json") // .catch(() => res.status(500).send()) // .then((res) => res.json()) // .catch(() => res.status(500).send()); // app.locals.buttons = buttonsJson; // app.locals.following = followingJson; // next(); // }) app.get('/', async (req, res) => { const statusesJson = await fetch("https://cdn.abtmtr.link/site_content/v13/statuses.json") .catch(() => res.status(500).send()) .then((res) => res.json()); res.render('index', { statuses: statusesJson }); }) app.get('/servers', async (req, res) => { const servicesJson = await fetch("https://cdn.abtmtr.link/site_content/v13/services.json") .catch(() => res.status(500).send()) .then((res) => res.json()); const computersJson = await fetch("https://cdn.abtmtr.link/site_content/v13/computers.json") .catch(() => res.status(500).send()) .then((res) => res.json()); res.render('servers', { services: servicesJson, computers: computersJson, visibility: [ "for domain performance", "for personal use", "for use by friends of abtmtr.link's webmaster(s)", "for restricted public use", "for public use" ] }); }); app.get('/about', async (req, res) => { const linksJson = await fetch("https://cdn.abtmtr.link/site_content/v13/links.json") .catch(() => res.status(500).send()) .then((res) => res.json()); res.render('about', { links: linksJson }); }); app.get('/sites', async (req, res) => { const buttonsJson = await fetch("https://cdn.abtmtr.link/site_content/buttons.json") .catch(() => res.status(500).send()) .then((res) => res.json()) .catch(() => res.status(500).send()); const followingJson = await fetch("https://cdn.abtmtr.link/site_content/following.json") .catch(() => res.status(500).send()) .then((res) => res.json()) .catch(() => res.status(500).send()); res.render('sites', { b: buttonsJson, f: followingJson }); }); app.get('/blurbs', async (req, res) => { const data = await db .selectFrom('blurbs') .selectAll() .where('verified', '=', 1) .orderBy('time', "desc") .execute(); res.render('blurbs', { data }); }); // brbrleibbghbelsbbsbuuebbuubsubss async function cleanupBlurbles() { const timeNow = Date.now() - 86400000; await db .deleteFrom('blurbs') .where('time', '<', timeNow) .where('verified', '=', 0) .execute() } app.get('/blurbs/testsend', async (req, res) => { res.render('blurbsent', { id: "THISISATESTLOL" }); }); app.get('/blurbs/send', async (req, res) => { const body = req.query; const errors = []; if (body.site == null) errors.push("Site domain required"); else { const status = await fetch(body.site).then(x => x.status).catch(_ => _); if (status != 200) errors.push("Site must be online"); } if (body.text == null) errors.push("Blurb text required"); else { if (body.text.length < 1) errors.push("Blurb text must not be blank"); if (body.text.length > 140) errors.push("Blurb text must not exceed 140 characters"); } if (errors.length > 0) return res.status(400).json({ error: "Bad Request", message: errors.join(", ") }); const postId = nanoid(32); try { await db.insertInto('blurbs') .values({ id: postId, site: body.site, blurb: body.text, verified: 0, time: Date.now() }) .executeTakeFirstOrThrow(); } catch (err) { console.log(err) return res.status(500).send('Internal Server Error'); } cleanupBlurbles(); res.render('blurbsent', { id: postId }); }); app.get('/blurbs/check', async (req, res) => { const body = req.query; const blurbFromId = await db .selectFrom('blurbs') .selectAll() .where('id', '=', body.id) .executeTakeFirst(); if (blurbFromId == null) return res.redirect("/blurbs/"); const site = await fetch(blurbFromId.site).then(x => x.text()).catch(_ => _); const dom = new JSDOM(site); const relationLink = dom.window.document.querySelector( `[rel=me][href="https://abtmtr.link/blurbs/#${blurbFromId.id}"]` ); if (relationLink != null) await db .updateTable('blurbs') .set({ verified: 1 }) .where('id', '=', body.id) .executeTakeFirst(); else { await db .updateTable('blurbs') .set({ verified: 0 }) .where('id', '=', body.id) .executeTakeFirst(); } cleanupBlurbles(); res.redirect("/blurbs/"); }); app.get("/favicon.ico", (req, res) => { res.redirect("https://cdn.abtmtr.link/site_content/favicon.ico") }) app.listen(process.env.PORT, () => { const url = new URL("http://localhost/"); url.port = process.env.PORT; console.log(`Example app listening on ${url.toString()}`); });