diff --git a/package.json b/package.json index 12191ea..27e92c8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "monofile", - "version": "1.3.0-pa", + "version": "1.3.0-alpha", "description": "Discord-based file sharing", "main": "index.js", "scripts": { diff --git a/src/server/routes/adminRoutes.ts b/src/server/routes/adminRoutes.ts index 13e680b..78cfcf2 100644 --- a/src/server/routes/adminRoutes.ts +++ b/src/server/routes/adminRoutes.ts @@ -4,6 +4,7 @@ import * as Accounts from "../lib/accounts"; import * as auth from "../lib/auth"; import bytes from "bytes" import {writeFile} from "fs"; +import { sendMail } from "../lib/mail"; import ServeError from "../lib/errors"; import Files from "../lib/files"; @@ -67,6 +68,36 @@ adminRoutes.post("/reset", parser, (req,res) => { }) +adminRoutes.post("/elevate", parser, (req,res) => { + + if (!auth.validate(req.cookies.auth)) { + ServeError(res, 401, "not logged in") + return + } + + let acc = Accounts.getFromToken(req.cookies.auth) as Accounts.Account + + if (!acc) return + if (!acc.admin) return + if (typeof req.body.target !== "string") { + res.status(404) + res.send() + return + } + + let targetAccount = Accounts.getFromUsername(req.body.target) + if (!targetAccount) { + res.status(404) + res.send() + return + } + + targetAccount.admin = true; + Accounts.save() + res.send() + +}) + adminRoutes.post("/delete", parser, (req,res) => { if (!auth.validate(req.cookies.auth)) { @@ -100,6 +131,55 @@ adminRoutes.post("/delete", parser, (req,res) => { }) +adminRoutes.post("/delete_account", parser, async (req,res) => { + if (!auth.validate(req.cookies.auth)) { + ServeError(res, 401, "not logged in") + return + } + + let acc = Accounts.getFromToken(req.cookies.auth) as Accounts.Account + + if (!acc) return + if (!acc.admin) return + if (typeof req.body.target !== "string") { + res.status(404) + res.send() + return + } + + let targetAccount = Accounts.getFromUsername(req.body.target) + if (!targetAccount) { + res.status(404) + res.send() + return + } + + let accId = targetAccount.id + + auth.AuthTokens.filter(e => e.account == accId).forEach((v) => { + auth.invalidate(v.token) + }) + + let cpl = () => Accounts.deleteAccount(accId).then(_ => { + if (targetAccount?.email) { + sendMail(targetAccount.email, "Notice of account deletion", `Your account, ${targetAccount.username}, has been deleted by ${acc.username} for the following reason:

${req.body.reason || "(no reason specified)"}

Your files ${req.body.deleteFiles ? "have been deleted" : "have not been modified"}. Thank you for using monofile.`) + } + res.send("account deleted") + }) + + if (req.body.deleteFiles) { + let f = targetAccount.files.map(e=>e) // make shallow copy so that iterating over it doesnt Die + for (let v of f) { + files.unlink(v,true).catch(err => console.error(err)) + } + + writeFile(process.cwd()+"/.data/files.json",JSON.stringify(files.files), (err) => { + if (err) console.log(err) + cpl() + }) + } else cpl() +}) + adminRoutes.post("/transfer", parser, (req,res) => { if (!auth.validate(req.cookies.auth)) { diff --git a/src/svelte/elem/prompts/admin.js b/src/svelte/elem/prompts/admin.js index 4b07a99..2c6487a 100644 --- a/src/svelte/elem/prompts/admin.js +++ b/src/svelte/elem/prompts/admin.js @@ -92,7 +92,6 @@ export function delFile(optPicker) { ]).then((exp) => { if (exp && exp.selected) { fetch(`/admin/delete`,{method:"POST", body:JSON.stringify({ - owner: exp.owner, target: exp.file })}).then((response) => { @@ -103,4 +102,92 @@ export function delFile(optPicker) { }) } }) +} + +export function elevateUser(optPicker) { + optPicker.picker("Elevate user",[ + { + name: "Username", + icon: "/static/assets/icons/person.svg", + id: "user", + inputSettings: {} + }, + { + name: "Elevate to admin", + icon: "/static/assets/icons/admin/update.svg", + id: true + } + ]).then((exp) => { + if (exp && exp.selected) { + fetch(`/admin/elevate`,{method:"POST", body:JSON.stringify({ + target: exp.user + })}).then((response) => { + + if (response.status != 200) { + optPicker.picker(`${response.status} ${response.statusText}`,[]) + } + + }) + } + }) +} + +// im really lazy so i just stole this from account.js + +export function deleteAccount(optPicker) { + optPicker.picker("What should we do with the target account's files?",[ + { + name: "Delete files", + icon: "/static/assets/icons/admin/delete_file.svg", + description: "Files will be permanently deleted", + id: true + }, + { + name: "Do nothing", + icon: "/static/assets/icons/file.svg", + description: "Files will not be affected", + id: false + } + ]).then((exp) => { + if (exp) { + let deleteFiles = exp.selected + + optPicker.picker(`Enter the target account's username to continue.`,[ + { + name: "Enter account username", + icon: "/static/assets/icons/person.svg", + inputSettings: {}, + id:"username" + }, + { + name: "Optional reason", + icon: "/static/assets/icons/more.svg", + inputSettings: {}, + id:"reason" + }, + { + name: `Delete account ${deleteFiles ? "& its files" : ""}`, + icon: "/static/assets/icons/delete_account.svg", + description: `This cannot be undone.`, + id: true + } + ]).then((fin) => { + if (fin && fin.selected) { + fetch(`/admin/delete_account`,{method:"POST", body:JSON.stringify({ + target: fin.username, + reason: fin.reason, + deleteFiles + })}).then((response) => { + + if (response.status != 200) { + optPicker.picker(`${response.status} ${response.statusText}`,[]) + } + + fetchAccountData() + }) + + } + }) + } + }) } \ No newline at end of file diff --git a/src/svelte/elem/pulldowns/Accounts.svelte b/src/svelte/elem/pulldowns/Accounts.svelte index b0cf738..32d2d91 100644 --- a/src/svelte/elem/pulldowns/Accounts.svelte +++ b/src/svelte/elem/pulldowns/Accounts.svelte @@ -198,7 +198,7 @@

Admin

- @@ -208,7 +208,7 @@

Change user password

- @@ -223,11 +223,6 @@

Delete file

- - {/if}


{$account.id}