From 9185367fcd7d95af89fac36dd892d8b064dbd94f Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sat, 14 Feb 2026 20:32:47 +0900 Subject: feat(openapi): generate OpenAPI specs from TypeSpec sources Migrate hand-written OpenAPI YAML to TypeSpec (.tsp) source files. TypeSpec compiles to OpenAPI 3.0 YAML, enabling type-safe API definitions. - Add typespec/ directory with api-server and fortee definitions - Integrate TypeSpec build into `just gen` and `just build` pipelines - Update backend handler code to match new generated type names (inlined error responses, separate GameType/ProblemLanguage enums) - Regenerate frontend TypeScript types from new OpenAPI output Co-Authored-By: Claude Opus 4.6 --- typespec/api-server/routes.tsp | 126 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 typespec/api-server/routes.tsp (limited to 'typespec/api-server/routes.tsp') diff --git a/typespec/api-server/routes.tsp b/typespec/api-server/routes.tsp new file mode 100644 index 0000000..3409cea --- /dev/null +++ b/typespec/api-server/routes.tsp @@ -0,0 +1,126 @@ +using TypeSpec.Http; +using TypeSpec.OpenAPI; + +namespace AlbatrossApi; + +// ---------- Auth ---------- + +@route("/login") +@post +@operationId("postLogin") +op postLogin(@body body: { + username: string; + password: string; +}): { + @body body: { + user: User; + }; +} | UnauthorizedError; + +@route("/logout") +@post +@operationId("postLogout") +op postLogout(): { + @statusCode statusCode: 200; +} | UnauthorizedError; + +@route("/me") +@get +@operationId("getMe") +op getMe(): { + @body body: { + user: User; + }; +} | UnauthorizedError; + +// ---------- Games ---------- + +@route("/games") +@get +@operationId("getGames") +op getGames(): { + @body body: { + games: Game[]; + }; +} | UnauthorizedError | ForbiddenError; + +@route("/games/{game_id}") +@get +@operationId("getGame") +op getGame(@path game_id: integer): { + @body body: { + game: Game; + }; +} | UnauthorizedError | ForbiddenError | NotFoundError; + +// ---------- Play ---------- + +@route("/games/{game_id}/play/latest_state") +@get +@operationId("getGamePlayLatestState") +op getGamePlayLatestState(@path game_id: integer): { + @body body: { + state: LatestGameState; + }; +} | UnauthorizedError | ForbiddenError | NotFoundError; + +@route("/games/{game_id}/play/code") +@post +@operationId("postGamePlayCode") +op postGamePlayCode( + @path game_id: integer, + @body body: { + code: string; + }, +): { + @statusCode statusCode: 200; +} | UnauthorizedError | ForbiddenError | NotFoundError; + +@route("/games/{game_id}/play/submit") +@post +@operationId("postGamePlaySubmit") +op postGamePlaySubmit( + @path game_id: integer, + @body body: { + code: string; + }, +): { + @statusCode statusCode: 200; +} | UnauthorizedError | ForbiddenError | NotFoundError; + +// ---------- Watch ---------- + +@route("/games/{game_id}/watch/ranking") +@get +@operationId("getGameWatchRanking") +op getGameWatchRanking(@path game_id: integer): { + @body body: { + ranking: RankingEntry[]; + }; +} | UnauthorizedError | ForbiddenError | NotFoundError; + +@route("/games/{game_id}/watch/latest_states") +@get +@operationId("getGameWatchLatestStates") +op getGameWatchLatestStates(@path game_id: integer): { + @body body: { + states: Record; + }; +} | UnauthorizedError | ForbiddenError | NotFoundError; + +// ---------- Tournament ---------- + +@route("/tournament") +@get +@operationId("getTournament") +op getTournament( + @query game1: integer, + @query game2: integer, + @query game3: integer, + @query game4: integer, + @query game5: integer, +): { + @body body: { + tournament: Tournament; + }; +} | UnauthorizedError | ForbiddenError | NotFoundError; -- cgit v1.3.1