Add buttons for blocking and restoring all of a user's files

This commit is contained in:
2024-11-05 14:58:12 +01:00
parent 4aeec05854
commit 6594ca3016
7 changed files with 366 additions and 69 deletions

View File

@@ -28,10 +28,7 @@ will be removed when reported.
under the _gore_ category. under the _gore_ category.
* **Gore** - Graphic and shocking videos or images depicting severe harm to * **Gore** - Graphic and shocking videos or images depicting severe harm to
humans (or animals). I will clarify that I am not strictly against shocking humans (or animals).
content, and it is also not illegal in most places. When a shocking video goes
viral it often ends up in the wrong places and it can cause mental issues for
unsuspecting viewers. For that reason I will remove it when it gets reported.
* **Malware and computer viruses** - Software programs designed to cause harm to * **Malware and computer viruses** - Software programs designed to cause harm to
computer systems. Please attach some proof that the file actually contains computer systems. Please attach some proof that the file actually contains

View File

@@ -18,7 +18,7 @@
<link rel="apple-touch-icon" sizes="180x180" href="/res/img/pixeldrain_180.png" /> <link rel="apple-touch-icon" sizes="180x180" href="/res/img/pixeldrain_180.png" />
<link rel="shortcut icon" sizes="196x196" href="/res/img/pixeldrain_196.png" /> <link rel="shortcut icon" sizes="196x196" href="/res/img/pixeldrain_196.png" />
<meta name="description" content="Pixeldrain is a free file sharing service, you <meta name="description" content="Pixeldrain is a file transfer service, you
can upload any file and you will be given a shareable link right away. can upload any file and you will be given a shareable link right away.
pixeldrain also supports previews for images, videos, audio, PDFs and much more." /> pixeldrain also supports previews for images, videos, audio, PDFs and much more." />
<meta name="keywords" content="file sharing, free file sharing, file transfer, <meta name="keywords" content="file sharing, free file sharing, file transfer,

View File

@@ -8,7 +8,7 @@ import UserManagement from "./UserManagement.svelte";
import EmailReporters from "./EmailReporters.svelte"; import EmailReporters from "./EmailReporters.svelte";
import MollieSettlements from "./MollieSettlements.svelte"; import MollieSettlements from "./MollieSettlements.svelte";
import PayPalTaxes from "./PayPalTaxes.svelte"; import PayPalTaxes from "./PayPalTaxes.svelte";
import UserBans from "./UserBans.svelte"; import UserBans from "./user_bans/UserBans.svelte";
let pages = [ let pages = [
{ {

View File

@@ -0,0 +1,64 @@
<script>
import Euro from "../../util/Euro.svelte";
import { formatDataVolume, formatDate } from "../../util/Formatting.svelte";
export let row = {}
</script>
<table>
<tr>
<td>Username</td>
<td>{row.user.username}</td>
</tr>
<tr>
<td>ID</td>
<td>{row.user_id}</td>
</tr>
<tr>
<td>Email</td>
<td>{row.user.email}</td>
</tr>
<tr>
<td>Subscription</td>
<td>{row.user.subscription.name}</td>
</tr>
<tr>
<td>Credit balance</td>
<td><Euro amount={row.user.balance_micro_eur}/></td>
</tr>
<tr>
<td>Storage used</td>
<td>{formatDataVolume(row.user.storage_space_used, 3)}</td>
</tr>
<tr>
<td>FS Storage used</td>
<td>{formatDataVolume(row.user.filesystem_storage_used, 3)}</td>
</tr>
</table>
<br/>
<div class="table_scroll">
<table>
<tr>
<td>Reason</td>
<td>Reporter</td>
<td>Ban time</td>
<td>Expire time</td>
<td>File</td>
</tr>
{#each row.offences as offence (offence.ban_time)}
<tr>
<td>{offence.reason}</td>
<td>{offence.reporter}</td>
<td>{formatDate(offence.ban_time, true, true, false)}</td>
<td>{formatDate(offence.expire_time, true, true, false)}</td>
<td>
{#if offence.file_link}
<a href={offence.file_link} target="_blank" rel="noreferrer">
{offence.file_name}
</a>
{/if}
</td>
</tr>
{/each}
</table>
</div>

View File

@@ -1,10 +1,12 @@
<script> <script>
import { onMount } from "svelte"; import { onMount } from "svelte";
import { formatDataVolume, formatDate } from "../util/Formatting.svelte"; import { formatDate } from "../../util/Formatting.svelte";
import Expandable from "../util/Expandable.svelte"; import Expandable from "../../util/Expandable.svelte";
import LoadingIndicator from "../util/LoadingIndicator.svelte"; import LoadingIndicator from "../../util/LoadingIndicator.svelte";
import Button from "../layout/Button.svelte" import Button from "../../layout/Button.svelte"
import Euro from "../util/Euro.svelte" import UserFiles from "./UserFiles.svelte";
import BanDetails from "./BanDetails.svelte";
import UserLists from "./UserLists.svelte";
let loading = true let loading = true
let rows = [] let rows = []
@@ -66,6 +68,40 @@ const impersonate = async user_id => {
window.open("/user", "_blank") window.open("/user", "_blank")
} }
const block_all_files = async (row, reason) => {
const form = new FormData()
form.append("user_id", row.user_id)
form.append("abuse_type", reason)
loading = true;
try {
const req = await fetch(
window.api_endpoint+"/admin/block_user_files",
{ method: "POST", body: form }
);
if(req.status >= 400) {
alert(await req.text())
return
}
const resp = await req.json()
if (reason === "none") {
alert("Restored "+resp.files_blocked.length+" files")
} else {
alert("Blocked "+resp.files_blocked.length+" files")
}
if (row.tab_element && row.tab_element.reload) {
row.tab_element.reload()
}
} catch (err) {
alert(err);
} finally {
loading = false;
}
}
onMount(get_bans); onMount(get_bans);
</script> </script>
@@ -112,64 +148,63 @@ onMount(get_bans);
</button> </button>
</div> </div>
<div class="toolbar">
<Button click={() => impersonate(row.user_id)} icon="login" label="Impersonate user"/> <Button click={() => impersonate(row.user_id)} icon="login" label="Impersonate user"/>
<table> <div class="toolbar_spacer"></div>
<tr> <div class="toolbar_label">
<td>Username</td> <i class="icon">block</i> Block all files
<td>{row.user.username}</td> <select bind:value={row.select_abuse_type}>
</tr> <option>copyright</option>
<tr> <option>child_abuse</option>
<td>ID</td> <option>zoophilia</option>
<td>{row.user_id}</td> <option>terrorism</option>
</tr> <option>gore</option>
<tr> <option>malware</option>
<td>Email</td> <option>doxing</option>
<td>{row.user.email}</td> <option>revenge_porn</option>
</tr> </select>
<tr> <Button
<td>Subscription</td> click={() => block_all_files(row, row.select_abuse_type)}
<td>{row.user.subscription.name}</td> label="Go"
</tr> />
<tr>
<td>Credit balance</td>
<td><Euro amount={row.user.balance_micro_eur}/></td>
</tr>
<tr>
<td>Storage used</td>
<td>{formatDataVolume(row.user.storage_space_used, 3)}</td>
</tr>
<tr>
<td>FS Storage used</td>
<td>{formatDataVolume(row.user.filesystem_storage_used, 3)}</td>
</tr>
</table>
<br/>
<div class="table_scroll">
<table>
<tr>
<td>Reason</td>
<td>Reporter</td>
<td>Ban time</td>
<td>Expire time</td>
<td>File</td>
</tr>
{#each row.offences as offence (offence.ban_time)}
<tr>
<td>{offence.reason}</td>
<td>{offence.reporter}</td>
<td>{formatDate(offence.ban_time, true, true, false)}</td>
<td>{formatDate(offence.expire_time, true, true, false)}</td>
<td>
{#if offence.file_link}
<a href={offence.file_link} target="_blank" rel="noreferrer">
{offence.file_name}
</a>
{/if}
</td>
</tr>
{/each}
</table>
</div> </div>
<div class="toolbar_spacer"></div>
<Button
click={() => block_all_files(row, "none")}
icon="undo"
label="Restore all files"
/>
</div>
<div class="tab_bar">
<Button
icon="person"
label="User details"
highlight={row.tab === undefined || row.tab === "user"}
click={() => row.tab = "user"}
/>
<Button
icon="image"
label="Files"
highlight={row.tab === "files"}
click={() => row.tab = "files"}
/>
<Button
icon="photo_library"
label="Lists"
highlight={row.tab === "lists"}
click={() => row.tab = "lists"}
/>
</div>
{#if row.tab === undefined || row.tab === "user"}
<BanDetails row={row} />
{:else if row.tab === "files"}
<UserFiles bind:this={row.tab_element} user_id={row.user_id} />
{:else if row.tab === "lists"}
<UserLists bind:this={row.tab_element} user_id={row.user_id} />
{/if}
</Expandable> </Expandable>
{/each} {/each}
</section> </section>
@@ -177,6 +212,7 @@ onMount(get_bans);
<style> <style>
.toolbar { .toolbar {
display: flex; display: flex;
flex-wrap: wrap;
flex-direction: row; flex-direction: row;
width: 100%; width: 100%;
text-align: left; text-align: left;
@@ -184,7 +220,10 @@ onMount(get_bans);
} }
.toolbar > * { flex: 0 0 auto; } .toolbar > * { flex: 0 0 auto; }
.toolbar_spacer { flex: 1 1 auto; } .toolbar_spacer { flex: 1 1 auto; }
.toolbar_label { margin: 5px; } .toolbar_label {
display: block;
margin: 5px;
}
.header { .header {
@@ -204,4 +243,7 @@ onMount(get_bans);
border-left: 1px solid var(--separator); border-left: 1px solid var(--separator);
text-align: center; text-align: center;
} }
.tab_bar {
border-bottom: 2px solid var(--separator);
}
</style> </style>

View File

@@ -0,0 +1,120 @@
<script>
import { onMount } from "svelte";
import LoadingIndicator from "../../util/LoadingIndicator.svelte";
import { formatDataVolume, formatDate } from "../../util/Formatting.svelte";
import SortButton from "../SortButton.svelte";
export let user_id = ""
let files = []
let loading = true
onMount(() => reload())
export const reload = async () => {
try {
const req = await fetch(
window.api_endpoint+"/user/files",
{
headers: {
"Admin-User-Override": user_id,
}
}
);
if(req.status >= 400) {
alert(await req.text())
return
}
files = (await req.json()).files
sort("")
} catch (err) {
alert(err);
} finally {
loading = false;
}
}
let sort_field = "date_upload"
let asc = false
const sort = (field) => {
if (field !== "" && field === sort_field) {
asc = !asc
}
if (field === "") {
field = sort_field
}
sort_field = field
console.log("sorting by", field, "asc", asc)
files.sort((a, b) => {
if (typeof (a[field]) === "number") {
// Sort ints from high to low
if (asc) {
return a[field] - b[field]
} else {
return b[field] - a[field]
}
} else {
// Sort strings alphabetically
if (asc) {
return a[field].localeCompare(b[field])
} else {
return b[field].localeCompare(a[field])
}
}
})
files = files
}
</script>
<LoadingIndicator loading={loading}/>
<div class="table_scroll">
<table>
<thead>
<tr>
<td></td>
<td><SortButton field="name" active_field={sort_field} asc={asc} sort_func={sort}>Name</SortButton></td>
<td><SortButton field="abuse_type" active_field={sort_field} asc={asc} sort_func={sort}>Abuse</SortButton></td>
<td><SortButton field="size" active_field={sort_field} asc={asc} sort_func={sort}>Size</SortButton></td>
<td><SortButton field="views" active_field={sort_field} asc={asc} sort_func={sort}>V</SortButton></td>
<td><SortButton field="downloads" active_field={sort_field} asc={asc} sort_func={sort}>DL</SortButton></td>
<td><SortButton field="date_upload" active_field={sort_field} asc={asc} sort_func={sort}>Created</SortButton></td>
</tr>
</thead>
<tbody>
{#each files as file (file.id)}
<tr>
<td style="padding: 0;">
<img src="{window.api_endpoint+file.thumbnail_href}?height=32&width=32" alt="thumbnail" class="thumbnail" />
</td>
<td>
<a href="/u/{file.id}" target="_blank">{file.name}</a>
</td>
<td>
{file.abuse_type}
</td>
<td>
{formatDataVolume(file.size, 3)}
</td>
<td>
{file.views}
</td>
<td>
{file.downloads}
</td>
<td>
{formatDate(file.date_upload, true, true)}
</td>
</tr>
{/each}
</tbody>
</table>
</div>
<style>
.thumbnail {
width: 32px;
height: 32px;
}
</style>

View File

@@ -0,0 +1,74 @@
<script>
import { onMount } from "svelte";
import LoadingIndicator from "../../util/LoadingIndicator.svelte";
import { formatDataVolume, formatDate } from "../../util/Formatting.svelte";
export let user_id = ""
let lists = []
let loading = true
onMount(() => reload())
export const reload = async () => {
try {
const req = await fetch(
window.api_endpoint+"/user/lists",
{
headers: {
"Admin-User-Override": user_id,
}
}
);
if(req.status >= 400) {
alert(await req.text())
return
}
lists = (await req.json()).lists
} catch (err) {
alert(err);
} finally {
loading = false;
}
}
</script>
<LoadingIndicator loading={loading}/>
<div class="table_scroll">
<table>
<thead>
<tr>
<td></td>
<td>Name</td>
<td>Files</td>
<td>Created</td>
</tr>
</thead>
<tbody>
{#each lists as list (list.id)}
<tr>
<td style="padding: 0;">
<img src="{window.api_endpoint}/list/{list.id}/thumbnail?height=32&width=32" alt="thumbnail" class="thumbnail" />
</td>
<td>
<a href="/l/{list.id}" target="_blank">{list.title}</a>
</td>
<td>
{list.file_count}
</td>
<td>
{formatDate(list.date_created, true, true, true)}
</td>
</tr>
{/each}
</tbody>
</table>
</div>
<style>
.thumbnail {
width: 32px;
height: 32px;
}
</style>