upload
This commit is contained in:
commit
ed8c61ea95
7
.dockerignore
Normal file
7
.dockerignore
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
readme.md
|
||||||
|
build
|
||||||
|
.env
|
||||||
|
./uploads
|
||||||
|
./tmp
|
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
uploads/*
|
||||||
|
tmp/*
|
||||||
|
.env
|
||||||
|
site
|
||||||
|
site/*
|
10
Dockerfile
Normal file
10
Dockerfile
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
FROM denoland/deno:latest as base
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY . ./
|
||||||
|
|
||||||
|
RUN deno cache server.ts
|
||||||
|
|
||||||
|
CMD ["run", "--allow-net", "--allow-write", "--allow-read", "--allow-env", "server.ts"]
|
||||||
|
|
19
fileTypes.ts
Normal file
19
fileTypes.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
|
||||||
|
const allowedFiletypes = new Map()
|
||||||
|
allowedFiletypes.set("image/jpeg", ".jpg")
|
||||||
|
allowedFiletypes.set("image/png", ".png")
|
||||||
|
allowedFiletypes.set("image/avif", ".avif")
|
||||||
|
allowedFiletypes.set("image/gif", ".gif")
|
||||||
|
allowedFiletypes.set("image/webp", ".webp")
|
||||||
|
allowedFiletypes.set("image/apng", ".apng")
|
||||||
|
allowedFiletypes.set("image/bmp", ".bmp")
|
||||||
|
allowedFiletypes.set("image/svg+xml", ".svg")
|
||||||
|
allowedFiletypes.set("image/tiff", ".tiff")
|
||||||
|
allowedFiletypes.set("video/webm", ".webm")
|
||||||
|
allowedFiletypes.set("video/mp4",".mp4")
|
||||||
|
allowedFiletypes.set("video/mpeg",".mpeg")
|
||||||
|
allowedFiletypes.set("video/x-msvideo", ".avi")
|
||||||
|
allowedFiletypes.set("application/epub+zip", ".epub")
|
||||||
|
allowedFiletypes.set("application/pdf", ".pdf")
|
||||||
|
|
||||||
|
export default allowedFiletypes
|
16
getFileHash.ts
Normal file
16
getFileHash.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { crypto, toHashString } from 'https://deno.land/std@0.176.0/crypto/mod.ts';
|
||||||
|
|
||||||
|
const getFileBuffer = (filePath: string) => {
|
||||||
|
const file = Deno.openSync(filePath);
|
||||||
|
const buf = new Uint8Array(file.statSync().size);
|
||||||
|
file.readSync(buf);
|
||||||
|
file.close();
|
||||||
|
return buf;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getBuffer = (data: BufferSource) => toHashString(crypto.subtle.digestSync('SHA-256', data));
|
||||||
|
|
||||||
|
const getFileHash = (filePath: string) => getBuffer(getFileBuffer(filePath));
|
||||||
|
|
||||||
|
|
||||||
|
export default getFileHash
|
10
mongodb.ts
Normal file
10
mongodb.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { Bson, MongoClient } from "https://deno.land/x/mongo@v0.31.1/mod.ts";
|
||||||
|
import "https://deno.land/std@0.178.0/dotenv/load.ts";
|
||||||
|
|
||||||
|
const client = new MongoClient();
|
||||||
|
|
||||||
|
await client.connect(Deno.env.get("MONGOURL"));
|
||||||
|
|
||||||
|
const db = client.database('hajabot')
|
||||||
|
|
||||||
|
export default db;
|
219
routes.ts
Normal file
219
routes.ts
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
import { RouterContext } from "https://deno.land/x/oak/mod.ts"
|
||||||
|
import "https://deno.land/std@0.178.0/dotenv/load.ts";
|
||||||
|
import { writeFileSync, copy } from "https://deno.land/std/fs/mod.ts";
|
||||||
|
import { renderFileToString } from "https://deno.land/x/dejs/mod.ts";
|
||||||
|
|
||||||
|
import getFileHash from './getFileHash.ts';
|
||||||
|
import allowedFiletypes from './fileTypes.ts';
|
||||||
|
import db from './mongodb.ts';
|
||||||
|
const imageCollection = db.collection('images');
|
||||||
|
const userTable = db.collection('users');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const icons = async (ctx: RouterContext, next: any) => {
|
||||||
|
const identifier = ctx.params.filename
|
||||||
|
|
||||||
|
console.log(["t_192.png", "t_48.png", "t_96.png"].indexOf(identifier))
|
||||||
|
|
||||||
|
if (["t_192.png", "t_48.png", "t_96.png"].indexOf(identifier) != -1){
|
||||||
|
const img = await Deno.readFile(`${Deno.cwd()}/site/i/${ctx.params.filename}`);
|
||||||
|
ctx.response.type = "image/png";
|
||||||
|
ctx.response.body = img;
|
||||||
|
} else {
|
||||||
|
next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const announce = async (ctx: RouterContext) => {
|
||||||
|
const body = await ctx.request.body({ type: 'json'}).value;
|
||||||
|
|
||||||
|
const count = await imageCollection.findOne({hash:body.hash});
|
||||||
|
|
||||||
|
if(body.size >= 50000000 && body.usercode == "anonymous"){
|
||||||
|
ctx.response.status = 403
|
||||||
|
}
|
||||||
|
else if(count){
|
||||||
|
ctx.response.body = {file: count.filename};
|
||||||
|
ctx.response.status = 409
|
||||||
|
}else{
|
||||||
|
if(body.size >= 50000000){
|
||||||
|
const users = await userTable.findOne({token:body.usercode});
|
||||||
|
if(users){
|
||||||
|
ctx.response.status = 200
|
||||||
|
}else{
|
||||||
|
ctx.response.status = 403
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
ctx.response.status = 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const chunks = async (ctx: RouterContext) => {
|
||||||
|
const hash = ctx.request.headers.get("file-hash");
|
||||||
|
const number = ctx.request.headers.get("file-hash");
|
||||||
|
|
||||||
|
const body = await ctx.request.body({ type: 'form-data'});
|
||||||
|
const formData = await body.value.read();
|
||||||
|
|
||||||
|
const size = ctx.request.headers.get("content-range").split("/")[1]
|
||||||
|
const data = await Deno.readFile(formData.files[0].filename)
|
||||||
|
|
||||||
|
Deno.writeFileSync(`${Deno.env.get("TMPPATH")}${hash}.${ctx.request.headers.get("content-number")}.temp`, data)
|
||||||
|
|
||||||
|
ctx.response.status = 200
|
||||||
|
}
|
||||||
|
|
||||||
|
export const mergeChunks = async (ctx: RouterContext) => {
|
||||||
|
const body = await ctx.request.body({ type: 'json'}).value;
|
||||||
|
|
||||||
|
const ts = new Date();
|
||||||
|
let newname = `${ts.valueOf()}${allowedFiletypes.get(body.type)}`
|
||||||
|
|
||||||
|
let files = []
|
||||||
|
for await (const dirEntry of Deno.readDir(Deno.env.get("TMPPATH"))) {
|
||||||
|
if(dirEntry.name.includes(body.hash) && dirEntry.isFile){
|
||||||
|
files.push(dirEntry.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const tempFile = await Deno.makeTempFile();
|
||||||
|
|
||||||
|
for (let i = 0; i < files.length; i++) {
|
||||||
|
let file = files.find(elem => elem.includes(`.${i}.temp`))
|
||||||
|
const data = await Deno.readFileSync(`${Deno.env.get("TMPPATH")}${file}`)
|
||||||
|
|
||||||
|
await Deno.writeFileSync(tempFile, data, {"append": true})
|
||||||
|
Deno.remove(`${Deno.env.get("TMPPATH")}${file}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
await copy(tempFile, `${Deno.env.get("IMAGEPATH")}${newname}`)
|
||||||
|
|
||||||
|
const id = await imageCollection.insertOne({
|
||||||
|
hash:body.hash,
|
||||||
|
mime:body.type,
|
||||||
|
filename:newname,
|
||||||
|
usercode:body.usercode,
|
||||||
|
time:ts
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const newhash = await getFileHash(tempFile)
|
||||||
|
if(body.hash == newhash){
|
||||||
|
ctx.response.body = {"file":newname}
|
||||||
|
ctx.response.status = 201
|
||||||
|
}else{
|
||||||
|
ctx.response.status = 418
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getImage = async (ctx: RouterContext) => {
|
||||||
|
const identifier = ctx.params.filename;
|
||||||
|
|
||||||
|
const info = await imageCollection.findOne({filename:identifier});
|
||||||
|
|
||||||
|
if(!info){
|
||||||
|
ctx.response.status = 404;
|
||||||
|
ctx.response.body = {message: 'image does not exist'};
|
||||||
|
return
|
||||||
|
}else{
|
||||||
|
const img = await Deno.readFile(`${Deno.env.get("IMAGEPATH")}${identifier}`);
|
||||||
|
|
||||||
|
ctx.response.type = info.mime;
|
||||||
|
ctx.response.body = img;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export const deleteImage = async (ctx: RouterContext) => {
|
||||||
|
const identifier = ctx.params.filename;
|
||||||
|
|
||||||
|
const count = await imageCollection.findOne({filename:identifier});
|
||||||
|
|
||||||
|
if(!count){
|
||||||
|
ctx.response.status = 404;
|
||||||
|
ctx.response.body = {message: 'image does not exist'};
|
||||||
|
return
|
||||||
|
}else{
|
||||||
|
console.log(count)
|
||||||
|
|
||||||
|
if(ctx.request.headers.get("usercode")==count.usercode){
|
||||||
|
const deleted = await imageCollection.deleteOne({_id:count._id})
|
||||||
|
if(!deleted){
|
||||||
|
ctx.response.status = 500;
|
||||||
|
}else{
|
||||||
|
await Deno.remove(`${Deno.env.get("IMAGEPATH")}${count.filename}`);
|
||||||
|
ctx.response.status = 204
|
||||||
|
}
|
||||||
|
|
||||||
|
}else{
|
||||||
|
ctx.response.body = {message: 'Unauthorized'};
|
||||||
|
ctx.response.status = 401
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const uploadImage = async (ctx: RouterContext) => {
|
||||||
|
const body = await ctx.request.body({ type: 'form-data'}).value;
|
||||||
|
const formData = await body.read();
|
||||||
|
|
||||||
|
const usercode = ctx.request.headers.get("usercode") ? ctx.request.headers.get("usercode") : "anonymous";
|
||||||
|
|
||||||
|
let promises = [];
|
||||||
|
let files = [];
|
||||||
|
|
||||||
|
formData.files.forEach(file => promises.push(new Promise(async (resolve, reject)=>{
|
||||||
|
if(!allowedFiletypes.get(file.contentType)){
|
||||||
|
reject("rejected filetype")
|
||||||
|
}else{
|
||||||
|
const timestamp = new Date()
|
||||||
|
const hash = getFileHash(file.filename)
|
||||||
|
|
||||||
|
const count = await imageCollection.findOne({hash:hash});
|
||||||
|
|
||||||
|
if(!count){
|
||||||
|
const newFilename = `${timestamp.valueOf()}${allowedFiletypes.get(file.contentType)}`
|
||||||
|
copy(file.filename, `${Deno.env.get("IMAGEPATH")}${newFilename}`, {overwrite: true})
|
||||||
|
const id = await imageCollection.insertOne({
|
||||||
|
hash:hash,
|
||||||
|
mime:file.contentType,
|
||||||
|
filename:newFilename,
|
||||||
|
usercode:usercode,
|
||||||
|
time:timestamp
|
||||||
|
});
|
||||||
|
|
||||||
|
files.push(newFilename)
|
||||||
|
resolve(newFilename)
|
||||||
|
}else{
|
||||||
|
files.push(count.filename)
|
||||||
|
resolve(count.filename)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})))
|
||||||
|
|
||||||
|
const result = await Promise.allSettled(promises)
|
||||||
|
|
||||||
|
const rejected = await result.filter(obj => Object.keys(obj).some(key => obj[key].includes("rejected")))
|
||||||
|
|
||||||
|
if(rejected.length >= 1){
|
||||||
|
ctx.response.status = 405;
|
||||||
|
ctx.response.body = {message:rejected[0].reason}
|
||||||
|
}else{
|
||||||
|
if(files.length > 1){
|
||||||
|
ctx.response.body = files
|
||||||
|
}else{
|
||||||
|
ctx.response.body = {"img":files[0]}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.response.status = 201
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
34
server.ts
Normal file
34
server.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { Application, Router } from "https://deno.land/x/oak/mod.ts";
|
||||||
|
import { getImage, uploadImage, deleteImage, chunks, mergeChunks, announce, icons} from './routes.ts';
|
||||||
|
import "https://deno.land/std@0.178.0/dotenv/load.ts";
|
||||||
|
|
||||||
|
|
||||||
|
const router = new Router();
|
||||||
|
|
||||||
|
router.get('/', async(ctx: RouterContext, next: any) => {
|
||||||
|
try {
|
||||||
|
await ctx.send({
|
||||||
|
root: `${Deno.cwd()}/site`,
|
||||||
|
index: "index.html"
|
||||||
|
})
|
||||||
|
} catch {
|
||||||
|
await next()
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
.get('/i/:filename', icons)
|
||||||
|
.post('/upload', uploadImage)
|
||||||
|
.post('/announce', announce)
|
||||||
|
.post('/uploadchunk', chunks)
|
||||||
|
.post('/finish', mergeChunks)
|
||||||
|
.delete('/image/:filename', deleteImage)
|
||||||
|
.get('/image/:filename', getImage)
|
||||||
|
|
||||||
|
|
||||||
|
const app = new Application();
|
||||||
|
|
||||||
|
app.use(router.routes())
|
||||||
|
app.use(router.allowedMethods());
|
||||||
|
|
||||||
|
app.listen({ port: Deno.env.get("PORT") });
|
||||||
|
console.log(`Server is running on port ${Deno.env.get("PORT")}`)
|
Loading…
Reference in New Issue
Block a user