This commit is contained in:
70
README.md
70
README.md
@@ -42,7 +42,7 @@
|
||||
</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
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
</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
|
||||
|
||||
@@ -117,16 +117,13 @@
|
||||
</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
|
||||
|
||||
```
|
||||
{
|
||||
// UUID eines Spielers
|
||||
"uuid": string
|
||||
}
|
||||
```
|
||||
| parameter | beschreibung |
|
||||
| --------- | ------------------- |
|
||||
| `uuid` | UUID eines Spielers |
|
||||
|
||||
##### Response Codes
|
||||
|
||||
@@ -161,6 +158,59 @@
|
||||
|
||||
</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
|
||||
|
||||
> 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,
|
||||
reporter: {
|
||||
id: reporter.id,
|
||||
username: reporter.username
|
||||
username: reporter.username,
|
||||
uuid: reporter.uuid
|
||||
},
|
||||
reported: {
|
||||
id: reported.id,
|
||||
username: reported.username
|
||||
username: reported.username,
|
||||
uuid: reported.uuid
|
||||
},
|
||||
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