This commit is contained in:
70
README.md
70
README.md
@@ -42,7 +42,7 @@
|
|||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><code>POST</code> <code>/api/report</code> (Erstellt einen Report)</summary>
|
<summary><code>POST</code> <code>/api/reports</code> (Erstellt einen Report)</summary>
|
||||||
|
|
||||||
##### Request Body
|
##### Request Body
|
||||||
|
|
||||||
@@ -78,7 +78,7 @@
|
|||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><code>PUT</code> <code>/api/report</code> (Erstellt einen Abgeschlossenen Report)</summary>
|
<summary><code>PUT</code> <code>/api/reports</code> (Erstellt einen Abgeschlossenen Report)</summary>
|
||||||
|
|
||||||
##### Request Body
|
##### Request Body
|
||||||
|
|
||||||
@@ -117,16 +117,13 @@
|
|||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><code>POST</code> <code>/api/player</code> (Status eines Spielers)</summary>
|
<summary><code>GET</code> <code>/api/users/{uuid}</code> (Status eines Spielers)</summary>
|
||||||
|
|
||||||
##### Request Body
|
#### Path Parameters
|
||||||
|
|
||||||
```
|
| parameter | beschreibung |
|
||||||
{
|
| --------- | ------------------- |
|
||||||
// UUID eines Spielers
|
| `uuid` | UUID eines Spielers |
|
||||||
"uuid": string
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
##### Response Codes
|
##### Response Codes
|
||||||
|
|
||||||
@@ -161,6 +158,59 @@
|
|||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><code>GET</code> <code>/api/users/{uuid}/reports</code> (Reports eines Spielers)</summary>
|
||||||
|
|
||||||
|
#### Path Parameters
|
||||||
|
|
||||||
|
| parameter | beschreibung |
|
||||||
|
| --------- | ------------------- |
|
||||||
|
| `uuid` | UUID eines Spielers |
|
||||||
|
|
||||||
|
##### Response Codes
|
||||||
|
|
||||||
|
| http code | beschreibung |
|
||||||
|
| --------- | ------------------------------------------ |
|
||||||
|
| 200 | / |
|
||||||
|
| 400 | Der Request Body ist falsch |
|
||||||
|
| 401 | Es wurde ein falsches API Secret angegeben |
|
||||||
|
| 404 | Der Spieler existiert nicht |
|
||||||
|
|
||||||
|
##### Response Body
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
// Alle Reports, die der Spieler selber erstellt hat
|
||||||
|
"from_self": {
|
||||||
|
// Die UUID des reporteten Spielers oder null falls ein unbekannter Spieler reportet wurde
|
||||||
|
"reported": string | null,
|
||||||
|
// Grund des Reports
|
||||||
|
"reason": string,
|
||||||
|
// Wann der Report abgeschickt wurde als UTC Millisekunden oder null falls der Report noch nicht abgeschickt wurde (=> kann noch bearbeitet werden)
|
||||||
|
"created": number | null,
|
||||||
|
// Status des Reports, "open" wenn er gerade bearbeitet wird, "closed" falls er bearbeitet wurde, null wenn nichts von beidem
|
||||||
|
"status": "open" | "closed" | null,
|
||||||
|
// Url zum Report auf der Website
|
||||||
|
"url": string
|
||||||
|
}[],
|
||||||
|
// Alle Reports, die gegen den Spieler erstellt wurden
|
||||||
|
"to_self": {
|
||||||
|
// Die UUID des Spielers, der den Report erstellt hat oder null falls der Report vom System kommt
|
||||||
|
"reporter": string | null,
|
||||||
|
// Grund des Reports
|
||||||
|
"reason": string,
|
||||||
|
// Wann der Report abgeschickt wurde als UTC Millisekunden oder null falls der Report noch nicht abgeschickt wurde (=> kann noch bearbeitet werden)
|
||||||
|
"created": number | null,
|
||||||
|
// Status des Reports, "open" wenn er gerade bearbeitet wird, "closed" falls er bearbeitet wurde, null wenn nichts von beidem
|
||||||
|
"status": "open" | "closed" | null,
|
||||||
|
// Url zum Report auf der Website
|
||||||
|
"url": string
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
## Webhook
|
## Webhook
|
||||||
|
|
||||||
> Die env variable `WEBHOOK_ENDPOINT` muss gesetzt und eine valide HTTP URL sein.
|
> Die env variable `WEBHOOK_ENDPOINT` muss gesetzt und eine valide HTTP URL sein.
|
||||||
|
|||||||
@@ -120,11 +120,13 @@ export async function getReports(db: Database, values: GetReportsReq) {
|
|||||||
createdAt: report.createdAt,
|
createdAt: report.createdAt,
|
||||||
reporter: {
|
reporter: {
|
||||||
id: reporter.id,
|
id: reporter.id,
|
||||||
username: reporter.username
|
username: reporter.username,
|
||||||
|
uuid: reporter.uuid
|
||||||
},
|
},
|
||||||
reported: {
|
reported: {
|
||||||
id: reported.id,
|
id: reported.id,
|
||||||
username: reported.username
|
username: reported.username,
|
||||||
|
uuid: reported.uuid
|
||||||
},
|
},
|
||||||
status: {
|
status: {
|
||||||
status: reportStatus.status,
|
status: reportStatus.status,
|
||||||
|
|||||||
34
src/pages/api/_api.ts
Normal file
34
src/pages/api/_api.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import type { APIRoute } from 'astro';
|
||||||
|
import { z } from 'astro:schema';
|
||||||
|
import { checkApiBasicAuth } from '@util/auth.ts';
|
||||||
|
|
||||||
|
export function externalApi<InputSchema extends z.ZodType | undefined>(params: {
|
||||||
|
input?: InputSchema;
|
||||||
|
auth?: boolean;
|
||||||
|
handler: ({
|
||||||
|
input,
|
||||||
|
params
|
||||||
|
}: {
|
||||||
|
input: InputSchema extends z.ZodType ? z.infer<InputSchema> : {};
|
||||||
|
params: Record<string, string | undefined>;
|
||||||
|
}) => Response | Promise<Response>;
|
||||||
|
}): APIRoute {
|
||||||
|
return async (context) => {
|
||||||
|
if (params.auth && !checkApiBasicAuth(context.request.headers)) {
|
||||||
|
return new Response(null, { status: 401 });
|
||||||
|
}
|
||||||
|
|
||||||
|
let input;
|
||||||
|
if (params.input) {
|
||||||
|
try {
|
||||||
|
input = await params.input.parseAsync(await context.request.json());
|
||||||
|
} catch (_) {
|
||||||
|
return new Response(null, { status: 400 });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
input = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return params.handler({ input: input, params: context.params });
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
import { z } from 'astro:schema';
|
|
||||||
import type { APIRoute } from 'astro';
|
|
||||||
import { db } from '@db/database.ts';
|
|
||||||
import { BASE_PATH } from 'astro:env/server';
|
|
||||||
import { checkApiBasicAuth } from '@util/auth.ts';
|
|
||||||
|
|
||||||
const postSchema = z.object({
|
|
||||||
event: z.string(),
|
|
||||||
title: z.string(),
|
|
||||||
uuids: z.array(z.string())
|
|
||||||
});
|
|
||||||
|
|
||||||
export const POST: APIRoute = async ({ request }) => {
|
|
||||||
if (!checkApiBasicAuth(request.headers)) {
|
|
||||||
return new Response(null, { status: 401 });
|
|
||||||
}
|
|
||||||
|
|
||||||
let parsed;
|
|
||||||
try {
|
|
||||||
parsed = await postSchema.parseAsync(await request.json());
|
|
||||||
} catch (_) {
|
|
||||||
return new Response(null, { status: 400 });
|
|
||||||
}
|
|
||||||
|
|
||||||
const feedbacks = await db.addUserFeedbacks({
|
|
||||||
event: parsed.event,
|
|
||||||
title: parsed.title,
|
|
||||||
uuids: parsed.uuids
|
|
||||||
});
|
|
||||||
|
|
||||||
const response = feedbacks.map((feedback) => ({
|
|
||||||
uuid: feedback.uuid,
|
|
||||||
url: `${BASE_PATH}/feedback/${feedback.urlHash}`
|
|
||||||
}));
|
|
||||||
|
|
||||||
return new Response(JSON.stringify({ feedback: response }), { status: 200 });
|
|
||||||
};
|
|
||||||
27
src/pages/api/feedback/index.ts
Normal file
27
src/pages/api/feedback/index.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { externalApi } from '../_api.ts';
|
||||||
|
import { z } from 'astro:schema';
|
||||||
|
import { db } from '@db/database.ts';
|
||||||
|
import { BASE_PATH } from 'astro:env/server';
|
||||||
|
|
||||||
|
export const POST = externalApi({
|
||||||
|
input: z.object({
|
||||||
|
event: z.string(),
|
||||||
|
title: z.string(),
|
||||||
|
uuids: z.array(z.string())
|
||||||
|
}),
|
||||||
|
auth: true,
|
||||||
|
handler: async ({ input }) => {
|
||||||
|
const feedbacks = await db.addUserFeedbacks({
|
||||||
|
event: input.event,
|
||||||
|
title: input.title,
|
||||||
|
uuids: input.uuids
|
||||||
|
});
|
||||||
|
|
||||||
|
const response = feedbacks.map((feedback) => ({
|
||||||
|
uuid: feedback.uuid,
|
||||||
|
url: `${BASE_PATH}/feedback/${feedback.urlHash}`
|
||||||
|
}));
|
||||||
|
|
||||||
|
return new Response(JSON.stringify({ feedback: response }), { status: 200 });
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
import { z } from 'astro:schema';
|
|
||||||
import type { APIRoute } from 'astro';
|
|
||||||
import { db } from '@db/database.ts';
|
|
||||||
import { checkApiBasicAuth } from '@util/auth.ts';
|
|
||||||
|
|
||||||
const postSchema = z.object({
|
|
||||||
uuid: z.string()
|
|
||||||
});
|
|
||||||
|
|
||||||
export const POST: APIRoute = async ({ request }) => {
|
|
||||||
if (!checkApiBasicAuth(request.headers)) {
|
|
||||||
return new Response(null, { status: 401 });
|
|
||||||
}
|
|
||||||
|
|
||||||
let parsed;
|
|
||||||
try {
|
|
||||||
parsed = await postSchema.parseAsync(await request.json());
|
|
||||||
} catch (_) {
|
|
||||||
return new Response(null, { status: 400 });
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = await db.getUserByUuid({ uuid: parsed.uuid });
|
|
||||||
if (!user) return new Response(null, { status: 404 });
|
|
||||||
|
|
||||||
const strikes = await db.getStrikesByUserId({ userId: user.id });
|
|
||||||
|
|
||||||
return new Response(
|
|
||||||
JSON.stringify({
|
|
||||||
firstname: user.firstname,
|
|
||||||
lastname: user.lastname,
|
|
||||||
username: user.username,
|
|
||||||
uuid: user.uuid,
|
|
||||||
strikes: strikes.map((s) => ({ at: s.at.getTime(), weight: s.reason.weight }))
|
|
||||||
}),
|
|
||||||
{ status: 200 }
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
import type { APIRoute } from 'astro';
|
|
||||||
import { z } from 'astro:schema';
|
|
||||||
import { db } from '@db/database.ts';
|
|
||||||
import { sendWebhook, WebhookAction } from '@util/webhook.ts';
|
|
||||||
import { checkApiBasicAuth } from '@util/auth.ts';
|
|
||||||
|
|
||||||
const postSchema = z.object({
|
|
||||||
reporter: z.string(),
|
|
||||||
reported: z.string().nullable(),
|
|
||||||
reason: z.string()
|
|
||||||
});
|
|
||||||
|
|
||||||
export const POST: APIRoute = async ({ request }) => {
|
|
||||||
if (!checkApiBasicAuth(request.headers)) {
|
|
||||||
return new Response(null, { status: 401 });
|
|
||||||
}
|
|
||||||
|
|
||||||
let parsed;
|
|
||||||
try {
|
|
||||||
parsed = await postSchema.parseAsync(await request.json());
|
|
||||||
} catch (_) {
|
|
||||||
return new Response(null, { status: 400 });
|
|
||||||
}
|
|
||||||
|
|
||||||
const reporter = await db.getUserByUuid({ uuid: parsed.reporter });
|
|
||||||
if (!reporter) return new Response(null, { status: 404 });
|
|
||||||
|
|
||||||
let reported = null;
|
|
||||||
if (parsed.reported) {
|
|
||||||
reported = await db.getUserByUuid({ uuid: parsed.reported });
|
|
||||||
if (!reported) return new Response(null, { status: 404 });
|
|
||||||
}
|
|
||||||
|
|
||||||
const report = await db.addReport({
|
|
||||||
reporterId: reporter.id,
|
|
||||||
reportedId: reported?.id,
|
|
||||||
reason: parsed.reason,
|
|
||||||
body: null
|
|
||||||
});
|
|
||||||
|
|
||||||
return new Response(JSON.stringify({ url: report.url }), { status: 200 });
|
|
||||||
};
|
|
||||||
|
|
||||||
const putSchema = z.object({
|
|
||||||
reporter: z.string().nullable(),
|
|
||||||
reported: z.string(),
|
|
||||||
reason: z.string(),
|
|
||||||
body: z.string().nullable(),
|
|
||||||
notice: z.string().nullable(),
|
|
||||||
statement: z.string().nullable(),
|
|
||||||
strike_reason_id: z.number()
|
|
||||||
});
|
|
||||||
|
|
||||||
export const PUT: APIRoute = async ({ request }) => {
|
|
||||||
if (!checkApiBasicAuth(request.headers)) {
|
|
||||||
return new Response(null, { status: 401 });
|
|
||||||
}
|
|
||||||
|
|
||||||
let parsed;
|
|
||||||
try {
|
|
||||||
parsed = await putSchema.parseAsync(await request.json());
|
|
||||||
} catch (_) {
|
|
||||||
return new Response(null, { status: 400 });
|
|
||||||
}
|
|
||||||
|
|
||||||
const reported = await db.getUserByUuid({ uuid: parsed.reported });
|
|
||||||
if (!reported) return new Response(null, { status: 404 });
|
|
||||||
|
|
||||||
let reporter = null;
|
|
||||||
if (parsed.reporter) {
|
|
||||||
reporter = await db.getUserByUuid({ uuid: parsed.reporter });
|
|
||||||
if (!reporter) return new Response(null, { status: 404 });
|
|
||||||
}
|
|
||||||
|
|
||||||
await db.transaction(async (tx) => {
|
|
||||||
const report = await tx.addReport({
|
|
||||||
reporterId: reporter?.id,
|
|
||||||
reportedId: reported.id,
|
|
||||||
createdAt: new Date(),
|
|
||||||
reason: parsed.reason,
|
|
||||||
body: parsed.body
|
|
||||||
});
|
|
||||||
|
|
||||||
await tx.editReportStatus({
|
|
||||||
reportId: report.id,
|
|
||||||
notice: parsed.notice,
|
|
||||||
statement: parsed.statement,
|
|
||||||
status: 'closed'
|
|
||||||
});
|
|
||||||
|
|
||||||
await tx.editStrike({
|
|
||||||
reportId: report.id,
|
|
||||||
strikeReasonId: parsed.strike_reason_id
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// send webhook in background
|
|
||||||
sendWebhook(WebhookAction.Strike, {
|
|
||||||
uuid: reported.uuid!
|
|
||||||
});
|
|
||||||
|
|
||||||
return new Response(null, { status: 200 });
|
|
||||||
};
|
|
||||||
84
src/pages/api/reports/index.ts
Normal file
84
src/pages/api/reports/index.ts
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import { externalApi } from '../_api.ts';
|
||||||
|
import { z } from 'astro:schema';
|
||||||
|
import { db } from '@db/database.ts';
|
||||||
|
import { sendWebhook, WebhookAction } from '@util/webhook.ts';
|
||||||
|
|
||||||
|
export const POST = externalApi({
|
||||||
|
input: z.object({
|
||||||
|
reporter: z.string(),
|
||||||
|
reported: z.string().nullable(),
|
||||||
|
reason: z.string()
|
||||||
|
}),
|
||||||
|
auth: true,
|
||||||
|
handler: async ({ input }) => {
|
||||||
|
const reporter = await db.getUserByUuid({ uuid: input.reporter });
|
||||||
|
if (!reporter) return new Response(null, { status: 404 });
|
||||||
|
|
||||||
|
let reported = null;
|
||||||
|
if (input.reported) {
|
||||||
|
reported = await db.getUserByUuid({ uuid: input.reported });
|
||||||
|
if (!reported) return new Response(null, { status: 404 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const report = await db.addReport({
|
||||||
|
reporterId: reporter.id,
|
||||||
|
reportedId: reported?.id,
|
||||||
|
reason: input.reason,
|
||||||
|
body: null
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Response(JSON.stringify({ url: report.url }), { status: 200 });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export const PUT = externalApi({
|
||||||
|
input: z.object({
|
||||||
|
reporter: z.string().nullable(),
|
||||||
|
reported: z.string(),
|
||||||
|
reason: z.string(),
|
||||||
|
body: z.string().nullable(),
|
||||||
|
notice: z.string().nullable(),
|
||||||
|
statement: z.string().nullable(),
|
||||||
|
strike_reason_id: z.number()
|
||||||
|
}),
|
||||||
|
auth: true,
|
||||||
|
handler: async ({ input }) => {
|
||||||
|
const reported = await db.getUserByUuid({ uuid: input.reported });
|
||||||
|
if (!reported) return new Response(null, { status: 404 });
|
||||||
|
|
||||||
|
let reporter = null;
|
||||||
|
if (input.reporter) {
|
||||||
|
reporter = await db.getUserByUuid({ uuid: input.reporter });
|
||||||
|
if (!reporter) return new Response(null, { status: 404 });
|
||||||
|
}
|
||||||
|
|
||||||
|
await db.transaction(async (tx) => {
|
||||||
|
const report = await tx.addReport({
|
||||||
|
reporterId: reporter?.id,
|
||||||
|
reportedId: reported.id,
|
||||||
|
createdAt: new Date(),
|
||||||
|
reason: input.reason,
|
||||||
|
body: input.body
|
||||||
|
});
|
||||||
|
|
||||||
|
await tx.editReportStatus({
|
||||||
|
reportId: report.id,
|
||||||
|
notice: input.notice,
|
||||||
|
statement: input.statement,
|
||||||
|
status: 'closed'
|
||||||
|
});
|
||||||
|
|
||||||
|
await tx.editStrike({
|
||||||
|
reportId: report.id,
|
||||||
|
strikeReasonId: input.strike_reason_id
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// send webhook in background
|
||||||
|
sendWebhook(WebhookAction.Strike, {
|
||||||
|
uuid: reported.uuid!
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Response(null, { status: 200 });
|
||||||
|
}
|
||||||
|
});
|
||||||
23
src/pages/api/users/[...uuid]/index.ts
Normal file
23
src/pages/api/users/[...uuid]/index.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { externalApi } from '../../_api.ts';
|
||||||
|
import { db } from '@db/database.ts';
|
||||||
|
|
||||||
|
export const GET = externalApi({
|
||||||
|
auth: true,
|
||||||
|
handler: async ({ params }) => {
|
||||||
|
const user = await db.getUserByUuid({ uuid: params['uuid']! });
|
||||||
|
if (!user) return new Response(null, { status: 404 });
|
||||||
|
|
||||||
|
const strikes = await db.getStrikesByUserId({ userId: user.id });
|
||||||
|
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
firstname: user.firstname,
|
||||||
|
lastname: user.lastname,
|
||||||
|
username: user.username,
|
||||||
|
uuid: user.uuid,
|
||||||
|
strikes: strikes.map((s) => ({ at: s.at.getTime(), weight: s.reason.weight }))
|
||||||
|
}),
|
||||||
|
{ status: 200 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
34
src/pages/api/users/[...uuid]/reports.ts
Normal file
34
src/pages/api/users/[...uuid]/reports.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import { externalApi } from '../../_api.ts';
|
||||||
|
import { db } from '@db/database.ts';
|
||||||
|
import { BASE_PATH } from 'astro:env/server';
|
||||||
|
|
||||||
|
export const GET = externalApi({
|
||||||
|
auth: true,
|
||||||
|
handler: async ({ params }) => {
|
||||||
|
const user = await db.getUserByUuid({ uuid: params['uuid']! });
|
||||||
|
if (!user) return new Response(null, { status: 404 });
|
||||||
|
|
||||||
|
const fromSelf = await db.getReports({ reporter: user.username });
|
||||||
|
const toSelf = await db.getReports({ reported: user.username });
|
||||||
|
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
from_self: fromSelf.map((report) => ({
|
||||||
|
reported: report.reported?.uuid ?? null,
|
||||||
|
reason: report.reason,
|
||||||
|
created: report.createdAt?.getTime() ?? null,
|
||||||
|
status: report.status?.status ?? null,
|
||||||
|
url: `${BASE_PATH}/report/${report.urlHash}`
|
||||||
|
})),
|
||||||
|
to_self: toSelf.map((report) => ({
|
||||||
|
reporter: report.reporter?.uuid ?? null,
|
||||||
|
reason: report.reason,
|
||||||
|
created: report.createdAt,
|
||||||
|
status: report.status?.status ?? null,
|
||||||
|
url: `${BASE_PATH}/report/${report.urlHash}`
|
||||||
|
}))
|
||||||
|
}),
|
||||||
|
{ status: 200 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user