this code actually sucks ass

god bless anyone who reads this
1.2.1 i'll need to clean this shit up lmao
This commit is contained in:
May 2022-12-29 17:12:44 -08:00
parent 0aad03c50e
commit 3ef9eeaf4c
8 changed files with 199 additions and 299 deletions

2
.vscode/tasks.json vendored
View file

@ -12,7 +12,7 @@
}, },
{ {
"type": "shell", "type": "shell",
"command":"npx tsc\nnode ./out/index.js\ndel ./server_out/* -Recurse", "command":"npx tsc\nnode ./out/index.js\ndel ./out/* -Recurse",
"group": { "group": {
"kind": "build", "kind": "build",
"isDefault": true "isDefault": true

View file

@ -17,10 +17,11 @@ TOKEN=KILL-YOURSELF.NOW
- [X] 1.1.2 fix file cloning with binary data - [X] 1.1.2 fix file cloning with binary data
- [X] 1.1.3 display current version on pages - [X] 1.1.3 display current version on pages
- [X] 1.1.4 serve /assets as static files & make /server endpoint - [X] 1.1.4 serve /assets as static files & make /server endpoint
- [ ] 1.2.0 add simple moderation tools - [X] 1.2.0 add file parameters section + custom ids
- [ ] 1.2.1 prevent cloning of local/private ip addresses - [ ] 1.2.1 clean up this shitty code
- [ ] 1.3.0 allow custom file ids - [ ] 1.2.2 add id locks, allowing you to set a key for a file that allows you to overwrite the file in the future
- [ ] 1.4.0 accounts??? maybe?? (this probably won't happen) - [ ] 1.2.3 prevent cloning of local/private ip addresses
- [ ] 1.3.0 add simple moderation tools
- [ ] 2.0.0 rewrite using theUnfunny's code as a base/rewrite using monofile-core - [ ] 2.0.0 rewrite using theUnfunny's code as a base/rewrite using monofile-core
also todo: monofile-core (written in eris) also todo: monofile-core (written in eris)

View file

@ -0,0 +1,30 @@
document.getElementById("uploadButton").addEventListener("click",() => {
let ask = prompt("Input a URL to clone.")
if (ask) {
let opt = getOptionsForUploading()
updateBtnTxt("Requesting clone. Please wait.")
let xmlhttp = new XMLHttpRequest()
xmlhttp.addEventListener("error",function(e) {
updateBtnTxt(`Upload failed.<br/>${e.toString()}`)
console.error(e)
})
xmlhttp.addEventListener("load",function() {
if (xmlhttp.status == 200) {
document.getElementById("CopyTB").value = `https://${location.hostname}/download/${xmlhttp.responseText}`
updateBtnTxt(`Upload complete.<br/><a style="color:blue;font-family:monospace;" href="javascript:document.getElementById('CopyTB').focus();document.getElementById('CopyTB').select();document.execCommand('copy');document.getElementById('CopyTB').blur();">Copy URL</a> <a style="color:blue;font-family:monospace;" href="javascript:prompt('This is your download URL.', document.getElementById('CopyTB').value);null">View URL</a>`)
} else {
updateBtnTxt(`Upload failed.<br/>${xmlhttp.responseText}`)
}
})
xmlhttp.open("POST","/clone")
xmlhttp.send(JSON.stringify({
url: ask,
...opt
}))
}
})

View file

@ -0,0 +1,37 @@
let FileUpload = document.createElement("input")
FileUpload.setAttribute("type","file")
document.getElementById("uploadButton").addEventListener("click",() => FileUpload.click())
FileUpload.addEventListener("input",() => {
if (FileUpload.files[0]) {
let opt = getOptionsForUploading()
let file = FileUpload.files[0]
updateBtnTxt("Uploading file. This may take a while, so stay put.")
let xmlhttp = new XMLHttpRequest()
xmlhttp.addEventListener("error",function(e) {
updateBtnTxt(`Upload failed.<br/>${e.toString()}`)
console.error(e)
})
xmlhttp.addEventListener("load",function() {
if (xmlhttp.status == 200) {
document.getElementById("CopyTB").value = `https://${location.hostname}/download/${xmlhttp.responseText}`
updateBtnTxt(`Upload complete.<br/><a style="color:blue;font-family:monospace;" href="javascript:document.getElementById('CopyTB').focus();document.getElementById('CopyTB').select();document.execCommand('copy');document.getElementById('CopyTB').blur();">Copy URL</a> <a style="color:blue;font-family:monospace;" href="javascript:prompt('This is your download URL.', document.getElementById('CopyTB').value);null">View URL</a>`)
} else {
updateBtnTxt(`Upload failed.<br/>${xmlhttp.responseText}`)
}
})
let fd = new FormData()
fd.append('file',file)
xmlhttp.open("POST","/upload")
xmlhttp.setRequestHeader("monofile-upload-id",opt.uploadId)
xmlhttp.send(fd)
}
})

View file

@ -37,31 +37,70 @@
left:50%; left:50%;
transform:translate(-50%,0); transform:translate(-50%,0);
} }
#uploadButton {
width:100%; button {
height:100%;
background-color: #66AAFF;
color:black; color:black;
font-weight:bold; font-weight:bold;
border:none; border:none;
position:absolute; position:absolute;
left:0px;
top:0px;
font-size:20px; font-size:20px;
} }
#uploadButton:hover {
button:hover {
border: 1px solid gray; border: 1px solid gray;
} }
#uploadButton {
width:calc( 100% - 50px );
height:100%;
background-color: #66AAFF;
left:0px;
top:0px;
}
#optionsButton {
width:50px;
height:100%;
background-color: #AAAAAA;
left:calc( 100% - 50px );
top:0px;
font-size:15px;
}
.note { .note {
padding:5px; padding:5px;
position:relative; position:relative;
width:calc( 100% - 60px ); width:calc( 100% - 62px );
left:25px; left:25px;
border:1px solid #AAAAAAFF; border:1px solid #AAAAAAFF;
border-radius: 8px; border-radius: 8px;
background-color: #AAAAAA66; background-color: #AAAAAA66;
} }
/* this is horrible css lmao */
#options > input {
font-family:monospace;
width:250px;
font-size:14px;
border: 1px solid #777777;
background-color:#AAAAAA;
outline:none;
text-align:center;
}
#options {
height:30px;
display:none;
}
#customId {
position:absolute;
left:50%;
top:50%;
transform:translate(-50%,-50%)
}
@media only screen and (max-width: 450px) { @media only screen and (max-width: 450px) {
#content { #content {
position:fixed; position:fixed;
@ -92,53 +131,41 @@
</ul> </ul>
</div> </div>
<div style="width:100%;height:25px"></div> <div style="width:100%;height:25px"></div>
<div id="btnContainer"><button id="uploadButton">Upload file</button></div> <div id="btnContainer">
<button id="uploadButton">$UploadButtonText</button>
<button id="optionsButton">• • •</button>
</div>
<div id="options" class="note" style="border-radius:0px;">
<input id="customId" autocomplete="off" placeholder = "custom id (30char, no spaces)" maxlength="30">
</div>
<p style="font-family:monospace;position:relative;width:calc( 100% - 50px );left:25px;text-align: center;"> <p style="font-family:monospace;position:relative;width:calc( 100% - 50px );left:25px;text-align: center;">
Max filesize on instance: $MaxInstanceFilesize Max filesize on instance: $MaxInstanceFilesize
</p> </p>
<p style="font-family:monospace;position:relative;width:calc( 100% - 50px );left:25px;text-align: center;font-size:12px;color:gray;">made by nbitzz — <a style="color:gray;font-family:monospace;font-size:12px;" href="https://github.com/nbitzz/monofile">github</a><a style="color:gray;font-family:monospace;font-size:12px;" href="/clone">clone from url...</a></p> <p style="font-family:monospace;position:relative;width:calc( 100% - 50px );left:25px;text-align: center;font-size:12px;color:gray;">made by nbitzz — <a style="color:gray;font-family:monospace;font-size:12px;" href="https://github.com/nbitzz/monofile">github</a><a style="color:gray;font-family:monospace;font-size:12px;" href="$otherPath">$otherText</a></p>
<div style="width:100%;height:25px"></div> <div style="width:100%;height:25px"></div>
</div> </div>
</body> </body>
<!-- bad thing to do but i'm being rushed -->
<script> <script>
let updateBtnTxt = (btntxt) => {document.getElementById("btnContainer").innerHTML = `<div class="note" style="width:calc( 100% - 10px );height:calc( 100% - 10px );position:absolute;left:0px;top:0px;"><p style="font-family:monospace;position:relative;width:calc( 100% - 50px );left:25px;text-align: center;">${btntxt}</p></div>`} let uploading = false
const updateBtnTxt = (btntxt) => {document.getElementById("btnContainer").innerHTML = `<div class="note" style="width:calc( 100% - 10px );height:calc( 100% - 10px );position:absolute;left:0px;top:0px;"><p style="font-family:monospace;position:relative;width:calc( 100% - 50px );left:25px;text-align: center;">${btntxt}</p></div>`}
let FileUpload = document.createElement("input") const getOptionsForUploading = () => {
FileUpload.setAttribute("type","file") uploading = true
document.getElementById("options").style.display = "none"
document.getElementById("uploadButton").addEventListener("click",() => FileUpload.click())
return {
FileUpload.addEventListener("input",() => { uploadId: document.getElementById("customId").value
if (FileUpload.files[0]) {
let file = FileUpload.files[0]
updateBtnTxt("Uploading file. This may take a while, so stay put.")
let xmlhttp = new XMLHttpRequest()
xmlhttp.addEventListener("error",function(e) {
updateBtnTxt(`Upload failed.<br/>${e.toString()}`)
console.error(e)
})
xmlhttp.addEventListener("load",function() {
if (xmlhttp.status == 200) {
document.getElementById("CopyTB").value = `https://${location.hostname}/download/${xmlhttp.responseText}`
updateBtnTxt(`Upload complete.<br/><a style="color:blue;font-family:monospace;" href="javascript:document.getElementById('CopyTB').focus();document.getElementById('CopyTB').select();document.execCommand('copy');document.getElementById('CopyTB').blur();">Copy URL</a> <a style="color:blue;font-family:monospace;" href="javascript:prompt('This is your download URL.', document.getElementById('CopyTB').value);null">View URL</a>`)
} else {
updateBtnTxt(`Upload failed.<br/>${xmlhttp.responseText}`)
}
})
let fd = new FormData()
fd.append('file',file)
xmlhttp.open("POST","/upload")
xmlhttp.send(fd)
} }
}
document.getElementById("optionsButton").addEventListener("click",() => {
let display = document.getElementById("options").style.display
if (!uploading) document.getElementById("options").style.display = !display || display == "none" ? "block" : "none"
}) })
</script> </script>
<script src="/static/script/$Handler.js"></script>
</html> </html>

View file

@ -1,135 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>monofile</title>
<meta name="og:site_name" content="$Version">
<meta name="application-name" content="monofile">
<meta name="description" content="Discord-based filesharing">
<style>
* {
font-family: sans-serif;
}
h1,h2 {
text-align:center;
}
#content {
position:fixed;
left:50%;
top:50%;
transform:translate(-50%,-50%);
background-color:white;
width:450px;
/*height:100%;*/
}
body {
background-color: darkgray;
}
#btnContainer {
width:calc( 100% - 50px );
height:50px;
position:relative;
left:50%;
transform:translate(-50%,0);
}
#uploadButton {
width:100%;
height:100%;
background-color: #66AAFF;
color:black;
font-weight:bold;
border:none;
position:absolute;
left:0px;
top:0px;
font-size:20px;
}
#uploadButton:hover {
border: 1px solid gray;
}
.note {
padding:5px;
position:relative;
width:calc( 100% - 60px );
left:25px;
border:1px solid #AAAAAAFF;
border-radius: 8px;
background-color: #AAAAAA66;
}
@media only screen and (max-width: 450px) {
#content {
position:fixed;
left:0%;
top:0%;
transform:translate(0%,0%);
background-color:white;
width:100%;
height:100%;
}
}
</style>
</head>
<body>
<input type="text" id="CopyTB" value="" readonly style="opacity:0;width:10px;height:0%;">
<div id="content">
<h1>
monofile<span style="font-style:italic;font-weight:bold;font-size:16px;color:#999999"> $Version</span>
</h1>
<h2><em>Discord-based file sharing</em></h2>
<div class="note" style="border-color:#FFAAAAFF;background-color:#FFAAAA66">
Before sharing files, please remember:
<ul>
<li>Do NOT share sensitive information via monofile</li>
<li>Do NOT share illegal content via monofile</li>
<li>The owner of this instance reserves the right to remove your files</li>
</ul>
</div>
<div style="width:100%;height:25px"></div>
<div id="btnContainer"><button id="uploadButton">Input a URL</button></div>
<p style="font-family:monospace;position:relative;width:calc( 100% - 50px );left:25px;text-align: center;">
Max filesize on instance: $MaxInstanceFilesize
</p>
<p style="font-family:monospace;position:relative;width:calc( 100% - 50px );left:25px;text-align: center;font-size:12px;color:gray;">made by nbitzz — <a style="color:gray;font-family:monospace;font-size:12px;" href="https://github.com/nbitzz/monofile">github</a><a style="color:gray;font-family:monospace;font-size:12px;" href="/">upload a file...</a></p>
<div style="width:100%;height:25px"></div>
</div>
</body>
<script>
let updateBtnTxt = (btntxt) => {document.getElementById("btnContainer").innerHTML = `<div class="note" style="width:calc( 100% - 10px );height:calc( 100% - 10px );position:absolute;left:0px;top:0px;"><p style="font-family:monospace;position:relative;width:calc( 100% - 50px );left:25px;text-align: center;">${btntxt}</p></div>`}
document.getElementById("uploadButton").addEventListener("click",() => {
let ask = prompt("Input a URL to clone.")
if (ask) {
updateBtnTxt("Requesting clone. Please wait.")
let xmlhttp = new XMLHttpRequest()
xmlhttp.addEventListener("error",function(e) {
updateBtnTxt(`Upload failed.<br/>${e.toString()}`)
console.error(e)
})
xmlhttp.addEventListener("load",function() {
if (xmlhttp.status == 200) {
document.getElementById("CopyTB").value = `https://${location.hostname}/download/${xmlhttp.responseText}`
updateBtnTxt(`Upload complete.<br/><a style="color:blue;font-family:monospace;" href="javascript:document.getElementById('CopyTB').focus();document.getElementById('CopyTB').select();document.execCommand('copy');document.getElementById('CopyTB').blur();">Copy URL</a> <a style="color:blue;font-family:monospace;" href="javascript:prompt('This is your download URL.', document.getElementById('CopyTB').value);null">View URL</a>`)
} else {
updateBtnTxt(`Upload failed.<br/>${xmlhttp.responseText}`)
}
})
xmlhttp.open("POST","/clone")
xmlhttp.send(ask)
}
})
</script>
</html>

View file

@ -1,3 +1,7 @@
/*
i really should split this up into different modules
*/
import bodyParser from "body-parser" import bodyParser from "body-parser"
import multer, {memoryStorage} from "multer" import multer, {memoryStorage} from "multer"
import Discord, { IntentsBitField, Client } from "discord.js" import Discord, { IntentsBitField, Client } from "discord.js"
@ -55,9 +59,11 @@ let uploadFile = (settings:FileUploadSettings,fBuffer:Buffer) => {
return new Promise<string>(async (resolve,reject) => { return new Promise<string>(async (resolve,reject) => {
if (!settings.name || !settings.mime) {reject({status:400,message:"missing name/mime"});return} if (!settings.name || !settings.mime) {reject({status:400,message:"missing name/mime"});return}
let uploadId = settings.uploadId || Math.random().toString().slice(2) let uploadId = (settings.uploadId || Math.random().toString().slice(2)).toString();
if ((uploadId.match(/[A-Za-z0-9_\-]+/)||[])[0] != uploadId || uploadId.length > 30) {reject({status:400,message:"invalid id"});return}
if (files[uploadId]) {reject({status:500,message:"please try again"});return} if (files[uploadId]) {reject({status:400,message:"a file with this id already exists"});return}
if (settings.name.length > 128) {reject({status:400,message:"name too long"}); return} if (settings.name.length > 128) {reject({status:400,message:"name too long"}); return}
if (settings.name.length > 128) {reject({status:400,message:"mime too long"}); return} if (settings.name.length > 128) {reject({status:400,message:"mime too long"}); return}
@ -104,24 +110,45 @@ let uploadFile = (settings:FileUploadSettings,fBuffer:Buffer) => {
} }
app.get("/", function(req,res) { app.get("/", function(req,res) {
fs.readFile(__dirname+"/../pages/upload.html",(err,buf) => { fs.readFile(__dirname+"/../pages/base.html",(err,buf) => {
if (err) {res.sendStatus(500);console.log(err);return} if (err) {res.sendStatus(500);console.log(err);return}
res.send(buf.toString().replace("$MaxInstanceFilesize",`${(config.maxDiscordFileSize*config.maxDiscordFiles)/1048576}MB`).replace(/\$Version/g,pkg.version)) res.send(
buf.toString()
.replace("$MaxInstanceFilesize",`${(config.maxDiscordFileSize*config.maxDiscordFiles)/1048576}MB`)
.replace(/\$Version/g,pkg.version)
.replace(/\$Handler/g,"upload_file")
.replace(/\$UploadButtonText/g,"Upload file")
.replace(/\$otherPath/g,"/clone")
.replace(/\$otherText/g,"clone from url...")
)
}) })
}) })
app.get("/clone", function(req,res) { app.get("/clone", function(req,res) {
fs.readFile(__dirname+"/../pages/clone.html",(err,buf) => { fs.readFile(__dirname+"/../pages/base.html",(err,buf) => {
if (err) {res.sendStatus(500);console.log(err);return} if (err) {res.sendStatus(500);console.log(err);return}
res.send(buf.toString().replace("$MaxInstanceFilesize",`${(config.maxDiscordFileSize*config.maxDiscordFiles)/1048576}MB`).replace(/\$Version/g,pkg.version)) res.send(
buf.toString()
.replace("$MaxInstanceFilesize",`${(config.maxDiscordFileSize*config.maxDiscordFiles)/1048576}MB`)
.replace(/\$Version/g,pkg.version)
.replace(/\$Handler/g,"clone_file")
.replace(/\$UploadButtonText/g,"Input a URL")
.replace(/\$otherPath/g,"/")
.replace(/\$otherText/g,"upload file...")
)
}) })
}) })
app.post("/upload",multerSetup.single('file'),async (req,res) => { app.post("/upload",multerSetup.single('file'),async (req,res) => {
if (req.file) { if (req.file) {
uploadFile({name:req.file.originalname,mime:req.file.mimetype},req.file.buffer) try {
.then((uID) => res.send(uID)) uploadFile({name:req.file.originalname,mime:req.file.mimetype,uploadId:req.header("monofile-upload-id")},req.file.buffer)
.catch((stat) => {res.status(stat.status);res.send(`[err] ${stat.message}`)}) .then((uID) => res.send(uID))
.catch((stat) => {res.status(stat.status);res.send(`[err] ${stat.message}`)})
} catch {
res.status(400)
res.send("[err] bad request")
}
} else { } else {
res.status(400) res.status(400)
res.send("[err] bad request") res.send("[err] bad request")
@ -129,14 +156,24 @@ app.post("/upload",multerSetup.single('file'),async (req,res) => {
}) })
app.post("/clone",(req,res) => { app.post("/clone",(req,res) => {
axios.get(req.body,{responseType:"arraybuffer"}).then((data:AxiosResponse) => { try {
uploadFile({name:req.body.split("/")[req.body.split("/").length-1] || "generic",mime:data.headers["content-type"]},Buffer.from(data.data)) let j = JSON.parse(req.body)
.then((uID) => res.send(uID)) if (!j.url) {
.catch((stat) => {res.status(stat.status);res.send(`[err] ${stat.message}`)}) res.status(400)
}).catch((err) => { res.send("[err] invalid url")
res.status(400) }
res.send(`[err] failed to fetch data`) axios.get(j.url,{responseType:"arraybuffer"}).then((data:AxiosResponse) => {
}) uploadFile({name:req.body.split("/")[req.body.split("/").length-1] || "generic",mime:data.headers["content-type"],uploadId:j.uploadId},Buffer.from(data.data))
.then((uID) => res.send(uID))
.catch((stat) => {res.status(stat.status);res.send(`[err] ${stat.message}`)})
}).catch((err) => {
res.status(400)
res.send(`[err] failed to fetch data`)
})
} catch {
res.status(500)
res.send("[err] an error occured")
}
}) })
app.get("/download/:fileId",(req,res) => { app.get("/download/:fileId",(req,res) => {
@ -192,8 +229,6 @@ app.get("*",(req,res) => {
ThrowError(res,404,"Page not found.") ThrowError(res,404,"Page not found.")
}) })
client.on("ready",() => { client.on("ready",() => {
console.log("Discord OK!") console.log("Discord OK!")

View file

@ -1,95 +0,0 @@
/*
hi, this is a port of nbitzz/theUnfunny@fb57d8d65f
so the code is pretty bad
(but let's be real, all of my code is bad lmao)
*/
import { Client, SlashCommandBuilder, Routes, ChatInputCommandInteraction } from "discord.js";
export class SlashCommand {
readonly builder: SlashCommandBuilder|Omit<SlashCommandBuilder, "addSubcommand" | "addSubcommandGroup">|Omit<SlashCommandBuilder, "addBooleanOption" | "addUserOption" | "addChannelOption" | "addRoleOption" | "addAttachmentOption" | "addMentionableOption" | "addStringOption" | "addIntegerOption" | "addNumberOption">
readonly assetPath: string
readonly type:string = "SCM.SlashCommand"
action?: (interaction:ChatInputCommandInteraction) => Promise<any>
ephmeralReply?:boolean
allowInDMs?:boolean
constructor(command:SlashCommandBuilder|Omit<SlashCommandBuilder, "addSubcommand" | "addSubcommandGroup">|Omit<SlashCommandBuilder, "addBooleanOption" | "addUserOption" | "addChannelOption" | "addRoleOption" | "addAttachmentOption" | "addMentionableOption" | "addStringOption" | "addIntegerOption" | "addNumberOption">) {
this.builder = command
this.assetPath = `${process.cwd()}/assets/commands/${command.name}/`
}
}
export class SlashCommandManager {
private commands:SlashCommand[] = []
private readonly client: Client
readonly type:string = "SCM.SlashCommandManager"
constructor(client: Client) {
this.client = client
}
/**
* @description Register slash commands
*/
register() {
return new Promise(async (resolve,reject) => {
console.log("[SlashCommandManager] Registering commands...")
if (this.client.user) {
this.commands.forEach((e) => {
e.builder.setDMPermission(e.allowInDMs)
})
let result = await this.client.rest.put(
Routes.applicationCommands(this.client.user.id),
{ body: this.commands.map(e => e.builder.toJSON()) }
)
console.log(`[SlashCommandManager] Slash commands registered.`)
resolve(result)
} else {
console.error("[SlashCommandManager] Not logged in")
reject("Not logged in")
}
})
}
/**
* @description Process a command
*/
call(int:ChatInputCommandInteraction) {
let command = this.commands.find(e => e.builder.name == int.commandName)
if (command && command.action) {
int.deferReply({
ephemeral:command.ephmeralReply
}).then(() => {
if (command && command.action) {
command.action(int).catch((err) => {
// error handling
int.editReply({
embeds:[
{description:"Oops, something broke. Maybe try that again?",color:0xff0000}
]
})
console.error(err)
})
}
})
}
}
/**
* @description Add a slash command to the manager
*/
add(command:SlashCommand) {
this.commands.push(command)
}
}
// type guard
export let isSlashCommand = (sc: any): sc is SlashCommand => {return sc&&sc.type=="SCM.SlashCommand"}