diff --git a/README.md b/README.md
index 23c4b0e..7ce2a8b 100644
--- a/README.md
+++ b/README.md
@@ -42,7 +42,7 @@
-POST /api/report (Erstellt einen Report)
+POST /api/reports (Erstellt einen Report)
##### Request Body
@@ -78,7 +78,7 @@
-PUT /api/report (Erstellt einen Abgeschlossenen Report)
+PUT /api/reports (Erstellt einen Abgeschlossenen Report)
##### Request Body
@@ -117,16 +117,13 @@
-POST /api/player (Status eines Spielers)
+GET /api/users/{uuid} (Status eines Spielers)
-##### Request Body
+#### Path Parameters
-```
-{
- // UUID eines Spielers
- "uuid": string
-}
-```
+| parameter | beschreibung |
+| --------- | ------------------- |
+| `uuid` | UUID eines Spielers |
##### Response Codes
@@ -161,6 +158,59 @@
+
+GET /api/users/{uuid}/reports (Reports eines Spielers)
+
+#### 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
+ }[]
+}
+```
+
+
+
## Webhook
> Die env variable `WEBHOOK_ENDPOINT` muss gesetzt und eine valide HTTP URL sein.
diff --git a/src/db/schema/report.ts b/src/db/schema/report.ts
index fc23c13..f2400a6 100644
--- a/src/db/schema/report.ts
+++ b/src/db/schema/report.ts
@@ -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,
diff --git a/src/pages/api/_api.ts b/src/pages/api/_api.ts
new file mode 100644
index 0000000..df61fcb
--- /dev/null
+++ b/src/pages/api/_api.ts
@@ -0,0 +1,34 @@
+import type { APIRoute } from 'astro';
+import { z } from 'astro:schema';
+import { checkApiBasicAuth } from '@util/auth.ts';
+
+export function externalApi(params: {
+ input?: InputSchema;
+ auth?: boolean;
+ handler: ({
+ input,
+ params
+ }: {
+ input: InputSchema extends z.ZodType ? z.infer : {};
+ params: Record;
+ }) => Response | Promise;
+}): 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 });
+ };
+}
diff --git a/src/pages/api/feedback.ts b/src/pages/api/feedback.ts
deleted file mode 100644
index 061f617..0000000
--- a/src/pages/api/feedback.ts
+++ /dev/null
@@ -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 });
-};
diff --git a/src/pages/api/feedback/index.ts b/src/pages/api/feedback/index.ts
new file mode 100644
index 0000000..5aa0bab
--- /dev/null
+++ b/src/pages/api/feedback/index.ts
@@ -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 });
+ }
+});
diff --git a/src/pages/api/player.ts b/src/pages/api/player.ts
deleted file mode 100644
index b562cef..0000000
--- a/src/pages/api/player.ts
+++ /dev/null
@@ -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 }
- );
-};
diff --git a/src/pages/api/report.ts b/src/pages/api/report.ts
deleted file mode 100644
index 6e948dc..0000000
--- a/src/pages/api/report.ts
+++ /dev/null
@@ -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 });
-};
diff --git a/src/pages/api/reports/index.ts b/src/pages/api/reports/index.ts
new file mode 100644
index 0000000..179e23c
--- /dev/null
+++ b/src/pages/api/reports/index.ts
@@ -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 });
+ }
+});
diff --git a/src/pages/api/users/[...uuid]/index.ts b/src/pages/api/users/[...uuid]/index.ts
new file mode 100644
index 0000000..bb489e6
--- /dev/null
+++ b/src/pages/api/users/[...uuid]/index.ts
@@ -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 }
+ );
+ }
+});
diff --git a/src/pages/api/users/[...uuid]/reports.ts b/src/pages/api/users/[...uuid]/reports.ts
new file mode 100644
index 0000000..05f964f
--- /dev/null
+++ b/src/pages/api/users/[...uuid]/reports.ts
@@ -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 }
+ );
+ }
+});