Initial commit.
This commit is contained in:
commit
acc92a6d9a
13 changed files with 1332 additions and 0 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
node_modules
|
||||||
|
package-lock.json
|
||||||
|
certificate.crt
|
||||||
|
private.key
|
||||||
14
config.json
Normal file
14
config.json
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"serverRoot" : "./www",
|
||||||
|
"logErrors" : true,
|
||||||
|
"useHTTPS" : true,
|
||||||
|
"httpsKey" : "./private.key",
|
||||||
|
"httpsCert" : "./certificate.crt",
|
||||||
|
"directoryListing" : true,
|
||||||
|
"defaultPage" : "/index.html",
|
||||||
|
"useDefaultPage" : true,
|
||||||
|
"listingTitle" : "Zephyrus Listing",
|
||||||
|
"listingFooter" : "Powered by <a href=\"https://git.disroot.org/adrianvictor/zephyrus\">Zephyrus</a>",
|
||||||
|
"listingCSS" : true,
|
||||||
|
"defaltToMime" : "application/octet-stream"
|
||||||
|
}
|
||||||
21
custom.css
Normal file
21
custom.css
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
body {
|
||||||
|
background-color: black;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: gray;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: orange;
|
||||||
|
}
|
||||||
|
|
||||||
|
#title {
|
||||||
|
color: orangered;
|
||||||
|
}
|
||||||
|
|
||||||
|
#previousDirectoryLink {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
11
license
Normal file
11
license
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
Copyright 2024 Adrian Victor de Abreu Alves
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
129
main.ts
Normal file
129
main.ts
Normal file
|
|
@ -0,0 +1,129 @@
|
||||||
|
const config = require('./config.json')
|
||||||
|
import fs, { stat } from 'fs';
|
||||||
|
import http, { IncomingMessage, ServerResponse } from 'http';
|
||||||
|
import https from 'https';
|
||||||
|
import url from 'url';
|
||||||
|
import path, { dirname } from 'path';
|
||||||
|
import mime from 'mime';
|
||||||
|
|
||||||
|
let server: http.Server | https.Server;
|
||||||
|
|
||||||
|
if(config.useHTTPS) {
|
||||||
|
const httpsArgs = {
|
||||||
|
key: fs.readFileSync(config.httpsKey),
|
||||||
|
cert: fs.readFileSync(config.httpsCert)
|
||||||
|
}
|
||||||
|
server = https.createServer(httpsArgs, requestHandler)
|
||||||
|
} else {
|
||||||
|
server = http.createServer(requestHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
function requestHandler(request: IncomingMessage, response: ServerResponse) {
|
||||||
|
const parsed = url.parse(request.url || '/', true);
|
||||||
|
const path_ = decodeURI(parsed.pathname || '/');
|
||||||
|
const serversidePath = path.join(config.serverRoot + path_);
|
||||||
|
const defaultPagePath = path.join(config.serverRoot + config.defaultPage);
|
||||||
|
const finalPath = config.useDefaultPage && request.url == '/' ? path.normalize(defaultPagePath) : serversidePath;
|
||||||
|
|
||||||
|
function showError(code: number, log: boolean = config.logErrors, info: string = 'no more information about the error was provided.') {
|
||||||
|
if (log) {
|
||||||
|
console.log(`Error ${code} - ${info}`)
|
||||||
|
}
|
||||||
|
if (!response.headersSent) {
|
||||||
|
switch (code) {
|
||||||
|
case 500:
|
||||||
|
response.writeHead(500, {"content-type" : 'text/plain'});
|
||||||
|
response.end("500 - internal error");
|
||||||
|
break;
|
||||||
|
case 404:
|
||||||
|
response.writeHead(404, {"content-type" : 'text/plain'});
|
||||||
|
response.end("404 - not found");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
response.writeHead(code, {"content-type" : 'text/plain'});
|
||||||
|
response.end(`Error ${code}`)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function returnDirectory(directory: string) {
|
||||||
|
try {
|
||||||
|
const dir = fs.readdirSync(directory);
|
||||||
|
dir.forEach(file => {
|
||||||
|
const filePath = `${directory}/${file}`
|
||||||
|
const stats = fs.statSync(filePath);
|
||||||
|
const response_ = `<p id="linkParagraph"><a id="filedirLink" href="http://${request.headers.host}${request.url}${file}${stats.isDirectory() ? '/' : ''}">${file}</a></p>`;
|
||||||
|
try {
|
||||||
|
response.write(response_);
|
||||||
|
} catch (err) {
|
||||||
|
showError(500, undefined, `while reading ${filePath}`)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
response.end(`<p id="endMessage">end of directory listing</p><footer>${config.listingFooter}</footer></body>`);
|
||||||
|
} catch (err) {
|
||||||
|
showError(500, undefined, `${err}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.log(`Requested ${path_}, accessing ${config.useDefaultPage && request.url == '/' ? defaultPagePath : serversidePath}`)
|
||||||
|
if (!finalPath.startsWith(path.normalize(config.serverRoot))) {
|
||||||
|
showError(403, undefined, `someone is trying to access files (${finalPath}) outside server root (${config.serverRoot})`)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fs.stat(finalPath, (err, stats) => {
|
||||||
|
if (err) {
|
||||||
|
showError(404, undefined, `while reading ${finalPath}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (stats.isDirectory() && config.directoryListing) {
|
||||||
|
response.writeHead(200, {"content-type" : 'text/html'});
|
||||||
|
var css;
|
||||||
|
const goBackLink = path.dirname(request.url || '')
|
||||||
|
if(config.listingCSS) {
|
||||||
|
try {
|
||||||
|
const css_ = fs.readFileSync('./custom.css');
|
||||||
|
css = css_;
|
||||||
|
} catch {
|
||||||
|
console.error(`You have CSS enabled, but we're having trouble to read it. You should either disable it in the server config or ensure it exists and is accessible`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
response.write(`
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<head>
|
||||||
|
<title>${config.listingTitle}</title>
|
||||||
|
<style>${config.listingCSS ? css : ''}</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1 id="title">listing of ${request.url}</h1>
|
||||||
|
<p id="previousDirectoryParagraph"><a id="previousDirectoryLink" href="http://${request.headers.host}${goBackLink}">go back</a></p>
|
||||||
|
`)
|
||||||
|
try {
|
||||||
|
returnDirectory(finalPath);
|
||||||
|
return;
|
||||||
|
} catch (err) {
|
||||||
|
showError(500);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (stats.isFile()) {
|
||||||
|
const extension = path.extname(finalPath)
|
||||||
|
const mimeType = mime.getType(extension) || config.defaultToMime;
|
||||||
|
response.writeHead(200, {"content-type" : mimeType});
|
||||||
|
fs.readFile(finalPath, (err, data) => {
|
||||||
|
if (err) {
|
||||||
|
showError(500);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
response.end(data);
|
||||||
|
})
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
showError(404, undefined, `while reading ${finalPath}`);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
server.listen(3000, () => {
|
||||||
|
console.log("Started at https://localhost:3000")
|
||||||
|
})
|
||||||
18
package.json
Normal file
18
package.json
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"name" : "zephyrus-webserver",
|
||||||
|
"description" : "A simple webserver that supports directory listing.",
|
||||||
|
"author" : "Adrian Victor de Abreu Alves <adrianvictor@disroot.org>",
|
||||||
|
"version" : "1.0.0",
|
||||||
|
"private" : false,
|
||||||
|
"scripts": {
|
||||||
|
"start": "ts-node main.ts"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^22.9.0",
|
||||||
|
"ts-node": "^10.9.2",
|
||||||
|
"typescript": "^5.6.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"mime": "^4.0.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
13
tsconfig.json
Normal file
13
tsconfig.json
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES6",
|
||||||
|
"module": "commonjs",
|
||||||
|
"strict": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"forceConsistentCasingInFileNames": true
|
||||||
|
},
|
||||||
|
"include": ["**/*.ts"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
|
|
||||||
BIN
www/background.jpg
Normal file
BIN
www/background.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.1 MiB |
104
www/index.html
Normal file
104
www/index.html
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background-color: black;
|
||||||
|
color: white;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items:center;
|
||||||
|
margin: 0;
|
||||||
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: orangered;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
border-top: 1px solid white;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
border-bottom: 1px solid white;
|
||||||
|
}
|
||||||
|
|
||||||
|
#everythingHelper::before {
|
||||||
|
content: "";
|
||||||
|
background-image: url(background.jpg);
|
||||||
|
position: absolute;
|
||||||
|
background-size: cover;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: -1;
|
||||||
|
opacity: .25;
|
||||||
|
}
|
||||||
|
|
||||||
|
#everythingHelper {
|
||||||
|
max-width: 50vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
#titleLinks {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#titleLinks * {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 800px) {
|
||||||
|
#everythingHelper {
|
||||||
|
max-width: 80vw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 280px) {
|
||||||
|
body {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#title {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#titleLinks {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#everythingHelper {
|
||||||
|
max-width: 90vw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<title>Adrian Victor</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="everythingHelper">
|
||||||
|
<header>
|
||||||
|
<h1 id="title">Zephyrus</h1>
|
||||||
|
<ul id="titleLinks"><a href="https://git.disroot.org/adrianvictor/zephyrus">source-code</a><a href="license.txt">license (3-Clause BSD License)</a></ul>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<h2>It's running!</h2>
|
||||||
|
<p>If you're seeing this when accessing your server's root, default page is enabled.</p>
|
||||||
|
<p>Drop your files at ./www (It's the default root, but you can change in the config)</p>
|
||||||
|
<p>Directory listing is enabled by default, you may want to change it.</p>
|
||||||
|
<p>Configure your server in config.json.</p>
|
||||||
|
</main>
|
||||||
|
<footer>
|
||||||
|
<p><a href="https://git.disroot.org/adrianvictor/">Adrian Victor.</a></p>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
1
www/index.txt
Normal file
1
www/index.txt
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
This is a demo file that comes with Zephyrus
|
||||||
11
www/license.txt
Normal file
11
www/license.txt
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
Copyright 2024 Adrian Victor de Abreu Alves
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
1000
www/lorem.txt
Normal file
1000
www/lorem.txt
Normal file
File diff suppressed because it is too large
Load diff
6
www/recipes/salted banana.txt
Normal file
6
www/recipes/salted banana.txt
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
Salted Banana recipe
|
||||||
|
Required ingredients:
|
||||||
|
- Random string
|
||||||
|
- Banana
|
||||||
|
Mix the random string and the banana together.
|
||||||
|
Remember, it's more secure when salted!
|
||||||
Loading…
Add table
Add a link
Reference in a new issue