mirror of
https://github.com/mollersuite/monofile.git
synced 2024-11-23 14:16:26 -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,
|
||||
"targetGuild": "1024080490677936248",
|
||||
"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",
|
||||
"axios": "^0.27.2",
|
||||
"body-parser": "^1.20.0",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"discord.js": "^14.7.1",
|
||||
"dotenv": "^16.0.2",
|
||||
"express": "^4.18.1",
|
||||
|
@ -22,6 +23,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-node-resolve": "^15.0.1",
|
||||
"@types/cookie-parser": "^1.4.3",
|
||||
"rollup": "^3.11.0",
|
||||
"rollup-plugin-svelte": "^7.1.0",
|
||||
"sass": "^1.57.1",
|
||||
|
@ -187,6 +189,15 @@
|
|||
"@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": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz",
|
||||
|
@ -485,6 +496,26 @@
|
|||
"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": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||
|
@ -1749,6 +1780,15 @@
|
|||
"@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": {
|
||||
"version": "1.0.0",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
"@types/multer": "^1.4.7",
|
||||
"axios": "^0.27.2",
|
||||
"body-parser": "^1.20.0",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"discord.js": "^14.7.1",
|
||||
"dotenv": "^16.0.2",
|
||||
"express": "^4.18.1",
|
||||
|
@ -27,6 +28,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-node-resolve": "^15.0.1",
|
||||
"@types/cookie-parser": "^1.4.3",
|
||||
"rollup": "^3.11.0",
|
||||
"rollup-plugin-svelte": "^7.1.0",
|
||||
"sass": "^1.57.1",
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
/*
|
||||
i really should split this up into different modules
|
||||
*/
|
||||
|
||||
import bodyParser from "body-parser"
|
||||
import multer, {memoryStorage} from "multer"
|
||||
import cookieParser from "cookie-parser";
|
||||
import Discord, { IntentsBitField, Client } from "discord.js"
|
||||
import express from "express"
|
||||
import fs, { link } from "fs"
|
||||
import axios, { AxiosResponse } from "axios"
|
||||
import ServeError from "./lib/errors"
|
||||
|
||||
import ServeError from "./lib/errors"
|
||||
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()
|
||||
|
||||
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(bodyParser.text({limit:(config.maxDiscordFileSize*config.maxDiscordFiles)+1048576,type:["application/json","text/plain"]}))
|
||||
app.use(cookieParser())
|
||||
|
||||
app.use("/auth",authRoutes)
|
||||
// funcs
|
||||
|
||||
// init data
|
||||
|
@ -59,7 +63,13 @@ app.post("/upload",multerSetup.single('file'),async (req,res) => {
|
|||
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))
|
||||
.catch((stat) => {
|
||||
res.status(stat.status);
|
||||
|
@ -83,12 +93,20 @@ app.post("/clone",(req,res) => {
|
|||
res.send("[err] invalid url")
|
||||
}
|
||||
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))
|
||||
.catch((stat) => {
|
||||
res.status(stat.status);
|
||||
res.send(`[err] ${stat.message}`)
|
||||
})
|
||||
|
||||
}).catch((err) => {
|
||||
console.log(err)
|
||||
res.status(400)
|
||||
|
@ -118,7 +136,15 @@ app.get("/download/:fileId",(req,res) => {
|
|||
.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 {
|
||||
|
|
|
@ -14,7 +14,8 @@ export interface Account {
|
|||
hash : string
|
||||
salt : string
|
||||
}
|
||||
accounts: string[]
|
||||
files : string[]
|
||||
collections : string[]
|
||||
admin : boolean
|
||||
}
|
||||
|
||||
|
@ -26,7 +27,8 @@ export function create(username:string,pwd:string,admin:boolean=false) {
|
|||
id: accId,
|
||||
username: username,
|
||||
password: password.hash(pwd),
|
||||
accounts: [],
|
||||
files: [],
|
||||
collections: [],
|
||||
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() {
|
||||
writeFile(`${process.cwd()}/.data/accounts.json`,JSON.stringify(Accounts))
|
||||
.catch((err) => console.error(err))
|
||||
|
|
|
@ -20,6 +20,7 @@ export default async function ServeError(
|
|||
}
|
||||
|
||||
// serve error
|
||||
res.statusMessage = reason
|
||||
res.status(code)
|
||||
res.send(
|
||||
errorPage
|
||||
|
|
|
@ -19,7 +19,8 @@ export function generateFileId() {
|
|||
export interface FileUploadSettings {
|
||||
name?: string,
|
||||
mime: string,
|
||||
uploadId?: string
|
||||
uploadId?: string,
|
||||
owner?:string
|
||||
}
|
||||
|
||||
export interface Configuration {
|
||||
|
@ -27,13 +28,19 @@ export interface Configuration {
|
|||
maxDiscordFileSize: number,
|
||||
targetGuild: string,
|
||||
targetChannel: string,
|
||||
requestTimeout: number
|
||||
requestTimeout: number,
|
||||
|
||||
accounts: {
|
||||
registrationEnabled: boolean,
|
||||
requiredForUpload: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export interface FilePointer {
|
||||
filename:string,
|
||||
mime:string,
|
||||
messageids:string[]
|
||||
messageids:string[],
|
||||
owner?:string
|
||||
}
|
||||
|
||||
export interface StatusCodeError {
|
||||
|
@ -86,6 +93,11 @@ export default class Files {
|
|||
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();
|
||||
|
||||
if ((uploadId.match(id_check_regex) || [])[0] != uploadId || uploadId.length > 30) {
|
||||
|
@ -159,7 +171,9 @@ export default class Files {
|
|||
{
|
||||
filename:settings.name,
|
||||
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 {
|
||||
display:flex;
|
||||
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 { scale } from "svelte/transition";
|
||||
import PulldownManager, {pulldownOpen} from "./PulldownManager.svelte";
|
||||
import { account } from "./stores.mjs";
|
||||
import { _void } from "./transition/_void";
|
||||
|
||||
/**
|
||||
|
@ -22,7 +23,7 @@
|
|||
<!-- too lazy to make this better -->
|
||||
|
||||
<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>
|
||||
|
||||
<div /> <!-- not sure what's offcenter but something is
|
||||
|
|
|
@ -3,19 +3,12 @@
|
|||
import { padding_scaleY } from "./transition/padding_scaleY.js"
|
||||
import { fade } from "svelte/transition";
|
||||
import { circIn, circOut } from "svelte/easing";
|
||||
import { serverStats, refresh_stats, account } from "./stores.mjs";
|
||||
|
||||
import AttachmentZone from "./uploader/AttachmentZone.svelte";
|
||||
|
||||
// stats
|
||||
|
||||
let ServerStats = {}
|
||||
|
||||
let refresh_stats = () => {
|
||||
fetch("/server").then(async (data) => {
|
||||
ServerStats = await data.json()
|
||||
})
|
||||
}
|
||||
|
||||
refresh_stats()
|
||||
|
||||
// uploads
|
||||
|
@ -118,7 +111,7 @@
|
|||
let eased = circOut(t)
|
||||
|
||||
return `
|
||||
height: ${eased*(node.offsetHeight-20)}px;
|
||||
height: ${eased*(node.offsetHeight-22)}px;
|
||||
padding: ${eased*10}px 10px;
|
||||
`
|
||||
}
|
||||
|
@ -130,7 +123,7 @@
|
|||
<div id="uploadWindow">
|
||||
<h1>monofile</h1>
|
||||
<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>
|
||||
|
||||
<div style:min-height="10px" />
|
||||
|
@ -195,18 +188,30 @@
|
|||
</div>
|
||||
|
||||
{#if uploadInProgress == false}
|
||||
|
||||
<!-- if required for upload, check if logged in -->
|
||||
{#if ($serverStats.accounts||{}).requiredForUpload ? !!$account.username : true}
|
||||
|
||||
<AttachmentZone bind:this={attachmentZone} on:addFiles={handle_file_upload}/>
|
||||
<div style:min-height="10px" transition:padding_scaleY />
|
||||
<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 style:min-height="10px" />
|
||||
<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}
|
||||
|
||||
<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 />
|
||||
</p>
|
||||
|
||||
|
|
|
@ -2,12 +2,67 @@
|
|||
import Pulldown from "./Pulldown.svelte"
|
||||
import { padding_scaleY } from "../transition/padding_scaleY"
|
||||
import { circIn,circOut } from "svelte/easing"
|
||||
import { account, fetchAccountData, serverStats } from "../stores.mjs";
|
||||
import { fade } from "svelte/transition";
|
||||
|
||||
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>
|
||||
|
||||
<Pulldown name="accounts">
|
||||
<div class="notLoggedIn">
|
||||
{#if Object.keys($account).length == 0}
|
||||
|
||||
<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>
|
||||
|
@ -15,9 +70,25 @@
|
|||
{#if targetAction}
|
||||
|
||||
<div class="fields" out:padding_scaleY|local={{easingFunc:circIn}} in:padding_scaleY|local>
|
||||
<input placeholder="username" type="text">
|
||||
<input placeholder="password" type="password">
|
||||
<button>{targetAction=="login" ? "Log in" : "Create account"}</button>
|
||||
{#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}
|
||||
|
||||
{#if authError}
|
||||
<div class="pwError" out:padding_scaleY|local={{easingFunc:circIn}} in:padding_scaleY|local>
|
||||
<div bind:this={pwErr}>
|
||||
<p><strong>{authError.status}</strong> {authError.message}</p>
|
||||
</div>
|
||||
</div>
|
||||
{/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}
|
||||
|
@ -30,4 +101,33 @@
|
|||
{/if}
|
||||
</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>
|
|
@ -1,3 +1,23 @@
|
|||
import { writable } from "svelte/store"
|
||||
|
||||
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"
|
||||
|
||||
export function _void(node, { duration, easingFunc, op, prop }) {
|
||||
export function _void(node, { duration, easingFunc, op, prop, rTarg }) {
|
||||
let rect = node.getBoundingClientRect()
|
||||
|
||||
return {
|
||||
|
@ -10,7 +10,7 @@ export function _void(node, { duration, easingFunc, op, prop }) {
|
|||
|
||||
return `
|
||||
white-space: nowrap;
|
||||
${prop||"height"}: ${(eased)*(rect[prop||"height"])}px;
|
||||
${prop||"height"}: ${(eased)*(rect[rTarg||prop||"height"])}px;
|
||||
padding: 0px;
|
||||
opacity:${eased};
|
||||
overflow: clip;
|
||||
|
|
Loading…
Reference in a new issue