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
- -Elevate account to admin
Delete file
Update files from before monofile 1.3
This may take a while
{$account.id}