Sync
This commit is contained in:
parent
192670ac15
commit
b73e4e778f
13 changed files with 263 additions and 164 deletions
16
components/footer.js
Normal file
16
components/footer.js
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
|
||||||
|
class Footer extends HTMLElement {
|
||||||
|
connectedCallback() {
|
||||||
|
this.innerHTML = `
|
||||||
|
<!-- components/footer.js -->
|
||||||
|
<footer>
|
||||||
|
<p>
|
||||||
|
Made with <span class="heart">♥</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
16
components/navbar.js
Normal 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
26
constants.js
Normal 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
60
html.js
|
|
@ -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();
|
|
||||||
75
index.html
75
index.html
|
|
@ -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">♥</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
10
layouts/default.html
Normal 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
27
loader.js
Normal 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");
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
"name": "tdpeuter",
|
"name": "tdpeuter",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "Portfolio website",
|
"description": "Portfolio website",
|
||||||
"main": "index.html",
|
"main": "pages/index.html",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "live-server --port=3000 .",
|
"start": "live-server --port=3000 .",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
|
|
||||||
17
pages/about.html
Normal file
17
pages/about.html
Normal 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
73
pages/index.html
Normal 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
21
plans.md
Normal 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
|
||||||
84
routes.js
84
routes.js
|
|
@ -1,27 +1,16 @@
|
||||||
|
const {promises: fs} = require('fs');
|
||||||
const http = require('http');
|
const http = require('http');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const {
|
||||||
|
MIME_TYPES_HEADER_NAME,
|
||||||
|
MIME_TYPES,
|
||||||
|
STATUS_CODES
|
||||||
|
} = require('./constants');
|
||||||
|
|
||||||
const host = 'localhost';
|
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({
|
const home = JSON.stringify({
|
||||||
message: 'My name is Tibo De Peuter. I like working with computers.'
|
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) {
|
function respondWithType(response, content, status, mimeType) {
|
||||||
response.setHeader(MIME_TYPES_HEADER_NAME, MIME_TYPES.json);
|
response.setHeader(MIME_TYPES_HEADER_NAME, mimeType);
|
||||||
response.writeHead(status);
|
response.writeHead(status);
|
||||||
response.end(content);
|
response.end(content);
|
||||||
return response;
|
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) {
|
switch (request.url) {
|
||||||
case '/':
|
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;
|
break;
|
||||||
case '/contact':
|
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;
|
break;
|
||||||
default:
|
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 server = http.createServer(requestListener);
|
||||||
|
const port = process.env.PORT || DEFAULT_PORT;
|
||||||
server.listen(port, host, () => {
|
server.listen(port, host, () => {
|
||||||
console.log(`Server is running on http://${host}:${port}`);
|
console.log(`Server is running on https://${host}:${port}`);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue