monofile/src/index.ts

162 lines
5.3 KiB
TypeScript

/*
i really should split this up into different modules
*/
import bodyParser from "body-parser"
import multer, {memoryStorage} from "multer"
import Discord, { IntentsBitField, Client } from "discord.js"
import express from "express"
import fs from "fs"
import axios, { AxiosResponse } from "axios"
import Files from "./lib/files"
require("dotenv").config()
const multerSetup = multer({storage:memoryStorage()})
let pkg = require(`${process.cwd()}/package.json`)
let app = express()
let config = require(`${process.cwd()}/config.json`)
app.use("/static",express.static("assets"))
app.use(bodyParser.text({limit:(config.maxDiscordFileSize*config.maxDiscordFiles)+1048576,type:["application/json","text/plain"]}))
// funcs
function ThrowError(response:express.Response,code:number,errorMessage:string) {
fs.readFile(__dirname+"/../pages/error.html",(err,buf) => {
if (err) {response.sendStatus(500);console.log(err);return}
response.status(code)
response.send(buf.toString().replace(/\$ErrorCode/g,code.toString()).replace(/\$ErrorMessage/g,errorMessage).replace(/\$Version/g,pkg.version))
})
}
// init data
if (!fs.existsSync(__dirname+"/../.data/")) fs.mkdirSync(__dirname+"/../.data/")
// discord
let client = new Client({intents:[
IntentsBitField.Flags.GuildMessages,
IntentsBitField.Flags.MessageContent
],rest:{timeout:config.requestTimeout}})
let files = new Files(client,config)
// routes (could probably make these use routers)
// index, clone
app.get("/", function(req,res) {
fs.readFile(__dirname+"/../pages/base.html",(err,buf) => {
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)
.replace(/\$Handler/g,"upload_file")
.replace(/\$UploadButtonText/g,"Upload file")
.replace(/\$otherPath/g,"/clone")
.replace(/\$otherText/g,"clone from url...")
.replace(/\$FileNum/g,Object.keys(files.files).length.toString())
)
})
})
app.get("/clone", function(req,res) {
fs.readFile(__dirname+"/../pages/base.html",(err,buf) => {
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)
.replace(/\$Handler/g,"clone_file")
.replace(/\$UploadButtonText/g,"Input a URL")
.replace(/\$otherPath/g,"/")
.replace(/\$otherText/g,"upload file...")
.replace(/\$FileNum/g,Object.keys(files.files).length.toString())
)
})
})
// upload handlers
app.post("/upload",multerSetup.single('file'),async (req,res) => {
if (req.file) {
try {
files.uploadFile({name:req.file.originalname,mime:req.file.mimetype,uploadId:req.header("monofile-upload-id")},req.file.buffer)
.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 {
res.status(400)
res.send("[err] bad request")
}
})
app.post("/clone",(req,res) => {
try {
let j = JSON.parse(req.body)
if (!j.url) {
res.status(400)
res.send("[err] invalid url")
}
axios.get(j.url,{responseType:"arraybuffer"}).then((data:AxiosResponse) => {
files.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) => {
console.log(err)
res.status(400)
res.send(`[err] failed to fetch data`)
})
} catch {
res.status(500)
res.send("[err] an error occured")
}
})
// serve files & download page
app.get("/download/:fileId",(req,res) => {
if (files.getFilePointer(req.params.fileId)) {
let file = files.getFilePointer(req.params.fileId)
fs.readFile(__dirname+"/../pages/download.html",(err,buf) => {
if (err) {res.sendStatus(500);console.log(err);return}
res.send(buf.toString().replace(/\$FileName/g,file.filename).replace(/\$FileId/g,req.params.fileId).replace(/\$Version/g,pkg.version))
})
} else {
ThrowError(res,404,"File not found.")
}
})
app.get("/file/:fileId",async (req,res) => {
files.readFileStream(req.params.fileId).then(f => {
res.setHeader("Content-Type",f.contentType)
res.status(200)
f.dataStream.pipe(res)
}).catch((err) => {
ThrowError(res,err.status,err.message)
})
})
app.get("*",(req,res) => {
ThrowError(res,404,"Page not found.")
})
app.get("/server",(req,res) => {
res.send(JSON.stringify({...config,version:pkg.version}))
})
// listen on 3000 or MONOFILE_PORT
app.listen(process.env.MONOFILE_PORT || 3000,function() {
console.log("Web OK!")
})
client.login(process.env.TOKEN)