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.
* **Gore** - Graphic and shocking videos or images depicting severe harm to
humans (or animals). I will clarify that I am not strictly against shocking
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.
humans (or animals).
* **Malware and computer viruses** - Software programs designed to cause harm to
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="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.
pixeldrain also supports previews for images, videos, audio, PDFs and much more." />
<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 MollieSettlements from "./MollieSettlements.svelte";
import PayPalTaxes from "./PayPalTaxes.svelte";
import UserBans from "./UserBans.svelte";
import UserBans from "./user_bans/UserBans.svelte";
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>
import { onMount } from "svelte";
import { formatDataVolume, formatDate } from "../util/Formatting.svelte";
import Expandable from "../util/Expandable.svelte";
import LoadingIndicator from "../util/LoadingIndicator.svelte";
import Button from "../layout/Button.svelte"
import Euro from "../util/Euro.svelte"
import { formatDate } from "../../util/Formatting.svelte";
import Expandable from "../../util/Expandable.svelte";
import LoadingIndicator from "../../util/LoadingIndicator.svelte";
import Button from "../../layout/Button.svelte"
import UserFiles from "./UserFiles.svelte";
import BanDetails from "./BanDetails.svelte";
import UserLists from "./UserLists.svelte";
let loading = true
let rows = []
@@ -66,6 +68,40 @@ const impersonate = async user_id => {
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);
</script>
@@ -112,64 +148,63 @@ onMount(get_bans);
</button>
</div>
<Button click={() => impersonate(row.user_id)} icon="login" label="Impersonate user"/>
<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 class="toolbar">
<Button click={() => impersonate(row.user_id)} icon="login" label="Impersonate user"/>
<div class="toolbar_spacer"></div>
<div class="toolbar_label">
<i class="icon">block</i> Block all files
<select bind:value={row.select_abuse_type}>
<option>copyright</option>
<option>child_abuse</option>
<option>zoophilia</option>
<option>terrorism</option>
<option>gore</option>
<option>malware</option>
<option>doxing</option>
<option>revenge_porn</option>
</select>
<Button
click={() => block_all_files(row, row.select_abuse_type)}
label="Go"
/>
</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>
{/each}
</section>
@@ -177,6 +212,7 @@ onMount(get_bans);
<style>
.toolbar {
display: flex;
flex-wrap: wrap;
flex-direction: row;
width: 100%;
text-align: left;
@@ -184,7 +220,10 @@ onMount(get_bans);
}
.toolbar > * { flex: 0 0 auto; }
.toolbar_spacer { flex: 1 1 auto; }
.toolbar_label { margin: 5px; }
.toolbar_label {
display: block;
margin: 5px;
}
.header {
@@ -204,4 +243,7 @@ onMount(get_bans);
border-left: 1px solid var(--separator);
text-align: center;
}
.tab_bar {
border-bottom: 2px solid var(--separator);
}
</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>