mirror of
https://github.com/schollz/hostyoself.git
synced 2026-01-23 02:15:14 +00:00
384 lines
No EOL
18 KiB
HTML
384 lines
No EOL
18 KiB
HTML
<!doctype html>
|
|
<html>
|
|
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
|
<meta name="msapplication-TileColor" content="#ffffff">
|
|
<meta name="theme-color" content="#ffffff">
|
|
<link rel="stylesheet" href="/static/dropzone.css">
|
|
<link rel="stylesheet" href="/static/style.css">
|
|
<title>hostyoself</title>
|
|
<style>
|
|
.main {
|
|
padding-top: 20px;
|
|
}
|
|
|
|
.list {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.list>div {
|
|
padding: 0.4em;
|
|
}
|
|
|
|
body {
|
|
text-decoration-skip: ink;
|
|
}
|
|
|
|
.hide {
|
|
display: none;
|
|
}
|
|
|
|
textarea {
|
|
width: 100%;
|
|
border: none;
|
|
resize: none;
|
|
height: 20em;
|
|
border: 1px solid #ccc;
|
|
background-color: #f5f5f5;
|
|
padding: 1em;
|
|
font-size: 0.9em;
|
|
}
|
|
|
|
details>p>code {
|
|
background-color: inherit;
|
|
}
|
|
|
|
.editer {
|
|
margin: 0.2em;
|
|
background-color: inherit;
|
|
display: inline;
|
|
border: none;
|
|
font-weight: bold;
|
|
font-family: var(--mono-font);
|
|
font-size: 1em;
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
<main>
|
|
<!-- <h1 align="center"><a href="/">hostyoself</a> </h1>
|
|
-->
|
|
<img src="/static/hostyoself2.png">
|
|
<p id="errormessage" class="error"></p>
|
|
<p>Your files will be relayed at <code>{{.PublicURL}}/<input type='text' id="inputDomain" class="editer" value="{{.GeneratedDomain}}" style="width:7em;"></code>. Multiple hosts can be used with this key: <code><input type='text' id="inputKey" class="editer" value="{{.GeneratedKey}}" style="width:3.5em;"></code>.</p>
|
|
<details>
|
|
<summary>Click here for FAQ.</summary>
|
|
<h2 id=" faq">FAQ</h2>
|
|
<p><strong>How do I start web hosting?</strong> You will need to setup port forwarding, a dynamic DNS, name
|
|
registration, MySQL, PHP, Apache and take a online course in Javascript. </p>
|
|
<p>Just <em>kidding</em>! You don't need any of that crap. Just drag and drop a folder, or select a file.
|
|
That's literally it. Now you can host a website from your laptop or your phone or your smartwatch or
|
|
your toaster.</p>
|
|
<p><strong>How is this possible?</strong> When the server you point at gets a request for a webpage, the
|
|
server turns back and asks <em>you</em> for that content and will use what you provide for the original
|
|
request.</p>
|
|
<p><strong>Seriously, how is this possible?</strong> The relay uses websockets in your browser to process
|
|
GET commands.</p>
|
|
<p><strong>Won't my website disappear when I close my browser?</strong> Yep! There is a command-line tool
|
|
that doesn't require a browser so it can run in the background if you need that. But yes, if your
|
|
computer turns off then your site is down. Welcome to the joys of hosting a site on the internet.</p>
|
|
<p><strong>Won't I have to reload my browser if I change a file?</strong> Yep! Welcome to the joys of
|
|
Javascript.</p>
|
|
<p><strong>Whats the largest file I can host using this?</strong> <code>¯\_(ツ)_/¯</code></p>
|
|
<p><strong>Should I use this to host a website?</strong> Dear god yes.</p>
|
|
<p><strong>Does this use AI or blockchain?</strong> Sure, why not. </p>
|
|
<p><strong>What inspired this?</strong> <a href="https://github.com/joewalnes/websocketd">websocketd</a>
|
|
which shows the magic of websockets and <a href="https://beakerbrowser.com/">beaker browser</a> which
|
|
shows the magic of browser hosting.</p>
|
|
<p><strong>What's the point of this?</strong> You can host a website! You can share a file! Anything you
|
|
want, directly from your browser!</p>
|
|
<p>
|
|
<details>
|
|
<summary>
|
|
<h2 id="terms" style="display:inline;">Terms of use</h2>
|
|
</summary>
|
|
<h2 id="introduction">Introduction</h2>
|
|
<p>These Terms of Service ("Terms") govern your use of hostyoself.com ("The Service").</p>
|
|
<h2 id="services">Services</h2>
|
|
<p>The Service provides a online utility to upload and host files on a temporary basis.</p>
|
|
<h2 id="yourcontentinourservices">Your Content in Our Services</h2>
|
|
<p>You may upload content as part of the features of the Services. By uploading content, you hereby
|
|
grant us a nonexclusive, royalty-free, worldwide license to use your content in connection with
|
|
the provision of the Services. You hereby represent and warrant that your content will not
|
|
infringe the rights of any third party and will comply with any content guidelines presented by
|
|
The Service. To report abusive Screenshots or to report claims of copyright or trademark
|
|
infringement, email us a link to the shot at schollz@mg.hostyoself.com.</p>
|
|
<h2 id="proprietaryrights">Proprietary Rights</h2>
|
|
<p>The Service does not grant you any intellectual property rights in the Services that are not
|
|
specifically stated in these Terms. For example, these Terms do not provide the right to use any
|
|
of The Service's copyrights, trade names, trademarks, service marks, logos, domain names, or
|
|
other distinctive brand features. The Services are distributed under and subject to the current
|
|
version of the MIT license.</p>
|
|
<h2 id="termtermination">Term; Termination</h2>
|
|
<p>These Terms will continue to apply until ended by either you or The Service. You can choose to
|
|
end them at any time for any reason by deleting your account, discontinuing your use of the
|
|
Services, and if applicable, unsubscribing from our emails.</p>
|
|
<h2 id="indemnification">Indemnification</h2>
|
|
<p>You agree to defend, indemnify and hold harmless The Service, its contractors, contributors,
|
|
licensors, and partners, and their respective directors, officers, employees and agents
|
|
("Indemnified Parties") from and against any and all third party claims and expenses, including
|
|
attorneys' fees, arising out of or related to your use of the Services (including, but not
|
|
limited to, from any content uploaded by you).</p>
|
|
<h2 id="disclaimerlimitationofliability">Disclaimer; Limitation of Liability</h2>
|
|
<p>THE SERVICES ARE PROVIDED "AS IS" WITH ALL FAULTS. TO THE EXTENT PERMITTED BY LAW, THE SERVICE
|
|
AND THE INDEMNIFIED PARTIES HEREBY DISCLAIM ALL WARRANTIES, WHETHER EXPRESS OR IMPLIED,
|
|
INCLUDING WITHOUT LIMITATION WARRANTIES THAT THE SERVICES ARE FREE OF DEFECTS, MERCHANTABLE, FIT
|
|
FOR A PARTICULAR PURPOSE, AND NON-INFRINGING. YOU BEAR THE ENTIRE RISK AS TO SELECTING THE
|
|
SERVICES FOR YOUR PURPOSES AND AS TO THE QUALITY AND PERFORMANCE OF THE SERVICES, INCLUDING
|
|
WITHOUT LIMITATION THE RISK THAT YOUR CONTENT IS DELETED OR CORRUPTED OR THAT SOMEONE ELSE
|
|
ACCESSES YOUR ONLINE ACCOUNTS. THIS LIMITATION WILL APPLY NOTWITHSTANDING THE FAILURE OF
|
|
ESSENTIAL PURPOSE OF ANY REMEDY. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF
|
|
IMPLIED WARRANTIES, SO THIS DISCLAIMER MAY NOT APPLY TO YOU.</p>
|
|
<p>EXCEPT AS REQUIRED BY LAW, THE SERVICE AND THE INDEMNIFIED PARTIES WILL NOT BE LIABLE FOR ANY
|
|
INDIRECT, SPECIAL, INCIDENTAL, CONSEQUENTIAL, OR EXEMPLARY DAMAGES ARISING OUT OF OR IN ANY WAY
|
|
RELATING TO THESE TERMS OR THE USE OF OR INABILITY TO USE THE SERVICES, INCLUDING WITHOUT
|
|
LIMITATION DIRECT AND INDIRECT DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, LOST PROFITS, LOSS
|
|
OF DATA, AND COMPUTER FAILURE OR MALFUNCTION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
|
|
AND REGARDLESS OF THE THEORY (CONTRACT, TORT, OR OTHERWISE) UPON WHICH SUCH CLAIM IS BASED. THE
|
|
COLLECTIVE LIABILITY OF THE SERVICE AND THE INDEMNIFIED PARTIES UNDER THIS AGREEMENT WILL NOT
|
|
EXCEED $500 (FIVE HUNDRED DOLLARS). SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION
|
|
OF INCIDENTAL, CONSEQUENTIAL, OR SPECIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY
|
|
TO YOU.</p>
|
|
<h2 id="modificationstotheseterms">Modifications to these Terms</h2>
|
|
<p>The Service may update these Terms from time to time to address a new feature of the Services or
|
|
to clarify a provision. The updated Terms will be posted online. Your continued use of the
|
|
Services after the effective date of such changes constitutes your acceptance of such changes.
|
|
</p>
|
|
<h2 id="miscellaneous">Miscellaneous</h2>
|
|
<p>These Terms constitute the entire agreement between you and The Service concerning the Services
|
|
and are governed by the laws of the state of Washington, U.S.A., excluding its conflict of law
|
|
provisions. If any portion of these Terms is held to be invalid or unenforceable, the remaining
|
|
portions will remain in full force and effect. In the event of a conflict between a translated
|
|
version of these terms and the English language version, the English language version shall
|
|
control.</p>
|
|
</details>
|
|
</p>
|
|
<p>
|
|
<details>
|
|
<summary>
|
|
<h2 id="privacy" style="display:inline;">Privacy policy</h2>
|
|
</summary>
|
|
Privacy policy
|
|
</details>
|
|
</p>
|
|
</details>
|
|
<div id="filesBox" class="dropzone">
|
|
<div class="dz-message" data-dz-message>
|
|
<span>Drag and drop a folder or click to share a file.<br>
|
|
<p><small></small></p>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div id="console" class="dropzone hide">
|
|
<div id="consoleHeader"></div>
|
|
<details>
|
|
<summary>Files served</summary>
|
|
<div id="fileList"></div>
|
|
</details>
|
|
<h3>Console:</h3>
|
|
<textarea id="consoleText" readonly></textarea>
|
|
</div>
|
|
<footer>
|
|
<!-- <p align="center" style="margin-bottom:0">
|
|
<img src="/static/logo47.png" style="max-width: 100px">
|
|
</p>
|
|
-->
|
|
<p align="center">Made by <a href="https://github.com/schollz">schollz</a>, source available on <a href="https://github.com/schollz/hostyoself">Github</a>.</p>
|
|
</footer>
|
|
</main>
|
|
<script src="/static/dropzone.js"></script>
|
|
<script>
|
|
var files = [];
|
|
var isConnected = false;
|
|
var relativeDirectory = "";
|
|
|
|
function consoleLog(s) {
|
|
console.log(s);
|
|
if (typeof s === 'object') {
|
|
s = JSON.stringify(s);
|
|
}
|
|
|
|
if (!(s.startsWith("[debug]"))) {
|
|
document.getElementById("consoleText").value = document.getElementById("consoleText").value + s + "\n";
|
|
document.getElementById("consoleText").scrollTop = document.getElementById("consoleText").scrollHeight;
|
|
}
|
|
}
|
|
|
|
function humanFileSize(bytes, si) {
|
|
var thresh = si ? 1000 : 1024;
|
|
if (Math.abs(bytes) < thresh) {
|
|
return bytes + ' B';
|
|
}
|
|
var units = si ? ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] : ['KiB', 'MiB', 'GiB', 'TiB', 'PiB',
|
|
'EiB', 'ZiB', 'YiB'
|
|
];
|
|
var u = -1;
|
|
do {
|
|
bytes /= thresh;
|
|
++u;
|
|
} while (Math.abs(bytes) >= thresh && u < units.length - 1);
|
|
return bytes.toFixed(1) + ' ' + units[u];
|
|
}
|
|
|
|
var Name = "";
|
|
var filesize = 0;
|
|
|
|
(function(Dropzone) {
|
|
Dropzone.autoDiscover = false;
|
|
|
|
let drop = new Dropzone('div#filesBox', {
|
|
maxFiles: 1000,
|
|
url: '/',
|
|
method: 'post',
|
|
createImageThumbnails: false,
|
|
previewTemplate: "<div id='preview' class='.dropzone-previews'></div>",
|
|
autoProcessQueue: false,
|
|
});
|
|
|
|
|
|
drop.on('addedfile', function(file) {
|
|
// console.log(file);
|
|
var domain = document.getElementById("inputDomain").value
|
|
files.push(file);
|
|
if (files.length == 1) {
|
|
relativeDirectory = file.webkitRelativePath.split("/")[0];
|
|
} else if (file.webkitRelativePath.split("/")[0] != relativeDirectory) {
|
|
relativeDirectory = "";
|
|
}
|
|
|
|
if (!(isConnected)) {
|
|
isConnected = true;
|
|
socketSend({
|
|
type: "domain",
|
|
message: domain,
|
|
key: document.getElementById("inputKey").value,
|
|
})
|
|
}
|
|
|
|
var filesString = "files are";
|
|
var domainName = `{{.PublicURL}}/${domain}/`;
|
|
if (files.length == 1) {
|
|
filesString = "file is"
|
|
domainName += `${file.name}`
|
|
}
|
|
|
|
document.getElementById("consoleHeader").innerHTML =
|
|
`<p>Your ${filesString} served at: <strong><a href="${domainName}" target="_blank">${domainName}</a></strong></p>`;
|
|
html = `<ul>`
|
|
for (i = 0; i < files.length; i++) {
|
|
var urlToFile = files[i].name;
|
|
if ('fullPath' in files[i]) {
|
|
urlToFile = files[i].fullPath;
|
|
}
|
|
html = html +
|
|
`<li><a href="/${domain}/${urlToFile}" target="_blank">/${urlToFile}</a></li>`
|
|
}
|
|
html = html + `</ul>`;
|
|
document.getElementById("fileList").innerHTML = html;
|
|
document.getElementById("filesBox").classList.add("hide");
|
|
document.getElementById("console").classList.remove("hide");
|
|
document.getElementById("inputKey").readOnly = "true";
|
|
document.getElementById("inputDomain").readOnly = "true";
|
|
})
|
|
|
|
})(Dropzone);
|
|
|
|
var socket; // websocket
|
|
|
|
|
|
/* websockets */
|
|
function socketSend(data) {
|
|
if (socket == null) {
|
|
return
|
|
}
|
|
if (socket.readyState != 1) {
|
|
return
|
|
}
|
|
jsonData = JSON.stringify(data);
|
|
socket.send(jsonData);
|
|
if (jsonData.length > 100) {
|
|
consoleLog("[debug] ws-> " + jsonData.substring(0, 99))
|
|
} else {
|
|
consoleLog("[debug] ws-> " + jsonData)
|
|
}
|
|
}
|
|
|
|
const socketMessageListener = (event) => {
|
|
var data = JSON.parse(event.data);
|
|
if (!('type' in data && 'message' in data)) {
|
|
consoleLog(`[warn] got bad data ${event.data}`);
|
|
return
|
|
}
|
|
console.log(data)
|
|
consoleLog(`[debug] ${data.message}`)
|
|
if (data.type == "get") {
|
|
var foundFile = false
|
|
var iToSend = 0
|
|
for (i = 0; i < files.length; i++) {
|
|
if (files[i].webkitRelativePath == data.message || files[i].name == data.message || files[i]
|
|
.webkitRelativePath == relativeDirectory + "/" + data.message) {
|
|
iToSend = i;
|
|
var reader = new FileReader();
|
|
reader.onload = function(theFile) {
|
|
socketSend({
|
|
type: "get",
|
|
message: reader.result,
|
|
success: true,
|
|
key: document.getElementById("inputKey").value,
|
|
})
|
|
consoleLog(
|
|
`${data.ip} [${(new Date()).toUTCString()}] /${data.message} 200 ${files[i].size}`
|
|
);
|
|
};
|
|
reader.readAsDataURL(files[i]);
|
|
foundFile = true
|
|
break
|
|
}
|
|
}
|
|
if (foundFile == false) {
|
|
socketSend({
|
|
type: "get",
|
|
message: "not found",
|
|
success: false,
|
|
key: document.getElementById("inputKey").value,
|
|
})
|
|
consoleLog(`${data.ip} [${(new Date()).toUTCString()}] /${data.message} 404`);
|
|
}
|
|
} else if (data.type == "domain") {
|
|
console.log(`[info] ${data.message}`);
|
|
} else if (data.type == "message") {
|
|
console.log(`[info] ${data.message}`);
|
|
} else {
|
|
consoleLog(`[debug] unknown`);
|
|
}
|
|
};
|
|
const socketOpenListener = (event) => {
|
|
consoleLog('[info] connected');
|
|
};
|
|
|
|
const socketCloseListener = (event) => {
|
|
if (socket) {
|
|
consoleLog('[info] disconnected');
|
|
}
|
|
var url = window.origin.replace("http", "ws") + '/ws';
|
|
try {
|
|
socket = new WebSocket(url);
|
|
socket.addEventListener('open', socketOpenListener);
|
|
socket.addEventListener('message', socketMessageListener);
|
|
socket.addEventListener('close', socketCloseListener);
|
|
} catch (err) {
|
|
consoleLog("[info] no connection available")
|
|
}
|
|
};
|
|
|
|
|
|
socketCloseListener();
|
|
</script>
|
|
</body>
|
|
|
|
</html> |