Tote CCs zurueckholen, Redesign-Beitritte und Problemfaelle
Vier Punkte aus dem Betrieb. Ein toter CC liess sich nicht zurueckholen. Der Knopf dafuer wurde nie angezeigt: bei Grund "Im RP gestorben" landete die Seite im Zweig "Bezahlt reaktivieren", den der Server zu Recht abweist - ein Toter kehrt nur durch einen Administrator zurueck. Der Dialog dafuer war fertig gebaut und wurde schlicht nie geoeffnet. Jetzt steht an einem toten CC "Zurueckholen", fuer Editoren stattdessen der Hinweis, dass das ein Administrator entscheidet. Der Abgleich eines einzelnen Spielers fragte nur dessen hinterlegte SteamID ab. Guthaben, das unter einem frueheren Konto gekauft wurde, blieb damit unsichtbar - obwohl das Konto hinterlegt war und der Sammellauf es beruecksichtigt hat. Jetzt werden alle Kennungen des Spielers abgefragt. Ein Beitritt zu einem Gruppen-CC ueber ein Redesign kostete nur das Guthaben. Damit entstand aus einem Redesign ein zusaetzlicher CC, was die Regel gerade nicht hergibt: ein Redesign ersetzt einen CC. Jetzt muss angegeben werden, welcher eigene CC dafuer aufgegeben wird; ein aktiver wird dabei inaktiv gesetzt, ein bereits inaktiver bleibt unveraendert. Ueber ein CC-Paket kommt weiterhin ein Platz hinzu, ohne Vorgaenger. Welcher CC aufgegeben wurde, haengt am Beitritt und bleibt nachvollziehbar. Faelle, die sich beim Eintragen nicht klaeren lassen - ein Antrag ohne auffindbares Paket, ein fehlender GExtension-Eintrag, widerspruechliche Angaben - koennen als Spieler angelegt und dabei als Problem vorgemerkt werden, mit Notiz. Auch nachtraeglich setzbar und wieder abraeumbar. Die Markierung ist folgenlos: sie sperrt nichts und veraendert weder Guthaben noch CCs. Die Uebersicht kennzeichnet betroffene Spieler und laesst sich darauf eingrenzen. Nebenbei: die Spielerliste gab die Markierung nicht mit aus, aufgefallen beim Test.
This commit is contained in:
@@ -148,11 +148,19 @@ export async function characterRoutes(app: FastifyInstance): Promise<void> {
|
||||
z.object({
|
||||
playerId: z.number().int().positive(),
|
||||
useKind: z.enum([CreditKind.CC, CreditKind.REDESIGN_PAID, CreditKind.REDESIGN_KULANZ]),
|
||||
/// Der eigene CC, den der Spieler dafür aufgibt. Pflicht, sobald der
|
||||
/// Platz über ein Redesign bezahlt wird.
|
||||
predecessorId: z.number().int().positive().optional(),
|
||||
note: optionalNote,
|
||||
}),
|
||||
request.body,
|
||||
);
|
||||
return addMember(id, body.playerId, { useKind: body.useKind, note: body.note }, contextOf(request));
|
||||
return addMember(
|
||||
id,
|
||||
body.playerId,
|
||||
{ useKind: body.useKind, predecessorId: body.predecessorId, note: body.note },
|
||||
contextOf(request),
|
||||
);
|
||||
});
|
||||
|
||||
app.delete('/api/characters/:id/members/:playerId', { preHandler: requireEditor }, async (request) => {
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
createPlayer,
|
||||
deletePlayer,
|
||||
getPlayerDetail,
|
||||
markProblem,
|
||||
removeAlias,
|
||||
searchPlayers,
|
||||
updatePlayer,
|
||||
@@ -20,6 +21,8 @@ const playerBody = z.object({
|
||||
steamId: z.string().trim().max(64).nullish(),
|
||||
forumName: z.string().trim().max(120).nullish(),
|
||||
notes: z.string().trim().max(2000).nullish(),
|
||||
isProblem: z.boolean().optional(),
|
||||
problemNote: z.string().trim().max(1000).nullish(),
|
||||
});
|
||||
|
||||
export async function playerRoutes(app: FastifyInstance): Promise<void> {
|
||||
@@ -29,10 +32,11 @@ export async function playerRoutes(app: FastifyInstance): Promise<void> {
|
||||
q: z.string().trim().max(120).default(''),
|
||||
take: z.coerce.number().int().min(1).max(200).default(50),
|
||||
skip: z.coerce.number().int().min(0).default(0),
|
||||
problems: z.enum(['true', 'false']).default('false'),
|
||||
}),
|
||||
request.query,
|
||||
);
|
||||
return searchPlayers(query.q, query.take, query.skip);
|
||||
return searchPlayers(query.q, query.take, query.skip, query.problems === 'true');
|
||||
});
|
||||
|
||||
app.get('/api/players/:id', { preHandler: requireAuth }, async (request) => {
|
||||
@@ -62,10 +66,28 @@ export async function playerRoutes(app: FastifyInstance): Promise<void> {
|
||||
|
||||
app.patch('/api/players/:id', { preHandler: requireEditor }, async (request) => {
|
||||
const { id } = parseWith(idParam, request.params);
|
||||
const body = parseWith(playerBody.partial(), request.body);
|
||||
// Die Problem-Markierung läuft über die eigene Route, damit ein Klären
|
||||
// oder Markieren als solches im Protokoll steht und nicht als "geändert".
|
||||
const body = parseWith(playerBody.omit({ isProblem: true, problemNote: true }).partial(), request.body);
|
||||
return updatePlayer(id, body, contextOf(request));
|
||||
});
|
||||
|
||||
/**
|
||||
* Einen Spieler als Problemfall markieren oder die Markierung aufheben.
|
||||
* Reine Merkhilfe - an Guthaben und CCs ändert sich dadurch nichts.
|
||||
*/
|
||||
app.post('/api/players/:id/problem', { preHandler: requireEditor }, async (request) => {
|
||||
const { id } = parseWith(idParam, request.params);
|
||||
const body = parseWith(
|
||||
z.object({
|
||||
isProblem: z.boolean(),
|
||||
note: z.string().trim().max(1000).nullish(),
|
||||
}),
|
||||
request.body,
|
||||
);
|
||||
return markProblem(id, body, contextOf(request));
|
||||
});
|
||||
|
||||
// Frühere Steam- und Forumskonten eines Spielers.
|
||||
app.post('/api/players/:id/aliases', { preHandler: requireEditor }, async (request, reply) => {
|
||||
const { id } = parseWith(idParam, request.params);
|
||||
|
||||
@@ -5,6 +5,7 @@ import { notFound } from '../errors.js';
|
||||
import { prisma } from '../db.js';
|
||||
import {
|
||||
knownSteamIds,
|
||||
steamIdsOf,
|
||||
listUnmatched,
|
||||
resolveUnmatched,
|
||||
scrapePlayers,
|
||||
@@ -32,20 +33,26 @@ export async function scraperRoutes(app: FastifyInstance): Promise<void> {
|
||||
return scrapePlayers(targets, contextOf(request));
|
||||
});
|
||||
|
||||
/** Einzelnes Profil abgleichen, etwa direkt von der Spielerseite aus. */
|
||||
/**
|
||||
* Einzelnes Profil abgleichen, etwa direkt von der Spielerseite aus.
|
||||
* Abgefragt werden alle Konten des Spielers, nicht nur das hinterlegte -
|
||||
* sonst fehlte das Guthaben, das unter einem früheren Konto gekauft wurde.
|
||||
*/
|
||||
app.post('/api/scrape/player/:id', { preHandler: requireEditor }, async (request) => {
|
||||
const { id } = parseWith(idParam, request.params);
|
||||
|
||||
const player = await prisma.player.findUnique({
|
||||
where: { id },
|
||||
select: { id: true, steamId: true },
|
||||
});
|
||||
const player = await prisma.player.findUnique({ where: { id }, select: { id: true } });
|
||||
if (!player) throw notFound('Spieler');
|
||||
if (!player.steamId) {
|
||||
|
||||
const steamIds = await steamIdsOf(id);
|
||||
if (steamIds.length === 0) {
|
||||
throw notFound('SteamID des Spielers');
|
||||
}
|
||||
|
||||
return scrapePlayers([{ playerId: player.id, steamId: player.steamId }], contextOf(request));
|
||||
return scrapePlayers(
|
||||
steamIds.map((steamId) => ({ playerId: id, steamId })),
|
||||
contextOf(request),
|
||||
);
|
||||
});
|
||||
|
||||
app.get('/api/scrape/unmatched', { preHandler: requireAuth }, async (request) => {
|
||||
|
||||
Reference in New Issue
Block a user