Compare commits
30
Commits
c2.1.0-s2.0.0
...
c3.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3f888cbeb8 | ||
|
|
f82b6ba8f6 | ||
|
|
bf0b5448b6 | ||
|
|
6271fbb3b0 | ||
|
|
eff86a1f3e | ||
|
|
403fc9d5cb | ||
|
|
67b8a4f581 | ||
|
|
e9d50f1392 | ||
|
|
5d2fd6674f | ||
|
|
2a128f49ab | ||
|
|
b5403fbc0c | ||
|
|
39aff9d684 | ||
|
|
90eff2d619 | ||
|
|
1f7c52a37b | ||
|
|
277ec48ee6 | ||
|
|
cf4c84df5f | ||
|
|
a358896c03 | ||
|
|
3e0462b74b | ||
|
|
1fc48b52e3 | ||
|
|
0850426683 | ||
|
|
6c67113227 | ||
|
|
b8f41fe691 | ||
|
|
1ba2a430fe | ||
|
|
9d44aa5d4e | ||
|
|
4b0f683901 | ||
|
|
7a23117328 | ||
|
|
bd3782cd93 | ||
|
|
09ed207ab6 | ||
|
|
f3f90df38e | ||
|
|
dfe2436718 |
@@ -1,4 +1,2 @@
|
|||||||
**/node_modules
|
|
||||||
**/dist
|
|
||||||
.git
|
.git
|
||||||
.gitignore
|
.gitignore
|
||||||
|
|||||||
-22
@@ -1,22 +0,0 @@
|
|||||||
## Node ###
|
|
||||||
# Logs
|
|
||||||
logs
|
|
||||||
*.log
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
|
|
||||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
|
||||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
|
||||||
|
|
||||||
# Runtime data
|
|
||||||
pids
|
|
||||||
*.pid
|
|
||||||
*.seed
|
|
||||||
*.pid.lock
|
|
||||||
|
|
||||||
# Dependency directories
|
|
||||||
node_modules/
|
|
||||||
|
|
||||||
# Build
|
|
||||||
dist/
|
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# Personal-Website
|
||||||
|
A nice introduction to my chaos
|
||||||
|
|
||||||
|
## Backstory
|
||||||
|
Instead of calling it my Personal-Website, you could better call it my Personal-Playground.
|
||||||
|
|
||||||
|
This repo is an example of not being afraid to change a codebase around.
|
||||||
|
It shows the journey of achieving code that fits the needs.
|
||||||
|
|
||||||
|
The posts started with pure excitement from building this site.
|
||||||
|
Making my own content and having complete contol over the format thrilled me.
|
||||||
|
For me, it is about making memorable content, something I can proudly look back on.
|
||||||
|
|
||||||
|
Sarcastic, serious and suprising content.
|
||||||
|
Which becomes super silly stuff when looking back at it ;)
|
||||||
|
|
||||||
|
## Content
|
||||||
|
Once in a while you can enjoy a new post on the website.
|
||||||
|
The posts started talking about the website and all changes that came along.
|
||||||
|
As the website is quite far in development, more focus is put into making relaxing content to read through.
|
||||||
|
|
||||||
|
## Experimentations
|
||||||
|
|
||||||
|
- Swapping QA page for a podcast page
|
||||||
|
- Adding a code++ page: page with a collection of helpful code improvements I came across
|
||||||
|
- Adding a portfolio
|
||||||
|
|
||||||
|
## Thank you
|
||||||
|
Thanks for reading all of this!
|
||||||
|
If you have any questions or sugestions, feel free to send an e-mail to me@raymon.dev.
|
||||||
@@ -1 +0,0 @@
|
|||||||
**/node_modules
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
posts/*
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
FROM nginx as production-stage
|
FROM nginx as production-stage
|
||||||
RUN mkdir /app
|
RUN mkdir /app
|
||||||
COPY /public /app
|
|
||||||
COPY nginx.conf /etc/nginx/nginx.conf
|
COPY nginx.conf /etc/nginx/nginx.conf
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
# Templates will be filled by posts
|
||||||
|
index_template = File.read!("./templates/post_index_page.html")
|
||||||
|
post_template = File.read!("./templates/post_single_page.html")
|
||||||
|
|
||||||
|
post_feed_item_template = File.read!("./templates/post_item.xml")
|
||||||
|
post_feed_template = File.read!("./templates/posts.xml")
|
||||||
|
|
||||||
|
# Converting handlebars in template to values
|
||||||
|
fill_template = fn template, pairs ->
|
||||||
|
Regex.compile!("{{(.*)}}") |>
|
||||||
|
Regex.replace(template, fn _, key -> if key != "content" do Map.get(pairs, String.to_atom(key)) else
|
||||||
|
Map.get(pairs, String.to_atom(key))
|
||||||
|
# Converting \n to paragraphs
|
||||||
|
|> String.split("\\n")
|
||||||
|
|> Enum.reject(fn(x) -> x == "" end)
|
||||||
|
|> Enum.map(fn paragraph -> ("<p>" <> paragraph <> "</p>\n") end)
|
||||||
|
|> Enum.join("")
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
fill_template_pretemplated = fn template -> &fill_template.(template, &1) end
|
||||||
|
|
||||||
|
post_contents = File.ls!("./posts")
|
||||||
|
|> Enum.reject(fn(x) -> String.starts_with?(x, ".") end)
|
||||||
|
|> Enum.map(fn f -> File.read!("./posts/" <> f) end)
|
||||||
|
|> Enum.map(fn c -> String.split(c, "\n") end)
|
||||||
|
|> Enum.map(fn c -> Enum.reject(c, fn(x) -> x == "" end) end)
|
||||||
|
|> Enum.map(fn c -> Enum.chunk_every(c, 2) end)
|
||||||
|
|> Enum.map(fn c ->
|
||||||
|
Enum.map(c, fn [k, v] -> %{String.to_atom(k) => v} end)
|
||||||
|
|> Enum.reduce(%{}, fn(x, acc) -> Map.merge(x, acc) end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
|
||||||
|
index_file = post_contents
|
||||||
|
|> Enum.sort_by(fn m -> Map.get(m, :date) |> (fn d -> Enum.find_index(months, &(&1 == String.slice(d, 8..10))) end).() end)
|
||||||
|
|> Enum.reverse()
|
||||||
|
# Group by month
|
||||||
|
|> Enum.group_by(fn m -> Map.get(m, :date) |> String.slice(8..15) end)
|
||||||
|
|> Enum.sort_by(fn {d, _c} -> (length months) * elem(Integer.parse(String.slice(d, 4..7)), 0)
|
||||||
|
+ Enum.find_index(months, &(&1 == String.slice(d, 0..2))) end)
|
||||||
|
|> Enum.reverse()
|
||||||
|
|> Enum.map(fn {month, posts} -> "\n<h1>" <> month <> "</h1>\n" <> (posts |>
|
||||||
|
Enum.map(fn post -> "<h2>" <> (Map.get(post, :date) |> String.slice(0..6)) <>
|
||||||
|
" - <a href=\"" <> Map.get(post, :id) <> ".html\">" <> Map.get(post, :title) <> "</a></h2>"
|
||||||
|
end) |> Enum.join("\n")) end)
|
||||||
|
|> Enum.join("\n")
|
||||||
|
|> (fn v -> %{index: v} end).()
|
||||||
|
|> fill_template_pretemplated.(index_template).()
|
||||||
|
|
||||||
|
File.open!("./public/gen/index.html", [:write])
|
||||||
|
|> IO.binwrite(index_file)
|
||||||
|
|> File.close()
|
||||||
|
|
||||||
|
post_contents
|
||||||
|
|> Enum.each(fn post ->
|
||||||
|
File.open!("./public/gen/" <> Map.get(post, :id) <> ".html", [:write])
|
||||||
|
|> IO.binwrite(fill_template.(post_template, post))
|
||||||
|
|> File.close()
|
||||||
|
end)
|
||||||
|
|
||||||
|
post_feed = post_contents
|
||||||
|
|> Enum.sort_by(fn m -> Map.get(m, :date) end)
|
||||||
|
|> Enum.reverse()
|
||||||
|
|> Enum.map(fill_template_pretemplated.(post_feed_item_template))
|
||||||
|
|> Enum.join("\n")
|
||||||
|
|> (fn v -> %{items: v} end).()
|
||||||
|
|> fill_template_pretemplated.(post_feed_template).()
|
||||||
|
|
||||||
|
File.open!("./public/gen/rss.xml", [:write])
|
||||||
|
|> IO.binwrite(post_feed)
|
||||||
|
|> File.close()
|
||||||
+2
-3
@@ -33,9 +33,8 @@ http {
|
|||||||
alias /app/lib;
|
alias /app/lib;
|
||||||
}
|
}
|
||||||
|
|
||||||
location /api {
|
location /posts {
|
||||||
proxy_pass https://raymon.dev/api;
|
alias /app/gen/;
|
||||||
proxy_buffering on;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
error_page 404 /404.html;
|
error_page 404 /404.html;
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
gen/*
|
||||||
@@ -21,6 +21,15 @@ nav {
|
|||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2.0em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 1.25em;
|
||||||
|
margin: auto 0;
|
||||||
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: light) {
|
@media (prefers-color-scheme: light) {
|
||||||
:root {
|
:root {
|
||||||
--primary-color: #ffffeb;
|
--primary-color: #ffffeb;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
href="https://cdn.statically.io/gh/dragonprojects/dragondesign/master/main.min.css"
|
href="https://cdn.statically.io/gh/dragonprojects/dragondesign/master/main.min.css"
|
||||||
media="all"
|
media="all"
|
||||||
>
|
>
|
||||||
<link defer rel="stylesheet" href="css/general.css" media="all">
|
<link defer rel="stylesheet" href="/css/general.css" media="all">
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -30,7 +30,6 @@
|
|||||||
<h1>Home</h1>
|
<h1>Home</h1>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h2>Who am I?</h2>
|
|
||||||
<p>
|
<p>
|
||||||
Hi there, good to see you on my website.
|
Hi there, good to see you on my website.
|
||||||
My name is Raymon Zutekouw (<span id="age">born on 29-08-2002</span>)
|
My name is Raymon Zutekouw (<span id="age">born on 29-08-2002</span>)
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
export function age() {
|
export function age() {
|
||||||
let birthdate = new Date(2002, 8, 29)
|
let birthdate = new Date("29 August 2002")
|
||||||
let now = new Date()
|
let now = new Date()
|
||||||
|
|
||||||
let age = now.getFullYear() - birthdate.getFullYear()
|
let age = now.getFullYear() - birthdate.getFullYear()
|
||||||
|
|
||||||
if (now.getMonth() < birthdate.getMonth()) {
|
if (now.getMonth() < birthdate.getMonth())
|
||||||
age--
|
age--
|
||||||
}
|
|
||||||
if (
|
if (
|
||||||
birthdate.getMonth() === now.getMonth() &&
|
birthdate.getMonth() === now.getMonth() &&
|
||||||
now.getDate() < birthdate.getDate()
|
now.getDate() < birthdate.getDate()
|
||||||
) {
|
)
|
||||||
age--
|
age--
|
||||||
}
|
|
||||||
return age
|
return age
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
export async function getPosts(id) {
|
|
||||||
const BASE_URL = 'https://raymon.dev'
|
|
||||||
const BASE_ENDPOINT = '/api/posts'
|
|
||||||
const URL = BASE_URL + BASE_ENDPOINT + (id ? `/${id}` : '?sort=-1')
|
|
||||||
|
|
||||||
let posts = []
|
|
||||||
|
|
||||||
return await fetch(URL)
|
|
||||||
.then(res => res.json())
|
|
||||||
.then(res => {
|
|
||||||
if (id !== undefined) {
|
|
||||||
if (res === undefined) {
|
|
||||||
throw Error("Response body empty")
|
|
||||||
}
|
|
||||||
return [res]
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.log(`Error: ${err}`)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta
|
|
||||||
name="description"
|
|
||||||
content="Raymon typing nonsense on his blog">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Raymon Zutekouw</title>
|
|
||||||
|
|
||||||
<link rel="preload" href="./lib/remote.mjs" as="script" crossorigin="anonymous" type="application/javascript">
|
|
||||||
<link rel="preload" href="posts.mjs" as="script" crossorigin="anonymous" type="application/javascript">
|
|
||||||
|
|
||||||
<link defer
|
|
||||||
rel="stylesheet"
|
|
||||||
href="https://cdn.statically.io/gh/dragonprojects/dragondesign/master/main.min.css"
|
|
||||||
media="all"
|
|
||||||
>
|
|
||||||
<link defer rel="stylesheet" href="css/general.css" media="all">
|
|
||||||
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<noscript>
|
|
||||||
<p>
|
|
||||||
This page isn't as fancy without JavaScript.
|
|
||||||
Some parts will not load, but this will be minimal.
|
|
||||||
</p>
|
|
||||||
</noscript>
|
|
||||||
<div id="app">
|
|
||||||
<nav>
|
|
||||||
<a href="/">Home</a>|
|
|
||||||
<a href="/posts">Posts</a>|
|
|
||||||
<a href="/qa">QA</a>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<!-- Placeholder for posts -->
|
|
||||||
<div id="posts"></div>
|
|
||||||
<script async type="module" src="posts.mjs"></script>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
import { getPosts } from "./lib/remote.mjs"
|
|
||||||
|
|
||||||
function toMonthYearString(str) {
|
|
||||||
const date = new Date(str)
|
|
||||||
return `${date.toLocaleString('default', { month: 'long' })} ${date.getFullYear()}`
|
|
||||||
}
|
|
||||||
async function showPost(id) {
|
|
||||||
const post_list = await getPosts(id)
|
|
||||||
const post = post_list[0]
|
|
||||||
const title = document.createElement("h1")
|
|
||||||
title.textContent = post.title
|
|
||||||
|
|
||||||
const postsDOM = document.getElementById("posts")
|
|
||||||
postsDOM.appendChild(title)
|
|
||||||
|
|
||||||
post.content.split('\n').forEach(paragraph => {
|
|
||||||
const p = document.createElement("p")
|
|
||||||
p.innerHTML = paragraph
|
|
||||||
postsDOM.appendChild(p)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updatePosts() {
|
|
||||||
const posts = await getPosts()
|
|
||||||
|
|
||||||
const months = posts.map(post => toMonthYearString(post.date))
|
|
||||||
const uniques = Array.from(new Set(posts.map(post => toMonthYearString(post.date))))
|
|
||||||
const month_lists = uniques.map(month =>
|
|
||||||
posts.filter(
|
|
||||||
post => toMonthYearString(post.date) === month
|
|
||||||
)
|
|
||||||
)
|
|
||||||
const postsDOM = document.getElementById("posts")
|
|
||||||
uniques.forEach((month, i) => {
|
|
||||||
const month_DOM = document.createElement("h1")
|
|
||||||
month_DOM.textContent = month
|
|
||||||
|
|
||||||
month_lists[i].forEach((post, i) => {
|
|
||||||
const post_DOM = document.createElement("h6")
|
|
||||||
post_DOM.textContent = `${post.date.substring(0, 10)} - `
|
|
||||||
|
|
||||||
const post_link = document.createElement("a")
|
|
||||||
post_link.href = 'posts?post=' + post._id
|
|
||||||
post_link.textContent = post.title
|
|
||||||
post_DOM.appendChild(post_link)
|
|
||||||
|
|
||||||
month_DOM.appendChild(post_DOM)
|
|
||||||
})
|
|
||||||
|
|
||||||
postsDOM.appendChild(month_DOM)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if a specific post is requested
|
|
||||||
let url = new URL(document.location.href);
|
|
||||||
url.searchParams.sort();
|
|
||||||
let post_id = url.searchParams.values().next().value;
|
|
||||||
|
|
||||||
const reg = /([0-9]|[a-f]){24}/
|
|
||||||
if (post_id && reg.test(post_id)) showPost(post_id)
|
|
||||||
else updatePosts()
|
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
href="https://cdn.statically.io/gh/dragonprojects/dragondesign/master/main.min.css"
|
href="https://cdn.statically.io/gh/dragonprojects/dragondesign/master/main.min.css"
|
||||||
media="all"
|
media="all"
|
||||||
>
|
>
|
||||||
<link defer rel="stylesheet" href="css/general.css" media="all">
|
<link defer rel="stylesheet" href="/css/general.css" media="all">
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta
|
||||||
|
name="description"
|
||||||
|
content="Raymon typing nonsense on his blog">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Raymon Zutekouw</title>
|
||||||
|
|
||||||
|
<link defer
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://cdn.statically.io/gh/dragonprojects/dragondesign/master/main.min.css"
|
||||||
|
media="all"
|
||||||
|
>
|
||||||
|
<link defer rel="stylesheet" href="/css/general.css" media="all">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
<nav>
|
||||||
|
<a href="/">Home</a>|
|
||||||
|
<a href="/posts">Posts</a>|
|
||||||
|
<a href="/qa">QA</a>
|
||||||
|
</nav>
|
||||||
|
<header>
|
||||||
|
<h1>Post listing</h1>
|
||||||
|
</header>
|
||||||
|
{{index}}
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<item>
|
||||||
|
<title>{{title}}</title>
|
||||||
|
<link>https://raymon.dev/posts/{{id}}.html</link>
|
||||||
|
<guid>https://raymon.dev/posts/{{id}}.html</guid>
|
||||||
|
<description>
|
||||||
|
<![CDATA[
|
||||||
|
{{content}}
|
||||||
|
]]>
|
||||||
|
</description>
|
||||||
|
<content type="html">
|
||||||
|
{{content}}
|
||||||
|
</content>
|
||||||
|
<pubDate>{{date}}</pubDate>
|
||||||
|
</item>
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta
|
||||||
|
name="description"
|
||||||
|
content="Raymon typing nonsense on his blog">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Raymon Zutekouw</title>
|
||||||
|
|
||||||
|
<link defer
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://cdn.statically.io/gh/dragonprojects/dragondesign/master/main.min.css"
|
||||||
|
media="all"
|
||||||
|
>
|
||||||
|
<link defer rel="stylesheet" href="/css/general.css" media="all">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
<nav>
|
||||||
|
<a href="/">Home</a>|
|
||||||
|
<a href="/posts">Posts</a>|
|
||||||
|
<a href="/qa">QA</a>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<article>
|
||||||
|
<header>
|
||||||
|
<h1>{{title}}</h1>
|
||||||
|
<h3>For those using a RSS reader, subscribe here: <a href="/posts/rss.xml">rss.xml</a></h3>
|
||||||
|
</header>
|
||||||
|
<time datetime="{{date}}"></time>
|
||||||
|
{{content}}
|
||||||
|
</article>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||||
|
<channel>
|
||||||
|
<atom:link href="https://raymon.dev/api/posts/rss.xml" rel="self" type="application/rss+xml" />
|
||||||
|
<title>Posts</title>
|
||||||
|
<link>https://raymon.dev</link>
|
||||||
|
<description>Personal blog</description>
|
||||||
|
<language>en-us</language>
|
||||||
|
|
||||||
|
{{items}}
|
||||||
|
</channel>
|
||||||
|
</rss>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
version: "3.3"
|
||||||
|
services:
|
||||||
|
client:
|
||||||
|
build: client/
|
||||||
|
ports:
|
||||||
|
- "5010:80"
|
||||||
|
volumes:
|
||||||
|
- "./client/public:/app:rw"
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
version: '3'
|
|
||||||
|
|
||||||
services:
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
**/node_modules
|
|
||||||
**/dist
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
FROM node:latest
|
|
||||||
|
|
||||||
WORKDIR /usr/src/server
|
|
||||||
|
|
||||||
COPY package*.json ./
|
|
||||||
|
|
||||||
RUN npm ci --only=production
|
|
||||||
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
CMD [ "node", "index.js" ]
|
|
||||||
|
|
||||||
EXPOSE 5000
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
const fastify = require('fastify')()
|
|
||||||
|
|
||||||
fastify.register(require('fastify-cors'))
|
|
||||||
|
|
||||||
const posts = require('./routes/api/posts')
|
|
||||||
|
|
||||||
// Assuming they are all get requests
|
|
||||||
for (let [route, resolver] of posts.entries()) {
|
|
||||||
fastify.get('/posts' + route, resolver)
|
|
||||||
}
|
|
||||||
|
|
||||||
const port = process.env.PORT || 5000
|
|
||||||
|
|
||||||
const start = async () => {
|
|
||||||
try {
|
|
||||||
await fastify.listen(port, '0.0.0.0')
|
|
||||||
console.log(`Launched on port ${port} 🚀`)
|
|
||||||
} catch (err) {
|
|
||||||
fastify.log.error(err)
|
|
||||||
process.exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
start()
|
|
||||||
Generated
-1315
File diff suppressed because it is too large
Load Diff
@@ -1,20 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "server",
|
|
||||||
"version": "1.3.0",
|
|
||||||
"description": "The API for the website",
|
|
||||||
"main": "index.js",
|
|
||||||
"scripts": {
|
|
||||||
"start": "node ./index.js",
|
|
||||||
"dev": "nodemon server/index.js"
|
|
||||||
},
|
|
||||||
"keywords": [],
|
|
||||||
"author": "Raymonzut",
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"fastify": "^2.14.1",
|
|
||||||
"fastify-cors": "^3.0.3"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"nodemon": "^2.0.4"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
*.json
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
All the posts will be in here seperate in JSON format
|
|
||||||
|
|
||||||
post
|
|
||||||
- title
|
|
||||||
- date
|
|
||||||
- content
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
const fs = require('fs')
|
|
||||||
|
|
||||||
const posts_dir = 'posts/'
|
|
||||||
|
|
||||||
const routes = new Map();
|
|
||||||
|
|
||||||
if (!fs.existsSync(posts_dir)) throw Error(`Missing ${posts_dir}`)
|
|
||||||
|
|
||||||
let posts = readPosts();
|
|
||||||
|
|
||||||
setInterval(() => posts = readPosts(), 1000 * 60 * 60)
|
|
||||||
|
|
||||||
function readPosts() {
|
|
||||||
console.warn("reading all posts")
|
|
||||||
const files = fs.readdirSync(posts_dir)
|
|
||||||
|
|
||||||
if (files.length === 0) throw Error(`Could not find posts in ${posts_dir}`)
|
|
||||||
|
|
||||||
return files.filter(file_name => file_name.endsWith('.json'))
|
|
||||||
.map(file_name => `${posts_dir}${file_name}`)
|
|
||||||
.map(readJSONAsObject)
|
|
||||||
}
|
|
||||||
|
|
||||||
function readJSONAsObject(filename) {
|
|
||||||
return JSON.parse(fs.readFileSync(filename, 'utf8'))
|
|
||||||
}
|
|
||||||
|
|
||||||
function notFoundResponse(res) {
|
|
||||||
res.status(404).send('Sorry, can not find that')
|
|
||||||
}
|
|
||||||
|
|
||||||
routes.set('', async (req, res) => {
|
|
||||||
if (req.query.sort === '-1' || req.query.sort === '1') {
|
|
||||||
const posts_sorted = posts.sort((a, b) => {
|
|
||||||
return new Date(a.date).getTime() - new Date(b.date).getTime()
|
|
||||||
})
|
|
||||||
|
|
||||||
if (req.query.sort === '1') res.send(posts_sorted)
|
|
||||||
else res.send(posts_sorted.reverse())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Default response when there are no interesting queries
|
|
||||||
res.send(posts)
|
|
||||||
})
|
|
||||||
|
|
||||||
routes.set('/:id', async (req, res) => {
|
|
||||||
const re = /[0-9A-Fa-f]{24}/g
|
|
||||||
|
|
||||||
if (!re.test(req.params.id)) {
|
|
||||||
notFoundResponse(res)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const results = posts.filter(post => post._id === req.params.id)
|
|
||||||
|
|
||||||
if (!results.length) notFoundResponse(res)
|
|
||||||
else res.send(results[0])
|
|
||||||
})
|
|
||||||
|
|
||||||
// Dynamic RSS feed
|
|
||||||
routes.set('/rss.xml', async (req, res) => {
|
|
||||||
const re = /(\/api\/)?(.*)\/rss/g
|
|
||||||
const result = [...req.raw.originalUrl.matchAll(re)]
|
|
||||||
if (!result) {
|
|
||||||
notFoundResponse(res)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const endpoint = result [1] ? result[1][2] : result[0][2]
|
|
||||||
// Assumes https
|
|
||||||
const BASE_URL = 'https://' + req.raw.hostname + endpoint
|
|
||||||
const ITEMS = posts.map(post =>
|
|
||||||
`\r\n <item>
|
|
||||||
<title>${post.title}</title>
|
|
||||||
<link>https://${req.raw.hostname}/posts?post=${post._id}</link>
|
|
||||||
<guid>https://${req.raw.hostname}/posts?post=${post._id}</guid>
|
|
||||||
<description>${post.content}</description>
|
|
||||||
</item>`
|
|
||||||
).join("\n")
|
|
||||||
const xml = `<?xml version="1.0" encoding="UTF-8" ?>\n<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
|
||||||
<channel>
|
|
||||||
<atom:link href="https://${req.raw.hostname}/api/posts/rss.xml" rel="self" type="application/rss+xml" />
|
|
||||||
<title>Posts</title>
|
|
||||||
<link>https://${req.raw.hostname}</link>
|
|
||||||
<description>Personal blog</description>
|
|
||||||
<language>en-us</language>
|
|
||||||
${ITEMS}
|
|
||||||
</channel>\n</rss>`
|
|
||||||
|
|
||||||
res.header('Content-Type', 'application/xml')
|
|
||||||
res.status(200).send(xml)
|
|
||||||
})
|
|
||||||
|
|
||||||
module.exports = routes
|
|
||||||
Reference in New Issue
Block a user