Skip to content

Commit 9dad96d

Browse files
committed
Resolved endpoints for saving and deleting links
1 parent 565bc3c commit 9dad96d

File tree

4 files changed

+91
-42
lines changed

4 files changed

+91
-42
lines changed

app.js

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ import fastifyFormbody from "@fastify/formbody";
55

66
import { Liquid } from "liquidjs";
77
import webpack from "webpack";
8+
import { JSONFilePreset } from "lowdb/node";
89

910
import fs from "node:fs";
1011
import path from "node:path";
1112
import url from "node:url";
1213
import childProcess from "node:child_process";
14+
import { randomUUID } from "node:crypto";
1315

1416
const __filename = url.fileURLToPath(import.meta.url);
1517
const __dirname = path.dirname(__filename);
@@ -18,6 +20,11 @@ const viewsPath = path.join(__dirname, "views");
1820
const staticsPath = path.join(__dirname, "statics");
1921
const publicPath = path.join(__dirname, "public");
2022

23+
const db = await JSONFilePreset(path.join(__dirname, "data", "data.json"), {
24+
create: [],
25+
consume: [],
26+
});
27+
2128
function buildClientsideAssets() {
2229
if (!fs.existsSync(publicPath)) {
2330
fs.mkdirSync(publicPath);
@@ -81,15 +88,7 @@ app.register(fastifyView, {
8188
app.register(fastifyFormbody);
8289

8390
app.get(getRoutePath(), (req, reply) => {
84-
// TODO: Replace mock data with content from database
85-
let links = {
86-
create: [
87-
{ id: "1", description: "Foobar (create)", url: "https://example.com" },
88-
],
89-
consume: [
90-
{ id: "2", description: "Foobar (consume)", url: "https://example.com" },
91-
],
92-
};
91+
const links = db.data;
9392

9493
return reply.view("./views/index.liquid", {
9594
links,
@@ -98,22 +97,51 @@ app.get(getRoutePath(), (req, reply) => {
9897
});
9998

10099
app.get(getRoutePath("links"), (req, reply) => {
101-
// get all links
100+
const links = db.data;
101+
102+
return reply.send(links);
102103
});
103104

104-
app.post(getRoutePath("create"), (req, reply) => {
105-
// Create a new link
105+
app.post(getRoutePath("create"), async (req, reply) => {
106106
const { type, url, description } = req.body;
107107

108+
const uuid = randomUUID();
109+
110+
db.data[type].push({
111+
id: uuid,
112+
url,
113+
description,
114+
});
115+
await db.write();
116+
108117
reply.redirect(getRoutePath());
109118
});
110119

111-
app.post(getRoutePath("delete"), (req, reply) => {
112-
// Delete a link
120+
app.post(getRoutePath("delete"), async (req, reply) => {
121+
console.log(req.body.id);
122+
let inCreateIdx = db.data.create.findIndex(({ id }) => req.body.id === id);
123+
console.log({ inCreateIdx });
124+
if (inCreateIdx !== -1) {
125+
db.data.create.splice(inCreateIdx, 1);
126+
127+
await db.write();
128+
return reply.send({ message: "link deleted" });
129+
}
130+
131+
let inConsumeIdx = db.data.consume.findIndex(({ id }) => req.body.id === id);
132+
console.log({ inConsumeIdx });
133+
if (inConsumeIdx !== -1) {
134+
db.data.consume.splice(inConsumeIdx, 1);
135+
await db.write();
136+
return reply.send({ message: "link deleted" });
137+
}
113138
});
114139

115-
app.post(getRoutePath("delete-all"), (req, reply) => {
116-
// Delete all links
140+
app.post(getRoutePath("delete-all"), async (req, reply) => {
141+
db.data.create = [];
142+
db.data.consume = [];
143+
await db.write();
144+
return reply.send({ message: "Successfully deleted all posts" });
117145
});
118146

119147
app.listen({ port: 3007, host: "0.0.0.0" }, (err, address) => {

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@
2727
"description": "A web app that allows me to save links to pages over a week to then be put into a weeknote post at the end of the week",
2828
"nodemonConfig": {
2929
"ignore": [
30-
"public/"
30+
"public/",
31+
"data/"
3132
],
3233
"ext": "liquid html js json"
3334
}

statics/js/main.js

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,23 @@ const routePrefix = document
22
.querySelector("meta[name='routePrefix']")
33
.getAttribute("content");
44

5-
console.log(routePrefix);
6-
75
for (let btn of document.getElementsByClassName("delete-link-btn")) {
8-
btn.addEventListener("click", (e) => {
9-
// Delete this link
10-
// TODO replace with likely a POST call to delete the given link via the ID
11-
e.target.parentElement.parentElement.remove();
6+
btn.addEventListener("click", async (e) => {
7+
let linkArticleElement = e.target.parentElement.parentElement;
8+
9+
let linkId = linkArticleElement.getAttribute("data-link-id");
10+
11+
console.log(`Going to delete ${linkId}`);
12+
13+
await fetch(`${routePrefix}/delete`, {
14+
method: "POST",
15+
headers: {
16+
"Content-Type": "application/json",
17+
},
18+
body: JSON.stringify({ id: linkId }),
19+
});
20+
21+
linkArticleElement.remove();
1222
});
1323
}
1424

@@ -29,7 +39,6 @@ for (let btn of document.getElementsByClassName("add-link-btn")) {
2939

3040
form.className = "px-2 border-b border-gray-800 py-4 bg-gray-800/30";
3141

32-
// TODO: Update the "Add" button to actually submit the form
3342
form.innerHTML = `
3443
<div class="flex items-start gap-3">
3544
<div class="flex-1">
@@ -75,28 +84,37 @@ for (let btn of document.getElementsByClassName("add-link-btn")) {
7584
});
7685
}
7786

78-
document.querySelector(".export-btn").addEventListener("click", (e) => {
79-
// TODO: Generate initial content of weeknote
80-
// Something like the following:
81-
/**
82-
* ## Things I worked on
83-
*
84-
* * [create link 1](url)
85-
* * [create link 2](url)
86-
*
87-
* ## Things I consumed
88-
*
89-
* * [consume link 1](url)
90-
* * [consume link 2](url)
91-
*/
87+
document.querySelector(".export-btn").addEventListener("click", async (e) => {
88+
const links = await fetch(`${routePrefix}/links`).then((resp) => resp.json());
89+
console.log(links);
90+
await navigator.clipboard.writeText(`## Things I worked on
91+
92+
${links.create
93+
.map((link) => {
94+
return `- [${link.description}](${link.url})`;
95+
})
96+
.join("\n")}
97+
98+
## Things I consumed
99+
100+
${links.consume
101+
.map((link) => {
102+
return `- [${link.description}](${link.url})`;
103+
})
104+
.join("\n")}`);
105+
106+
console.log("Saved to clipboard!");
92107
});
93108

94-
document.querySelector(".clear-btn").addEventListener("click", (e) => {
109+
document.querySelector(".clear-btn").addEventListener("click", async (e) => {
95110
const result = confirm("Are you sure you want to clear all links?");
96111
if (result) {
97112
document.querySelectorAll(".link-item").forEach((item) => {
98113
item.remove();
99114
});
100-
// TODO: Send a POST request up to clear the database completely
115+
116+
await fetch(`${routePrefix}/delete-all`, {
117+
method: "POST",
118+
});
101119
}
102120
});

views/index.liquid

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313
<main class="min-h-screen">
1414
<div class="max-w-4xl mx-auto px-8 py-12">
1515
<header class="mb-8">
16-
<h1 class="text-4xl font-bold text-white mb-2">Weekly Note</h1>
17-
<p class="text-gray-500">Curate your weekly discoveries</p>
16+
<h1 class="text-4xl font-bold text-white mb-2">
17+
Weeknotes Collector
18+
</h1>
19+
<p class="text-gray-500">Save links across the web for curation</p>
1820
</header>
1921

2022
<section class="mb-12">

0 commit comments

Comments
 (0)