add feedback and report things
All checks were successful
deploy / build-and-deploy (/testvaro, /opt/website-test, website-test) (push) Successful in 22s
deploy / build-and-deploy (/varo, /opt/website, website) (push) Successful in 21s

This commit is contained in:
2025-06-21 14:45:39 +02:00
parent 9c49585873
commit ee8f595ecc
25 changed files with 898 additions and 57 deletions

View File

@ -1,10 +1,84 @@
import { defineAction } from 'astro:actions';
import { ActionError, defineAction } from 'astro:actions';
import { Session } from '@util/session.ts';
import { Permissions } from '@util/permissions.ts';
import { db } from '@db/database.ts';
import { z } from 'astro:schema';
import { UPLOAD_PATH } from 'astro:env/server';
import fs from 'node:fs';
import crypto from 'node:crypto';
import path from 'node:path';
import { sendWebhook, WebhookAction } from '@util/webhook.ts';
export const report = {
submitReport: defineAction({
input: z.object({
urlHash: z.string(),
reason: z.string(),
body: z.string(),
files: z.array(z.instanceof(File)).nullable()
}),
handler: async (input) => {
const report = await db.getReportByUrlHash({ urlHash: input.urlHash });
if (!report) {
throw new ActionError({
code: 'NOT_FOUND'
});
}
if (!UPLOAD_PATH) {
throw new ActionError({
code: 'FORBIDDEN',
message: 'Es dürfen keine Anhänge hochgeladen werden'
});
}
const filePaths = [] as string[];
try {
await db.transaction(async (tx) => {
for (const file of input.files ?? []) {
const uuid = crypto.randomUUID();
const tmpFilePath = path.join(UPLOAD_PATH!, uuid);
const tmpFileStream = fs.createWriteStream(tmpFilePath);
filePaths.push(tmpFilePath);
const md5Hash = crypto.createHash('md5');
for await (const chunk of file.stream()) {
md5Hash.update(chunk);
tmpFileStream.write(chunk);
}
const hash = md5Hash.digest('hex');
const filePath = path.join(UPLOAD_PATH!, hash);
await tx.addReportAttachment({
type: 'video',
hash: hash,
reportId: report.id
});
fs.renameSync(tmpFilePath, filePath);
filePaths.pop();
filePaths.push(filePath);
}
await tx.submitReport({
urlHash: input.urlHash,
reason: input.reason,
body: input.body
});
});
} catch (e) {
for (const filePath of filePaths) {
fs.rmSync(filePath);
}
throw e;
}
},
accept: 'form'
}),
addReport: defineAction({
input: z.object({
reason: z.string(),
@ -19,7 +93,7 @@ export const report = {
const { id } = await db.addReport({
reason: input.reason,
body: input.body,
createdAt: input.createdAt,
createdAt: input.createdAt ? new Date(input.createdAt) : null,
reporterTeamId: input.reporter,
reportedTeamId: input.reported
});
@ -52,6 +126,9 @@ export const report = {
handler: async (input, context) => {
Session.actionSessionFromCookies(context.cookies, Permissions.Reports);
let preReportStrike;
if (input.status === 'closed') preReportStrike = await db.getStrikeByReportId({ reportId: input.reportId });
await db.transaction(async (tx) => {
await tx.editReportStatus(input);
@ -62,6 +139,20 @@ export const report = {
});
}
});
if (input.status === 'closed' && preReportStrike?.strikeReasonId != input.strikeReasonId) {
const report = await db.getReportById({ id: input.reportId });
if (report.reported) {
const strikes = await db.getStrikesByTeamId({ teamId: report.reported.id });
const teamMembers = await db.getTeamMembersByTeamId({ teamId: report.reported.id });
// send webhook in background
sendWebhook(WebhookAction.Strike, {
users: teamMembers.map((tm) => tm.user.uuid!),
totalWeight: strikes.map((strike) => strike.reason.weight).reduce((a, b) => a + b, 0)
});
}
}
}
}),
reports: defineAction({
@ -118,5 +209,29 @@ export const report = {
strikeReasons: await db.getStrikeReasons({})
};
}
}),
teamNamesByUsername: defineAction({
input: z.object({
username: z.string().nullish()
}),
handler: async (input) => {
const teams = await db.getTeamsByUsername({ username: input.username ?? '', limit: 5 });
return {
teamNames: teams.map((team) => ({ name: team.user.username, value: team.team.name }))
};
}
}),
teamNamesByTeamName: defineAction({
input: z.object({
teamName: z.string().nullish()
}),
handler: async (input) => {
const teams = await db.getTeams({ name: input.teamName, limit: 5 });
return {
teamNames: teams.map((team) => ({ name: team.name, value: team.name }))
};
}
})
};