This commit is contained in:
Tibo De Peuter 2025-10-01 15:48:30 +02:00
parent 192670ac15
commit b73e4e778f
13 changed files with 263 additions and 164 deletions

16
components/footer.js Normal file
View file

@ -0,0 +1,16 @@
class Footer extends HTMLElement {
connectedCallback() {
this.innerHTML = `
<!-- components/footer.js -->
<footer>
<p>
Made with <span class="heart">&hearts;</span>.
<a href="https://git.depeuter.dev/tdpeuter/tdpeuter" about="source code of this webpage" class="hidden">Source code</a>
</p>
</footer>
`;
}
}
export default Footer;

16
components/navbar.js Normal file
View file

@ -0,0 +1,16 @@
class NavBar extends HTMLElement {
connectedCallback() {
this.innerHTML = `
<!-- components/navbar.js -->
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about" about="Find out more about me.">About</a></li>
<li><a href="/contact" about="Contact me">Contact</a></li>
</ul>
</nav>
`;
}
}
export default NavBar;

26
constants.js Normal file
View file

@ -0,0 +1,26 @@
const MIME_TYPES_HEADER_NAME = 'Content-Type';
const MIME_TYPES = {
default: 'application/octet-stream',
html: 'text/html',
js: 'application/javascript',
json: 'application/json',
css: 'text/css',
png: 'image/png',
jpg: 'image/jpg',
gif: 'image/gif',
ico: 'image/x-icon',
svg: 'image/svg+xml',
};
const STATUS_CODES = {
OK: 200,
MOVED_PERMANENTLY: 301,
NOT_FOUND: 404,
INTERNAL_SERVER_ERROR: 500
};
module.exports = {
MIME_TYPES_HEADER_NAME,
MIME_TYPES,
STATUS_CODES
}

60
html.js
View file

@ -1,60 +0,0 @@
const http = require('http');
const fs = require('fs').promises;
const host = 'localhost';
const port = 8008;
const MIME_TYPES_HEADER_NAME = 'Content-Type';
const MIME_TYPES = {
default: 'application/octet-stream',
html: 'text/html; charset=UTF-8',
js: 'application/javascript',
json: 'application/json',
css: 'text/css',
png: 'image/png',
jpg: 'image/jpg',
gif: 'image/gif',
ico: 'image/x-icon',
svg: 'image/svg+xml',
};
const STATUS_CODES = {
OK: 200,
INTERNAL_SERVER_ERROR: 500
};
let indexFile;
function respondWithHTML(response, content, status) {
response.setHeader(MIME_TYPES_HEADER_NAME, MIME_TYPES.html);
response.writeHead(status);
response.end(content);
return response;
}
function respondWithJSON(response, message, status) {
response.setHeader(MIME_TYPES_HEADER_NAME, MIME_TYPES.json);
response.writeHead(status);
response.end(`{"message": "${message}"}`);
return response;
}
function requestListener(_, response) {
respondWithHTML(response, indexFile, STATUS_CODES.OK);
};
async function start() {
const server = http.createServer(requestListener);
try {
indexFile = await fs.readFile(__dirname + '/index.html');
server.listen(port, host, () => {
console.log(`Server is running on http://${host}:${port}`);
});
} catch (e) {
respondWithHTML(response, '<html lang="en"><body><p>500 Internal server error</p></body></html>', STATUS_CODES.INTERNAL_SERVER_ERROR);
process.exit(1);
}
}
start();

View file

@ -1,75 +0,0 @@
<!DOCTYPE html>
<html prefix="schema: http://schema.org/ foaf: https://xmlns.com/foaf/0.1/ og: http://ogp.me/ns#"
typeof="schema:WebPage"
lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="color-scheme" content="light dark">
<title>Tibo De Peuter</title>
<meta name="description" content="Computer Science student. Personal portfolio">
<link rel="alternate" type="application/n-quads" href="https://tibo.depeuter.dev/tdpeuter.ttl">
<meta property="og:title" content="Tibo De Peuter">
<meta property="og:type" content="website">
<meta property="og:image" content="https://tibo.depeuter.dev/assets/owl_circuit.png">
<meta property="og:url" content="https://tibo.depeuter.dev/">
<meta property="og:description" content="Computer Science student. Personal portfolio">
<meta property="og:locale" content="en_GB">
<meta property="foaf:name" content="Tibo De Peuter">
<meta property="foaf:givenName" content="Tibo">
<meta property="foaf:familyName" content="De Peuter">
<meta property="foaf:img" content="https://tibo.depeuter.dev/assets/owl_circuit.png">
<meta property="foaf:homepage" content="https://tibo.depeuter.dev/">
<meta property="foaf:gender" content="male">
<link rel="foaf:maker" content="https://tibo.depeuter.dev/">
<link rel="stylesheet" type="text/css" href="style.css">
<link rel="shortcut icon" type="image/png" href="assets/owl_circuit.png">
<link rel="apple-touch-icon" href="assets/owl_circuit.png">
</head>
<body>
<div class="wrapper">
<div class="quote">
<p>My name is Tibo De Peuter.</p>
<p>I like working with computers.</p>
</div>
<ul class="links">
<li>
<a href="mailto:tibo@depeuter.dev" title="My mail address">
<img src="assets/icons/mail.svg" alt="Mail me"/>
</a>
</li>
<li>
<a href="https://git.depeuter.dev/tdpeuter" title="My personal git">
<img src="assets/icons/git.svg" alt="Gitea"/>
</a>
</li>
<li>
<a href="https://github.com/tdpeuter" title="My GitHub account">
<img src="assets/icons/github.svg" alt="GitHub"/>
</a>
</li>
<li>
<a href="https://twitter.com/tdpeuter" title="My Twitter account">
<img src="assets/icons/twitter.svg" alt="Twitter"/>
</a>
</li>
<li>
<a href="https://www.linkedin.com/in/tdpeuter/" title="My LinkedIn account">
<img src="assets/icons/linkedin.svg" alt="LinkedIn"/>
</a>
</li>
</ul>
</div>
<footer>
<p>
Made with <span class="heart">&hearts;</span>.
<a href="https://git.depeuter.dev/tdpeuter/tdpeuter" about="source code of this webpage" class="hidden">Source code</a>
</p>
</footer>
</body>
</html>

10
layouts/default.html Normal file
View file

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
</body>
</html>

27
loader.js Normal file
View file

@ -0,0 +1,27 @@
import Footer from "./components/footer.js";
import NavBar from "./components/navbar.js";
function loadStyleSheet(path) {
const link = document.createElement("link");
link.rel = "stylesheet";
link.href = path;
link.type = "text/css";
document.head.appendChild(link);
}
function loadWebComponent(name, component) {
if (!customElements.get(name)) {
customElements.define(name, component);
}
}
const webComponents = {
'footer-wc': Footer,
'navbar-wc': NavBar
};
for (const [name, component] of Object.entries(webComponents)) {
loadWebComponent(name, component);
}
loadStyleSheet("/assets/css/style.css");

View file

@ -2,7 +2,7 @@
"name": "tdpeuter",
"version": "0.1.0",
"description": "Portfolio website",
"main": "index.html",
"main": "pages/index.html",
"scripts": {
"start": "live-server --port=3000 .",
"test": "echo \"Error: no test specified\" && exit 1"

17
pages/about.html Normal file
View file

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<navbar-wc></navbar-wc>
<main>
<p>
This is the about page.
</p>
</main>
<footer-wc></footer-wc>
<script src="/loader.js" type="module"></script>
</body>
</html>

73
pages/index.html Normal file
View file

@ -0,0 +1,73 @@
<!DOCTYPE html>
<html prefix="schema: http://schema.org/ foaf: https://xmlns.com/foaf/0.1/ og: http://ogp.me/ns#"
typeof="schema:WebPage"
lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="color-scheme" content="light dark">
<title>Tibo De Peuter</title>
<meta name="description" content="Computer Science student. Personal portfolio">
<link rel="alternate" type="application/n-quads" href="https://tibo.depeuter.dev/tdpeuter.ttl">
<meta property="og:title" content="Tibo De Peuter">
<meta property="og:type" content="website">
<meta property="og:image" content="https://tibo.depeuter.dev/assets/owl_circuit.png">
<meta property="og:url" content="https://tibo.depeuter.dev/">
<meta property="og:description" content="Computer Science student. Personal portfolio">
<meta property="og:locale" content="en_GB">
<meta property="foaf:name" content="Tibo De Peuter">
<meta property="foaf:givenName" content="Tibo">
<meta property="foaf:familyName" content="De Peuter">
<meta property="foaf:img" content="https://tibo.depeuter.dev/assets/owl_circuit.png">
<meta property="foaf:homepage" content="https://tibo.depeuter.dev/">
<meta property="foaf:gender" content="male">
<link rel="foaf:maker" content="https://tibo.depeuter.dev/">
<link rel="shortcut icon" type="image/png" href="../assets/owl_circuit.png">
<link rel="apple-touch-icon" href="../assets/owl_circuit.png">
</head>
<body>
<navbar-wc></navbar-wc>
<main>
<div class="wrapper">
<div class="quote">
<p>My name is Tibo De Peuter.</p>
<p>I like working with computers.</p>
</div>
<ul class="links">
<li>
<a href="mailto:tibo@depeuter.dev" title="My mail address">
<img src="../assets/icons/mail.svg" alt="Mail me"/>
</a>
</li>
<li>
<a href="https://git.depeuter.dev/tdpeuter" title="My personal git">
<img src="../assets/icons/git.svg" alt="Gitea"/>
</a>
</li>
<li>
<a href="https://github.com/tdpeuter" title="My GitHub account">
<img src="../assets/icons/github.svg" alt="GitHub"/>
</a>
</li>
<li>
<a href="https://twitter.com/tdpeuter" title="My Twitter account">
<img src="../assets/icons/twitter.svg" alt="Twitter"/>
</a>
</li>
<li>
<a href="https://www.linkedin.com/in/tdpeuter/" title="My LinkedIn account">
<img src="../assets/icons/linkedin.svg" alt="LinkedIn"/>
</a>
</li>
</ul>
</div>
</main>
<footer-wc></footer-wc>
<script src="/loader.js" type="module"></script>
</body>
</html>

21
plans.md Normal file
View file

@ -0,0 +1,21 @@
This website should contain the following things:
- [ ] Social media presence
- [ ] Contact me
- [ ] List of projects, if applicable
- [ ] Blog, if applicable
- [ ] Resume
It should look:
- [ ] Modern
- [ ] Minimalist
- [ ] Professional
It should also:
- [ ] Support minimalist browsers such as Lynx
- [ ] Provide data using linked-data principles
- [ ] Present data in different formats if requested, such as JSON
- [ ] Be browsable for let's say future AI and crawlers
- [ ] Support localisation

View file

@ -1,27 +1,16 @@
const {promises: fs} = require('fs');
const http = require('http');
const path = require('path');
const {
MIME_TYPES_HEADER_NAME,
MIME_TYPES,
STATUS_CODES
} = require('./constants');
const host = 'localhost';
const port = 8008;
const DEFAULT_PORT = 8008;
const MIME_TYPES_HEADER_NAME = 'Content-Type';
const MIME_TYPES = {
default: 'application/octet-stream',
html: 'text/html; charset=UTF-8',
js: 'application/javascript',
json: 'application/json',
css: 'text/css',
png: 'image/png',
jpg: 'image/jpg',
gif: 'image/gif',
ico: 'image/x-icon',
svg: 'image/svg+xml',
};
const STATUS_CODES = {
OK: 200,
NOT_FOUND: 404,
INTERNAL_SERVER_ERROR: 500
};
const home = JSON.stringify({
message: 'My name is Tibo De Peuter. I like working with computers.'
@ -50,27 +39,66 @@ const contact = JSON.stringify([
}
]);
function respondWithJSON(response, content, status) {
response.setHeader(MIME_TYPES_HEADER_NAME, MIME_TYPES.json);
function respondWithType(response, content, status, mimeType) {
response.setHeader(MIME_TYPES_HEADER_NAME, mimeType);
response.writeHead(status);
response.end(content);
return response;
}
const requestListener = function (request, response) {
function respondWithRedirect(response, target, status) {
response.setHeader('Location', target);
response.writeHead(status);
response.end();
return response;
}
async function requestListener(request, response) {
/* Check if it is a static file or not. */
if (request.url.startsWith('/assets/') || request.url.startsWith('/components/')) {
let filePath = __dirname + request.url;
let content = await fs.readFile(filePath);
let fileExt = path.extname(filePath).substring(1).toLowerCase();
let mimeType = MIME_TYPES[fileExt] || MIME_TYPES.default;
respondWithType(response, content, STATUS_CODES.OK, mimeType);
return;
}
switch (request.url) {
case '/':
respondWithJSON(response, home, STATUS_CODES.OK);
if (request.headers.accept.startsWith(MIME_TYPES.html)) {
let indexFile = await fs.readFile(__dirname + '/pages/index.html');
respondWithType(response, indexFile, STATUS_CODES.OK, MIME_TYPES.html);
} else {
respondWithType(response, home, STATUS_CODES.OK, MIME_TYPES.json);
}
break;
case '/loader.js':
respondWithType(response, await fs.readFile(__dirname + '/loader.js', 'utf8'), STATUS_CODES.OK, MIME_TYPES.js);
break;
case '/about':
if (request.headers.accept.startsWith(MIME_TYPES.html)) {
let indexFile = await fs.readFile(__dirname + '/pages/about.html');
respondWithType(response, indexFile, STATUS_CODES.OK, MIME_TYPES.html);
} else {
respondWithType(response, contact, STATUS_CODES.OK, MIME_TYPES.json);
}
break;
case '/contact':
respondWithJSON(response, contact, STATUS_CODES.OK);
respondWithType(response, contact, STATUS_CODES.OK, MIME_TYPES.json);
break;
case '/pgp':
const url = 'https://keys.openpgp.org/vks/v1/by-fingerprint/08A9C1C8CF9159C9172ABA129B11F5243089DB5B';
respondWithRedirect(response, url, STATUS_CODES.MOVED_PERMANENTLY);
break;
default:
respondWithJSON(response, JSON.stringify({error: 'Resource not found'}), STATUS_CODES.NOT_FOUND);
respondWithType(response, JSON.stringify({error: 'Resource not found'}), STATUS_CODES.NOT_FOUND, MIME_TYPES.json);
}
};
}
const server = http.createServer(requestListener);
const port = process.env.PORT || DEFAULT_PORT;
server.listen(port, host, () => {
console.log(`Server is running on http://${host}:${port}`);
console.log(`Server is running on https://${host}:${port}`);
});