add option to create a report via the admin panel
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				delpoy / build-and-deploy (push) Successful in 47s
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	delpoy / build-and-deploy (push) Successful in 47s
				
			This commit is contained in:
		
							
								
								
									
										78
									
								
								src/lib/components/Input/Search.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								src/lib/components/Input/Search.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,78 @@ | |||||||
|  | <script lang="ts"> | ||||||
|  | 	export let id: string | null = null; | ||||||
|  | 	export let value = ''; | ||||||
|  | 	export let suggestionRequired = false; | ||||||
|  | 	export let searchSuggestionFunc: ( | ||||||
|  | 		input: string | ||||||
|  | 	) => Promise<{ name: string; value: string }[]> = () => Promise.resolve([]); | ||||||
|  | 	export let size: 'xs' | 'sm' | 'md' | 'lg' = 'md'; | ||||||
|  | 	export let label: string | null = null; | ||||||
|  | 	export let required = false; | ||||||
|  |  | ||||||
|  | 	let inputValue: string; | ||||||
|  | 	let searchSuggestions: { name: string; value: string }[] = []; | ||||||
|  | 	$: if (!suggestionRequired) value = inputValue; | ||||||
|  | </script> | ||||||
|  |  | ||||||
|  | <div class="relative"> | ||||||
|  | 	<div> | ||||||
|  | 		{#if label} | ||||||
|  | 			<label class="label" for={id}> | ||||||
|  | 				<span class="label-text"> | ||||||
|  | 					{label} | ||||||
|  | 					{#if required} | ||||||
|  | 						<span class="text-red-700">*</span> | ||||||
|  | 					{/if} | ||||||
|  | 				</span> | ||||||
|  | 			</label> | ||||||
|  | 		{/if} | ||||||
|  | 		<input | ||||||
|  | 			type="search" | ||||||
|  | 			class="input input-bordered w-full" | ||||||
|  | 			class:input-xs={size === 'xs'} | ||||||
|  | 			class:input-sm={size === 'sm'} | ||||||
|  | 			class:input-md={size === 'md'} | ||||||
|  | 			class:input-lg={size === 'md'} | ||||||
|  | 			{id} | ||||||
|  | 			{required} | ||||||
|  | 			bind:value={inputValue} | ||||||
|  | 			on:input={() => { | ||||||
|  | 				value = ''; | ||||||
|  | 				searchSuggestionFunc(inputValue).then((v) => { | ||||||
|  | 					searchSuggestions = v; | ||||||
|  | 					let searchSuggestionValue = v.find((v) => v.name === inputValue); | ||||||
|  | 					if (searchSuggestionValue !== undefined) { | ||||||
|  | 						value = searchSuggestionValue.value; | ||||||
|  | 						searchSuggestions = []; | ||||||
|  | 					} | ||||||
|  | 				}); | ||||||
|  | 			}} | ||||||
|  | 			pattern={suggestionRequired ? `${value ? inputValue : 'a^'}` : null} | ||||||
|  | 		/> | ||||||
|  | 	</div> | ||||||
|  |  | ||||||
|  | 	{#if inputValue && searchSuggestions.length !== 0} | ||||||
|  | 		<ul class="absolute bg-base-200 w-full z-20 menu menu-sm rounded-box"> | ||||||
|  | 			{#each searchSuggestions as searchSuggestion} | ||||||
|  | 				<li class="w-full text-left"> | ||||||
|  | 					<button | ||||||
|  | 						class="block w-full overflow-hidden text-ellipsis whitespace-nowrap" | ||||||
|  | 						title="{searchSuggestion.name} ({searchSuggestion.value})" | ||||||
|  | 						on:click|preventDefault={() => { | ||||||
|  | 							inputValue = searchSuggestion.name; | ||||||
|  | 							value = searchSuggestion.value; | ||||||
|  | 							searchSuggestions = []; | ||||||
|  | 						}}>{searchSuggestion.name}</button | ||||||
|  | 					> | ||||||
|  | 				</li> | ||||||
|  | 			{/each} | ||||||
|  | 		</ul> | ||||||
|  | 	{/if} | ||||||
|  | </div> | ||||||
|  |  | ||||||
|  | {#if inputValue && searchSuggestions.length !== 0} | ||||||
|  | 	<button | ||||||
|  | 		class="absolute top-0 left-0 z-10 w-full h-full cursor-default" | ||||||
|  | 		on:click={() => (searchSuggestions = [])} | ||||||
|  | 	/> | ||||||
|  | {/if} | ||||||
| @@ -8,6 +8,9 @@ | |||||||
| 	import Input from '$lib/components/Input/Input.svelte'; | 	import Input from '$lib/components/Input/Input.svelte'; | ||||||
| 	import Textarea from '$lib/components/Input/Textarea.svelte'; | 	import Textarea from '$lib/components/Input/Textarea.svelte'; | ||||||
| 	import { reportCount } from '$lib/stores'; | 	import { reportCount } from '$lib/stores'; | ||||||
|  | 	import HeaderBar from './HeaderBar.svelte'; | ||||||
|  | 	import { IconOutline } from 'svelte-heros-v2'; | ||||||
|  | 	import NewReportModal from './NewReportModal.svelte'; | ||||||
|  |  | ||||||
| 	export let data: PageData; | 	export let data: PageData; | ||||||
|  |  | ||||||
| @@ -47,29 +50,12 @@ | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	let saveActiveReportChangesModal: HTMLDialogElement; | 	let saveActiveReportChangesModal: HTMLDialogElement; | ||||||
|  | 	let newReportModal: HTMLDialogElement; | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
| <div class="h-screen flex flex-row"> | <div class="h-screen flex flex-row"> | ||||||
| 	<div class="w-full flex flex-col"> | 	<div class="w-full flex flex-col"> | ||||||
| 		<form class="flex flex-row justify-center space-x-4 mx-4 my-2"> | 		<HeaderBar bind:reportFilter /> | ||||||
| 			<Input size="sm" placeholder="Alle" bind:value={reportFilter.reporter}> |  | ||||||
| 				<span slot="label">Report Ersteller</span> |  | ||||||
| 			</Input> |  | ||||||
| 			<Input size="sm" placeholder="Alle" bind:value={reportFilter.reported}> |  | ||||||
| 				<span slot="label">Reportete Spieler</span> |  | ||||||
| 			</Input> |  | ||||||
| 			<Select label="Bearbeitungsstatus" size="sm" bind:value={reportFilter.status}> |  | ||||||
| 				<option value="none">Unbearbeitet</option> |  | ||||||
| 				<option value="review">In Bearbeitung</option> |  | ||||||
| 				<option value={null}>Unbearbeitet & In Bearbeitung</option> |  | ||||||
| 				<option value="reviewed">Bearbeitet</option> |  | ||||||
| 			</Select> |  | ||||||
| 			<Select label="Reportstatus" size="sm" bind:value={reportFilter.draft}> |  | ||||||
| 				<option value={false}>Erstellt</option> |  | ||||||
| 				<option value={true}>Entwurf</option> |  | ||||||
| 				<option value={null}>Erstellt & Entwurf</option> |  | ||||||
| 			</Select> |  | ||||||
| 		</form> |  | ||||||
| 		<hr class="divider my-1 mx-8 border-none" /> | 		<hr class="divider my-1 mx-8 border-none" /> | ||||||
| 		<table class="table table-fixed h-fit"> | 		<table class="table table-fixed h-fit"> | ||||||
| 			<colgroup> | 			<colgroup> | ||||||
| @@ -124,6 +110,16 @@ | |||||||
| 						<td>{report.draft ? 'Entwurf' : 'Erstellt'}</td> | 						<td>{report.draft ? 'Entwurf' : 'Erstellt'}</td> | ||||||
| 					</tr> | 					</tr> | ||||||
| 				{/each} | 				{/each} | ||||||
|  | 				<tr> | ||||||
|  | 					<td colspan="100"> | ||||||
|  | 						<div class="flex justify-center items-center"> | ||||||
|  | 							<button class="btn btn-sm" on:click={() => newReportModal.show()}> | ||||||
|  | 								<IconOutline name="plus-outline" /> | ||||||
|  | 								<span>Neuer Report</span> | ||||||
|  | 							</button> | ||||||
|  | 						</div> | ||||||
|  | 					</td> | ||||||
|  | 				</tr> | ||||||
| 			</tbody> | 			</tbody> | ||||||
| 		</table> | 		</table> | ||||||
| 	</div> | 	</div> | ||||||
| @@ -162,7 +158,7 @@ | |||||||
| 				> | 				> | ||||||
| 					<Textarea | 					<Textarea | ||||||
| 						label="Interne Notizen" | 						label="Interne Notizen" | ||||||
| 						readonly={activeReport.auditor === null && activeReport.notice === null} | 						readonly={activeReport.status === 'none'} | ||||||
| 						rows={1} | 						rows={1} | ||||||
| 						bind:value={activeReport.notice} | 						bind:value={activeReport.notice} | ||||||
| 					/> | 					/> | ||||||
| @@ -175,13 +171,15 @@ | |||||||
| 				> | 				> | ||||||
| 					<Textarea | 					<Textarea | ||||||
| 						label="(Öffentliche) Report Antwort" | 						label="(Öffentliche) Report Antwort" | ||||||
| 						readonly={activeReport.auditor === null && activeReport.notice === null} | 						readonly={activeReport.status === 'none'} | ||||||
| 						rows={3} | 						rows={3} | ||||||
| 						bind:value={activeReport.statement} | 						bind:value={activeReport.statement} | ||||||
| 					/> | 					/> | ||||||
| 				</div> | 				</div> | ||||||
| 				<Select label="Bearbeitungsstatus" size="sm" bind:value={activeReport.status}> | 				<Select label="Bearbeitungsstatus" size="sm" bind:value={activeReport.status}> | ||||||
| 					<option value="none" disabled={activeReport.auditor != null || activeReport.notice} | 					<option | ||||||
|  | 						value="none" | ||||||
|  | 						disabled={activeReport.auditor != null || activeReport.notice || activeReport.statement} | ||||||
| 						>Unbearbeitet</option | 						>Unbearbeitet</option | ||||||
| 					> | 					> | ||||||
| 					<option value="review">In Bearbeitung</option> | 					<option value="review">In Bearbeitung</option> | ||||||
| @@ -227,3 +225,13 @@ | |||||||
| 		<button>close</button> | 		<button>close</button> | ||||||
| 	</form> | 	</form> | ||||||
| </dialog> | </dialog> | ||||||
|  |  | ||||||
|  | <dialog class="modal" bind:this={newReportModal}> | ||||||
|  | 	<NewReportModal | ||||||
|  | 		on:submit={(e) => { | ||||||
|  | 			if (!e.detail.draft) $reportCount += 1; | ||||||
|  | 			currentPageReports = [e.detail, ...currentPageReports]; | ||||||
|  | 			activeReport = currentPageReports[0]; | ||||||
|  | 		}} | ||||||
|  | 	/> | ||||||
|  | </dialog> | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ import { Admin, Report, User } from '$lib/server/database'; | |||||||
| import type { Attributes } from 'sequelize'; | import type { Attributes } from 'sequelize'; | ||||||
| import { Op } from 'sequelize'; | import { Op } from 'sequelize'; | ||||||
| import { env } from '$env/dynamic/private'; | import { env } from '$env/dynamic/private'; | ||||||
|  | import crypto from 'crypto'; | ||||||
|  |  | ||||||
| export const POST = (async ({ request, cookies }) => { | export const POST = (async ({ request, cookies }) => { | ||||||
| 	if (getSession(cookies, { permissions: [Permissions.ReportRead] }) == null) { | 	if (getSession(cookies, { permissions: [Permissions.ReportRead] }) == null) { | ||||||
| @@ -104,3 +105,49 @@ export const PATCH = (async ({ request, cookies }) => { | |||||||
|  |  | ||||||
| 	return new Response(); | 	return new Response(); | ||||||
| }) satisfies RequestHandler; | }) satisfies RequestHandler; | ||||||
|  |  | ||||||
|  | export const PUT = (async ({ request, cookies, url }) => { | ||||||
|  | 	if (getSession(cookies, { permissions: [Permissions.ReportWrite] }) == null) { | ||||||
|  | 		return new Response(null, { | ||||||
|  | 			status: 401 | ||||||
|  | 		}); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	const data: { | ||||||
|  | 		reporter: string; | ||||||
|  | 		reported: string; | ||||||
|  | 		reason: string; | ||||||
|  | 		body: string | null; | ||||||
|  | 	} = await request.json(); | ||||||
|  |  | ||||||
|  | 	if ( | ||||||
|  | 		data.reporter == null || | ||||||
|  | 		data.reported == null || | ||||||
|  | 		data.reason == null || | ||||||
|  | 		data.body === undefined | ||||||
|  | 	) | ||||||
|  | 		return new Response(null, { status: 400 }); | ||||||
|  |  | ||||||
|  | 	const reporter = await User.findOne({ where: { uuid: data.reporter } }); | ||||||
|  | 	const reported = await User.findOne({ where: { uuid: data.reported } }); | ||||||
|  |  | ||||||
|  | 	if (reporter == null || reported == null) return new Response(null, { status: 400 }); | ||||||
|  |  | ||||||
|  | 	const report = await Report.create({ | ||||||
|  | 		subject: data.reason, | ||||||
|  | 		body: data.body, | ||||||
|  | 		draft: data.body === null, | ||||||
|  | 		status: 'none', | ||||||
|  | 		url_hash: crypto.randomBytes(18).toString('hex'), | ||||||
|  | 		completed: false, | ||||||
|  | 		reporter_id: reporter.id, | ||||||
|  | 		reported_id: reported.id | ||||||
|  | 	}); | ||||||
|  | 	report.dataValues.reporter = await User.findOne({ where: { id: report.reporter_id } }); | ||||||
|  | 	report.dataValues.reported = await User.findOne({ where: { id: report.reported_id } }); | ||||||
|  | 	report.dataValues.auditor = null; | ||||||
|  |  | ||||||
|  | 	return new Response(JSON.stringify(report), { | ||||||
|  | 		status: 201 | ||||||
|  | 	}); | ||||||
|  | }) satisfies RequestHandler; | ||||||
|   | |||||||
							
								
								
									
										33
									
								
								src/routes/admin/reports/HeaderBar.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/routes/admin/reports/HeaderBar.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | |||||||
|  | <script lang="ts"> | ||||||
|  | 	import Select from '$lib/components/Input/Select.svelte'; | ||||||
|  | 	import Input from '$lib/components/Input/Input.svelte'; | ||||||
|  |  | ||||||
|  | 	export let reportFilter = { | ||||||
|  | 		reporter: null, | ||||||
|  | 		reported: null, | ||||||
|  | 		status: null, | ||||||
|  | 		draft: false | ||||||
|  | 	}; | ||||||
|  | </script> | ||||||
|  |  | ||||||
|  | <div class="flex flex-row justify-center items-end"> | ||||||
|  | 	<form class="flex flex-row justify-center space-x-4 mx-4 my-2"> | ||||||
|  | 		<Input size="sm" placeholder="Alle" bind:value={reportFilter.reporter}> | ||||||
|  | 			<span slot="label">Report Ersteller</span> | ||||||
|  | 		</Input> | ||||||
|  | 		<Input size="sm" placeholder="Alle" bind:value={reportFilter.reported}> | ||||||
|  | 			<span slot="label">Reportete Spieler</span> | ||||||
|  | 		</Input> | ||||||
|  | 		<Select label="Bearbeitungsstatus" size="sm" bind:value={reportFilter.status}> | ||||||
|  | 			<option value="none">Unbearbeitet</option> | ||||||
|  | 			<option value="review">In Bearbeitung</option> | ||||||
|  | 			<option value={null}>Unbearbeitet & In Bearbeitung</option> | ||||||
|  | 			<option value="reviewed">Bearbeitet</option> | ||||||
|  | 		</Select> | ||||||
|  | 		<Select label="Reportstatus" size="sm" bind:value={reportFilter.draft}> | ||||||
|  | 			<option value={false}>Erstellt</option> | ||||||
|  | 			<option value={true}>Entwurf</option> | ||||||
|  | 			<option value={null}>Erstellt & Entwurf</option> | ||||||
|  | 		</Select> | ||||||
|  | 	</form> | ||||||
|  | </div> | ||||||
							
								
								
									
										137
									
								
								src/routes/admin/reports/NewReportModal.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								src/routes/admin/reports/NewReportModal.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,137 @@ | |||||||
|  | <script lang="ts"> | ||||||
|  | 	import Input from '$lib/components/Input/Input.svelte'; | ||||||
|  | 	import { env } from '$env/dynamic/public'; | ||||||
|  | 	import Textarea from '$lib/components/Input/Textarea.svelte'; | ||||||
|  | 	import Search from '$lib/components/Input/Search.svelte'; | ||||||
|  | 	import { createEventDispatcher } from 'svelte'; | ||||||
|  |  | ||||||
|  | 	const dispatch = createEventDispatcher(); | ||||||
|  |  | ||||||
|  | 	let reporter: string; | ||||||
|  | 	let reported: string; | ||||||
|  | 	let reason = ''; | ||||||
|  | 	let body = ''; | ||||||
|  |  | ||||||
|  | 	async function usernameSuggestions(username: string): Promise<{ name: string; value: string }[]> { | ||||||
|  | 		const response = await fetch(`${env.PUBLIC_BASE_PATH}/admin/users`, { | ||||||
|  | 			method: 'POST', | ||||||
|  | 			body: JSON.stringify({ | ||||||
|  | 				limit: 6, | ||||||
|  | 				search: username, | ||||||
|  | 				slim: true | ||||||
|  | 			}) | ||||||
|  | 		}); | ||||||
|  | 		const json: { username: string; uuid: string }[] = await response.json(); | ||||||
|  | 		return json.map((v) => { | ||||||
|  | 			return { name: v.username, value: v.uuid }; | ||||||
|  | 		}); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	async function newReport() { | ||||||
|  | 		const response = await fetch(`${env.PUBLIC_BASE_PATH}/admin/reports`, { | ||||||
|  | 			method: 'PUT', | ||||||
|  | 			body: JSON.stringify({ | ||||||
|  | 				reporter: reporter, | ||||||
|  | 				reported: reported, | ||||||
|  | 				reason: reason, | ||||||
|  | 				body: body || null | ||||||
|  | 			}) | ||||||
|  | 		}); | ||||||
|  | 		if (response.ok) dispatch('submit', await response.json()); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	let globalCloseForm: HTMLFormElement; | ||||||
|  |  | ||||||
|  | 	let reportForm: HTMLFormElement; | ||||||
|  | 	let confirmDialog: HTMLDialogElement; | ||||||
|  | </script> | ||||||
|  |  | ||||||
|  | <form method="dialog" class="modal-box" bind:this={reportForm}> | ||||||
|  | 	<button | ||||||
|  | 		class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2" | ||||||
|  | 		on:click|preventDefault={() => globalCloseForm.submit()}>✕</button | ||||||
|  | 	> | ||||||
|  | 	<h3 class="font-roboto text-xl">Neuer Report</h3> | ||||||
|  | 	<div class="space-y-2 mt-2 px-1 max-h-[70vh] overflow-y-scroll"> | ||||||
|  | 		<div> | ||||||
|  | 			<Search | ||||||
|  | 				size="sm" | ||||||
|  | 				suggestionRequired={true} | ||||||
|  | 				searchSuggestionFunc={usernameSuggestions} | ||||||
|  | 				label="Reporter" | ||||||
|  | 				required={true} | ||||||
|  | 				bind:value={reporter} | ||||||
|  | 			/> | ||||||
|  | 			<Search | ||||||
|  | 				size="sm" | ||||||
|  | 				suggestionRequired={true} | ||||||
|  | 				searchSuggestionFunc={usernameSuggestions} | ||||||
|  | 				label="Reporteter User" | ||||||
|  | 				required={true} | ||||||
|  | 				bind:value={reported} | ||||||
|  | 			/> | ||||||
|  | 		</div> | ||||||
|  | 		<div class="divider mx-4 pt-3" /> | ||||||
|  | 		<Input type="text" bind:value={reason} required={true} pickyWidth={false}> | ||||||
|  | 			<span slot="label">Report Grund</span> | ||||||
|  | 		</Input> | ||||||
|  | 		<div> | ||||||
|  | 			<Textarea rows={4} label="Details über den Report Grund" bind:value={body} /> | ||||||
|  | 		</div> | ||||||
|  | 	</div> | ||||||
|  | 	<div class="flex flex-row space-x-2 mt-6"> | ||||||
|  | 		<Input | ||||||
|  | 			type="submit" | ||||||
|  | 			value="Erstellen" | ||||||
|  | 			on:click={(e) => { | ||||||
|  | 				if (reportForm.checkValidity()) { | ||||||
|  | 					e.detail.preventDefault(); | ||||||
|  | 					confirmDialog.show(); | ||||||
|  | 				} | ||||||
|  | 			}} | ||||||
|  | 		/> | ||||||
|  | 		<Input | ||||||
|  | 			type="submit" | ||||||
|  | 			value="Abbrechen" | ||||||
|  | 			on:click={(e) => { | ||||||
|  | 				e.detail.preventDefault(); | ||||||
|  | 				globalCloseForm.submit(); | ||||||
|  | 			}} | ||||||
|  | 		/> | ||||||
|  | 	</div> | ||||||
|  | </form> | ||||||
|  | <form method="dialog" class="modal-backdrop bg-[rgba(0,0,0,.3)]" bind:this={globalCloseForm}> | ||||||
|  | 	<button>close</button> | ||||||
|  | </form> | ||||||
|  |  | ||||||
|  | <dialog class="modal" bind:this={confirmDialog}> | ||||||
|  | 	<form method="dialog" class="modal-box"> | ||||||
|  | 		<button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">✕</button> | ||||||
|  | 		<h3 class="font-roboto text-xl mb-2">Report Erstellen?</h3> | ||||||
|  | 		{#if body} | ||||||
|  | 			<p> | ||||||
|  | 				Dadurch, dass bereits Details über den Report Grund hinzugefügt wurden, ist es nach dem | ||||||
|  | 				Erstellen nicht mehr möglich, den Report Inhalt zu ändern | ||||||
|  | 			</p> | ||||||
|  | 		{:else} | ||||||
|  | 			<p> | ||||||
|  | 				Der Report wird als Entwurf gespeichert und kann nach dem Erstellen über den Report-Link | ||||||
|  | 				bearbeitet werden | ||||||
|  | 			</p> | ||||||
|  | 		{/if} | ||||||
|  | 		<div class="flex flex-row space-x-2 mt-6"> | ||||||
|  | 			<Input | ||||||
|  | 				type="submit" | ||||||
|  | 				value="Erstellen" | ||||||
|  | 				on:click={async () => { | ||||||
|  | 					await newReport(); | ||||||
|  | 					globalCloseForm.submit(); | ||||||
|  | 				}} | ||||||
|  | 			/> | ||||||
|  | 			<Input type="submit" value="Abbrechen" /> | ||||||
|  | 		</div> | ||||||
|  | 	</form> | ||||||
|  | 	<form method="dialog" class="modal-backdrop bg-[rgba(0,0,0,.3)]"> | ||||||
|  | 		<button>close</button> | ||||||
|  | 	</form> | ||||||
|  | </dialog> | ||||||
| @@ -1,7 +1,8 @@ | |||||||
| import { getSession } from '$lib/server/session'; | import { getSession } from '$lib/server/session'; | ||||||
| import { Permissions } from '$lib/permissions'; | import { Permissions } from '$lib/permissions'; | ||||||
| import type { RequestHandler } from '@sveltejs/kit'; | import type { RequestHandler } from '@sveltejs/kit'; | ||||||
| import { Admin, User } from '$lib/server/database'; | import { User } from '$lib/server/database'; | ||||||
|  | import { type Attributes, Op } from 'sequelize'; | ||||||
|  |  | ||||||
| export const POST = (async ({ request, cookies }) => { | export const POST = (async ({ request, cookies }) => { | ||||||
| 	if (getSession(cookies, { permissions: [Permissions.UserRead] }) == null) { | 	if (getSession(cookies, { permissions: [Permissions.UserRead] }) == null) { | ||||||
| @@ -10,11 +11,28 @@ export const POST = (async ({ request, cookies }) => { | |||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	const data = await request.json(); | 	const data: { | ||||||
| 	const limit = data['limit'] || 100; | 		limit: number | null; | ||||||
| 	const from = data['from'] || 0; | 		from: number | null; | ||||||
|  | 		search: string | null; | ||||||
|  | 		slim: boolean | null; | ||||||
|  | 	} = await request.json(); | ||||||
|  |  | ||||||
| 	const users = await User.findAll({ offset: from, limit: limit }); | 	const usersFindOptions: Attributes<User> = {}; | ||||||
|  | 	if (data.search) { | ||||||
|  | 		Object.assign(usersFindOptions, { | ||||||
|  | 			[Op.or]: { | ||||||
|  | 				username: { [Op.like]: `%${data.search}%` }, | ||||||
|  | 				uuid: { [Op.like]: `%${data.search}%` } | ||||||
|  | 			} | ||||||
|  | 		}); | ||||||
|  | 	} | ||||||
|  | 	const users = await User.findAll({ | ||||||
|  | 		where: usersFindOptions, | ||||||
|  | 		attributes: data.slim ? ['username', 'uuid'] : undefined, | ||||||
|  | 		offset: data.from || 0, | ||||||
|  | 		limit: data.limit || 100 | ||||||
|  | 	}); | ||||||
|  |  | ||||||
| 	return new Response(JSON.stringify(users)); | 	return new Response(JSON.stringify(users)); | ||||||
| }) satisfies RequestHandler; | }) satisfies RequestHandler; | ||||||
|   | |||||||
| @@ -1,11 +1,12 @@ | |||||||
| import type { RequestHandler } from '@sveltejs/kit'; | import type { RequestHandler } from '@sveltejs/kit'; | ||||||
| import { Report, User } from '$lib/server/database'; | import { Report, User } from '$lib/server/database'; | ||||||
| import * as crypto from 'crypto'; | import * as crypto from 'crypto'; | ||||||
|  | import { env } from '$env/dynamic/public'; | ||||||
|  |  | ||||||
| export const POST = (async ({ request, url }) => { | export const POST = (async ({ request, url }) => { | ||||||
| 	const data: { reporter: string; reported: string; reason: string } = await request.json(); | 	const data: { reporter: string; reported: string; reason: string } = await request.json(); | ||||||
|  |  | ||||||
| 	if (data.reporter == undefined || data.reported == undefined || data.reason == undefined) | 	if (data.reporter == null || data.reported == null || data.reason == null) | ||||||
| 		return new Response(null, { status: 400 }); | 		return new Response(null, { status: 400 }); | ||||||
|  |  | ||||||
| 	const reporter = await User.findOne({ where: { uuid: data.reporter } }); | 	const reporter = await User.findOne({ where: { uuid: data.reporter } }); | ||||||
| @@ -25,7 +26,9 @@ export const POST = (async ({ request, url }) => { | |||||||
| 	}); | 	}); | ||||||
|  |  | ||||||
| 	return new Response( | 	return new Response( | ||||||
| 		JSON.stringify({ url: `${url.toString().replace(/\/$/, '')}/${report.url_hash}` }), | 		JSON.stringify({ | ||||||
|  | 			url: `${url.protocol}//${url.host}${env.PUBLIC_BASE_PATH || ''}/report/${report.url_hash}` | ||||||
|  | 		}), | ||||||
| 		{ | 		{ | ||||||
| 			status: 201 | 			status: 201 | ||||||
| 		} | 		} | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user