mirror of
https://github.com/mollersuite/monofile.git
synced 2024-11-21 13:36:25 -08:00
this code SUUUUCKs but i dont CARE
This commit is contained in:
parent
55e10f5408
commit
b95a33e39d
1
assets/icons/README.md
Normal file
1
assets/icons/README.md
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Icons are part of Microsoft's Fluent icons
|
1
assets/icons/change_password.svg
Normal file
1
assets/icons/change_password.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.95 8.6a6.554 6.554 0 0 1 6.55-6.55c3.596 0 6.55 2.819 6.55 6.45a6.554 6.554 0 0 1-6.55 6.55c-.531 0-1.055-.076-1.552-.204A1.25 1.25 0 0 1 12.7 16.05h-1.75v1.75c0 .69-.56 1.25-1.25 1.25H7.95v1.25a1.75 1.75 0 0 1-1.75 1.75H3.7a1.75 1.75 0 0 1-1.75-1.75v-2.172c0-.73.29-1.429.806-1.944L8.99 9.948a.275.275 0 0 0 .07-.244A6.386 6.386 0 0 1 8.95 8.6Zm9.3-1.6a1.25 1.25 0 1 0-2.5 0 1.25 1.25 0 0 0 2.5 0Z" fill="#DDDDDD"/></svg>
|
After Width: | Height: | Size: 529 B |
1
assets/icons/delete_account.svg
Normal file
1
assets/icons/delete_account.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M17.5 12a5.5 5.5 0 1 1 0 11 5.5 5.5 0 0 1 0-11Zm-5.478 2A6.47 6.47 0 0 0 11 17.5c0 1.644.61 3.145 1.617 4.29-.802.141-1.675.21-2.617.21-2.89 0-5.128-.656-6.691-2a3.75 3.75 0 0 1-1.305-2.843v-.907A2.25 2.25 0 0 1 4.254 14h7.768Zm3.071.966-.07.058-.057.07a.5.5 0 0 0 0 .568l.058.069 1.77 1.77-1.768 1.766-.057.07a.5.5 0 0 0 0 .568l.058.07.069.057a.5.5 0 0 0 .568 0l.07-.058 1.766-1.767 1.77 1.77.069.058a.5.5 0 0 0 .568 0l.07-.058.058-.07a.5.5 0 0 0 0-.568l-.058-.07-1.77-1.768 1.772-1.77.058-.07a.5.5 0 0 0 0-.568l-.058-.069-.069-.058a.5.5 0 0 0-.569 0l-.069.058-1.771 1.77-1.77-1.77-.07-.058a.5.5 0 0 0-.492-.043l-.076.043ZM10 2.004a5 5 0 1 1 0 10 5 5 0 0 1 0-10Z" fill="#DDDDDD"/></svg>
|
After Width: | Height: | Size: 791 B |
1
assets/icons/logout.svg
Normal file
1
assets/icons/logout.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M6.25 2.75a1.5 1.5 0 0 0-1.5 1.5v15.5a1.5 1.5 0 0 0 1.5 1.5h5.94a6.5 6.5 0 0 1 7.06-10.012V4.25a1.5 1.5 0 0 0-1.5-1.5H6.25Zm2.25 10.5a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3Zm9 9.75a5.5 5.5 0 1 0 0-11 5.5 5.5 0 0 0 0 11Zm3.5-5.5a.5.5 0 0 1-.5.5h-4.793l1.647 1.646a.5.5 0 0 1-.708.708l-2.5-2.5a.5.5 0 0 1 0-.708l2.5-2.5a.5.5 0 0 1 .708.708L15.707 17H20.5a.5.5 0 0 1 .5.5Z" fill="#DDDDDD"/></svg>
|
After Width: | Height: | Size: 494 B |
|
@ -3,5 +3,10 @@
|
||||||
"maxDiscordFileSize": 8388608,
|
"maxDiscordFileSize": 8388608,
|
||||||
"targetGuild": "1024080490677936248",
|
"targetGuild": "1024080490677936248",
|
||||||
"targetChannel": "1024080525993971913",
|
"targetChannel": "1024080525993971913",
|
||||||
"requestTimeout":120000
|
"requestTimeout":120000,
|
||||||
|
|
||||||
|
"accounts": {
|
||||||
|
"registrationEnabled": true,
|
||||||
|
"requiredForUpload": true
|
||||||
|
}
|
||||||
}
|
}
|
56
package-lock.json
generated
56
package-lock.json
generated
|
@ -14,6 +14,7 @@
|
||||||
"@types/multer": "^1.4.7",
|
"@types/multer": "^1.4.7",
|
||||||
"axios": "^0.27.2",
|
"axios": "^0.27.2",
|
||||||
"body-parser": "^1.20.0",
|
"body-parser": "^1.20.0",
|
||||||
|
"cookie-parser": "^1.4.6",
|
||||||
"discord.js": "^14.7.1",
|
"discord.js": "^14.7.1",
|
||||||
"dotenv": "^16.0.2",
|
"dotenv": "^16.0.2",
|
||||||
"express": "^4.18.1",
|
"express": "^4.18.1",
|
||||||
|
@ -22,6 +23,7 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-node-resolve": "^15.0.1",
|
"@rollup/plugin-node-resolve": "^15.0.1",
|
||||||
|
"@types/cookie-parser": "^1.4.3",
|
||||||
"rollup": "^3.11.0",
|
"rollup": "^3.11.0",
|
||||||
"rollup-plugin-svelte": "^7.1.0",
|
"rollup-plugin-svelte": "^7.1.0",
|
||||||
"sass": "^1.57.1",
|
"sass": "^1.57.1",
|
||||||
|
@ -187,6 +189,15 @@
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/cookie-parser": {
|
||||||
|
"version": "1.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.3.tgz",
|
||||||
|
"integrity": "sha512-CqSKwFwefj4PzZ5n/iwad/bow2hTCh0FlNAeWLtQM3JA/NX/iYagIpWG2cf1bQKQ2c9gU2log5VUCrn7LDOs0w==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/express": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/estree": {
|
"node_modules/@types/estree": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz",
|
||||||
|
@ -485,6 +496,26 @@
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/cookie-parser": {
|
||||||
|
"version": "1.4.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz",
|
||||||
|
"integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==",
|
||||||
|
"dependencies": {
|
||||||
|
"cookie": "0.4.1",
|
||||||
|
"cookie-signature": "1.0.6"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cookie-parser/node_modules/cookie": {
|
||||||
|
"version": "0.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
|
||||||
|
"integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/cookie-signature": {
|
"node_modules/cookie-signature": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||||
|
@ -1749,6 +1780,15 @@
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/cookie-parser": {
|
||||||
|
"version": "1.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.3.tgz",
|
||||||
|
"integrity": "sha512-CqSKwFwefj4PzZ5n/iwad/bow2hTCh0FlNAeWLtQM3JA/NX/iYagIpWG2cf1bQKQ2c9gU2log5VUCrn7LDOs0w==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/express": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/estree": {
|
"@types/estree": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz",
|
||||||
|
@ -1990,6 +2030,22 @@
|
||||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
|
||||||
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw=="
|
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw=="
|
||||||
},
|
},
|
||||||
|
"cookie-parser": {
|
||||||
|
"version": "1.4.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz",
|
||||||
|
"integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==",
|
||||||
|
"requires": {
|
||||||
|
"cookie": "0.4.1",
|
||||||
|
"cookie-signature": "1.0.6"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"cookie": {
|
||||||
|
"version": "0.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
|
||||||
|
"integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"cookie-signature": {
|
"cookie-signature": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
"@types/multer": "^1.4.7",
|
"@types/multer": "^1.4.7",
|
||||||
"axios": "^0.27.2",
|
"axios": "^0.27.2",
|
||||||
"body-parser": "^1.20.0",
|
"body-parser": "^1.20.0",
|
||||||
|
"cookie-parser": "^1.4.6",
|
||||||
"discord.js": "^14.7.1",
|
"discord.js": "^14.7.1",
|
||||||
"dotenv": "^16.0.2",
|
"dotenv": "^16.0.2",
|
||||||
"express": "^4.18.1",
|
"express": "^4.18.1",
|
||||||
|
@ -27,6 +28,7 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-node-resolve": "^15.0.1",
|
"@rollup/plugin-node-resolve": "^15.0.1",
|
||||||
|
"@types/cookie-parser": "^1.4.3",
|
||||||
"rollup": "^3.11.0",
|
"rollup": "^3.11.0",
|
||||||
"rollup-plugin-svelte": "^7.1.0",
|
"rollup-plugin-svelte": "^7.1.0",
|
||||||
"sass": "^1.57.1",
|
"sass": "^1.57.1",
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
/*
|
|
||||||
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 cookieParser from "cookie-parser";
|
||||||
import Discord, { IntentsBitField, Client } from "discord.js"
|
import Discord, { IntentsBitField, Client } from "discord.js"
|
||||||
import express from "express"
|
import express from "express"
|
||||||
import fs, { link } from "fs"
|
import fs, { link } from "fs"
|
||||||
import axios, { AxiosResponse } from "axios"
|
import axios, { AxiosResponse } from "axios"
|
||||||
import ServeError from "./lib/errors"
|
|
||||||
|
|
||||||
|
import ServeError from "./lib/errors"
|
||||||
import Files from "./lib/files"
|
import Files from "./lib/files"
|
||||||
|
import * as auth from "./lib/auth"
|
||||||
|
import * as Accounts from "./lib/accounts"
|
||||||
|
|
||||||
|
import { authRoutes } from "./routes/authRoutes";
|
||||||
require("dotenv").config()
|
require("dotenv").config()
|
||||||
|
|
||||||
const multerSetup = multer({storage:memoryStorage()})
|
const multerSetup = multer({storage:memoryStorage()})
|
||||||
|
@ -23,6 +24,9 @@ app.use("/static/style",express.static("out/style"))
|
||||||
app.use("/static/js",express.static("out/client"))
|
app.use("/static/js",express.static("out/client"))
|
||||||
|
|
||||||
app.use(bodyParser.text({limit:(config.maxDiscordFileSize*config.maxDiscordFiles)+1048576,type:["application/json","text/plain"]}))
|
app.use(bodyParser.text({limit:(config.maxDiscordFileSize*config.maxDiscordFiles)+1048576,type:["application/json","text/plain"]}))
|
||||||
|
app.use(cookieParser())
|
||||||
|
|
||||||
|
app.use("/auth",authRoutes)
|
||||||
// funcs
|
// funcs
|
||||||
|
|
||||||
// init data
|
// init data
|
||||||
|
@ -59,7 +63,13 @@ app.post("/upload",multerSetup.single('file'),async (req,res) => {
|
||||||
params = JSON.parse(prm)
|
params = JSON.parse(prm)
|
||||||
}
|
}
|
||||||
|
|
||||||
files.uploadFile({uploadId:params.uploadId,name:req.file.originalname,mime:req.file.mimetype},req.file.buffer)
|
files.uploadFile({
|
||||||
|
owner: auth.validate(req.cookies.auth),
|
||||||
|
|
||||||
|
uploadId:params.uploadId,
|
||||||
|
name:req.file.originalname,
|
||||||
|
mime:req.file.mimetype
|
||||||
|
},req.file.buffer)
|
||||||
.then((uID) => res.send(uID))
|
.then((uID) => res.send(uID))
|
||||||
.catch((stat) => {
|
.catch((stat) => {
|
||||||
res.status(stat.status);
|
res.status(stat.status);
|
||||||
|
@ -83,12 +93,20 @@ app.post("/clone",(req,res) => {
|
||||||
res.send("[err] invalid url")
|
res.send("[err] invalid url")
|
||||||
}
|
}
|
||||||
axios.get(j.url,{responseType:"arraybuffer"}).then((data:AxiosResponse) => {
|
axios.get(j.url,{responseType:"arraybuffer"}).then((data:AxiosResponse) => {
|
||||||
files.uploadFile({name:j.url.split("/")[req.body.split("/").length-1] || "generic",mime:data.headers["content-type"],uploadId:j.uploadId},Buffer.from(data.data))
|
|
||||||
|
files.uploadFile({
|
||||||
|
owner: auth.validate(req.cookies.auth),
|
||||||
|
|
||||||
|
name:j.url.split("/")[req.body.split("/").length-1] || "generic",
|
||||||
|
mime:data.headers["content-type"],
|
||||||
|
uploadId:j.uploadId
|
||||||
|
},Buffer.from(data.data))
|
||||||
.then((uID) => res.send(uID))
|
.then((uID) => res.send(uID))
|
||||||
.catch((stat) => {
|
.catch((stat) => {
|
||||||
res.status(stat.status);
|
res.status(stat.status);
|
||||||
res.send(`[err] ${stat.message}`)
|
res.send(`[err] ${stat.message}`)
|
||||||
})
|
})
|
||||||
|
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
console.log(err)
|
console.log(err)
|
||||||
res.status(400)
|
res.status(400)
|
||||||
|
@ -118,7 +136,15 @@ app.get("/download/:fileId",(req,res) => {
|
||||||
.replace(/\</g,"<")
|
.replace(/\</g,"<")
|
||||||
.replace(/\>/g,">")
|
.replace(/\>/g,">")
|
||||||
)
|
)
|
||||||
.replace(/\$metaTags/g,file.mime.startsWith("image/") ? `<meta name="og:image" content="https://${req.headers.host}/file/${req.params.fileId}" />` : "")
|
.replace(/\$metaTags/g,
|
||||||
|
file.mime.startsWith("image/")
|
||||||
|
? `<meta name="og:image" content="https://${req.headers.host}/file/${req.params.fileId}" />`
|
||||||
|
: (
|
||||||
|
file.mime.startsWith("video/")
|
||||||
|
? `<meta name="og:video:url" content="https://${req.headers.host}/file/${req.params.fileId}" />\n<meta name="og:video:type" content="${file.mime.replace(/\"/g,"")}">`
|
||||||
|
: ""
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -8,14 +8,15 @@ import { readFile, writeFile } from "fs/promises"
|
||||||
export let Accounts: Account[] = []
|
export let Accounts: Account[] = []
|
||||||
|
|
||||||
export interface Account {
|
export interface Account {
|
||||||
id : string
|
id : string
|
||||||
username: string
|
username : string
|
||||||
password: {
|
password : {
|
||||||
hash: string
|
hash : string
|
||||||
salt: string
|
salt : string
|
||||||
}
|
}
|
||||||
accounts: string[]
|
files : string[]
|
||||||
admin : boolean
|
collections : string[]
|
||||||
|
admin : boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export function create(username:string,pwd:string,admin:boolean=false) {
|
export function create(username:string,pwd:string,admin:boolean=false) {
|
||||||
|
@ -26,7 +27,8 @@ export function create(username:string,pwd:string,admin:boolean=false) {
|
||||||
id: accId,
|
id: accId,
|
||||||
username: username,
|
username: username,
|
||||||
password: password.hash(pwd),
|
password: password.hash(pwd),
|
||||||
accounts: [],
|
files: [],
|
||||||
|
collections: [],
|
||||||
admin: admin
|
admin: admin
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -82,39 +84,6 @@ export namespace password {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export namespace rbxaccounts {
|
|
||||||
export function add(id:string,name:string) {
|
|
||||||
let acc = getFromId(id)
|
|
||||||
if (!acc) return
|
|
||||||
|
|
||||||
/* check for account that already has name */
|
|
||||||
let idx = acc.accounts.findIndex(e=>e==name)
|
|
||||||
if (idx > -1) return
|
|
||||||
|
|
||||||
acc.accounts = [...acc.accounts,name]
|
|
||||||
save()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
export function remove(id:string,name:string) {
|
|
||||||
let acc = getFromId(id)
|
|
||||||
if (!acc) return
|
|
||||||
let idx = acc.accounts.findIndex(e=>e==name)
|
|
||||||
if (idx < 0) return
|
|
||||||
acc.accounts.splice(idx,1)
|
|
||||||
save()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
export function clear(id:string) {
|
|
||||||
let acc = getFromId(id)
|
|
||||||
if (!acc) return
|
|
||||||
acc.accounts = []
|
|
||||||
save()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function save() {
|
export function save() {
|
||||||
writeFile(`${process.cwd()}/.data/accounts.json`,JSON.stringify(Accounts))
|
writeFile(`${process.cwd()}/.data/accounts.json`,JSON.stringify(Accounts))
|
||||||
.catch((err) => console.error(err))
|
.catch((err) => console.error(err))
|
||||||
|
|
|
@ -20,6 +20,7 @@ export default async function ServeError(
|
||||||
}
|
}
|
||||||
|
|
||||||
// serve error
|
// serve error
|
||||||
|
res.statusMessage = reason
|
||||||
res.status(code)
|
res.status(code)
|
||||||
res.send(
|
res.send(
|
||||||
errorPage
|
errorPage
|
||||||
|
|
|
@ -19,7 +19,8 @@ export function generateFileId() {
|
||||||
export interface FileUploadSettings {
|
export interface FileUploadSettings {
|
||||||
name?: string,
|
name?: string,
|
||||||
mime: string,
|
mime: string,
|
||||||
uploadId?: string
|
uploadId?: string,
|
||||||
|
owner?:string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Configuration {
|
export interface Configuration {
|
||||||
|
@ -27,13 +28,19 @@ export interface Configuration {
|
||||||
maxDiscordFileSize: number,
|
maxDiscordFileSize: number,
|
||||||
targetGuild: string,
|
targetGuild: string,
|
||||||
targetChannel: string,
|
targetChannel: string,
|
||||||
requestTimeout: number
|
requestTimeout: number,
|
||||||
|
|
||||||
|
accounts: {
|
||||||
|
registrationEnabled: boolean,
|
||||||
|
requiredForUpload: boolean
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FilePointer {
|
export interface FilePointer {
|
||||||
filename:string,
|
filename:string,
|
||||||
mime:string,
|
mime:string,
|
||||||
messageids:string[]
|
messageids:string[],
|
||||||
|
owner?:string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StatusCodeError {
|
export interface StatusCodeError {
|
||||||
|
@ -86,6 +93,11 @@ export default class Files {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!settings.owner && this.config.accounts.requiredForUpload) {
|
||||||
|
reject({status:401,message:"an account is required for upload"});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let uploadId = (settings.uploadId || generateFileId()).toString();
|
let uploadId = (settings.uploadId || generateFileId()).toString();
|
||||||
|
|
||||||
if ((uploadId.match(id_check_regex) || [])[0] != uploadId || uploadId.length > 30) {
|
if ((uploadId.match(id_check_regex) || [])[0] != uploadId || uploadId.length > 30) {
|
||||||
|
@ -159,7 +171,9 @@ export default class Files {
|
||||||
{
|
{
|
||||||
filename:settings.name,
|
filename:settings.name,
|
||||||
messageids:msgIds,
|
messageids:msgIds,
|
||||||
mime:settings.mime
|
mime:settings.mime,
|
||||||
|
|
||||||
|
owner:settings.owner
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
|
|
137
src/server/routes/authRoutes.ts
Normal file
137
src/server/routes/authRoutes.ts
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
import bodyParser from "body-parser";
|
||||||
|
import { Router } from "express";
|
||||||
|
import * as Accounts from "../lib/accounts";
|
||||||
|
import * as auth from "../lib/auth";
|
||||||
|
|
||||||
|
import ServeError from "../lib/errors";
|
||||||
|
|
||||||
|
let parser = bodyParser.json({
|
||||||
|
type: ["text/plain","application/json"]
|
||||||
|
})
|
||||||
|
|
||||||
|
export let authRoutes = Router();
|
||||||
|
|
||||||
|
let config = require(`${process.cwd()}/config.json`)
|
||||||
|
|
||||||
|
|
||||||
|
authRoutes.post("/login", parser, (req,res) => {
|
||||||
|
let body:{[key:string]:any}
|
||||||
|
try {
|
||||||
|
body = JSON.parse(req.body)
|
||||||
|
} catch {
|
||||||
|
ServeError(res,400,"bad request")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof body.username != "string" || typeof body.password != "string") {
|
||||||
|
ServeError(res,400,"please provide a username or password")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auth.validate(req.cookies.auth)) return
|
||||||
|
|
||||||
|
/*
|
||||||
|
check if account exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
let acc = Accounts.getFromUsername(body.username)
|
||||||
|
|
||||||
|
if (!acc) {
|
||||||
|
ServeError(res,401,"username or password incorrect")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Accounts.password.check(acc.id,body.password)) {
|
||||||
|
ServeError(res,401,"username or password incorrect")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
assign token
|
||||||
|
*/
|
||||||
|
|
||||||
|
res.cookie("auth",auth.create(acc.id,(3*24*60*60*1000)))
|
||||||
|
res.status(200)
|
||||||
|
res.end()
|
||||||
|
})
|
||||||
|
|
||||||
|
authRoutes.post("/create", parser, (req,res) => {
|
||||||
|
if (!config.accounts.registrationEnabled) {
|
||||||
|
ServeError(res,403,"account registration disabled")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let body:{[key:string]:any}
|
||||||
|
try {
|
||||||
|
body = JSON.parse(req.body)
|
||||||
|
} catch {
|
||||||
|
ServeError(res,400,"bad request")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auth.validate(req.cookies.auth)) return
|
||||||
|
|
||||||
|
if (typeof body.username != "string" || typeof body.password != "string") {
|
||||||
|
ServeError(res,400,"please provide a username or password")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
check if account exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
let acc = Accounts.getFromUsername(body.username)
|
||||||
|
|
||||||
|
if (acc) {
|
||||||
|
ServeError(res,400,"account with this username already exists")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (body.username.length < 3 || body.username.length > 20) {
|
||||||
|
ServeError(res,400,"username must be over or equal to 3 characters or under or equal to 20 characters in length")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((body.username.match(/[A-Za-z0-9_\-\.]+/) || [])[0] != body.username) {
|
||||||
|
ServeError(res,400,"username contains invalid characters")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (body.password.length < 8) {
|
||||||
|
ServeError(res,400,"password must be 8 characters or longer")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let newAcc = Accounts.create(body.username,body.password)
|
||||||
|
|
||||||
|
/*
|
||||||
|
assign token
|
||||||
|
*/
|
||||||
|
|
||||||
|
res.cookie("auth",auth.create(newAcc,(3*24*60*60*1000)))
|
||||||
|
res.status(200)
|
||||||
|
res.end()
|
||||||
|
})
|
||||||
|
|
||||||
|
authRoutes.post("/logout", (req,res) => {
|
||||||
|
if (!auth.validate(req.cookies.auth)) {
|
||||||
|
ServeError(res, 401, "not logged in")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
auth.invalidate(req.cookies.auth)
|
||||||
|
res.send("logged out")
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
authRoutes.get("/me", (req,res) => {
|
||||||
|
if (!auth.validate(req.cookies.auth)) {
|
||||||
|
ServeError(res, 401, "not logged in")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// lazy rn so
|
||||||
|
|
||||||
|
let acc = Accounts.getFromToken(req.cookies.auth)
|
||||||
|
res.send(acc)
|
||||||
|
})
|
|
@ -69,6 +69,25 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pwError {
|
||||||
|
div {
|
||||||
|
border:none;
|
||||||
|
border-radius:0;
|
||||||
|
width:100%;
|
||||||
|
padding:5px;
|
||||||
|
background-color:#663333;
|
||||||
|
color:#dddddd;
|
||||||
|
outline:none;
|
||||||
|
font-size:14px;
|
||||||
|
text-align:left;
|
||||||
|
|
||||||
|
@media screen and (max-width: 500px) {
|
||||||
|
font-size:16px;
|
||||||
|
padding:10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.lgBtnContainer {
|
.lgBtnContainer {
|
||||||
display:flex;
|
display:flex;
|
||||||
position:relative;
|
position:relative;
|
||||||
|
@ -117,4 +136,53 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.loggedIn {
|
||||||
|
position:absolute;
|
||||||
|
left:10px;
|
||||||
|
top:10px;
|
||||||
|
width:calc( 100% - 20px );
|
||||||
|
height:calc( 100% - 20px );
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-weight:600;
|
||||||
|
font-size:20px;
|
||||||
|
color: #AAAAAA;
|
||||||
|
}
|
||||||
|
|
||||||
|
.accountOptions {
|
||||||
|
button {
|
||||||
|
position:relative;
|
||||||
|
width:100%;
|
||||||
|
cursor:pointer;
|
||||||
|
height:50px;
|
||||||
|
background-color: #191919;
|
||||||
|
border:none;
|
||||||
|
border-bottom:1px solid #AAAAAA;
|
||||||
|
transition-duration:150ms;
|
||||||
|
|
||||||
|
img {
|
||||||
|
position:absolute;
|
||||||
|
left:13px;
|
||||||
|
top:50%;
|
||||||
|
transform:translateY(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
position:absolute;
|
||||||
|
top:50%;
|
||||||
|
left:50px;
|
||||||
|
color:#DDDDDD;
|
||||||
|
transform:translateY(-50%);
|
||||||
|
font-size:14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transition-duration:150ms;
|
||||||
|
background-color: #252525;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -2,6 +2,7 @@
|
||||||
import { circOut } from "svelte/easing";
|
import { circOut } from "svelte/easing";
|
||||||
import { scale } from "svelte/transition";
|
import { scale } from "svelte/transition";
|
||||||
import PulldownManager, {pulldownOpen} from "./PulldownManager.svelte";
|
import PulldownManager, {pulldownOpen} from "./PulldownManager.svelte";
|
||||||
|
import { account } from "./stores.mjs";
|
||||||
import { _void } from "./transition/_void";
|
import { _void } from "./transition/_void";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -22,7 +23,7 @@
|
||||||
<!-- too lazy to make this better -->
|
<!-- too lazy to make this better -->
|
||||||
|
|
||||||
<button class="menuBtn" on:click={() => pulldown.openPulldown("files")}>files</button>
|
<button class="menuBtn" on:click={() => pulldown.openPulldown("files")}>files</button>
|
||||||
<button class="menuBtn" on:click={() => pulldown.openPulldown("account")}>account</button>
|
<button class="menuBtn" on:click={() => pulldown.openPulldown("account")}>{$account.username ? `@${$account.username}` : "account"}</button>
|
||||||
<button class="menuBtn" on:click={() => pulldown.openPulldown("help")}>help</button>
|
<button class="menuBtn" on:click={() => pulldown.openPulldown("help")}>help</button>
|
||||||
|
|
||||||
<div /> <!-- not sure what's offcenter but something is
|
<div /> <!-- not sure what's offcenter but something is
|
||||||
|
|
|
@ -3,19 +3,12 @@
|
||||||
import { padding_scaleY } from "./transition/padding_scaleY.js"
|
import { padding_scaleY } from "./transition/padding_scaleY.js"
|
||||||
import { fade } from "svelte/transition";
|
import { fade } from "svelte/transition";
|
||||||
import { circIn, circOut } from "svelte/easing";
|
import { circIn, circOut } from "svelte/easing";
|
||||||
|
import { serverStats, refresh_stats, account } from "./stores.mjs";
|
||||||
|
|
||||||
import AttachmentZone from "./uploader/AttachmentZone.svelte";
|
import AttachmentZone from "./uploader/AttachmentZone.svelte";
|
||||||
|
|
||||||
// stats
|
// stats
|
||||||
|
|
||||||
let ServerStats = {}
|
|
||||||
|
|
||||||
let refresh_stats = () => {
|
|
||||||
fetch("/server").then(async (data) => {
|
|
||||||
ServerStats = await data.json()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
refresh_stats()
|
refresh_stats()
|
||||||
|
|
||||||
// uploads
|
// uploads
|
||||||
|
@ -118,7 +111,7 @@
|
||||||
let eased = circOut(t)
|
let eased = circOut(t)
|
||||||
|
|
||||||
return `
|
return `
|
||||||
height: ${eased*(node.offsetHeight-20)}px;
|
height: ${eased*(node.offsetHeight-22)}px;
|
||||||
padding: ${eased*10}px 10px;
|
padding: ${eased*10}px 10px;
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
@ -130,7 +123,7 @@
|
||||||
<div id="uploadWindow">
|
<div id="uploadWindow">
|
||||||
<h1>monofile</h1>
|
<h1>monofile</h1>
|
||||||
<p style:color="#999999">
|
<p style:color="#999999">
|
||||||
<span class="number">{ServerStats.version ? `v${ServerStats.version}` : "•••"}</span>  — Discord based file sharing
|
<span class="number">{$serverStats.version ? `v${$serverStats.version}` : "•••"}</span>  — Discord based file sharing
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div style:min-height="10px" />
|
<div style:min-height="10px" />
|
||||||
|
@ -195,18 +188,30 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if uploadInProgress == false}
|
{#if uploadInProgress == false}
|
||||||
<AttachmentZone bind:this={attachmentZone} on:addFiles={handle_file_upload}/>
|
|
||||||
<div style:min-height="10px" transition:padding_scaleY />
|
<!-- if required for upload, check if logged in -->
|
||||||
{#if Object.keys(uploads).length > 0}
|
{#if ($serverStats.accounts||{}).requiredForUpload ? !!$account.username : true}
|
||||||
<button in:padding_scaleY={{easingFunc:circOut}} out:_void on:click={upload_files}>upload</button>
|
|
||||||
<div transition:_void style:min-height="10px" />
|
<AttachmentZone bind:this={attachmentZone} on:addFiles={handle_file_upload}/>
|
||||||
|
<div style:min-height="10px" transition:_void={{rTarg:"height",prop:"min-height"}} />
|
||||||
|
{#if Object.keys(uploads).length > 0}
|
||||||
|
<button in:padding_scaleY={{easingFunc:circOut}} out:_void on:click={upload_files}>upload</button>
|
||||||
|
<div transition:_void={{rTarg:"height",prop:"min-height"}} style:min-height="10px" />
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{:else}
|
||||||
|
|
||||||
|
<p transition:_void style:color="#999999" style:text-align="center">Please log in to upload files.</p>
|
||||||
|
<div transition:_void={{rTarg:"height",prop:"min-height"}} style:min-height="10px" />
|
||||||
|
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<p style:color="#999999" style:text-align="center">
|
<p style:color="#999999" style:text-align="center">
|
||||||
Hosting <span class="number" style:font-weight="600">{ServerStats.files || "•••"}</span> files
|
Hosting <span class="number" style:font-weight="600">{$serverStats.files || "•••"}</span> files
|
||||||
—
|
—
|
||||||
Maximum filesize is <span class="number" style:font-weight="600">{((ServerStats.maxDiscordFileSize || 0)*(ServerStats.maxDiscordFiles || 0))/1048576 || "•••"}MB</span>
|
Maximum filesize is <span class="number" style:font-weight="600">{(($serverStats.maxDiscordFileSize || 0)*($serverStats.maxDiscordFiles || 0))/1048576 || "•••"}MB</span>
|
||||||
<br />
|
<br />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
|
@ -2,32 +2,132 @@
|
||||||
import Pulldown from "./Pulldown.svelte"
|
import Pulldown from "./Pulldown.svelte"
|
||||||
import { padding_scaleY } from "../transition/padding_scaleY"
|
import { padding_scaleY } from "../transition/padding_scaleY"
|
||||||
import { circIn,circOut } from "svelte/easing"
|
import { circIn,circOut } from "svelte/easing"
|
||||||
|
import { account, fetchAccountData, serverStats } from "../stores.mjs";
|
||||||
|
import { fade } from "svelte/transition";
|
||||||
|
|
||||||
let targetAction
|
let targetAction
|
||||||
|
let inProgress
|
||||||
|
let authError
|
||||||
|
|
||||||
|
let pwErr
|
||||||
|
|
||||||
|
// lazy
|
||||||
|
|
||||||
|
let username
|
||||||
|
let password
|
||||||
|
|
||||||
|
let execute = () => {
|
||||||
|
if (inProgress) return
|
||||||
|
|
||||||
|
inProgress = true
|
||||||
|
|
||||||
|
fetch(`/auth/${targetAction}`, {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({
|
||||||
|
username, password
|
||||||
|
})
|
||||||
|
}).then(async (res) => {
|
||||||
|
inProgress = false
|
||||||
|
|
||||||
|
if (res.status != 200) {
|
||||||
|
authError = await res.json().catch(() => {
|
||||||
|
return {
|
||||||
|
status: res.status,
|
||||||
|
message: res.statusText
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchAccountData();
|
||||||
|
|
||||||
|
|
||||||
|
}).catch(() => {})
|
||||||
|
}
|
||||||
|
|
||||||
|
$: {
|
||||||
|
if (pwErr && authError) {
|
||||||
|
pwErr.animate({
|
||||||
|
backgroundColor: ["#885555","#663333"],
|
||||||
|
easing: "ease-out"
|
||||||
|
},650)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// actual account menu
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Pulldown name="accounts">
|
<Pulldown name="accounts">
|
||||||
<div class="notLoggedIn">
|
{#if Object.keys($account).length == 0}
|
||||||
<div class="container_div">
|
|
||||||
<h1>monofile <span style:color="#999999">accounts</span></h1>
|
|
||||||
<p class="flavor">Gain control of your uploads.</p>
|
|
||||||
|
|
||||||
{#if targetAction}
|
<div class="notLoggedIn" transition:fade={{duration:200}}>
|
||||||
|
<div class="container_div">
|
||||||
|
<h1>monofile <span style:color="#999999">accounts</span></h1>
|
||||||
|
<p class="flavor">Gain control of your uploads.</p>
|
||||||
|
|
||||||
<div class="fields" out:padding_scaleY|local={{easingFunc:circIn}} in:padding_scaleY|local>
|
{#if targetAction}
|
||||||
<input placeholder="username" type="text">
|
|
||||||
<input placeholder="password" type="password">
|
|
||||||
<button>{targetAction=="login" ? "Log in" : "Create account"}</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{:else}
|
<div class="fields" out:padding_scaleY|local={{easingFunc:circIn}} in:padding_scaleY|local>
|
||||||
|
{#if !$serverStats.accounts.registrationEnabled && targetAction == "create"}
|
||||||
|
<div class="pwError">
|
||||||
|
<div style:background-color="#554C33">
|
||||||
|
<p>Account registration has been disabled by this instance's owner</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<div class="lgBtnContainer" out:padding_scaleY|local={{easingFunc:circIn}} in:padding_scaleY|local>
|
{#if authError}
|
||||||
<button on:click={() => targetAction="login"}>Log in</button>
|
<div class="pwError" out:padding_scaleY|local={{easingFunc:circIn}} in:padding_scaleY|local>
|
||||||
<button on:click={() => targetAction="create"}>Sign up</button>
|
<div bind:this={pwErr}>
|
||||||
</div>
|
<p><strong>{authError.status}</strong> {authError.message}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
{/if}
|
<input placeholder="username" type="text" bind:value={username}>
|
||||||
|
<input placeholder="password" type="password" bind:value={password}>
|
||||||
|
<button on:click={execute}>{ inProgress ? "• • •" : (targetAction=="login" ? "Log in" : "Create account") }</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{:else}
|
||||||
|
|
||||||
|
<div class="lgBtnContainer" out:padding_scaleY|local={{easingFunc:circIn}} in:padding_scaleY|local>
|
||||||
|
<button on:click={() => targetAction="login"}>Log in</button>
|
||||||
|
<button on:click={() => targetAction="create"}>Sign up</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
{:else}
|
||||||
|
|
||||||
|
<div class="loggedIn" transition:fade={{duration:200}}>
|
||||||
|
<h1>
|
||||||
|
Hey there, <span class="monospace" style:font-size="18px">@{$account.username}</span>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div style:min-height="10px" style:border-bottom="1px solid #AAAAAA" />
|
||||||
|
|
||||||
|
<div class="accountOptions">
|
||||||
|
<button>
|
||||||
|
<img src="/static/assets/icons/change_password.svg" alt="change password">
|
||||||
|
<p>Change password</p>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button>
|
||||||
|
<img src="/static/assets/icons/delete_account.svg" alt="delete account">
|
||||||
|
<p>Delete account</p>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button on:click={() => fetch(`/auth/logout`,{method:"POST"}).then(() => fetchAccountData())}>
|
||||||
|
<img src="/static/assets/icons/logout.svg" alt="logout">
|
||||||
|
<p>Log out</p>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/if}
|
||||||
</Pulldown>
|
</Pulldown>
|
|
@ -1,3 +1,23 @@
|
||||||
import { writable } from "svelte/store"
|
import { writable } from "svelte/store"
|
||||||
|
|
||||||
export let pulldownManager = writable(0)
|
export let pulldownManager = writable(0)
|
||||||
|
export let account = writable({})
|
||||||
|
export let serverStats = writable({})
|
||||||
|
|
||||||
|
export let fetchAccountData = function() {
|
||||||
|
fetch("/auth/me").then(async (response) => {
|
||||||
|
if (response.status == 200) {
|
||||||
|
account.set(await response.json())
|
||||||
|
} else {
|
||||||
|
account.set({})
|
||||||
|
}
|
||||||
|
}).catch((err) => { console.error(err) })
|
||||||
|
}
|
||||||
|
|
||||||
|
export let refresh_stats = () => {
|
||||||
|
fetch("/server").then(async (data) => {
|
||||||
|
serverStats.set(await data.json())
|
||||||
|
}).catch((err) => { console.error(err) })
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchAccountData()
|
|
@ -1,6 +1,6 @@
|
||||||
import { circIn, circOut } from "svelte/easing"
|
import { circIn, circOut } from "svelte/easing"
|
||||||
|
|
||||||
export function _void(node, { duration, easingFunc, op, prop }) {
|
export function _void(node, { duration, easingFunc, op, prop, rTarg }) {
|
||||||
let rect = node.getBoundingClientRect()
|
let rect = node.getBoundingClientRect()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -10,7 +10,7 @@ export function _void(node, { duration, easingFunc, op, prop }) {
|
||||||
|
|
||||||
return `
|
return `
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
${prop||"height"}: ${(eased)*(rect[prop||"height"])}px;
|
${prop||"height"}: ${(eased)*(rect[rTarg||prop||"height"])}px;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
opacity:${eased};
|
opacity:${eased};
|
||||||
overflow: clip;
|
overflow: clip;
|
||||||
|
|
Loading…
Reference in a new issue