diff --git a/src/lib/server/database.ts b/src/lib/server/database.ts index dbcc9e7..0ecb50f 100644 --- a/src/lib/server/database.ts +++ b/src/lib/server/database.ts @@ -62,6 +62,9 @@ export class Report extends Model { @Column({ type: DataTypes.INTEGER }) @ForeignKey(() => Admin) declare auditor_id: number; + @Column({ type: DataTypes.INTEGER }) + @ForeignKey(() => StrikeReason) + declare strike_reason_id: number | null; @BelongsTo(() => User, 'reporter_id') declare reporter: User; @@ -69,6 +72,38 @@ export class Report extends Model { declare reported: User; @BelongsTo(() => Admin, 'auditor_id') declare auditor: Admin; + @BelongsTo(() => StrikeReason, 'strike_reason_id') + declare strike_reason: StrikeReason; +} + +@Table({ modelName: 'strike_reason', underscored: true }) +export class StrikeReason extends Model { + @Column({ type: DataTypes.INTEGER, allowNull: false }) + declare weight: number; + @Column({ type: DataTypes.STRING, allowNull: false }) + declare name: string; +} + +@Table({ modelName: 'strike_punishment', underscored: true }) +export class StrikePunishment extends Model { + @Column({ type: DataTypes.INTEGER, allowNull: false }) + declare weight: number; + @Column({ type: DataTypes.INTEGER, allowNull: false }) + declare ban_in_seconds: number; +} + +@Table({ modelName: 'strike', underscored: true }) +export class Strike extends Model { + @Column({ type: DataTypes.INTEGER, allowNull: false, defaultValue: 0 }) + declare weight: number; + @Column({ type: DataTypes.DATE, allowNull: false, defaultValue: 0 }) + declare ban_until: Date; + @Column({ type: DataTypes.INTEGER, allowNull: false }) + @ForeignKey(() => User) + declare user_id: number; + + @BelongsTo(() => User, 'user_id') + declare user: User; } @Table({ modelName: 'admin', underscored: true }) @@ -122,5 +157,5 @@ export class Settings 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, Report, Admin, Settings] + models: [User, Report, StrikeReason, StrikePunishment, Strike, Admin, Settings] }); diff --git a/src/routes/admin/reports/+page.server.ts b/src/routes/admin/reports/+page.server.ts index 9023020..21e265e 100644 --- a/src/routes/admin/reports/+page.server.ts +++ b/src/routes/admin/reports/+page.server.ts @@ -3,6 +3,7 @@ import { redirect } from '@sveltejs/kit'; import { env } from '$env/dynamic/public'; import { getSession } from '$lib/server/session'; import { Permissions } from '$lib/permissions'; +import { StrikeReason } from '$lib/server/database'; export const load: PageServerLoad = async ({ parent, cookies }) => { const { reportCount } = await parent(); @@ -12,6 +13,7 @@ export const load: PageServerLoad = async ({ parent, cookies }) => { return { count: getSession(cookies, { permissions: [Permissions.UserRead] }) != null ? reportCount : 0, + strike_reasons: JSON.parse(JSON.stringify(await StrikeReason.findAll())), self: self }; }; diff --git a/src/routes/admin/reports/+page.svelte b/src/routes/admin/reports/+page.svelte index bcc42b4..37a0537 100644 --- a/src/routes/admin/reports/+page.svelte +++ b/src/routes/admin/reports/+page.svelte @@ -84,11 +84,12 @@ method: 'PATCH', body: JSON.stringify({ id: activeReport.id, - auditor: data.self?.id || -1, - notice: activeReport.notice || '', - statement: activeReport.statement || '', + auditor: data.self?.id ?? -1, + notice: activeReport.notice ?? '', + statement: activeReport.statement ?? '', status: activeReport.status, - reported: activeReport.reported?.uuid || null + reported: activeReport.reported?.uuid ?? null, + strike_reason: activeReport.strike_reason_id ?? null }) }); } @@ -193,7 +194,7 @@ {#if activeReport}
@@ -294,6 +295,20 @@ +
+ +
{ await updateActiveReport(); - if (activeReport.reported?.username && activeReport.reported?.id === undefined) { - activeReport.reported.id = -1; + if (activeReport.reported?.username) { + if (activeReport.reported?.id === undefined) { + activeReport.reported.id = -1; + } } else { activeReport.reported = undefined; } diff --git a/src/routes/admin/reports/+server.ts b/src/routes/admin/reports/+server.ts index eb45966..09fdf4d 100644 --- a/src/routes/admin/reports/+server.ts +++ b/src/routes/admin/reports/+server.ts @@ -1,7 +1,7 @@ import type { RequestHandler } from '@sveltejs/kit'; import { getSession } from '$lib/server/session'; import { Permissions } from '$lib/permissions'; -import { Admin, Report, User } from '$lib/server/database'; +import { Admin, Report, Strike, StrikePunishment, StrikeReason, User } from '$lib/server/database'; import type { Attributes } from 'sequelize'; import { Op } from 'sequelize'; import { env } from '$env/dynamic/private'; @@ -55,7 +55,8 @@ export const POST = (async ({ request, cookies }) => { include: [ { model: User, as: 'reporter' }, { model: User, as: 'reported' }, - { model: Admin, as: 'auditor' } + { model: Admin, as: 'auditor' }, + { model: StrikeReason, as: 'strike_reason' } ], order: ['created_at'], offset: data.from || 0, @@ -77,6 +78,9 @@ export const POST = (async ({ request, cookies }) => { } else if (r.auditor) { delete r.dataValues.auditor.password; } + if (!r.strike_reason) { + r.strike_reason_id = -1; + } return r; }); @@ -97,6 +101,7 @@ export const PATCH = (async ({ request, cookies }) => { notice: string | null; statement: string | null; status: 'none' | 'review' | 'reviewed' | null; + strike_reason: number | null; } = await request.json(); if (data.id === null || data.auditor === null) return new Response(null, { status: 400 }); @@ -113,6 +118,74 @@ export const PATCH = (async ({ request, cookies }) => { if (data.notice != null) report.notice = data.notice; if (data.statement != null) report.statement = data.statement; if (data.status != null) report.status = data.status; + if (data.strike_reason != null) { + if (data.status !== 'reviewed') { + if (data.strike_reason == -1) { + report.strike_reason_id = null; + } else { + const strike_reason = await StrikeReason.findByPk(data.strike_reason); + if (strike_reason == null) return new Response(null, { status: 400 }); + report.strike_reason_id = strike_reason.id; + } + } else if (data.strike_reason == -1) { + const strike = await Strike.findOne({ where: { user_id: report.reported_id } }); + if (strike != null && report.strike_reason_id != null) { + const strike_weight = (await StrikeReason.findByPk(report.strike_reason_id, { + attributes: ['weight'] + }))!.weight; + const current_ban_in_seconds = ( + await StrikePunishment.findOne({ + attributes: ['ban_in_seconds'], + where: { weight: { [Op.lte]: strike.weight } } + }) + )?.ban_in_seconds; + if (current_ban_in_seconds) { + const new_ban_in_seconds = ( + await StrikePunishment.findOne({ + attributes: ['ban_in_seconds'], + where: { weight: { [Op.lte]: strike.weight - strike_weight } } + }) + )?.ban_in_seconds; + const time_left = + strike.ban_until.getTime() / 1000 - current_ban_in_seconds + (new_ban_in_seconds || 0); + strike.ban_until = new Date(time_left * 1000); + } + strike.weight -= strike_weight; + await strike.save(); + report.strike_reason_id = null; + } + } else if (data.strike_reason != report.strike_reason_id) { + if (!report.reported_id) return new Response(null, { status: 400 }); + const strike_reason = await StrikeReason.findByPk(data.strike_reason); + if (strike_reason == null) return new Response(null, { status: 400 }); + const [strike] = await Strike.findOrCreate({ where: { user_id: report.reported_id } }); + const old_punishment = ( + await StrikePunishment.findOne({ + attributes: ['ban_in_seconds'], + where: { weight: { [Op.lte]: strike.weight } } + }) + )?.ban_in_seconds; + if (report.strike_reason_id) { + strike.weight -= + (await StrikeReason.findByPk(report.strike_reason_id, { attributes: ['weight'] })) + ?.weight || 0; + } + strike.weight += (await StrikeReason.findByPk(strike_reason.id, { + attributes: ['weight'] + }))!.weight; + const new_punishment = ( + await StrikePunishment.findOne({ + attributes: ['ban_in_seconds'], + where: { weight: { [Op.lte]: strike.weight } } + }) + )?.ban_in_seconds; + if (old_punishment != new_punishment && new_punishment != null) { + strike.ban_until = new Date(Date.now() + new_punishment / 1000); + } + await strike.save(); + report.strike_reason_id = strike_reason.id; + } + } if (admin != null) report.auditor_id = admin.id; await report.save(); diff --git a/src/routes/api/user/+server.ts b/src/routes/api/user/+server.ts index 771b7b7..6e114af 100644 --- a/src/routes/api/user/+server.ts +++ b/src/routes/api/user/+server.ts @@ -1,5 +1,5 @@ import type { RequestHandler } from '@sveltejs/kit'; -import { User } from '$lib/server/database'; +import { Strike, User } from '$lib/server/database'; import { env } from '$env/dynamic/private'; export const GET = (async ({ url }) => { @@ -12,12 +12,15 @@ export const GET = (async ({ url }) => { const user = await User.findOne({ where: { uuid: uuid } }); if (user == null) return new Response(null, { status: 400 }); + const strike = await Strike.findOne({ where: { user_id: user.id } }); + return new Response( JSON.stringify({ uuid: user.uuid, username: user.username, firstname: user.firstname, - lastname: user.lastname + lastname: user.lastname, + ban_until: strike ? strike.ban_until.getTime() / 1000 : null }), { status: 200 } ); diff --git a/src/routes/register/RegistrationComplete.svelte b/src/routes/register/RegistrationComplete.svelte index 38b06d5..c490e46 100644 --- a/src/routes/register/RegistrationComplete.svelte +++ b/src/routes/register/RegistrationComplete.svelte @@ -77,7 +77,7 @@ {#if skin} {:else} - + {/if}