feat(ttt): match history done

This commit is contained in:
Maix0 2026-01-06 23:44:02 +01:00 committed by Maix0
parent 8f3ed71d8a
commit 2bf5e6e700
17 changed files with 1185 additions and 18 deletions

View file

@ -7,7 +7,201 @@
"components": {
"schemas": {}
},
"paths": {},
"paths": {
"/api/ttt/history/{user}": {
"get": {
"operationId": "tttHistory",
"parameters": [
{
"schema": {
"type": "string"
},
"in": "path",
"name": "user",
"required": true,
"description": "'me' | <userid>"
}
],
"responses": {
"200": {
"description": "Default Response",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"kind",
"msg",
"payload"
],
"properties": {
"kind": {
"enum": [
"success"
]
},
"msg": {
"enum": [
"ttthistory.success"
]
},
"payload": {
"type": "object",
"required": [
"data"
],
"properties": {
"data": {
"type": "array",
"items": {
"type": "object",
"required": [
"gameId",
"playerX",
"playerO",
"date",
"outcome"
],
"properties": {
"gameId": {
"type": "string",
"description": "gameId"
},
"playerX": {
"type": "object",
"required": [
"id",
"name"
],
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"playerO": {
"type": "object",
"required": [
"id",
"name"
],
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"date": {
"type": "string"
},
"outcome": {
"enum": [
"winX",
"winO",
"other"
]
}
}
}
}
}
}
}
}
}
}
},
"401": {
"description": "Default Response",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"type": "object",
"required": [
"kind",
"msg"
],
"properties": {
"kind": {
"enum": [
"notLoggedIn"
]
},
"msg": {
"enum": [
"auth.noCookie",
"auth.invalidKind",
"auth.noUser",
"auth.invalid"
]
}
}
},
{
"type": "object",
"required": [
"kind",
"msg"
],
"properties": {
"kind": {
"enum": [
"notLoggedIn"
]
},
"msg": {
"enum": [
"auth.noCookie",
"auth.invalidKind",
"auth.noUser",
"auth.invalid"
]
}
}
}
]
}
}
}
},
"404": {
"description": "Default Response",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"kind",
"msg"
],
"properties": {
"kind": {
"enum": [
"failure"
]
},
"msg": {
"enum": [
"ttthistory.failure.notfound"
]
}
}
}
}
}
}
}
}
}
},
"servers": [
{
"url": "https://local.maix.me:8888",

View file

@ -0,0 +1,58 @@
import { UserId } from '@shared/database/mixin/user';
import { isNullish, MakeStaticResponse, typeResponse } from '@shared/utils';
import { FastifyPluginAsync } from 'fastify';
import { Static, Type } from 'typebox';
const TTTHistoryParams = Type.Object({
user: Type.String({ description: '\'me\' | <userid>' }),
});
type TTTHistoryParams = Static<typeof TTTHistoryParams>;
const TTTHistoryResponse = {
'200': typeResponse('success', 'ttthistory.success', {
data: Type.Array(
Type.Object({
gameId: Type.String({ description: 'gameId' }),
playerX: Type.Object({id: Type.String(), name: Type.String()}),
playerO: Type.Object({id: Type.String(), name: Type.String()}),
date: Type.String(),
outcome: Type.Enum(['winX', 'winO', 'other']),
}),
),
}),
'404': typeResponse('failure', 'ttthistory.failure.notfound'),
};
type TTTHistoryResponse = MakeStaticResponse<typeof TTTHistoryResponse>;
const route: FastifyPluginAsync = async (fastify): Promise<void> => {
fastify.get<{ Params: TTTHistoryParams }>(
'/api/ttt/history/:user',
{
schema: {
params: TTTHistoryParams,
response: TTTHistoryResponse,
operationId: 'tttHistory',
},
config: { requireAuth: true },
},
async function(req, res) {
if (req.params.user === 'me') { req.params.user = req.authUser!.id; }
const user = this.db.getUser(req.params.user);
if (isNullish(user)) { return res.makeResponse(404, 'failure', 'ttthistory.failure.notfound'); }
const data = this.db.getAllTTTGameForUser(req.params.user as UserId);
if (isNullish(data)) { return res.makeResponse(404, 'failure', 'ttthistory.failure.notfound'); }
return res.makeResponse(200, 'success', 'ttthistory.success', {
data: data.map(v => ({
gameId: v.id,
playerX: { id: v.playerX, name: v.nameX },
playerO: { id: v.playerO, name: v.nameO },
date: v.time.toString(),
outcome: v.outcome,
})),
});
},
);
};
export default route;