add report page

This commit is contained in:
bytedream 2023-09-28 01:12:06 +02:00
parent c57e514613
commit 37c230575d
15 changed files with 280 additions and 9 deletions

View File

@ -10,6 +10,7 @@
export let required = false;
export let disabled = false;
export let size: 'xs' | 'sm' | 'md' | 'lg' = 'md';
export let pickyWidth = true;
export let inputElement: HTMLInputElement | undefined = undefined;
@ -60,7 +61,10 @@
</span>
</label>
{/if}
<div class="relative flex items-center" class:sm:max-w-[16rem]={type !== 'checkbox'}>
<div
class="relative flex items-center"
class:sm:max-w-[16rem]={type !== 'checkbox' && pickyWidth}
>
<input
class:checkbox={type === 'checkbox'}
class:checkbox-xs={type === 'checkbox' && size === 'xs'}

View File

@ -0,0 +1,42 @@
<script lang="ts">
export let id: string | null = null;
export let name: string | null = null;
export let value: string | null = null;
export let label: string | null = null;
export let notice: string | null = null;
export let required = false;
export let disabled = false;
export let size: 'xs' | 'sm' | 'md' | 'lg' = 'md';
export let rows = 2;
</script>
<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}
<textarea
class="textarea textarea-bordered w-full"
class:textarea-xs={size === 'xs'}
class:textarea-sm={size === 'sm'}
class:textarea-md={size === 'md'}
class:textarea-lg={size === 'lg'}
{id}
{name}
{required}
{disabled}
{rows}
bind:value
/>
{#if notice}
<label class="label" for={id}>
<span class="label-text-alt">{notice}</span>
</label>
{/if}
</div>

View File

@ -5,15 +5,17 @@ import * as bcrypt from 'bcrypt';
import {
BeforeCreate,
BeforeUpdate,
BelongsTo,
Column,
ForeignKey,
Index,
Model,
Sequelize,
Table,
Unique
Table
} from 'sequelize-typescript';
import { Permissions } from '$lib/permissions';
@Table({ modelName: 'user' })
@Table({ modelName: 'user', underscored: true })
export class User extends Model {
@Column({ type: DataTypes.STRING, allowNull: false })
declare firstname: string;
@ -30,10 +32,37 @@ export class User extends Model {
@Column({ type: DataTypes.STRING })
declare password: string;
@Column({ type: DataTypes.UUIDV4 })
@Index
declare uuid: string;
}
@Table({ modelName: 'admin' })
@Table({ modelName: 'report', underscored: true })
export class Report extends Model {
@Column({ type: DataTypes.STRING, allowNull: false, unique: true })
@Index
declare url_id: string;
@Column({ type: DataTypes.STRING, allowNull: false })
declare subject: string;
@Column({ type: DataTypes.STRING })
declare body: string;
@Column({ type: DataTypes.BOOLEAN, allowNull: false })
declare draft: boolean;
@Column({ type: DataTypes.BOOLEAN, allowNull: false })
declare completed: boolean;
@Column({ type: DataTypes.INTEGER, allowNull: false })
@ForeignKey(() => User)
declare reporter_user_id: number;
@Column({ type: DataTypes.INTEGER, allowNull: false })
@ForeignKey(() => User)
declare reported_user_id: number;
@BelongsTo(() => User)
declare reporter: User;
@BelongsTo(() => User)
declare reported: User;
}
@Table({ modelName: 'admin', underscored: true })
export class Admin extends Model {
@Column({ type: DataTypes.STRING, allowNull: false, unique: true })
declare username: string;
@ -68,5 +97,5 @@ export class Admin extends Model {
export const sequelize = new Sequelize(building ? 'sqlite::memory:' : env.DATABASE_URI, {
// only log sql queries in dev mode
logging: dev ? console.log : false,
models: [User, Admin]
models: [User, Report, Admin]
});

View File

@ -1,3 +1,3 @@
<div class="flex justify-center items-center w-full">
<div class="flex justify-center w-full">
<slot />
</div>

View File

@ -0,0 +1,5 @@
<div class="h-screen flex justify-center items-center">
<h1 class="text-4xl">
Reports können nur ingame mittels des <code>/report</code> Befehls erstellt werden
</h1>
</div>

View File

@ -0,0 +1,32 @@
import type { RequestHandler } from '@sveltejs/kit';
import { Report, User } from '$lib/server/database';
import * as crypto from 'crypto';
export const POST = (async ({ request, url }) => {
const data: { reporter: string; reported: string; reason: string } = await request.json();
if (data.reporter == undefined || data.reported == undefined || data.reason == 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: null,
draft: true,
url_id: crypto.randomBytes(18).toString('hex'),
completed: false,
reporter_user_id: reporter.id,
reported_user_id: reported.id
});
return new Response(
JSON.stringify({ url: `${url.toString().replace(/\/$/, '')}/${report.url_id}` }),
{
status: 201
}
);
}) satisfies RequestHandler;

View File

@ -0,0 +1,3 @@
<div class="flex justify-center items-center w-full min-h-screen h-full">
<slot />
</div>

View File

@ -0,0 +1,28 @@
import type { PageServerLoad } from './$types';
import { Report, User } from '$lib/server/database';
import { redirect } from '@sveltejs/kit';
import { env } from '$env/dynamic/public';
export const load: PageServerLoad = async ({ params }) => {
const report = await Report.findOne({
where: { url_id: params.url_id },
include: [
{ model: User, as: 'reporter' },
{ model: User, as: 'reported' }
]
});
if (report == null) throw redirect(302, `${env.PUBLIC_BASE_PATH}/`);
return {
draft: report.draft,
completed: report.completed,
reason: report.subject,
reporter: {
name: report.reporter.username
},
reported: {
name: report.reported.username
}
};
};

View File

@ -0,0 +1,32 @@
<script lang="ts">
import { fly } from 'svelte/transition';
import type { PageData } from './$types';
import ReportDraft from './ReportDraft.svelte';
import ReportCompleted from './ReportCompleted.svelte';
import ReportSubmitted from './ReportSubmitted.svelte';
export let data: PageData;
</script>
<svelte:head>
<!-- just in case... -->
<meta name="robots" content="noindex" />
</svelte:head>
<div class="absolute top-12 grid card w-11/12 xl:w-2/3 2xl:w-1/2 p-6 shadow-lg">
{#if data.draft}
<div class="col-[1] row-[1]" transition:fly={{ x: -200, duration: 300 }}>
<ReportDraft
reason={data.reason}
reportedName={data.reported.name}
on:submit={() => (data.draft = false)}
/>
</div>
{:else if data.completed}
<ReportCompleted />
{:else}
<div class="col-[1] row-[1]" transition:fly={{ x: 200, duration: 300 }}>
<ReportSubmitted />
</div>
{/if}
</div>

View File

@ -0,0 +1,13 @@
import type { RequestHandler } from '@sveltejs/kit';
import { Report } from '$lib/server/database';
export const POST = (async ({ params }) => {
const report = await Report.findOne({ where: { url_id: params.url_id } });
if (report == null) return new Response(null, { status: 400 });
report.draft = false;
await report.save();
return new Response(null, { status: 200 });
}) satisfies RequestHandler;

View File

@ -0,0 +1,3 @@
<div>
<h2 class="text-xl text-center">Dieser Report wurde von einem Admin bearbeitet</h2>
</div>

View File

@ -0,0 +1,67 @@
<script lang="ts">
import Textarea from '$lib/components/Input/Textarea.svelte';
import Input from '$lib/components/Input/Input.svelte';
import { env } from '$env/dynamic/public';
import { page } from '$app/stores';
import { createEventDispatcher } from 'svelte';
export let reportedName: string;
export let reason: string;
async function submitReport() {
await fetch(`${env.PUBLIC_BASE_PATH}/report/${$page.params.url_id}`, {
method: 'POST'
});
}
let dispatch = createEventDispatcher();
let submitModal: HTMLDialogElement;
</script>
<div>
<h2 class="text-3xl text-center">Report für <code>{reportedName}</code></h2>
<form on:submit|preventDefault={() => submitModal.show()}>
<div class="space-y-4 my-4">
<div>
<Input type="text" value={reason} required={true} pickyWidth={false}>
<span slot="label">Report Grund</span>
</Input>
</div>
<div>
<Textarea required={true} rows={4} label="Details über den Report Grund" />
</div>
</div>
<div>
<Input type="submit" value="Reporten" />
</div>
</form>
</div>
<dialog class="modal" bind:this={submitModal}>
<form method="dialog" class="modal-box">
<button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"></button>
<div>
<h3 class="font-roboto font-medium text-xl">Report abschicken?</h3>
<div class="my-4">
<p>
Nach dem Abschicken des Reports wird sich ein Moderator schnellstmöglich darum kümmern.
</p>
</div>
<div class="flex flex-row space-x-1">
<Input
type="submit"
value="Abschicken"
on:click={async () => {
await submitReport();
dispatch('submit');
}}
/>
<Input type="submit" value="Abbrechen" />
</div>
</div>
</form>
<form method="dialog" class="modal-backdrop bg-[rgba(0,0,0,.3)]">
<button>close</button>
</form>
</dialog>

View File

@ -0,0 +1,6 @@
<div>
<h2 class="text-2xl text-center">Report abgeschickt</h2>
<p class="mt-4">
Dein Report wurde abgeschickt und wird so schnell wie möglich von einem Admin bearbeitet.
</p>
</div>

View File

@ -11,7 +11,10 @@ const config = {
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
// If your environment is not supported or you settled on a specific environment, switch out the adapter.
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
adapter: adapter()
adapter: adapter(),
csrf: {
checkOrigin: false
}
}
};

View File

@ -8,5 +8,9 @@ export default {
roboto: ['Roboto']
}
},
plugins: [require('daisyui')]
plugins: [require('daisyui')],
daisyui: {
logs: false
}
};