blurbs
This commit is contained in:
parent
d6038af33f
commit
bad3eccbde
14 changed files with 1157 additions and 24 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1 +1,2 @@
|
||||||
node_modules
|
node_modules
|
||||||
|
abtmtr.db
|
|
@ -74,6 +74,21 @@ h1, h2, h3 {
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin: 1em 0;
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin: 0.5em 0;
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 0.25em 0;
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
section {
|
section {
|
||||||
margin: 1em;
|
margin: 1em;
|
||||||
}
|
}
|
||||||
|
@ -164,6 +179,7 @@ hr {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
margin: 4px 0;
|
margin: 4px 0;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
text-align: center;
|
||||||
/* text-overflow: ellipsis;
|
/* text-overflow: ellipsis;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding: 0 8px;
|
padding: 0 8px;
|
||||||
|
|
111
index.js
111
index.js
|
@ -1,9 +1,23 @@
|
||||||
import express from "express";
|
import express from "express";
|
||||||
import { config } from "dotenv";
|
import { config } from "dotenv";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
import SQLite from 'better-sqlite3';
|
||||||
|
import { Kysely, SqliteDialect } from 'kysely';
|
||||||
|
import { nanoid } from "nanoid";
|
||||||
|
import { JSDOM } from "jsdom";
|
||||||
|
|
||||||
const __dirname = import.meta.dirname;
|
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();
|
const app = express();
|
||||||
|
|
||||||
app.set('view engine', 'ejs');
|
app.set('view engine', 'ejs');
|
||||||
|
@ -80,6 +94,103 @@ app.get('/about', (req, res) => {
|
||||||
res.render('about/index');
|
res.render('about/index');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.get('/blurbs', async (req, res) => {
|
||||||
|
const data = await db
|
||||||
|
.selectFrom('blurbs')
|
||||||
|
.selectAll()
|
||||||
|
.where('verified', '=', 1)
|
||||||
|
.execute();
|
||||||
|
res.render('blurbs/index', { data });
|
||||||
|
});
|
||||||
|
|
||||||
|
// brbrleibbghbelsbbsbuuebbuubsubss
|
||||||
|
async function cleanupBlurbles() {
|
||||||
|
const timeNow = Date.now() - 86400000;
|
||||||
|
await db
|
||||||
|
.deleteFrom('blurbs')
|
||||||
|
.where('time', '<', timeNow)
|
||||||
|
.execute()
|
||||||
|
}
|
||||||
|
|
||||||
|
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('blurbs/sent', { 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) => {
|
app.get("/favicon.ico", (req, res) => {
|
||||||
res.redirect("https://cdn.abtmtr.link/site_content/favicon.ico")
|
res.redirect("https://cdn.abtmtr.link/site_content/favicon.ico")
|
||||||
})
|
})
|
||||||
|
|
905
package-lock.json
generated
905
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -7,7 +7,8 @@
|
||||||
},
|
},
|
||||||
"nodemonConfig": {
|
"nodemonConfig": {
|
||||||
"ignore": [
|
"ignore": [
|
||||||
"node_modules/**"
|
"node_modules/**",
|
||||||
|
"abtmtr.db"
|
||||||
],
|
],
|
||||||
"ext": "*"
|
"ext": "*"
|
||||||
},
|
},
|
||||||
|
@ -16,10 +17,13 @@
|
||||||
"license": "SEE LICENSE IN LICENSE",
|
"license": "SEE LICENSE IN LICENSE",
|
||||||
"description": "",
|
"description": "",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"better-sqlite3": "^11.2.1",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"ejs": "^3.1.10",
|
"ejs": "^3.1.10",
|
||||||
"express": "^4.19.2",
|
"express": "^4.19.2",
|
||||||
|
"jsdom": "^25.0.0",
|
||||||
"kysely": "^0.27.4",
|
"kysely": "^0.27.4",
|
||||||
|
"nanoid": "^5.0.7",
|
||||||
"pg": "^8.12.0"
|
"pg": "^8.12.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -1,4 +1,11 @@
|
||||||
<div class="Aboutbox">
|
<div class="Aboutbox">
|
||||||
|
<div class="AboutboxElement">
|
||||||
|
<div class="AboutboxElementText">
|
||||||
|
<h1><a href="/blurbs/">BLURBS</a></h1>
|
||||||
|
<p>What are other sites saying about abtmtr.link?</p>
|
||||||
|
</div>
|
||||||
|
<img class="AboutboxElementImage" src="/assets/icons/friends.png" />
|
||||||
|
</div>
|
||||||
<div class="AboutboxElement">
|
<div class="AboutboxElement">
|
||||||
<div class="AboutboxElementText">
|
<div class="AboutboxElementText">
|
||||||
<h1><a href="/matkap/">MATKAP ZONE</a></h1>
|
<h1><a href="/matkap/">MATKAP ZONE</a></h1>
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
<footer>
|
<div class="fakemain">
|
||||||
<section>
|
<section>
|
||||||
<h2>abtmtr.link v13</h2>
|
<h2>buttons</h2>
|
||||||
<p>© <a href="/about">MeowcaTheoRange</a> 2023-2024</p>
|
<p>friends (<%= buttons.length %>)</p>
|
||||||
<p>Licensed under the <a href="https://cdn.abtmtr.link/licenses/kkpl/license-v2.2.txt">Karkat Public License</a>.</p>
|
|
||||||
<p>Git repository: <a href="https://git.abtmtr.link/MeowcaTheoRange/abtmtr-v13" target="_blank">MeowcaTheoRange/abtmtr-v13</a></p>
|
|
||||||
</section>
|
</section>
|
||||||
<section style="text-align: center;">
|
<section style="text-align: center;">
|
||||||
<% buttons.forEach((button) => { %>
|
<% buttons.forEach((button) => { %>
|
||||||
|
@ -14,6 +12,9 @@
|
||||||
<% } %>
|
<% } %>
|
||||||
<% }) %>
|
<% }) %>
|
||||||
</section>
|
</section>
|
||||||
|
<section>
|
||||||
|
<p>following (<%= following.length %>)</p>
|
||||||
|
</section>
|
||||||
<section style="text-align: center;">
|
<section style="text-align: center;">
|
||||||
<% following.forEach((button) => { %>
|
<% following.forEach((button) => { %>
|
||||||
<% if (button.img != null) { %>
|
<% if (button.img != null) { %>
|
||||||
|
@ -23,4 +24,14 @@
|
||||||
<% } %>
|
<% } %>
|
||||||
<% }) %>
|
<% }) %>
|
||||||
</section>
|
</section>
|
||||||
</footer>
|
</div>
|
||||||
|
<div class="fakemain">
|
||||||
|
<footer>
|
||||||
|
<section>
|
||||||
|
<h2>abtmtr.link v13</h2>
|
||||||
|
<p>© <a href="/about">MeowcaTheoRange</a> 2023-2024</p>
|
||||||
|
<p>Licensed under the <a href="https://cdn.abtmtr.link/licenses/kkpl/license-v2.2.txt">Karkat Public License</a>.</p>
|
||||||
|
<p>Git repository: <a href="https://git.abtmtr.link/MeowcaTheoRange/abtmtr-v13" target="_blank">MeowcaTheoRange/abtmtr-v13</a></p>
|
||||||
|
</section>
|
||||||
|
</footer>
|
||||||
|
</div>
|
|
@ -37,9 +37,7 @@
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
<div class="fakemain">
|
|
||||||
<%- include("../../components/footer.ejs") %>
|
<%- include("../../components/footer.ejs") %>
|
||||||
</div>
|
|
||||||
<script src="/assets/script.js"></script>
|
<script src="/assets/script.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|
54
views/pages/blurbs/index.ejs
Normal file
54
views/pages/blurbs/index.ejs
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<title>abtmtr.link</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<link rel="stylesheet" href="/assets/style.css" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="fakemain">
|
||||||
|
<%- include("../../components/header.ejs") %>
|
||||||
|
</div>
|
||||||
|
<main>
|
||||||
|
<p class="tagline">What are other sites saying about abtmtr.link?</p>
|
||||||
|
<section>
|
||||||
|
<form action="/blurbs/send" method="get">
|
||||||
|
<p>
|
||||||
|
<label for="siteName">Website URL:</label>
|
||||||
|
<input type="url" name="site" id="siteName">
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<label for="blurbText">Blurb:</label>
|
||||||
|
<input type="text" name="text" id="blurbText" maxlength="140" minlength="1">
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<input type="submit" value="Submit Blurb">
|
||||||
|
</p>
|
||||||
|
<p style="opacity: 0.5;">by clicking this button and successfully submitting a blurb, you agree that your input may be displayed on this site and stored on abtmtr.link servers, even if the blurb is not verified. unverified blurbs will be marked for removal one day after creation.</p>
|
||||||
|
</form>
|
||||||
|
<ul>
|
||||||
|
<% data.forEach((blurb) => { %>
|
||||||
|
<li class="blurb"><a href="<%= blurb.site %>" target="_blank"><%= new URL(blurb.site).host %></a>: <%= blurb.blurb %> <a class="removePopup" href="/blurbs/check?id=<%= blurb.id %>">remove</a></li>
|
||||||
|
<% }) %>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
<style>
|
||||||
|
.blurb .removePopup {
|
||||||
|
visibility: hidden;
|
||||||
|
opacity: 0.5;
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
.blurb:hover .removePopup {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</main>
|
||||||
|
<%- include("../../components/footer.ejs") %>
|
||||||
|
<script src="/assets/script.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
36
views/pages/blurbs/sent.ejs
Normal file
36
views/pages/blurbs/sent.ejs
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<title>abtmtr.link</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<link rel="stylesheet" href="/assets/style.css" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="fakemain">
|
||||||
|
<%- include("../../components/header.ejs") %>
|
||||||
|
</div>
|
||||||
|
<main>
|
||||||
|
<p class="tagline">congrations! you done it</p>
|
||||||
|
<section>
|
||||||
|
<p>make sure to add this to the page you have linked:</p>
|
||||||
|
<p style="background-color: black;color:white;padding: 0.5em;"><link rel="me" href="https://abtmtr.link/blurbs/#<%= id %>"></p>
|
||||||
|
<p>you can remove the tag later. use this to remove your blurb from the site if you feel like it</p>
|
||||||
|
<p>do not leave this site until you have added the snippet! you can bookmark this page if you need to:</p>
|
||||||
|
<p style="background-color: black;color:white;padding: 0.5em;">https://abtmtr.link/blurbs/check/?id=<%= id %></p>
|
||||||
|
<p>then click this button (or visit the above bookmark) once you have added it. make sure your page has updated in your browser!</p>
|
||||||
|
<form action="/blurbs/check/" method="get">
|
||||||
|
<input type="hidden" value="<%= id %>" name="id">
|
||||||
|
<input type="submit" value="Done and added">
|
||||||
|
</form>
|
||||||
|
<p style="opacity: 0.5;">can't/don't want to add the snippet? <a href="/about" target="_blank">contact me</a>.</p>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
<%- include("../../components/footer.ejs") %>
|
||||||
|
<script src="/assets/script.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -21,9 +21,7 @@
|
||||||
<% }) %>
|
<% }) %>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
<div class="fakemain">
|
|
||||||
<%- include("../../components/footer.ejs") %>
|
<%- include("../../components/footer.ejs") %>
|
||||||
</div>
|
|
||||||
<script src="/assets/script.js"></script>
|
<script src="/assets/script.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|
|
@ -24,9 +24,7 @@
|
||||||
<p class="tagline">abtmtr.link is a domain for all kinds of services, from web search to live-streaming.</p>
|
<p class="tagline">abtmtr.link is a domain for all kinds of services, from web search to live-streaming.</p>
|
||||||
<%- include("../components/about-boxes.ejs") %>
|
<%- include("../components/about-boxes.ejs") %>
|
||||||
</main>
|
</main>
|
||||||
<div class="fakemain">
|
|
||||||
<%- include("../components/footer.ejs") %>
|
<%- include("../components/footer.ejs") %>
|
||||||
</div>
|
|
||||||
<script src="/assets/script.js"></script>
|
<script src="/assets/script.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|
|
@ -21,9 +21,7 @@
|
||||||
<% }) %>
|
<% }) %>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
<div class="fakemain">
|
|
||||||
<%- include("../../components/footer.ejs") %>
|
<%- include("../../components/footer.ejs") %>
|
||||||
</div>
|
|
||||||
<script src="/assets/script.js"></script>
|
<script src="/assets/script.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|
|
@ -21,9 +21,7 @@
|
||||||
<% }) %>
|
<% }) %>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
<div class="fakemain">
|
|
||||||
<%- include("../../components/footer.ejs") %>
|
<%- include("../../components/footer.ejs") %>
|
||||||
</div>
|
|
||||||
<script src="/assets/script.js"></script>
|
<script src="/assets/script.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue