diff --git a/frontend/src/api/generated/.openapi-generator/FILES b/frontend/src/api/generated/.openapi-generator/FILES index 0a4207f..dd4bc5c 100644 --- a/frontend/src/api/generated/.openapi-generator/FILES +++ b/frontend/src/api/generated/.openapi-generator/FILES @@ -1,6 +1,8 @@ apis/OpenapiOtherApi.ts apis/index.ts index.ts +models/AddFriend200Response.ts +models/AddFriend404Response.ts models/AllowGuestMessage200Response.ts models/AllowGuestMessage403Response.ts models/ChangeDesc200Response.ts @@ -38,6 +40,9 @@ models/GuestLogin200ResponsePayload.ts models/GuestLogin400Response.ts models/GuestLogin500Response.ts models/GuestLoginRequest.ts +models/ListFriend200Response.ts +models/ListFriend200ResponsePayload.ts +models/ListFriend200ResponsePayloadFriendsInner.ts models/Login200Response.ts models/Login202Response.ts models/Login202ResponsePayload.ts @@ -60,6 +65,8 @@ models/ProviderList200Response.ts models/ProviderList200ResponsePayload.ts models/ProviderList200ResponsePayloadListInner.ts models/ProviderList200ResponsePayloadListInnerColors.ts +models/RemoveFriend200Response.ts +models/RemoveFriend404Response.ts models/Signin200Response.ts models/Signin200ResponsePayload.ts models/Signin400Response.ts @@ -82,7 +89,6 @@ models/TournamentList404Response.ts models/TttHistory200Response.ts models/TttHistory200ResponsePayload.ts models/TttHistory200ResponsePayloadDataInner.ts -models/TttHistory200ResponsePayloadDataInnerPlayerX.ts models/TttHistory404Response.ts models/index.ts runtime.ts diff --git a/frontend/src/api/generated/apis/OpenapiOtherApi.ts b/frontend/src/api/generated/apis/OpenapiOtherApi.ts index 84da0d5..0ca170c 100644 --- a/frontend/src/api/generated/apis/OpenapiOtherApi.ts +++ b/frontend/src/api/generated/apis/OpenapiOtherApi.ts @@ -15,6 +15,8 @@ import * as runtime from '../runtime'; import type { + AddFriend200Response, + AddFriend404Response, AllowGuestMessage200Response, AllowGuestMessage403Response, ChangeDesc200Response, @@ -46,6 +48,7 @@ import type { GuestLogin400Response, GuestLogin500Response, GuestLoginRequest, + ListFriend200Response, Login200Response, Login202Response, Login400Response, @@ -60,6 +63,8 @@ import type { PongHistory200Response, PongHistory404Response, ProviderList200Response, + RemoveFriend200Response, + RemoveFriend404Response, Signin200Response, Signin400Response, Signin500Response, @@ -74,6 +79,10 @@ import type { TttHistory404Response, } from '../models/index'; import { + AddFriend200ResponseFromJSON, + AddFriend200ResponseToJSON, + AddFriend404ResponseFromJSON, + AddFriend404ResponseToJSON, AllowGuestMessage200ResponseFromJSON, AllowGuestMessage200ResponseToJSON, AllowGuestMessage403ResponseFromJSON, @@ -136,6 +145,8 @@ import { GuestLogin500ResponseToJSON, GuestLoginRequestFromJSON, GuestLoginRequestToJSON, + ListFriend200ResponseFromJSON, + ListFriend200ResponseToJSON, Login200ResponseFromJSON, Login200ResponseToJSON, Login202ResponseFromJSON, @@ -164,6 +175,10 @@ import { PongHistory404ResponseToJSON, ProviderList200ResponseFromJSON, ProviderList200ResponseToJSON, + RemoveFriend200ResponseFromJSON, + RemoveFriend200ResponseToJSON, + RemoveFriend404ResponseFromJSON, + RemoveFriend404ResponseToJSON, Signin200ResponseFromJSON, Signin200ResponseToJSON, Signin400ResponseFromJSON, @@ -190,6 +205,10 @@ import { TttHistory404ResponseToJSON, } from '../models/index'; +export interface AddFriendRequest { + user: string; +} + export interface ChangeDescOperationRequest { changeDescRequest: ChangeDescRequest; } @@ -226,6 +245,10 @@ export interface PongHistoryRequest { user: string; } +export interface RemoveFriendRequest { + user: string; +} + export interface SigninRequest { loginRequest: LoginRequest; } @@ -243,6 +266,60 @@ export interface TttHistoryRequest { */ export class OpenapiOtherApi extends runtime.BaseAPI { + /** + */ + async addFriendRaw(requestParameters: AddFriendRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['user'] == null) { + throw new runtime.RequiredError( + 'user', + 'Required parameter "user" was null or undefined when calling addFriend().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + + let urlPath = `/api/user/friend/add/{user}`; + urlPath = urlPath.replace(`{${"user"}}`, encodeURIComponent(String(requestParameters['user']))); + + const response = await this.request({ + path: urlPath, + method: 'PUT', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + // CHANGED: Handle all status codes defined in the OpenAPI spec, not just 2xx responses + // This allows typed access to error responses (4xx, 5xx) and other status codes. + // The code routes responses based on the actual HTTP status code and returns + // appropriately typed ApiResponse wrappers for each status code. + if (response.status === 200) { + // Object response for status 200 + return new runtime.JSONApiResponse(response, (jsonValue) => AddFriend200ResponseFromJSON(jsonValue)); + } + if (response.status === 401) { + // Object response for status 401 + return new runtime.JSONApiResponse(response, (jsonValue) => ChangePassword401ResponseFromJSON(jsonValue)); + } + if (response.status === 404) { + // Object response for status 404 + return new runtime.JSONApiResponse(response, (jsonValue) => AddFriend404ResponseFromJSON(jsonValue)); + } + // CHANGED: Throw error if status code is not handled by any of the defined responses + // This ensures all code paths return a value and provides clear error messages for unexpected status codes + // Only throw if responses were defined but none matched the actual status code + throw new runtime.ResponseError(response, `Unexpected status code: ${response.status}. Expected one of: 200, 401, 404`); + } + + /** + */ + async addFriend(requestParameters: AddFriendRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.addFriendRaw(requestParameters, initOverrides); + return await response.value(); + } + /** */ async allowGuestMessageRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { @@ -766,6 +843,48 @@ export class OpenapiOtherApi extends runtime.BaseAPI { return await response.value(); } + /** + */ + async listFriendRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + + let urlPath = `/api/user/friend/list`; + + const response = await this.request({ + path: urlPath, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + // CHANGED: Handle all status codes defined in the OpenAPI spec, not just 2xx responses + // This allows typed access to error responses (4xx, 5xx) and other status codes. + // The code routes responses based on the actual HTTP status code and returns + // appropriately typed ApiResponse wrappers for each status code. + if (response.status === 200) { + // Object response for status 200 + return new runtime.JSONApiResponse(response, (jsonValue) => ListFriend200ResponseFromJSON(jsonValue)); + } + if (response.status === 401) { + // Object response for status 401 + return new runtime.JSONApiResponse(response, (jsonValue) => StatusOtp401ResponseFromJSON(jsonValue)); + } + // CHANGED: Throw error if status code is not handled by any of the defined responses + // This ensures all code paths return a value and provides clear error messages for unexpected status codes + // Only throw if responses were defined but none matched the actual status code + throw new runtime.ResponseError(response, `Unexpected status code: ${response.status}. Expected one of: 200, 401`); + } + + /** + */ + async listFriend(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.listFriendRaw(initOverrides); + return await response.value(); + } + /** */ async loginRaw(requestParameters: LoginOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { @@ -1016,6 +1135,60 @@ export class OpenapiOtherApi extends runtime.BaseAPI { return await response.value(); } + /** + */ + async removeFriendRaw(requestParameters: RemoveFriendRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['user'] == null) { + throw new runtime.RequiredError( + 'user', + 'Required parameter "user" was null or undefined when calling removeFriend().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + + let urlPath = `/api/user/friend/remove/{user}`; + urlPath = urlPath.replace(`{${"user"}}`, encodeURIComponent(String(requestParameters['user']))); + + const response = await this.request({ + path: urlPath, + method: 'PUT', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + // CHANGED: Handle all status codes defined in the OpenAPI spec, not just 2xx responses + // This allows typed access to error responses (4xx, 5xx) and other status codes. + // The code routes responses based on the actual HTTP status code and returns + // appropriately typed ApiResponse wrappers for each status code. + if (response.status === 200) { + // Object response for status 200 + return new runtime.JSONApiResponse(response, (jsonValue) => RemoveFriend200ResponseFromJSON(jsonValue)); + } + if (response.status === 401) { + // Object response for status 401 + return new runtime.JSONApiResponse(response, (jsonValue) => ChangePassword401ResponseFromJSON(jsonValue)); + } + if (response.status === 404) { + // Object response for status 404 + return new runtime.JSONApiResponse(response, (jsonValue) => RemoveFriend404ResponseFromJSON(jsonValue)); + } + // CHANGED: Throw error if status code is not handled by any of the defined responses + // This ensures all code paths return a value and provides clear error messages for unexpected status codes + // Only throw if responses were defined but none matched the actual status code + throw new runtime.ResponseError(response, `Unexpected status code: ${response.status}. Expected one of: 200, 401, 404`); + } + + /** + */ + async removeFriend(requestParameters: RemoveFriendRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.removeFriendRaw(requestParameters, initOverrides); + return await response.value(); + } + /** */ async signinRaw(requestParameters: SigninRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { diff --git a/frontend/src/api/generated/models/AddFriend200Response.ts b/frontend/src/api/generated/models/AddFriend200Response.ts new file mode 100644 index 0000000..0ce06f7 --- /dev/null +++ b/frontend/src/api/generated/models/AddFriend200Response.ts @@ -0,0 +1,93 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * @fastify/swagger + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 9.6.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface AddFriend200Response + */ +export interface AddFriend200Response { + /** + * + * @type {string} + * @memberof AddFriend200Response + */ + kind: AddFriend200ResponseKindEnum; + /** + * + * @type {string} + * @memberof AddFriend200Response + */ + msg: AddFriend200ResponseMsgEnum; +} + + +/** + * @export + */ +export const AddFriend200ResponseKindEnum = { + Success: 'success' +} as const; +export type AddFriend200ResponseKindEnum = typeof AddFriend200ResponseKindEnum[keyof typeof AddFriend200ResponseKindEnum]; + +/** + * @export + */ +export const AddFriend200ResponseMsgEnum = { + AddFriendSuccess: 'addFriend.success' +} as const; +export type AddFriend200ResponseMsgEnum = typeof AddFriend200ResponseMsgEnum[keyof typeof AddFriend200ResponseMsgEnum]; + + +/** + * Check if a given object implements the AddFriend200Response interface. + */ +export function instanceOfAddFriend200Response(value: object): value is AddFriend200Response { + if (!('kind' in value) || value['kind'] === undefined) return false; + if (!('msg' in value) || value['msg'] === undefined) return false; + return true; +} + +export function AddFriend200ResponseFromJSON(json: any): AddFriend200Response { + return AddFriend200ResponseFromJSONTyped(json, false); +} + +export function AddFriend200ResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): AddFriend200Response { + if (json == null) { + return json; + } + return { + + 'kind': json['kind'], + 'msg': json['msg'], + }; +} + +export function AddFriend200ResponseToJSON(json: any): AddFriend200Response { + return AddFriend200ResponseToJSONTyped(json, false); +} + +export function AddFriend200ResponseToJSONTyped(value?: AddFriend200Response | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'kind': value['kind'], + 'msg': value['msg'], + }; +} + diff --git a/frontend/src/api/generated/models/AddFriend404Response.ts b/frontend/src/api/generated/models/AddFriend404Response.ts new file mode 100644 index 0000000..6431f79 --- /dev/null +++ b/frontend/src/api/generated/models/AddFriend404Response.ts @@ -0,0 +1,93 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * @fastify/swagger + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 9.6.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface AddFriend404Response + */ +export interface AddFriend404Response { + /** + * + * @type {string} + * @memberof AddFriend404Response + */ + kind: AddFriend404ResponseKindEnum; + /** + * + * @type {string} + * @memberof AddFriend404Response + */ + msg: AddFriend404ResponseMsgEnum; +} + + +/** + * @export + */ +export const AddFriend404ResponseKindEnum = { + Failure: 'failure' +} as const; +export type AddFriend404ResponseKindEnum = typeof AddFriend404ResponseKindEnum[keyof typeof AddFriend404ResponseKindEnum]; + +/** + * @export + */ +export const AddFriend404ResponseMsgEnum = { + AddFriendFailureUnknownUser: 'addFriend.failure.unknownUser' +} as const; +export type AddFriend404ResponseMsgEnum = typeof AddFriend404ResponseMsgEnum[keyof typeof AddFriend404ResponseMsgEnum]; + + +/** + * Check if a given object implements the AddFriend404Response interface. + */ +export function instanceOfAddFriend404Response(value: object): value is AddFriend404Response { + if (!('kind' in value) || value['kind'] === undefined) return false; + if (!('msg' in value) || value['msg'] === undefined) return false; + return true; +} + +export function AddFriend404ResponseFromJSON(json: any): AddFriend404Response { + return AddFriend404ResponseFromJSONTyped(json, false); +} + +export function AddFriend404ResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): AddFriend404Response { + if (json == null) { + return json; + } + return { + + 'kind': json['kind'], + 'msg': json['msg'], + }; +} + +export function AddFriend404ResponseToJSON(json: any): AddFriend404Response { + return AddFriend404ResponseToJSONTyped(json, false); +} + +export function AddFriend404ResponseToJSONTyped(value?: AddFriend404Response | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'kind': value['kind'], + 'msg': value['msg'], + }; +} + diff --git a/frontend/src/api/generated/models/ListFriend200Response.ts b/frontend/src/api/generated/models/ListFriend200Response.ts new file mode 100644 index 0000000..b354192 --- /dev/null +++ b/frontend/src/api/generated/models/ListFriend200Response.ts @@ -0,0 +1,110 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * @fastify/swagger + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 9.6.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { ListFriend200ResponsePayload } from './ListFriend200ResponsePayload'; +import { + ListFriend200ResponsePayloadFromJSON, + ListFriend200ResponsePayloadFromJSONTyped, + ListFriend200ResponsePayloadToJSON, + ListFriend200ResponsePayloadToJSONTyped, +} from './ListFriend200ResponsePayload'; + +/** + * + * @export + * @interface ListFriend200Response + */ +export interface ListFriend200Response { + /** + * + * @type {string} + * @memberof ListFriend200Response + */ + kind: ListFriend200ResponseKindEnum; + /** + * + * @type {string} + * @memberof ListFriend200Response + */ + msg: ListFriend200ResponseMsgEnum; + /** + * + * @type {ListFriend200ResponsePayload} + * @memberof ListFriend200Response + */ + payload: ListFriend200ResponsePayload; +} + + +/** + * @export + */ +export const ListFriend200ResponseKindEnum = { + Success: 'success' +} as const; +export type ListFriend200ResponseKindEnum = typeof ListFriend200ResponseKindEnum[keyof typeof ListFriend200ResponseKindEnum]; + +/** + * @export + */ +export const ListFriend200ResponseMsgEnum = { + ListFriendSuccess: 'listFriend.success' +} as const; +export type ListFriend200ResponseMsgEnum = typeof ListFriend200ResponseMsgEnum[keyof typeof ListFriend200ResponseMsgEnum]; + + +/** + * Check if a given object implements the ListFriend200Response interface. + */ +export function instanceOfListFriend200Response(value: object): value is ListFriend200Response { + if (!('kind' in value) || value['kind'] === undefined) return false; + if (!('msg' in value) || value['msg'] === undefined) return false; + if (!('payload' in value) || value['payload'] === undefined) return false; + return true; +} + +export function ListFriend200ResponseFromJSON(json: any): ListFriend200Response { + return ListFriend200ResponseFromJSONTyped(json, false); +} + +export function ListFriend200ResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): ListFriend200Response { + if (json == null) { + return json; + } + return { + + 'kind': json['kind'], + 'msg': json['msg'], + 'payload': ListFriend200ResponsePayloadFromJSON(json['payload']), + }; +} + +export function ListFriend200ResponseToJSON(json: any): ListFriend200Response { + return ListFriend200ResponseToJSONTyped(json, false); +} + +export function ListFriend200ResponseToJSONTyped(value?: ListFriend200Response | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'kind': value['kind'], + 'msg': value['msg'], + 'payload': ListFriend200ResponsePayloadToJSON(value['payload']), + }; +} + diff --git a/frontend/src/api/generated/models/ListFriend200ResponsePayload.ts b/frontend/src/api/generated/models/ListFriend200ResponsePayload.ts new file mode 100644 index 0000000..51c2fa5 --- /dev/null +++ b/frontend/src/api/generated/models/ListFriend200ResponsePayload.ts @@ -0,0 +1,74 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * @fastify/swagger + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 9.6.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { ListFriend200ResponsePayloadFriendsInner } from './ListFriend200ResponsePayloadFriendsInner'; +import { + ListFriend200ResponsePayloadFriendsInnerFromJSON, + ListFriend200ResponsePayloadFriendsInnerFromJSONTyped, + ListFriend200ResponsePayloadFriendsInnerToJSON, + ListFriend200ResponsePayloadFriendsInnerToJSONTyped, +} from './ListFriend200ResponsePayloadFriendsInner'; + +/** + * + * @export + * @interface ListFriend200ResponsePayload + */ +export interface ListFriend200ResponsePayload { + /** + * + * @type {Array} + * @memberof ListFriend200ResponsePayload + */ + friends: Array; +} + +/** + * Check if a given object implements the ListFriend200ResponsePayload interface. + */ +export function instanceOfListFriend200ResponsePayload(value: object): value is ListFriend200ResponsePayload { + if (!('friends' in value) || value['friends'] === undefined) return false; + return true; +} + +export function ListFriend200ResponsePayloadFromJSON(json: any): ListFriend200ResponsePayload { + return ListFriend200ResponsePayloadFromJSONTyped(json, false); +} + +export function ListFriend200ResponsePayloadFromJSONTyped(json: any, ignoreDiscriminator: boolean): ListFriend200ResponsePayload { + if (json == null) { + return json; + } + return { + + 'friends': ((json['friends'] as Array).map(ListFriend200ResponsePayloadFriendsInnerFromJSON)), + }; +} + +export function ListFriend200ResponsePayloadToJSON(json: any): ListFriend200ResponsePayload { + return ListFriend200ResponsePayloadToJSONTyped(json, false); +} + +export function ListFriend200ResponsePayloadToJSONTyped(value?: ListFriend200ResponsePayload | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'friends': ((value['friends'] as Array).map(ListFriend200ResponsePayloadFriendsInnerToJSON)), + }; +} + diff --git a/frontend/src/api/generated/models/ListFriend200ResponsePayloadFriendsInner.ts b/frontend/src/api/generated/models/ListFriend200ResponsePayloadFriendsInner.ts new file mode 100644 index 0000000..de5ba72 --- /dev/null +++ b/frontend/src/api/generated/models/ListFriend200ResponsePayloadFriendsInner.ts @@ -0,0 +1,75 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * @fastify/swagger + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 9.6.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface ListFriend200ResponsePayloadFriendsInner + */ +export interface ListFriend200ResponsePayloadFriendsInner { + /** + * + * @type {string} + * @memberof ListFriend200ResponsePayloadFriendsInner + */ + id: string; + /** + * + * @type {string} + * @memberof ListFriend200ResponsePayloadFriendsInner + */ + name: string; +} + +/** + * Check if a given object implements the ListFriend200ResponsePayloadFriendsInner interface. + */ +export function instanceOfListFriend200ResponsePayloadFriendsInner(value: object): value is ListFriend200ResponsePayloadFriendsInner { + if (!('id' in value) || value['id'] === undefined) return false; + if (!('name' in value) || value['name'] === undefined) return false; + return true; +} + +export function ListFriend200ResponsePayloadFriendsInnerFromJSON(json: any): ListFriend200ResponsePayloadFriendsInner { + return ListFriend200ResponsePayloadFriendsInnerFromJSONTyped(json, false); +} + +export function ListFriend200ResponsePayloadFriendsInnerFromJSONTyped(json: any, ignoreDiscriminator: boolean): ListFriend200ResponsePayloadFriendsInner { + if (json == null) { + return json; + } + return { + + 'id': json['id'], + 'name': json['name'], + }; +} + +export function ListFriend200ResponsePayloadFriendsInnerToJSON(json: any): ListFriend200ResponsePayloadFriendsInner { + return ListFriend200ResponsePayloadFriendsInnerToJSONTyped(json, false); +} + +export function ListFriend200ResponsePayloadFriendsInnerToJSONTyped(value?: ListFriend200ResponsePayloadFriendsInner | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'id': value['id'], + 'name': value['name'], + }; +} + diff --git a/frontend/src/api/generated/models/ListFriend404Response.ts b/frontend/src/api/generated/models/ListFriend404Response.ts new file mode 100644 index 0000000..251ea33 --- /dev/null +++ b/frontend/src/api/generated/models/ListFriend404Response.ts @@ -0,0 +1,93 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * @fastify/swagger + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 9.6.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface ListFriend404Response + */ +export interface ListFriend404Response { + /** + * + * @type {string} + * @memberof ListFriend404Response + */ + kind: ListFriend404ResponseKindEnum; + /** + * + * @type {string} + * @memberof ListFriend404Response + */ + msg: ListFriend404ResponseMsgEnum; +} + + +/** + * @export + */ +export const ListFriend404ResponseKindEnum = { + Failure: 'failure' +} as const; +export type ListFriend404ResponseKindEnum = typeof ListFriend404ResponseKindEnum[keyof typeof ListFriend404ResponseKindEnum]; + +/** + * @export + */ +export const ListFriend404ResponseMsgEnum = { + RemoveFriendFailureUnknownUser: 'removeFriend.failure.unknownUser' +} as const; +export type ListFriend404ResponseMsgEnum = typeof ListFriend404ResponseMsgEnum[keyof typeof ListFriend404ResponseMsgEnum]; + + +/** + * Check if a given object implements the ListFriend404Response interface. + */ +export function instanceOfListFriend404Response(value: object): value is ListFriend404Response { + if (!('kind' in value) || value['kind'] === undefined) return false; + if (!('msg' in value) || value['msg'] === undefined) return false; + return true; +} + +export function ListFriend404ResponseFromJSON(json: any): ListFriend404Response { + return ListFriend404ResponseFromJSONTyped(json, false); +} + +export function ListFriend404ResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): ListFriend404Response { + if (json == null) { + return json; + } + return { + + 'kind': json['kind'], + 'msg': json['msg'], + }; +} + +export function ListFriend404ResponseToJSON(json: any): ListFriend404Response { + return ListFriend404ResponseToJSONTyped(json, false); +} + +export function ListFriend404ResponseToJSONTyped(value?: ListFriend404Response | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'kind': value['kind'], + 'msg': value['msg'], + }; +} + diff --git a/frontend/src/api/generated/models/RemoveFriend200Response.ts b/frontend/src/api/generated/models/RemoveFriend200Response.ts new file mode 100644 index 0000000..aa3f7d7 --- /dev/null +++ b/frontend/src/api/generated/models/RemoveFriend200Response.ts @@ -0,0 +1,93 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * @fastify/swagger + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 9.6.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface RemoveFriend200Response + */ +export interface RemoveFriend200Response { + /** + * + * @type {string} + * @memberof RemoveFriend200Response + */ + kind: RemoveFriend200ResponseKindEnum; + /** + * + * @type {string} + * @memberof RemoveFriend200Response + */ + msg: RemoveFriend200ResponseMsgEnum; +} + + +/** + * @export + */ +export const RemoveFriend200ResponseKindEnum = { + Success: 'success' +} as const; +export type RemoveFriend200ResponseKindEnum = typeof RemoveFriend200ResponseKindEnum[keyof typeof RemoveFriend200ResponseKindEnum]; + +/** + * @export + */ +export const RemoveFriend200ResponseMsgEnum = { + RemoveFriendSuccess: 'removeFriend.success' +} as const; +export type RemoveFriend200ResponseMsgEnum = typeof RemoveFriend200ResponseMsgEnum[keyof typeof RemoveFriend200ResponseMsgEnum]; + + +/** + * Check if a given object implements the RemoveFriend200Response interface. + */ +export function instanceOfRemoveFriend200Response(value: object): value is RemoveFriend200Response { + if (!('kind' in value) || value['kind'] === undefined) return false; + if (!('msg' in value) || value['msg'] === undefined) return false; + return true; +} + +export function RemoveFriend200ResponseFromJSON(json: any): RemoveFriend200Response { + return RemoveFriend200ResponseFromJSONTyped(json, false); +} + +export function RemoveFriend200ResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): RemoveFriend200Response { + if (json == null) { + return json; + } + return { + + 'kind': json['kind'], + 'msg': json['msg'], + }; +} + +export function RemoveFriend200ResponseToJSON(json: any): RemoveFriend200Response { + return RemoveFriend200ResponseToJSONTyped(json, false); +} + +export function RemoveFriend200ResponseToJSONTyped(value?: RemoveFriend200Response | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'kind': value['kind'], + 'msg': value['msg'], + }; +} + diff --git a/frontend/src/api/generated/models/RemoveFriend404Response.ts b/frontend/src/api/generated/models/RemoveFriend404Response.ts new file mode 100644 index 0000000..7987a33 --- /dev/null +++ b/frontend/src/api/generated/models/RemoveFriend404Response.ts @@ -0,0 +1,93 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * @fastify/swagger + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 9.6.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface RemoveFriend404Response + */ +export interface RemoveFriend404Response { + /** + * + * @type {string} + * @memberof RemoveFriend404Response + */ + kind: RemoveFriend404ResponseKindEnum; + /** + * + * @type {string} + * @memberof RemoveFriend404Response + */ + msg: RemoveFriend404ResponseMsgEnum; +} + + +/** + * @export + */ +export const RemoveFriend404ResponseKindEnum = { + Failure: 'failure' +} as const; +export type RemoveFriend404ResponseKindEnum = typeof RemoveFriend404ResponseKindEnum[keyof typeof RemoveFriend404ResponseKindEnum]; + +/** + * @export + */ +export const RemoveFriend404ResponseMsgEnum = { + RemoveFriendFailureUnknownUser: 'removeFriend.failure.unknownUser' +} as const; +export type RemoveFriend404ResponseMsgEnum = typeof RemoveFriend404ResponseMsgEnum[keyof typeof RemoveFriend404ResponseMsgEnum]; + + +/** + * Check if a given object implements the RemoveFriend404Response interface. + */ +export function instanceOfRemoveFriend404Response(value: object): value is RemoveFriend404Response { + if (!('kind' in value) || value['kind'] === undefined) return false; + if (!('msg' in value) || value['msg'] === undefined) return false; + return true; +} + +export function RemoveFriend404ResponseFromJSON(json: any): RemoveFriend404Response { + return RemoveFriend404ResponseFromJSONTyped(json, false); +} + +export function RemoveFriend404ResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): RemoveFriend404Response { + if (json == null) { + return json; + } + return { + + 'kind': json['kind'], + 'msg': json['msg'], + }; +} + +export function RemoveFriend404ResponseToJSON(json: any): RemoveFriend404Response { + return RemoveFriend404ResponseToJSONTyped(json, false); +} + +export function RemoveFriend404ResponseToJSONTyped(value?: RemoveFriend404Response | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'kind': value['kind'], + 'msg': value['msg'], + }; +} + diff --git a/frontend/src/api/generated/models/TttHistory200ResponsePayloadDataInner.ts b/frontend/src/api/generated/models/TttHistory200ResponsePayloadDataInner.ts index 72f38ee..972dac5 100644 --- a/frontend/src/api/generated/models/TttHistory200ResponsePayloadDataInner.ts +++ b/frontend/src/api/generated/models/TttHistory200ResponsePayloadDataInner.ts @@ -13,13 +13,13 @@ */ import { mapValues } from '../runtime'; -import type { TttHistory200ResponsePayloadDataInnerPlayerX } from './TttHistory200ResponsePayloadDataInnerPlayerX'; +import type { ListFriend200ResponsePayloadFriendsInner } from './ListFriend200ResponsePayloadFriendsInner'; import { - TttHistory200ResponsePayloadDataInnerPlayerXFromJSON, - TttHistory200ResponsePayloadDataInnerPlayerXFromJSONTyped, - TttHistory200ResponsePayloadDataInnerPlayerXToJSON, - TttHistory200ResponsePayloadDataInnerPlayerXToJSONTyped, -} from './TttHistory200ResponsePayloadDataInnerPlayerX'; + ListFriend200ResponsePayloadFriendsInnerFromJSON, + ListFriend200ResponsePayloadFriendsInnerFromJSONTyped, + ListFriend200ResponsePayloadFriendsInnerToJSON, + ListFriend200ResponsePayloadFriendsInnerToJSONTyped, +} from './ListFriend200ResponsePayloadFriendsInner'; /** * @@ -35,16 +35,16 @@ export interface TttHistory200ResponsePayloadDataInner { gameId: string; /** * - * @type {TttHistory200ResponsePayloadDataInnerPlayerX} + * @type {ListFriend200ResponsePayloadFriendsInner} * @memberof TttHistory200ResponsePayloadDataInner */ - playerX: TttHistory200ResponsePayloadDataInnerPlayerX; + playerX: ListFriend200ResponsePayloadFriendsInner; /** * - * @type {TttHistory200ResponsePayloadDataInnerPlayerX} + * @type {ListFriend200ResponsePayloadFriendsInner} * @memberof TttHistory200ResponsePayloadDataInner */ - playerO: TttHistory200ResponsePayloadDataInnerPlayerX; + playerO: ListFriend200ResponsePayloadFriendsInner; /** * * @type {string} @@ -95,8 +95,8 @@ export function TttHistory200ResponsePayloadDataInnerFromJSONTyped(json: any, ig return { 'gameId': json['gameId'], - 'playerX': TttHistory200ResponsePayloadDataInnerPlayerXFromJSON(json['playerX']), - 'playerO': TttHistory200ResponsePayloadDataInnerPlayerXFromJSON(json['playerO']), + 'playerX': ListFriend200ResponsePayloadFriendsInnerFromJSON(json['playerX']), + 'playerO': ListFriend200ResponsePayloadFriendsInnerFromJSON(json['playerO']), 'date': json['date'], 'outcome': json['outcome'], }; @@ -114,8 +114,8 @@ export function TttHistory200ResponsePayloadDataInnerToJSONTyped(value?: TttHist return { 'gameId': value['gameId'], - 'playerX': TttHistory200ResponsePayloadDataInnerPlayerXToJSON(value['playerX']), - 'playerO': TttHistory200ResponsePayloadDataInnerPlayerXToJSON(value['playerO']), + 'playerX': ListFriend200ResponsePayloadFriendsInnerToJSON(value['playerX']), + 'playerO': ListFriend200ResponsePayloadFriendsInnerToJSON(value['playerO']), 'date': value['date'], 'outcome': value['outcome'], }; diff --git a/frontend/src/api/generated/models/index.ts b/frontend/src/api/generated/models/index.ts index a5a3877..b7e0ef1 100644 --- a/frontend/src/api/generated/models/index.ts +++ b/frontend/src/api/generated/models/index.ts @@ -1,5 +1,7 @@ /* tslint:disable */ /* eslint-disable */ +export * from './AddFriend200Response'; +export * from './AddFriend404Response'; export * from './AllowGuestMessage200Response'; export * from './AllowGuestMessage403Response'; export * from './ChangeDesc200Response'; @@ -37,6 +39,9 @@ export * from './GuestLogin200ResponsePayload'; export * from './GuestLogin400Response'; export * from './GuestLogin500Response'; export * from './GuestLoginRequest'; +export * from './ListFriend200Response'; +export * from './ListFriend200ResponsePayload'; +export * from './ListFriend200ResponsePayloadFriendsInner'; export * from './Login200Response'; export * from './Login202Response'; export * from './Login202ResponsePayload'; @@ -59,6 +64,8 @@ export * from './ProviderList200Response'; export * from './ProviderList200ResponsePayload'; export * from './ProviderList200ResponsePayloadListInner'; export * from './ProviderList200ResponsePayloadListInnerColors'; +export * from './RemoveFriend200Response'; +export * from './RemoveFriend404Response'; export * from './Signin200Response'; export * from './Signin200ResponsePayload'; export * from './Signin400Response'; @@ -81,5 +88,4 @@ export * from './TournamentList404Response'; export * from './TttHistory200Response'; export * from './TttHistory200ResponsePayload'; export * from './TttHistory200ResponsePayloadDataInner'; -export * from './TttHistory200ResponsePayloadDataInnerPlayerX'; export * from './TttHistory404Response'; diff --git a/src/@shared/src/database/index.ts b/src/@shared/src/database/index.ts index 2d30508..dcea4d9 100644 --- a/src/@shared/src/database/index.ts +++ b/src/@shared/src/database/index.ts @@ -4,17 +4,19 @@ import { FastifyInstance, FastifyPluginAsync } from 'fastify'; import { Database as DbImpl } from './mixin/_base'; import { IUserDb, UserImpl } from './mixin/user'; import { IBlockedDb, BlockedImpl } from './mixin/blocked'; +import { IFriendsDb, FriendsImpl } from './mixin/friends'; import { ITicTacToeDb, TicTacToeImpl } from './mixin/tictactoe'; import { IPongDb, PongImpl } from './mixin/pong'; import { ITournamentDb, TournamentImpl } from './mixin/tournament'; Object.assign(DbImpl.prototype, UserImpl); Object.assign(DbImpl.prototype, BlockedImpl); +Object.assign(DbImpl.prototype, FriendsImpl); Object.assign(DbImpl.prototype, TicTacToeImpl); Object.assign(DbImpl.prototype, PongImpl); Object.assign(DbImpl.prototype, TournamentImpl); -export interface Database extends DbImpl, IUserDb, IBlockedDb, ITicTacToeDb, IPongDb, ITournamentDb { } +export interface Database extends DbImpl, IUserDb, IBlockedDb, ITicTacToeDb, IPongDb, ITournamentDb, IFriendsDb { } // When using .decorate you have to specify added properties for Typescript declare module 'fastify' { diff --git a/src/@shared/src/database/init.sql b/src/@shared/src/database/init.sql index cb72332..41aae21 100644 --- a/src/@shared/src/database/init.sql +++ b/src/@shared/src/database/init.sql @@ -27,6 +27,15 @@ CREATE TABLE IF NOT EXISTS blocked ( CREATE UNIQUE INDEX IF NOT EXISTS idx_blocked_user_pair ON blocked (user, blocked); +CREATE TABLE IF NOT EXISTS friends ( + id INTEGER PRIMARY KEY NOT NULL, + user TEXT NOT NULL, + friend TEXT NOT NULL, + FOREIGN KEY (user) REFERENCES user (id) FOREIGN KEY (friend) REFERENCES user (id) +); + +CREATE UNIQUE INDEX IF NOT EXISTS idx_friends_user_pair ON friends (user, friend); + ---------------- -- TICTACTOE -- ---------------- diff --git a/src/@shared/src/database/mixin/friends.ts b/src/@shared/src/database/mixin/friends.ts new file mode 100644 index 0000000..916c99d --- /dev/null +++ b/src/@shared/src/database/mixin/friends.ts @@ -0,0 +1,72 @@ +import { isNullish } from '@shared/utils'; +import type { Database } from './_base'; +import { UserId } from './user'; + + +// describe every function in the object +export interface IFriendsDb extends Database { + getFriendsUserFor(id: UserId): FriendsData[], + addFriendsUserFor(id: UserId, friend: UserId): void, + removeFriendsUserFor(id: UserId, friend: UserId): void, + removeAllFriendUserFor(id: UserId): void, + getAllFriendsUsers(this: IFriendsDb): FriendsData[] | undefined, + +}; + +export const FriendsImpl: Omit = { + getFriendsUserFor(this: IFriendsDb, id: UserId): FriendsData[] { + const query = this.prepare('SELECT * FROM friends WHERE user = @id'); + const data = query.all({ id }) as Partial[]; + return data.map(friendsFromRow).filter(b => !isNullish(b)); + }, + + removeAllFriendUserFor(this: IFriendsDb, id: UserId): void { + this.prepare('DELETE FROM friends WHERE user = @id').run({ id }); + }, + addFriendsUserFor(this: IFriendsDb, id: UserId, friend: UserId): void { + this.prepare('INSERT OR IGNORE INTO friends (user, friend) VALUES (@id, @friend)').run({ id, friend }); + }, + removeFriendsUserFor(this: IFriendsDb, id: UserId, friend: UserId): void { + this.prepare('DELETE FROM friends WHERE user = @id AND friend = @friend').run({ id, friend }); + }, + + /** + * Get all friends user + * + * @param + * + * @returns The list of users if it exists, undefined otherwise + */ + getAllFriendsUsers(this: IFriendsDb): FriendsData[] { + const rows = this.prepare('SELECT * FROM friends').all() as Partial[]; + + return rows + .map(row => friendsFromRow(row)) + .filter((u): u is FriendsData => u !== undefined); + }, + +}; + +export type FriendsId = number & { readonly __brand: unique symbol }; + +export type FriendsData = { + readonly id: FriendsId; + readonly user: UserId; + readonly friend: UserId; +}; + +/** + * Get a friends from a row + * + * @param row The data from sqlite + * + * @returns The friends if it exists, undefined otherwise + */ +export function friendsFromRow(row?: Partial): FriendsData | undefined { + if (isNullish(row)) return undefined; + if (isNullish(row.id)) return undefined; + if (isNullish(row.user)) return undefined; + if (isNullish(row.friend)) return undefined; + + return row as FriendsData; +} diff --git a/src/icons/openapi.json b/src/icons/openapi.json new file mode 100644 index 0000000..38cd725 --- /dev/null +++ b/src/icons/openapi.json @@ -0,0 +1,21 @@ +{ + "openapi": "3.1.0", + "info": { + "version": "9.6.1", + "title": "@fastify/swagger" + }, + "components": { + "schemas": {} + }, + "paths": {}, + "servers": [ + { + "url": "https://local.maix.me:8888", + "description": "direct from docker" + }, + { + "url": "https://local.maix.me:8000", + "description": "using fnginx" + } + ] +} diff --git a/src/icons/src/routes/set.ts b/src/icons/src/routes/set.ts index 5837a6c..b7aa917 100644 --- a/src/icons/src/routes/set.ts +++ b/src/icons/src/routes/set.ts @@ -6,18 +6,17 @@ import sharp from 'sharp'; import path from 'path'; import fs from 'node:fs/promises'; - export const IconSetRes = { '200': typeResponse('success', 'iconset.success'), - '400': typeResponse('success', ['iconset.failure.invalidFile', 'iconset.failure.noFile']), + '400': typeResponse('success', [ + 'iconset.failure.invalidFile', + 'iconset.failure.noFile', + ]), }; export type IconSetRes = MakeStaticResponse; -const validMimeTypes = new Set([ - 'image/jpeg', - 'image/png', -]); +const validMimeTypes = new Set(['image/jpeg', 'image/png']); async function resizeAndSaveImage( imageBuffer: Buffer, @@ -42,20 +41,42 @@ const route: FastifyPluginAsync = async (fastify, _opts): Promise => { await fastify.register(multipart); fastify.post( '/api/icons/set', - { schema: { response: IconSetRes, operationId: 'setIcons' }, config: { requireAuth: true } }, + { + schema: { + response: IconSetRes, + hide: true, + }, + config: { requireAuth: true }, + }, async function(req, res) { // req.authUser is always set, since this is gated const userid = req.authUser!.id; const file = await req.file(); if (!file) { - return res.makeResponse(400, 'failure', 'iconset.failure.noFile'); + return res.makeResponse( + 400, + 'failure', + 'iconset.failure.noFile', + ); } if (!validMimeTypes.has(file.mimetype)) { - return res.makeResponse(400, 'failure', 'iconset.failure.invalidFile'); + return res.makeResponse( + 400, + 'failure', + 'iconset.failure.invalidFile', + ); } const buf = await file.toBuffer(); - if (!validMimeTypes.has((await fileTypeFromBuffer(buf))?.mime ?? 'unknown')) { - return res.makeResponse(400, 'failure', 'iconset.failure.invalidFile'); + if ( + !validMimeTypes.has( + (await fileTypeFromBuffer(buf))?.mime ?? 'unknown', + ) + ) { + return res.makeResponse( + 400, + 'failure', + 'iconset.failure.invalidFile', + ); } try { resizeAndSaveImage(buf, userid); @@ -63,7 +84,11 @@ const route: FastifyPluginAsync = async (fastify, _opts): Promise => { } catch (e: unknown) { this.log.warn(e); - return res.makeResponse(400, 'failure', 'iconset.failure.invalidFile'); + return res.makeResponse( + 400, + 'failure', + 'iconset.failure.invalidFile', + ); } }, ); diff --git a/src/openapi.json b/src/openapi.json index d99dfc7..615cae1 100644 --- a/src/openapi.json +++ b/src/openapi.json @@ -1714,6 +1714,326 @@ ] } }, + "/api/user/friend/add/{user}": { + "put": { + "operationId": "addFriend", + "parameters": [ + { + "schema": { + "type": "string" + }, + "in": "path", + "name": "user", + "required": true + } + ], + "responses": { + "200": { + "description": "Default Response", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "kind", + "msg" + ], + "properties": { + "kind": { + "enum": [ + "success" + ] + }, + "msg": { + "enum": [ + "addFriend.success" + ] + } + } + } + } + } + }, + "401": { + "description": "Default Response", + "content": { + "application/json": { + "schema": { + "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": [ + "addFriend.failure.unknownUser" + ] + } + } + } + } + } + } + }, + "tags": [ + "openapi_other" + ] + } + }, + "/api/user/friend/list": { + "get": { + "operationId": "listFriend", + "responses": { + "200": { + "description": "Default Response", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "kind", + "msg", + "payload" + ], + "properties": { + "kind": { + "enum": [ + "success" + ] + }, + "msg": { + "enum": [ + "listFriend.success" + ] + }, + "payload": { + "type": "object", + "required": [ + "friends" + ], + "properties": { + "friends": { + "type": "array", + "items": { + "type": "object", + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + } + } + } + } + } + } + } + } + } + }, + "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" + ] + } + } + } + ] + } + } + } + } + }, + "tags": [ + "openapi_other" + ] + } + }, + "/api/user/friend/remove/{user}": { + "put": { + "operationId": "removeFriend", + "parameters": [ + { + "schema": { + "type": "string" + }, + "in": "path", + "name": "user", + "required": true + } + ], + "responses": { + "200": { + "description": "Default Response", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "kind", + "msg" + ], + "properties": { + "kind": { + "enum": [ + "success" + ] + }, + "msg": { + "enum": [ + "removeFriend.success" + ] + } + } + } + } + } + }, + "401": { + "description": "Default Response", + "content": { + "application/json": { + "schema": { + "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": [ + "removeFriend.failure.unknownUser" + ] + } + } + } + } + } + } + }, + "tags": [ + "openapi_other" + ] + } + }, "/api/user/info/{user}": { "get": { "operationId": "getUser", diff --git a/src/user/openapi.json b/src/user/openapi.json index 8f7df5f..1f34e15 100644 --- a/src/user/openapi.json +++ b/src/user/openapi.json @@ -476,6 +476,317 @@ } } }, + "/api/user/friend/add/{user}": { + "put": { + "operationId": "addFriend", + "parameters": [ + { + "schema": { + "type": "string" + }, + "in": "path", + "name": "user", + "required": true + } + ], + "responses": { + "200": { + "description": "Default Response", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "kind", + "msg" + ], + "properties": { + "kind": { + "enum": [ + "success" + ] + }, + "msg": { + "enum": [ + "addFriend.success" + ] + } + } + } + } + } + }, + "401": { + "description": "Default Response", + "content": { + "application/json": { + "schema": { + "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": [ + "addFriend.failure.unknownUser" + ] + } + } + } + } + } + } + } + } + }, + "/api/user/friend/list": { + "get": { + "operationId": "listFriend", + "responses": { + "200": { + "description": "Default Response", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "kind", + "msg", + "payload" + ], + "properties": { + "kind": { + "enum": [ + "success" + ] + }, + "msg": { + "enum": [ + "listFriend.success" + ] + }, + "payload": { + "type": "object", + "required": [ + "friends" + ], + "properties": { + "friends": { + "type": "array", + "items": { + "type": "object", + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + } + } + } + } + } + } + } + } + } + }, + "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" + ] + } + } + } + ] + } + } + } + } + } + } + }, + "/api/user/friend/remove/{user}": { + "put": { + "operationId": "removeFriend", + "parameters": [ + { + "schema": { + "type": "string" + }, + "in": "path", + "name": "user", + "required": true + } + ], + "responses": { + "200": { + "description": "Default Response", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "kind", + "msg" + ], + "properties": { + "kind": { + "enum": [ + "success" + ] + }, + "msg": { + "enum": [ + "removeFriend.success" + ] + } + } + } + } + } + }, + "401": { + "description": "Default Response", + "content": { + "application/json": { + "schema": { + "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": [ + "removeFriend.failure.unknownUser" + ] + } + } + } + } + } + } + } + } + }, "/api/user/info/{user}": { "get": { "operationId": "getUser", diff --git a/src/user/src/routes/friendAdd.ts b/src/user/src/routes/friendAdd.ts new file mode 100644 index 0000000..00bebd1 --- /dev/null +++ b/src/user/src/routes/friendAdd.ts @@ -0,0 +1,44 @@ +import { FastifyPluginAsync } from 'fastify'; +import { MakeStaticResponse, typeResponse } from '@shared/utils'; +import Type, { Static } from 'typebox'; + +export const AddFriendRes = { + '200': typeResponse('success', 'addFriend.success'), + '404': typeResponse('failure', 'addFriend.failure.unknownUser'), +}; + +export type AddFriendRes = MakeStaticResponse; + +const AddFriendParams = Type.Object({ + user: Type.String(), +}); +export type AddFriendParams = Static; + +const route: FastifyPluginAsync = async (fastify, _opts): Promise => { + void _opts; + fastify.put<{ Params: AddFriendParams }>( + '/api/user/friend/add/:user', + { + schema: { + params: AddFriendParams, + response: AddFriendRes, + operationId: 'addFriend', + }, + config: { requireAuth: true }, + }, + async function(req, res) { + const friend = this.db.getUser(req.params.user); + if (!friend) { + return res.makeResponse( + 404, + 'failure', + 'addFriend.failure.unknownUser', + ); + } + this.db.addFriendsUserFor(req.authUser!.id, friend.id); + return res.makeResponse(200, 'success', 'addFriend.success'); + }, + ); +}; + +export default route; diff --git a/src/user/src/routes/friendList.ts b/src/user/src/routes/friendList.ts new file mode 100644 index 0000000..4ef7f48 --- /dev/null +++ b/src/user/src/routes/friendList.ts @@ -0,0 +1,40 @@ +import { FastifyPluginAsync } from 'fastify'; +import { isNullish, MakeStaticResponse, typeResponse } from '@shared/utils'; +import Type, { Static } from 'typebox'; + +export const ListFriendRes = { + '200': typeResponse('success', 'listFriend.success', { + friends: Type.Array(Type.Object({ + id: Type.String(), + name: Type.String(), + })), + }), +}; + +export type ListFriendRes = MakeStaticResponse; + +const RemoveFriendParams = Type.Object({ + user: Type.String(), +}); +export type RemoveFriendParams = Static; + +const route: FastifyPluginAsync = async (fastify, _opts): Promise => { + void _opts; + fastify.get( + '/api/user/friend/list', + { + schema: { + response: ListFriendRes, + operationId: 'listFriend', + }, + config: { requireAuth: true }, + }, + async function(req, res) { + void req; + const friends: ListFriendRes['200']['payload']['friends'] = this.db.getFriendsUserFor(req.authUser!.id).map(v => this.db.getUser(v.friend)).filter(v => !isNullish(v)).map(v => ({ id: v.id, name: v.name })); + return res.makeResponse(200, 'success', 'listFriend.success', { friends }); + }, + ); +}; + +export default route; diff --git a/src/user/src/routes/friendRemove.ts b/src/user/src/routes/friendRemove.ts new file mode 100644 index 0000000..e4fc6f2 --- /dev/null +++ b/src/user/src/routes/friendRemove.ts @@ -0,0 +1,44 @@ +import { FastifyPluginAsync } from 'fastify'; +import { MakeStaticResponse, typeResponse } from '@shared/utils'; +import Type, { Static } from 'typebox'; + +export const RemoveFriendRes = { + '200': typeResponse('success', 'removeFriend.success'), + '404': typeResponse('failure', 'removeFriend.failure.unknownUser'), +}; + +export type RemoveFriendRes = MakeStaticResponse; + +const RemoveFriendParams = Type.Object({ + user: Type.String(), +}); +export type RemoveFriendParams = Static; + +const route: FastifyPluginAsync = async (fastify, _opts): Promise => { + void _opts; + fastify.put<{ Params: RemoveFriendParams }>( + '/api/user/friend/remove/:user', + { + schema: { + params: RemoveFriendParams, + response: RemoveFriendRes, + operationId: 'removeFriend', + }, + config: { requireAuth: true }, + }, + async function(req, res) { + const friend = this.db.getUser(req.params.user); + if (!friend) { + return res.makeResponse( + 404, + 'failure', + 'removeFriend.failure.unknownUser', + ); + } + this.db.removeFriendsUserFor(req.authUser!.id, friend.id); + return res.makeResponse(200, 'success', 'removeFriend.success'); + }, + ); +}; + +export default route;