Compare commits
10 commits
946478f301
...
4cb0104124
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4cb0104124 | ||
|
|
48c33b3568 | ||
|
|
ef568c9899 | ||
|
|
c5eea6e29e | ||
|
|
590604b385 | ||
|
|
df79bc5a80 | ||
|
|
5884407f35 | ||
|
|
3cccc18e9a | ||
|
|
95784c9719 | ||
|
|
c547c2301f |
56 changed files with 2817 additions and 106 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -22,3 +22,7 @@ pnpm-lock.yaml
|
||||||
package-lock.json
|
package-lock.json
|
||||||
.idea
|
.idea
|
||||||
.dev
|
.dev
|
||||||
|
openapi-template
|
||||||
|
nginx-dev
|
||||||
|
src/redocly.yaml
|
||||||
|
flake/
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,8 @@ ifeq "$(REDUCED_SET)" "y"
|
||||||
tic-tac-toe \
|
tic-tac-toe \
|
||||||
nginx \
|
nginx \
|
||||||
user \
|
user \
|
||||||
pong
|
pong \
|
||||||
|
icons
|
||||||
endif
|
endif
|
||||||
|
|
||||||
all: build
|
all: build
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ services:
|
||||||
- '9090:8443'
|
- '9090:8443'
|
||||||
volumes:
|
volumes:
|
||||||
# if you need to share files with nginx, you do it here.
|
# if you need to share files with nginx, you do it here.
|
||||||
- static-volume:/volumes/static
|
- icons-volume:/volumes/icons
|
||||||
env_file: .env
|
env_file: .env
|
||||||
logging:
|
logging:
|
||||||
driver: gelf
|
driver: gelf
|
||||||
|
|
@ -72,7 +72,7 @@ services:
|
||||||
- app
|
- app
|
||||||
volumes:
|
volumes:
|
||||||
- sqlite-volume:/volumes/database
|
- sqlite-volume:/volumes/database
|
||||||
- static-volume:/volumes/static
|
- icons-volume:/volumes/icons
|
||||||
- ./src/auth/config:/config
|
- ./src/auth/config:/config
|
||||||
env_file: .env
|
env_file: .env
|
||||||
logging:
|
logging:
|
||||||
|
|
@ -98,7 +98,32 @@ services:
|
||||||
- app
|
- app
|
||||||
volumes:
|
volumes:
|
||||||
- sqlite-volume:/volumes/database
|
- sqlite-volume:/volumes/database
|
||||||
- static-volume:/volumes/static
|
env_file: .env
|
||||||
|
logging:
|
||||||
|
driver: gelf
|
||||||
|
options:
|
||||||
|
gelf-address: "udp://127.0.0.1:12201"
|
||||||
|
tag: "{{.Name}}"
|
||||||
|
|
||||||
|
|
||||||
|
###############
|
||||||
|
# ICONS #
|
||||||
|
###############
|
||||||
|
icons:
|
||||||
|
build:
|
||||||
|
context: ./src/
|
||||||
|
args:
|
||||||
|
- SERVICE=icons
|
||||||
|
additional_contexts:
|
||||||
|
pnpm_base: "service:pnpm_base"
|
||||||
|
pnpm_deps: "service:pnpm_deps"
|
||||||
|
container_name: app-icons
|
||||||
|
restart: always
|
||||||
|
networks:
|
||||||
|
- app
|
||||||
|
volumes:
|
||||||
|
- sqlite-volume:/volumes/database
|
||||||
|
- icons-volume:/volumes/icons
|
||||||
env_file: .env
|
env_file: .env
|
||||||
logging:
|
logging:
|
||||||
driver: gelf
|
driver: gelf
|
||||||
|
|
@ -124,7 +149,6 @@ services:
|
||||||
env_file: .env
|
env_file: .env
|
||||||
volumes:
|
volumes:
|
||||||
- sqlite-volume:/volumes/database
|
- sqlite-volume:/volumes/database
|
||||||
- static-volume:/volumes/static
|
|
||||||
logging:
|
logging:
|
||||||
driver: gelf
|
driver: gelf
|
||||||
options:
|
options:
|
||||||
|
|
@ -148,7 +172,6 @@ services:
|
||||||
- app
|
- app
|
||||||
volumes:
|
volumes:
|
||||||
- sqlite-volume:/volumes/database
|
- sqlite-volume:/volumes/database
|
||||||
- static-volume:/volumes/static
|
|
||||||
env_file: .env
|
env_file: .env
|
||||||
logging:
|
logging:
|
||||||
driver: gelf
|
driver: gelf
|
||||||
|
|
@ -173,7 +196,6 @@ services:
|
||||||
- app
|
- app
|
||||||
volumes:
|
volumes:
|
||||||
- sqlite-volume:/volumes/database
|
- sqlite-volume:/volumes/database
|
||||||
- static-volume:/volumes/static
|
|
||||||
env_file: .env
|
env_file: .env
|
||||||
logging:
|
logging:
|
||||||
driver: gelf
|
driver: gelf
|
||||||
|
|
@ -358,6 +380,6 @@ services:
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
sqlite-volume:
|
sqlite-volume:
|
||||||
static-volume:
|
icons-volume:
|
||||||
grafana-data:
|
grafana-data:
|
||||||
elastic-data:
|
elastic-data:
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@
|
||||||
<a href="/ttt" class="hover:bg-gray-700 rounded-md px-3 py-2">⭕ Tic-Tac-Toe</a>
|
<a href="/ttt" class="hover:bg-gray-700 rounded-md px-3 py-2">⭕ Tic-Tac-Toe</a>
|
||||||
<a href="/pong" class="hover:bg-gray-700 rounded-md px-3 py-2">▮•▮ Ping Pong</a>
|
<a href="/pong" class="hover:bg-gray-700 rounded-md px-3 py-2">▮•▮ Ping Pong</a>
|
||||||
<a href="/tour" class="hover:bg-gray-700 rounded-md px-3 py-2">🏆 Tournaments</a>
|
<a href="/tour" class="hover:bg-gray-700 rounded-md px-3 py-2">🏆 Tournaments</a>
|
||||||
|
<a href="/friends" class="hover:bg-gray-700 rounded-md px-3 py-2">😎 Friends</a>
|
||||||
<a href="/logout" class="hover:bg-gray-700 rounded-md px-3 py-2">🚪 Logout</a>
|
<a href="/logout" class="hover:bg-gray-700 rounded-md px-3 py-2">🚪 Logout</a>
|
||||||
</nav>
|
</nav>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
apis/OpenapiOtherApi.ts
|
apis/OpenapiOtherApi.ts
|
||||||
apis/index.ts
|
apis/index.ts
|
||||||
index.ts
|
index.ts
|
||||||
|
models/AddFriend200Response.ts
|
||||||
|
models/AddFriend404Response.ts
|
||||||
models/AllowGuestMessage200Response.ts
|
models/AllowGuestMessage200Response.ts
|
||||||
models/AllowGuestMessage403Response.ts
|
models/AllowGuestMessage403Response.ts
|
||||||
models/ChangeDesc200Response.ts
|
models/ChangeDesc200Response.ts
|
||||||
|
|
@ -38,6 +40,9 @@ models/GuestLogin200ResponsePayload.ts
|
||||||
models/GuestLogin400Response.ts
|
models/GuestLogin400Response.ts
|
||||||
models/GuestLogin500Response.ts
|
models/GuestLogin500Response.ts
|
||||||
models/GuestLoginRequest.ts
|
models/GuestLoginRequest.ts
|
||||||
|
models/ListFriend200Response.ts
|
||||||
|
models/ListFriend200ResponsePayload.ts
|
||||||
|
models/ListFriend200ResponsePayloadFriendsInner.ts
|
||||||
models/Login200Response.ts
|
models/Login200Response.ts
|
||||||
models/Login202Response.ts
|
models/Login202Response.ts
|
||||||
models/Login202ResponsePayload.ts
|
models/Login202ResponsePayload.ts
|
||||||
|
|
@ -60,6 +65,8 @@ models/ProviderList200Response.ts
|
||||||
models/ProviderList200ResponsePayload.ts
|
models/ProviderList200ResponsePayload.ts
|
||||||
models/ProviderList200ResponsePayloadListInner.ts
|
models/ProviderList200ResponsePayloadListInner.ts
|
||||||
models/ProviderList200ResponsePayloadListInnerColors.ts
|
models/ProviderList200ResponsePayloadListInnerColors.ts
|
||||||
|
models/RemoveFriend200Response.ts
|
||||||
|
models/RemoveFriend404Response.ts
|
||||||
models/Signin200Response.ts
|
models/Signin200Response.ts
|
||||||
models/Signin200ResponsePayload.ts
|
models/Signin200ResponsePayload.ts
|
||||||
models/Signin400Response.ts
|
models/Signin400Response.ts
|
||||||
|
|
@ -82,7 +89,6 @@ models/TournamentList404Response.ts
|
||||||
models/TttHistory200Response.ts
|
models/TttHistory200Response.ts
|
||||||
models/TttHistory200ResponsePayload.ts
|
models/TttHistory200ResponsePayload.ts
|
||||||
models/TttHistory200ResponsePayloadDataInner.ts
|
models/TttHistory200ResponsePayloadDataInner.ts
|
||||||
models/TttHistory200ResponsePayloadDataInnerPlayerX.ts
|
|
||||||
models/TttHistory404Response.ts
|
models/TttHistory404Response.ts
|
||||||
models/index.ts
|
models/index.ts
|
||||||
runtime.ts
|
runtime.ts
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@
|
||||||
|
|
||||||
import * as runtime from '../runtime';
|
import * as runtime from '../runtime';
|
||||||
import type {
|
import type {
|
||||||
|
AddFriend200Response,
|
||||||
|
AddFriend404Response,
|
||||||
AllowGuestMessage200Response,
|
AllowGuestMessage200Response,
|
||||||
AllowGuestMessage403Response,
|
AllowGuestMessage403Response,
|
||||||
ChangeDesc200Response,
|
ChangeDesc200Response,
|
||||||
|
|
@ -46,6 +48,7 @@ import type {
|
||||||
GuestLogin400Response,
|
GuestLogin400Response,
|
||||||
GuestLogin500Response,
|
GuestLogin500Response,
|
||||||
GuestLoginRequest,
|
GuestLoginRequest,
|
||||||
|
ListFriend200Response,
|
||||||
Login200Response,
|
Login200Response,
|
||||||
Login202Response,
|
Login202Response,
|
||||||
Login400Response,
|
Login400Response,
|
||||||
|
|
@ -60,6 +63,8 @@ import type {
|
||||||
PongHistory200Response,
|
PongHistory200Response,
|
||||||
PongHistory404Response,
|
PongHistory404Response,
|
||||||
ProviderList200Response,
|
ProviderList200Response,
|
||||||
|
RemoveFriend200Response,
|
||||||
|
RemoveFriend404Response,
|
||||||
Signin200Response,
|
Signin200Response,
|
||||||
Signin400Response,
|
Signin400Response,
|
||||||
Signin500Response,
|
Signin500Response,
|
||||||
|
|
@ -74,6 +79,10 @@ import type {
|
||||||
TttHistory404Response,
|
TttHistory404Response,
|
||||||
} from '../models/index';
|
} from '../models/index';
|
||||||
import {
|
import {
|
||||||
|
AddFriend200ResponseFromJSON,
|
||||||
|
AddFriend200ResponseToJSON,
|
||||||
|
AddFriend404ResponseFromJSON,
|
||||||
|
AddFriend404ResponseToJSON,
|
||||||
AllowGuestMessage200ResponseFromJSON,
|
AllowGuestMessage200ResponseFromJSON,
|
||||||
AllowGuestMessage200ResponseToJSON,
|
AllowGuestMessage200ResponseToJSON,
|
||||||
AllowGuestMessage403ResponseFromJSON,
|
AllowGuestMessage403ResponseFromJSON,
|
||||||
|
|
@ -136,6 +145,8 @@ import {
|
||||||
GuestLogin500ResponseToJSON,
|
GuestLogin500ResponseToJSON,
|
||||||
GuestLoginRequestFromJSON,
|
GuestLoginRequestFromJSON,
|
||||||
GuestLoginRequestToJSON,
|
GuestLoginRequestToJSON,
|
||||||
|
ListFriend200ResponseFromJSON,
|
||||||
|
ListFriend200ResponseToJSON,
|
||||||
Login200ResponseFromJSON,
|
Login200ResponseFromJSON,
|
||||||
Login200ResponseToJSON,
|
Login200ResponseToJSON,
|
||||||
Login202ResponseFromJSON,
|
Login202ResponseFromJSON,
|
||||||
|
|
@ -164,6 +175,10 @@ import {
|
||||||
PongHistory404ResponseToJSON,
|
PongHistory404ResponseToJSON,
|
||||||
ProviderList200ResponseFromJSON,
|
ProviderList200ResponseFromJSON,
|
||||||
ProviderList200ResponseToJSON,
|
ProviderList200ResponseToJSON,
|
||||||
|
RemoveFriend200ResponseFromJSON,
|
||||||
|
RemoveFriend200ResponseToJSON,
|
||||||
|
RemoveFriend404ResponseFromJSON,
|
||||||
|
RemoveFriend404ResponseToJSON,
|
||||||
Signin200ResponseFromJSON,
|
Signin200ResponseFromJSON,
|
||||||
Signin200ResponseToJSON,
|
Signin200ResponseToJSON,
|
||||||
Signin400ResponseFromJSON,
|
Signin400ResponseFromJSON,
|
||||||
|
|
@ -190,6 +205,10 @@ import {
|
||||||
TttHistory404ResponseToJSON,
|
TttHistory404ResponseToJSON,
|
||||||
} from '../models/index';
|
} from '../models/index';
|
||||||
|
|
||||||
|
export interface AddFriendRequest {
|
||||||
|
user: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ChangeDescOperationRequest {
|
export interface ChangeDescOperationRequest {
|
||||||
changeDescRequest: ChangeDescRequest;
|
changeDescRequest: ChangeDescRequest;
|
||||||
}
|
}
|
||||||
|
|
@ -226,6 +245,10 @@ export interface PongHistoryRequest {
|
||||||
user: string;
|
user: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface RemoveFriendRequest {
|
||||||
|
user: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface SigninRequest {
|
export interface SigninRequest {
|
||||||
loginRequest: LoginRequest;
|
loginRequest: LoginRequest;
|
||||||
}
|
}
|
||||||
|
|
@ -243,6 +266,60 @@ export interface TttHistoryRequest {
|
||||||
*/
|
*/
|
||||||
export class OpenapiOtherApi extends runtime.BaseAPI {
|
export class OpenapiOtherApi extends runtime.BaseAPI {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
async addFriendRaw(requestParameters: AddFriendRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<AddFriend200Response | ChangePassword401Response | AddFriend404Response>> {
|
||||||
|
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<AddFriend200Response | ChangePassword401Response | AddFriend404Response> {
|
||||||
|
const response = await this.addFriendRaw(requestParameters, initOverrides);
|
||||||
|
return await response.value();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
async allowGuestMessageRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<AllowGuestMessage200Response | StatusOtp401Response | AllowGuestMessage403Response>> {
|
async allowGuestMessageRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<AllowGuestMessage200Response | StatusOtp401Response | AllowGuestMessage403Response>> {
|
||||||
|
|
@ -766,6 +843,48 @@ export class OpenapiOtherApi extends runtime.BaseAPI {
|
||||||
return await response.value();
|
return await response.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
async listFriendRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<ListFriend200Response | StatusOtp401Response>> {
|
||||||
|
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<ListFriend200Response | StatusOtp401Response> {
|
||||||
|
const response = await this.listFriendRaw(initOverrides);
|
||||||
|
return await response.value();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
async loginRaw(requestParameters: LoginOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<Login200Response | Login202Response | Login400Response>> {
|
async loginRaw(requestParameters: LoginOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<Login200Response | Login202Response | Login400Response>> {
|
||||||
|
|
@ -1016,6 +1135,60 @@ export class OpenapiOtherApi extends runtime.BaseAPI {
|
||||||
return await response.value();
|
return await response.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
async removeFriendRaw(requestParameters: RemoveFriendRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<RemoveFriend200Response | ChangePassword401Response | RemoveFriend404Response>> {
|
||||||
|
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<RemoveFriend200Response | ChangePassword401Response | RemoveFriend404Response> {
|
||||||
|
const response = await this.removeFriendRaw(requestParameters, initOverrides);
|
||||||
|
return await response.value();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
async signinRaw(requestParameters: SigninRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<Signin200Response | Signin400Response | Signin500Response>> {
|
async signinRaw(requestParameters: SigninRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<Signin200Response | Signin400Response | Signin500Response>> {
|
||||||
|
|
|
||||||
93
frontend/src/api/generated/models/AddFriend200Response.ts
Normal file
93
frontend/src/api/generated/models/AddFriend200Response.ts
Normal file
|
|
@ -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'],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
93
frontend/src/api/generated/models/AddFriend404Response.ts
Normal file
93
frontend/src/api/generated/models/AddFriend404Response.ts
Normal file
|
|
@ -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'],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
110
frontend/src/api/generated/models/ListFriend200Response.ts
Normal file
110
frontend/src/api/generated/models/ListFriend200Response.ts
Normal file
|
|
@ -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']),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -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<ListFriend200ResponsePayloadFriendsInner>}
|
||||||
|
* @memberof ListFriend200ResponsePayload
|
||||||
|
*/
|
||||||
|
friends: Array<ListFriend200ResponsePayloadFriendsInner>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<any>).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<any>).map(ListFriend200ResponsePayloadFriendsInnerToJSON)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -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'],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
93
frontend/src/api/generated/models/ListFriend404Response.ts
Normal file
93
frontend/src/api/generated/models/ListFriend404Response.ts
Normal file
|
|
@ -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'],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
93
frontend/src/api/generated/models/RemoveFriend200Response.ts
Normal file
93
frontend/src/api/generated/models/RemoveFriend200Response.ts
Normal file
|
|
@ -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'],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
93
frontend/src/api/generated/models/RemoveFriend404Response.ts
Normal file
93
frontend/src/api/generated/models/RemoveFriend404Response.ts
Normal file
|
|
@ -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'],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -13,13 +13,13 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { mapValues } from '../runtime';
|
import { mapValues } from '../runtime';
|
||||||
import type { TttHistory200ResponsePayloadDataInnerPlayerX } from './TttHistory200ResponsePayloadDataInnerPlayerX';
|
import type { ListFriend200ResponsePayloadFriendsInner } from './ListFriend200ResponsePayloadFriendsInner';
|
||||||
import {
|
import {
|
||||||
TttHistory200ResponsePayloadDataInnerPlayerXFromJSON,
|
ListFriend200ResponsePayloadFriendsInnerFromJSON,
|
||||||
TttHistory200ResponsePayloadDataInnerPlayerXFromJSONTyped,
|
ListFriend200ResponsePayloadFriendsInnerFromJSONTyped,
|
||||||
TttHistory200ResponsePayloadDataInnerPlayerXToJSON,
|
ListFriend200ResponsePayloadFriendsInnerToJSON,
|
||||||
TttHistory200ResponsePayloadDataInnerPlayerXToJSONTyped,
|
ListFriend200ResponsePayloadFriendsInnerToJSONTyped,
|
||||||
} from './TttHistory200ResponsePayloadDataInnerPlayerX';
|
} from './ListFriend200ResponsePayloadFriendsInner';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|
@ -35,16 +35,16 @@ export interface TttHistory200ResponsePayloadDataInner {
|
||||||
gameId: string;
|
gameId: string;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {TttHistory200ResponsePayloadDataInnerPlayerX}
|
* @type {ListFriend200ResponsePayloadFriendsInner}
|
||||||
* @memberof TttHistory200ResponsePayloadDataInner
|
* @memberof TttHistory200ResponsePayloadDataInner
|
||||||
*/
|
*/
|
||||||
playerX: TttHistory200ResponsePayloadDataInnerPlayerX;
|
playerX: ListFriend200ResponsePayloadFriendsInner;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {TttHistory200ResponsePayloadDataInnerPlayerX}
|
* @type {ListFriend200ResponsePayloadFriendsInner}
|
||||||
* @memberof TttHistory200ResponsePayloadDataInner
|
* @memberof TttHistory200ResponsePayloadDataInner
|
||||||
*/
|
*/
|
||||||
playerO: TttHistory200ResponsePayloadDataInnerPlayerX;
|
playerO: ListFriend200ResponsePayloadFriendsInner;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {string}
|
* @type {string}
|
||||||
|
|
@ -95,8 +95,8 @@ export function TttHistory200ResponsePayloadDataInnerFromJSONTyped(json: any, ig
|
||||||
return {
|
return {
|
||||||
|
|
||||||
'gameId': json['gameId'],
|
'gameId': json['gameId'],
|
||||||
'playerX': TttHistory200ResponsePayloadDataInnerPlayerXFromJSON(json['playerX']),
|
'playerX': ListFriend200ResponsePayloadFriendsInnerFromJSON(json['playerX']),
|
||||||
'playerO': TttHistory200ResponsePayloadDataInnerPlayerXFromJSON(json['playerO']),
|
'playerO': ListFriend200ResponsePayloadFriendsInnerFromJSON(json['playerO']),
|
||||||
'date': json['date'],
|
'date': json['date'],
|
||||||
'outcome': json['outcome'],
|
'outcome': json['outcome'],
|
||||||
};
|
};
|
||||||
|
|
@ -114,8 +114,8 @@ export function TttHistory200ResponsePayloadDataInnerToJSONTyped(value?: TttHist
|
||||||
return {
|
return {
|
||||||
|
|
||||||
'gameId': value['gameId'],
|
'gameId': value['gameId'],
|
||||||
'playerX': TttHistory200ResponsePayloadDataInnerPlayerXToJSON(value['playerX']),
|
'playerX': ListFriend200ResponsePayloadFriendsInnerToJSON(value['playerX']),
|
||||||
'playerO': TttHistory200ResponsePayloadDataInnerPlayerXToJSON(value['playerO']),
|
'playerO': ListFriend200ResponsePayloadFriendsInnerToJSON(value['playerO']),
|
||||||
'date': value['date'],
|
'date': value['date'],
|
||||||
'outcome': value['outcome'],
|
'outcome': value['outcome'],
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
|
export * from './AddFriend200Response';
|
||||||
|
export * from './AddFriend404Response';
|
||||||
export * from './AllowGuestMessage200Response';
|
export * from './AllowGuestMessage200Response';
|
||||||
export * from './AllowGuestMessage403Response';
|
export * from './AllowGuestMessage403Response';
|
||||||
export * from './ChangeDesc200Response';
|
export * from './ChangeDesc200Response';
|
||||||
|
|
@ -37,6 +39,9 @@ export * from './GuestLogin200ResponsePayload';
|
||||||
export * from './GuestLogin400Response';
|
export * from './GuestLogin400Response';
|
||||||
export * from './GuestLogin500Response';
|
export * from './GuestLogin500Response';
|
||||||
export * from './GuestLoginRequest';
|
export * from './GuestLoginRequest';
|
||||||
|
export * from './ListFriend200Response';
|
||||||
|
export * from './ListFriend200ResponsePayload';
|
||||||
|
export * from './ListFriend200ResponsePayloadFriendsInner';
|
||||||
export * from './Login200Response';
|
export * from './Login200Response';
|
||||||
export * from './Login202Response';
|
export * from './Login202Response';
|
||||||
export * from './Login202ResponsePayload';
|
export * from './Login202ResponsePayload';
|
||||||
|
|
@ -59,6 +64,8 @@ export * from './ProviderList200Response';
|
||||||
export * from './ProviderList200ResponsePayload';
|
export * from './ProviderList200ResponsePayload';
|
||||||
export * from './ProviderList200ResponsePayloadListInner';
|
export * from './ProviderList200ResponsePayloadListInner';
|
||||||
export * from './ProviderList200ResponsePayloadListInnerColors';
|
export * from './ProviderList200ResponsePayloadListInnerColors';
|
||||||
|
export * from './RemoveFriend200Response';
|
||||||
|
export * from './RemoveFriend404Response';
|
||||||
export * from './Signin200Response';
|
export * from './Signin200Response';
|
||||||
export * from './Signin200ResponsePayload';
|
export * from './Signin200ResponsePayload';
|
||||||
export * from './Signin400Response';
|
export * from './Signin400Response';
|
||||||
|
|
@ -81,5 +88,4 @@ export * from './TournamentList404Response';
|
||||||
export * from './TttHistory200Response';
|
export * from './TttHistory200Response';
|
||||||
export * from './TttHistory200ResponsePayload';
|
export * from './TttHistory200ResponsePayload';
|
||||||
export * from './TttHistory200ResponsePayloadDataInner';
|
export * from './TttHistory200ResponsePayloadDataInner';
|
||||||
export * from './TttHistory200ResponsePayloadDataInnerPlayerX';
|
|
||||||
export * from './TttHistory404Response';
|
export * from './TttHistory404Response';
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { showError } from "@app/toast";
|
import { showError } from "@app/toast";
|
||||||
import client from "@app/api";
|
import client from "@app/api";
|
||||||
import cookie from "js-cookie";
|
import cookie from "js-cookie";
|
||||||
import { ensureWindowState, isNullish } from "@app/utils";
|
import { ensureWindowState, isNullish, updateFriendsList } from "@app/utils";
|
||||||
import { handleRoute, navigateTo } from "./routing";
|
import { handleRoute, navigateTo } from "./routing";
|
||||||
|
|
||||||
cookie.remove("pkce");
|
cookie.remove("pkce");
|
||||||
|
|
@ -105,10 +105,11 @@ if (!window.__state._headerProfile) {
|
||||||
|
|
||||||
window.__state._reloadOnAuthChange ??= false;
|
window.__state._reloadOnAuthChange ??= false;
|
||||||
if (!window.__state._reloadOnAuthChange) {
|
if (!window.__state._reloadOnAuthChange) {
|
||||||
document.addEventListener("ft:userChange", () => {
|
document.addEventListener("ft:userChange", async () => {
|
||||||
// if the last forced auth change is less than 1000 sec old -> we do nothing
|
// if the last forced auth change is less than 1000 sec old -> we do nothing
|
||||||
if (Date.now() - (window.__state.lastAuthChange ?? Date.now()) < 1000)
|
if (Date.now() - (window.__state.lastAuthChange ?? Date.now()) < 1000)
|
||||||
return;
|
return;
|
||||||
|
await updateFriendsList();
|
||||||
handleRoute();
|
handleRoute();
|
||||||
});
|
});
|
||||||
window.__state._reloadOnAuthChange = true;
|
window.__state._reloadOnAuthChange = true;
|
||||||
|
|
|
||||||
|
|
@ -291,6 +291,13 @@ div-private {
|
||||||
right-12
|
right-12
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.popup-b-friend {
|
||||||
|
@apply
|
||||||
|
absolute
|
||||||
|
bottom-32
|
||||||
|
left-12
|
||||||
|
}
|
||||||
|
|
||||||
.popup-b-game {
|
.popup-b-game {
|
||||||
@apply
|
@apply
|
||||||
absolute
|
absolute
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import { windowStateVisable } from "./chatHelperFunctions/windowStateVisable";
|
||||||
import { cmdList } from "./chatHelperFunctions/cmdList";
|
import { cmdList } from "./chatHelperFunctions/cmdList";
|
||||||
import { actionBtnTTTGames } from "./chatHelperFunctions/actionBtnTTTGames";
|
import { actionBtnTTTGames } from "./chatHelperFunctions/actionBtnTTTGames";
|
||||||
import { showError } from "@app/toast";
|
import { showError } from "@app/toast";
|
||||||
|
import { actionBtnFriend } from "./chatHelperFunctions/actionBtnFriend";
|
||||||
|
|
||||||
const MAX_SYSTEM_MESSAGES = 10;
|
const MAX_SYSTEM_MESSAGES = 10;
|
||||||
let inviteMsgFlag: boolean = false;
|
let inviteMsgFlag: boolean = false;
|
||||||
|
|
@ -38,6 +39,7 @@ let keysPressed: Record<string, boolean> = {};
|
||||||
declare module "ft_state" {
|
declare module "ft_state" {
|
||||||
interface State {
|
interface State {
|
||||||
chatSock?: Socket;
|
chatSock?: Socket;
|
||||||
|
friendList: { id: string; name: string }[];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -81,18 +83,20 @@ const sendButton = document.getElementById("b-send") as HTMLButtonElement;
|
||||||
const sendtextbox = document.getElementById(
|
const sendtextbox = document.getElementById(
|
||||||
"t-chat-window",
|
"t-chat-window",
|
||||||
) as HTMLButtonElement;
|
) as HTMLButtonElement;
|
||||||
const systemWindow = document.getElementById("chat-system-box") as HTMLDivElement;
|
const systemWindow = document.getElementById(
|
||||||
|
"chat-system-box",
|
||||||
|
) as HTMLDivElement;
|
||||||
|
|
||||||
function chatKeyToggle() {
|
function chatKeyToggle() {
|
||||||
let anti_flicker_control = false;
|
let anti_flicker_control = false;
|
||||||
const chat_hide_key = 'escape';
|
const chat_hide_key = "escape";
|
||||||
const chat_display_key = 'f2';
|
const chat_display_key = "f2";
|
||||||
const home_display_key = 'f8';
|
const home_display_key = "f8";
|
||||||
document.addEventListener("keydown", (event) => {
|
document.addEventListener("keydown", (event) => {
|
||||||
if (event.repeat && keysPressed[chat_hide_key] === true) {
|
if (event.repeat && keysPressed[chat_hide_key] === true) {
|
||||||
anti_flicker_control = true;
|
anti_flicker_control = true;
|
||||||
return ;
|
return;
|
||||||
};
|
}
|
||||||
keysPressed[event.key.toLowerCase()] = true;
|
keysPressed[event.key.toLowerCase()] = true;
|
||||||
});
|
});
|
||||||
document.addEventListener("keyup", (event) => {
|
document.addEventListener("keyup", (event) => {
|
||||||
|
|
@ -101,34 +105,33 @@ function chatKeyToggle() {
|
||||||
anti_flicker_control = false;
|
anti_flicker_control = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
setInterval( () => {
|
setInterval(() => {
|
||||||
if(keysPressed[chat_hide_key] === true) {
|
if (keysPressed[chat_hide_key] === true) {
|
||||||
overlay.classList.remove("opacity-60");
|
overlay.classList.remove("opacity-60");
|
||||||
chatBox.classList.add("hidden");
|
chatBox.classList.add("hidden");
|
||||||
chatMessageIn?.classList.add("hidden");
|
chatMessageIn?.classList.add("hidden");
|
||||||
chatMessageIn!.textContent = '';
|
chatMessageIn!.textContent = "";
|
||||||
profilList?.classList.add("hidden");
|
profilList?.classList.add("hidden");
|
||||||
windowStateHidden();
|
windowStateHidden();
|
||||||
}
|
}
|
||||||
if (keysPressed[chat_display_key] === true) {
|
if (keysPressed[chat_display_key] === true) {
|
||||||
anti_flicker_control = false;
|
anti_flicker_control = false;
|
||||||
chatBox.classList.remove("hidden");
|
chatBox.classList.remove("hidden");
|
||||||
overlay.classList.add("opacity-60");
|
overlay.classList.add("opacity-60");
|
||||||
chatMessageIn?.classList.add("hidden");
|
chatMessageIn?.classList.add("hidden");
|
||||||
chatMessageIn!.textContent = '';
|
chatMessageIn!.textContent = "";
|
||||||
let socket = window.__state.chatSock;
|
let socket = window.__state.chatSock;
|
||||||
if (!socket) return;
|
if (!socket) return;
|
||||||
connected(socket);
|
connected(socket);
|
||||||
sendtextbox.focus();
|
sendtextbox.focus();
|
||||||
windowStateVisable();
|
windowStateVisable();
|
||||||
|
|
||||||
}
|
}
|
||||||
if (keysPressed[home_display_key] === true) {
|
if (keysPressed[home_display_key] === true) {
|
||||||
navigateTo('/app/');
|
navigateTo("/app/");
|
||||||
quitChat();
|
quitChat();
|
||||||
}
|
}
|
||||||
}, 1000/10);
|
}, 1000 / 10);
|
||||||
};
|
}
|
||||||
|
|
||||||
function initChatSocket() {
|
function initChatSocket() {
|
||||||
let socket = getSocket();
|
let socket = getSocket();
|
||||||
|
|
@ -151,9 +154,10 @@ function initChatSocket() {
|
||||||
!profilList ||
|
!profilList ||
|
||||||
!sendButton ||
|
!sendButton ||
|
||||||
!sendtextbox ||
|
!sendtextbox ||
|
||||||
!systemWindow
|
!systemWindow
|
||||||
) return showError("fatal error");
|
)
|
||||||
|
return showError("fatal error");
|
||||||
|
|
||||||
// Listen for the 'connect' event
|
// Listen for the 'connect' event
|
||||||
socket.on("connect", async () => {
|
socket.on("connect", async () => {
|
||||||
await waitSocketConnected(socket);
|
await waitSocketConnected(socket);
|
||||||
|
|
@ -201,10 +205,10 @@ function initChatSocket() {
|
||||||
if (socket) {
|
if (socket) {
|
||||||
connected(socket);
|
connected(socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chatWindow && data.message.destination === "") {
|
if (chatWindow && data.message.destination === "") {
|
||||||
chatMessageIn?.classList.remove("hidden");
|
chatMessageIn?.classList.remove("hidden");
|
||||||
chatMessageIn!.textContent = '🔵';
|
chatMessageIn!.textContent = "🔵";
|
||||||
const messageElement = document.createElement("div");
|
const messageElement = document.createElement("div");
|
||||||
messageElement.textContent = `${data.message.user}: ${data.message.text}`;
|
messageElement.textContent = `${data.message.user}: ${data.message.text}`;
|
||||||
chatWindow.appendChild(messageElement);
|
chatWindow.appendChild(messageElement);
|
||||||
|
|
@ -213,7 +217,7 @@ function initChatSocket() {
|
||||||
|
|
||||||
if (chatWindow && data.message.destination === "privateMsg") {
|
if (chatWindow && data.message.destination === "privateMsg") {
|
||||||
chatMessageIn?.classList.remove("hidden");
|
chatMessageIn?.classList.remove("hidden");
|
||||||
chatMessageIn!.textContent = '🔴';
|
chatMessageIn!.textContent = "🔴";
|
||||||
const messageElement = document.createElement("div-private");
|
const messageElement = document.createElement("div-private");
|
||||||
messageElement.textContent = `🔒${data.message.user}: ${data.message.text}`;
|
messageElement.textContent = `🔒${data.message.user}: ${data.message.text}`;
|
||||||
chatWindow.appendChild(messageElement);
|
chatWindow.appendChild(messageElement);
|
||||||
|
|
@ -222,7 +226,7 @@ function initChatSocket() {
|
||||||
|
|
||||||
if (chatWindow && data.message.destination === "inviteMsg") {
|
if (chatWindow && data.message.destination === "inviteMsg") {
|
||||||
chatMessageIn?.classList.remove("hidden");
|
chatMessageIn?.classList.remove("hidden");
|
||||||
chatMessageIn!.textContent = '🟢';
|
chatMessageIn!.textContent = "🟢";
|
||||||
const messageElement = document.createElement("div-private");
|
const messageElement = document.createElement("div-private");
|
||||||
const chatWindow = document.getElementById(
|
const chatWindow = document.getElementById(
|
||||||
"t-chatbox",
|
"t-chatbox",
|
||||||
|
|
@ -235,14 +239,13 @@ function initChatSocket() {
|
||||||
if (systemWindow && data.message.destination === "system-info") {
|
if (systemWindow && data.message.destination === "system-info") {
|
||||||
const messageElement = document.createElement("div");
|
const messageElement = document.createElement("div");
|
||||||
messageElement.textContent = `${data.message.user}: ${data.message.text}`;
|
messageElement.textContent = `${data.message.user}: ${data.message.text}`;
|
||||||
|
|
||||||
// keep only last 10
|
// keep only last 10
|
||||||
while (systemWindow.children.length > MAX_SYSTEM_MESSAGES) {
|
while (systemWindow.children.length > MAX_SYSTEM_MESSAGES) {
|
||||||
systemWindow.removeChild(systemWindow.firstChild!);
|
systemWindow.removeChild(systemWindow.firstChild!);
|
||||||
}
|
}
|
||||||
systemWindow.appendChild(messageElement);
|
systemWindow.appendChild(messageElement);
|
||||||
systemWindow.lastElementChild?.scrollIntoView({ block: "end" });
|
systemWindow.lastElementChild?.scrollIntoView({ block: "end" });
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -256,6 +259,7 @@ function initChatSocket() {
|
||||||
actionBtnPopUpBlock(profil, socket);
|
actionBtnPopUpBlock(profil, socket);
|
||||||
actionBtnPongGames(profil, socket);
|
actionBtnPongGames(profil, socket);
|
||||||
actionBtnTTTGames(profil, socket);
|
actionBtnTTTGames(profil, socket);
|
||||||
|
actionBtnFriend(profil, socket);
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("blockUser", (blocked: ClientProfil) => {
|
socket.on("blockUser", (blocked: ClientProfil) => {
|
||||||
|
|
@ -276,9 +280,9 @@ function initChatSocket() {
|
||||||
if (blockUserBtn) {
|
if (blockUserBtn) {
|
||||||
let message = "";
|
let message = "";
|
||||||
if (data.userState === "block") {
|
if (data.userState === "block") {
|
||||||
(message = "un-block");
|
message = "un-block";
|
||||||
} else {
|
} else {
|
||||||
(message = "block");
|
message = "block";
|
||||||
}
|
}
|
||||||
blockUserBtn.textContent = message;
|
blockUserBtn.textContent = message;
|
||||||
}
|
}
|
||||||
|
|
@ -292,17 +296,15 @@ function initChatSocket() {
|
||||||
const htmlBaliseRegex = /<a\b[^>]*>[\s\S]*?<\/a>/;
|
const htmlBaliseRegex = /<a\b[^>]*>[\s\S]*?<\/a>/;
|
||||||
const htmlBaliseMatch = message.match(htmlBaliseRegex);
|
const htmlBaliseMatch = message.match(htmlBaliseRegex);
|
||||||
|
|
||||||
if (htmlBaliseMatch)
|
if (htmlBaliseMatch) addInviteMessage(message);
|
||||||
addInviteMessage(message);
|
else addMessage(message);
|
||||||
else
|
|
||||||
addMessage(message);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//receives broadcast of the next GAME
|
//receives broadcast of the next GAME
|
||||||
socket.on("nextGame", (message: string) => {
|
socket.on("nextGame", (message: string) => {
|
||||||
openMessagePopup(message);
|
openMessagePopup(message);
|
||||||
});
|
});
|
||||||
|
|
||||||
//receives broadcast of the next GAME
|
//receives broadcast of the next GAME
|
||||||
socket.on("tourStatus", (message: string) => {
|
socket.on("tourStatus", (message: string) => {
|
||||||
openMessagePopup(message);
|
openMessagePopup(message);
|
||||||
|
|
@ -389,20 +391,20 @@ sendButton?.addEventListener("click", () => {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "@pong":
|
case "@pong":
|
||||||
if (msgCommand[1] === "") {
|
if (msgCommand[1] === "") {
|
||||||
navigateTo("/app/pong/games");
|
navigateTo("/app/pong/games");
|
||||||
quitChat();
|
quitChat();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "@ttt":
|
case "@ttt":
|
||||||
if (msgCommand[1] === "") {
|
if (msgCommand[1] === "") {
|
||||||
navigateTo("/app/ttt/games");
|
navigateTo("/app/ttt/games");
|
||||||
quitChat();
|
quitChat();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "@guest":
|
case "@guest":
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -503,7 +505,6 @@ clearText?.addEventListener("click", () => {
|
||||||
|
|
||||||
bquit?.addEventListener("click", () => {
|
bquit?.addEventListener("click", () => {
|
||||||
quitChat();
|
quitChat();
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
myGames?.addEventListener("click", () => {
|
myGames?.addEventListener("click", () => {
|
||||||
|
|
@ -525,7 +526,7 @@ sendtextbox.addEventListener("keydown", (event) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
chatButton!.addEventListener("click",() => {
|
chatButton!.addEventListener("click", () => {
|
||||||
if (chatBox.classList.contains("hidden")) {
|
if (chatBox.classList.contains("hidden")) {
|
||||||
chatBox.classList.toggle("hidden");
|
chatBox.classList.toggle("hidden");
|
||||||
overlay.classList.add("opacity-60");
|
overlay.classList.add("opacity-60");
|
||||||
|
|
@ -534,14 +535,14 @@ chatButton!.addEventListener("click",() => {
|
||||||
if (!socket) return;
|
if (!socket) return;
|
||||||
connected(socket);
|
connected(socket);
|
||||||
chatMessageIn?.classList.add("hidden");
|
chatMessageIn?.classList.add("hidden");
|
||||||
chatMessageIn!.textContent = '';
|
chatMessageIn!.textContent = "";
|
||||||
sendtextbox.focus();
|
sendtextbox.focus();
|
||||||
} else {
|
} else {
|
||||||
chatBox.classList.toggle("hidden");
|
chatBox.classList.toggle("hidden");
|
||||||
overlay.classList.remove("opacity-60");
|
overlay.classList.remove("opacity-60");
|
||||||
windowStateHidden();
|
windowStateHidden();
|
||||||
chatMessageIn?.classList.add("hidden");
|
chatMessageIn?.classList.add("hidden");
|
||||||
chatMessageIn!.textContent = '';
|
chatMessageIn!.textContent = "";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
35
frontend/src/chat/chatHelperFunctions/actionBtnFriend.ts
Normal file
35
frontend/src/chat/chatHelperFunctions/actionBtnFriend.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import client from "@app/api";
|
||||||
|
import type { ClientProfil } from "../types_front";
|
||||||
|
import { Socket } from "socket.io-client";
|
||||||
|
import { showError, showSuccess } from "@app/toast";
|
||||||
|
import { getFriendList, updateFriendsList } from "@app/utils";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* function listens for a click on the TTT game History button
|
||||||
|
* @param profile - Clients target profil
|
||||||
|
* @param senderSocket - socket from the sender
|
||||||
|
**/
|
||||||
|
|
||||||
|
export function actionBtnFriend(profile: ClientProfil, senderSocket: Socket) {
|
||||||
|
setTimeout(() => {
|
||||||
|
const friend = document.querySelector("#btn-friend");
|
||||||
|
friend?.addEventListener("click", async () => {
|
||||||
|
let friendList = getFriendList();
|
||||||
|
if (!friendList.some(v => v.id === profile.userID!)) {
|
||||||
|
let req = await client.addFriend({ user: profile.userID! });
|
||||||
|
if (req.kind === 'success')
|
||||||
|
showSuccess('Successfully added a new Friend')
|
||||||
|
else
|
||||||
|
showError('Failed to add a new Friend');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let req = await client.removeFriend({ user: profile.userID! });
|
||||||
|
if (req.kind === 'success')
|
||||||
|
showSuccess('Successfully removed a Friend')
|
||||||
|
else
|
||||||
|
showError('Failed to remove a Friend');
|
||||||
|
}
|
||||||
|
await updateFriendsList();
|
||||||
|
});
|
||||||
|
}, 0)
|
||||||
|
};
|
||||||
|
|
@ -6,17 +6,18 @@ export async function openProfilePopup(profil: ClientProfil) {
|
||||||
modalname.innerHTML =
|
modalname.innerHTML =
|
||||||
`
|
`
|
||||||
<div class="profile-info">
|
<div class="profile-info">
|
||||||
<div-profil-name id="profilName" class="text-xl font-bold text-blue-500"> Profile of ${profil.user} </div>
|
<div-profil-name id="profilName" class="text-xl font-bold text-blue-500"> Profile of ${profil.user} </div>
|
||||||
<div-login-name id="loginName"> Login status: <span class="recessed">${profil.loginName ?? 'Guest'}</span> </div>
|
<div-login-name id="loginName"> Login status: <span class="recessed">${profil.loginName ?? 'Guest'}</span> </div>
|
||||||
</br>
|
</br>
|
||||||
<div-login-name id="loginName"> Login ID: <span class="recessed">${profil.userID ?? ''}</span> </div>
|
<div-login-name id="loginName"> Login ID: <span class="recessed">${profil.userID ?? ''}</span> </div>
|
||||||
|
<img src="/icons/${profil.userID}" class="w-30 h-30 absoluet absolute top-26 right-10" > </img>
|
||||||
</br>
|
</br>
|
||||||
<button id="popup-b-invite" class="btn-style popup-b-invite">U Game ?</button>
|
<button id="popup-b-invite" class="btn-style popup-b-invite">U Game ?</button>
|
||||||
<button id="popup-b-block" class="btn-style popup-b-block">Block User</button>
|
<button id="popup-b-block" class="btn-style popup-b-block">Block User</button>
|
||||||
<div id="profile-about" class="text-2xl">About: <span class="recessed">${profil.text}</span> </div>
|
<div id="profile-about" class="text-2xl">About: <span class="recessed">${profil.text}</span> </div>
|
||||||
<button id="popup-b-hGame" class="btn-style-games popup-b-game">View Pong Games</button>
|
<button id="popup-b-hGame" class="btn-style-games popup-b-game">View Pong Games</button>
|
||||||
<button id="popup-b-hTGame" class="btn-style-games popup-b-TTTgame">View TTT Games</button>
|
<button id="popup-b-hTGame" class="btn-style-games popup-b-TTTgame">View TTT Games</button>
|
||||||
|
<button id="btn-friend" class="btn-style popup-b-friend">friend</button>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
const profilList = document.getElementById("profile-modal") ?? null;
|
const profilList = document.getElementById("profile-modal") ?? null;
|
||||||
|
|
|
||||||
9
frontend/src/pages/friendList/friendList.html
Normal file
9
frontend/src/pages/friendList/friendList.html
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
<div class="fixed inset-0 flex items-center justify-center bg-[#43536b]">
|
||||||
|
<div
|
||||||
|
class="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-gray-200 w-212.5 p-6 rounded-xl shadow-2xl text-center z-50">
|
||||||
|
<h2 class="text-2xl font-bold text-center mb-4 text-gray-700">
|
||||||
|
FriendList
|
||||||
|
</h2>
|
||||||
|
<div id="friendList" class="max-w-3xl mx-auto space-y-2 max-h-[50dvh] overflow-scroll"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
41
frontend/src/pages/friendList/friendList.ts
Normal file
41
frontend/src/pages/friendList/friendList.ts
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
import { addRoute, navigateTo, setTitle, type RouteHandlerParams, type RouteHandlerReturn } from "@app/routing";
|
||||||
|
import page from './friendList.html?raw';
|
||||||
|
import { getFriendList, isNullish, updateFriendsList } from "@app/utils";
|
||||||
|
import client from "@app/api";
|
||||||
|
import { updateUser } from "@app/auth";
|
||||||
|
import { showError } from "@app/toast";
|
||||||
|
|
||||||
|
|
||||||
|
async function friends(_url: string, args: RouteHandlerParams): Promise<RouteHandlerReturn> {
|
||||||
|
setTitle("Tic Tac Toe Games");
|
||||||
|
let user = await updateUser();
|
||||||
|
if (isNullish(user)) {
|
||||||
|
return { html: '<span> You aren\'t logged in </span>', postInsert: () => { showError("You must be logged in !"); navigateTo("/") } };
|
||||||
|
}
|
||||||
|
await updateFriendsList();
|
||||||
|
let friendList = getFriendList();
|
||||||
|
friendList.sort();
|
||||||
|
|
||||||
|
let friendsElem = friendList.map(g => {
|
||||||
|
let e = document.createElement('div');
|
||||||
|
e.className = 'grid grid-cols-[1fr_auto_1fr] items-center bg-zinc-800 rounded-lg px-4 py-3';
|
||||||
|
|
||||||
|
e.innerHTML = `
|
||||||
|
<div class="text-center font-semibold text-white">${g.name}</div>
|
||||||
|
<a href="/ttt/games/${g.id}" class="text-center">TTT Games</a>
|
||||||
|
<a href="/pong/games/${g.id}" class="text-center">Pong Games</a>
|
||||||
|
`;
|
||||||
|
return e;
|
||||||
|
}).filter(v => !isNullish(v));
|
||||||
|
|
||||||
|
return {
|
||||||
|
html: page, postInsert: async (app) => {
|
||||||
|
if (!app) return;
|
||||||
|
const friendsBox = app.querySelector<HTMLDivElement>("#friendList");
|
||||||
|
if (!friendsBox) return;
|
||||||
|
friendsElem.forEach(c => friendsBox.appendChild(c));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
addRoute('/friends', friends);
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { setTitle, handleRoute } from '@app/routing';
|
import { setTitle, handleRoute } from '@app/routing';
|
||||||
import './root/root.ts'
|
import './root/root.ts'
|
||||||
import '../chat/chat.ts'
|
|
||||||
import './pong/pong.ts'
|
import './pong/pong.ts'
|
||||||
import './login/login.ts'
|
import './login/login.ts'
|
||||||
import './signin/signin.ts'
|
import './signin/signin.ts'
|
||||||
|
|
@ -10,6 +9,7 @@ import './logout/logout.ts'
|
||||||
import './pongHistory/pongHistory.ts'
|
import './pongHistory/pongHistory.ts'
|
||||||
import './tttHistory/tttHistory.ts'
|
import './tttHistory/tttHistory.ts'
|
||||||
import './tourHistory/tourHistory.ts'
|
import './tourHistory/tourHistory.ts'
|
||||||
|
import './friendList/friendList.ts'
|
||||||
|
|
||||||
// ---- Initial load ----
|
// ---- Initial load ----
|
||||||
setTitle("");
|
setTitle("");
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,9 @@ function tourinfoButtons(tourInfo : HTMLButtonElement, tourScoreScreen : HTMLDiv
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function gameJoinButtons(socket : CSocket, inTournament : boolean, currentGame : currentGameInfo | null,
|
let inTournament: boolean = false;
|
||||||
|
|
||||||
|
function gameJoinButtons(socket : CSocket, currentGame : currentGameInfo | null,
|
||||||
tournament : HTMLButtonElement, queue : HTMLButtonElement, localGame : HTMLButtonElement, ready : HTMLButtonElement)
|
tournament : HTMLButtonElement, queue : HTMLButtonElement, localGame : HTMLButtonElement, ready : HTMLButtonElement)
|
||||||
{
|
{
|
||||||
tournament.addEventListener("click", () => {
|
tournament.addEventListener("click", () => {
|
||||||
|
|
@ -150,6 +152,10 @@ function gameJoinButtons(socket : CSocket, inTournament : boolean, currentGame :
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
localGame.addEventListener("click", () => {
|
localGame.addEventListener("click", () => {
|
||||||
|
if (inTournament) {
|
||||||
|
showError("You can't queue up currently !");
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
queue.innerText !== QueueState.Iddle ||
|
queue.innerText !== QueueState.Iddle ||
|
||||||
currentGame !== null ||
|
currentGame !== null ||
|
||||||
|
|
@ -274,7 +280,7 @@ function pongClient(
|
||||||
setTitle("Pong Game");
|
setTitle("Pong Game");
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
let game_req_join = urlParams.get("game");
|
let game_req_join = urlParams.get("game");
|
||||||
let inTournament = false;
|
inTournament = false;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
html: authHtml,
|
html: authHtml,
|
||||||
|
|
@ -548,7 +554,7 @@ function pongClient(
|
||||||
|
|
||||||
setInterval(() => {keys_listen_setup(currentGame, socket, keys, playHow, playHow_b, tourScoreScreen, queue)}, 1000 / 60);
|
setInterval(() => {keys_listen_setup(currentGame, socket, keys, playHow, playHow_b, tourScoreScreen, queue)}, 1000 / 60);
|
||||||
|
|
||||||
gameJoinButtons(socket, inTournament, currentGame, tournament, queue, localGame, ready);
|
gameJoinButtons(socket, currentGame, tournament, queue, localGame, ready);
|
||||||
playhowButtons(playHow_b, playHow);
|
playhowButtons(playHow_b, playHow);
|
||||||
tourinfoButtons(tourInfo, tourScoreScreen);
|
tourinfoButtons(tourInfo, tourScoreScreen);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,19 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="iconBox" class="mb-1 grid grid-cols-[auto_1fr] gap-2 hidden">
|
||||||
|
<div id="viewIconBox" class="h-[128px] w-[128px] p-2 border-gray-700 border-2 rounded-lg">
|
||||||
|
<img/>
|
||||||
|
</div>
|
||||||
|
<div id="updateIconBox">
|
||||||
|
<form id="updateIconForm" class="flex flex-col justify-evenly h-full">
|
||||||
|
<input type="file" name="upload" accept="image/png, image/jpeg" class="text-black border-black border-2 rounded-lg px-4 w-[230px]"></input>
|
||||||
|
<span class="text-black font-base">Select Image</span>
|
||||||
|
<button type="submit" class="w-full bg-blue-600 text-white py-2 rounded hover:bg-blue-700"> Update </button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Display Name -->
|
<!-- Display Name -->
|
||||||
<div id="displayNameWrapper" class="py-2">
|
<div id="displayNameWrapper" class="py-2">
|
||||||
<label class="block font-medium mb-1 text-gray-700">Display Name</label>
|
<label class="block font-medium mb-1 text-gray-700">Display Name</label>
|
||||||
|
|
@ -52,8 +65,7 @@
|
||||||
<label class="block font-medium mb-1 text-gray-700">Description</label>
|
<label class="block font-medium mb-1 text-gray-700">Description</label>
|
||||||
<input id="descBox" type="text" placeholder="Description..." name="Description"
|
<input id="descBox" type="text" placeholder="Description..." name="Description"
|
||||||
class="w-full px-4 py-2 border border-gray-300 text-gray-700 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500" />
|
class="w-full px-4 py-2 border border-gray-300 text-gray-700 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500" />
|
||||||
<button id="descButton"
|
<button id="descButton" class="w-full bg-blue-600 text-white py-2 rounded hover:bg-blue-700">Update</button>
|
||||||
class="w-full bg-blue-600 text-white py-2 rounded hover:bg-blue-700">Update</button>
|
|
||||||
</div>
|
</div>
|
||||||
<!-- TOTP -->
|
<!-- TOTP -->
|
||||||
<div class="border rounded p-4" id="totpWrapper" hidden>
|
<div class="border rounded p-4" id="totpWrapper" hidden>
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,45 @@ function removeBgColor(...elem: HTMLElement[]) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function setup_profile_image(container: HTMLDivElement, url: string) {
|
||||||
|
let imgNode = container.querySelector<HTMLImageElement>("img");
|
||||||
|
let formNode = container.querySelector<HTMLFormElement>("form");
|
||||||
|
if (!imgNode || !formNode) return;
|
||||||
|
imgNode.src = url;
|
||||||
|
container.classList.remove("hidden");
|
||||||
|
formNode.addEventListener("submit", async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
let form = e.target;
|
||||||
|
if (!form) return;
|
||||||
|
let data = new FormData(form as HTMLFormElement);
|
||||||
|
let req = await fetch("/api/icons/set", {
|
||||||
|
body: data,
|
||||||
|
method: "POST",
|
||||||
|
});
|
||||||
|
if (req.status === 200 || req.status === 400) {
|
||||||
|
let json = await req.json();
|
||||||
|
if (!("kind" in json) || !("msg" in json))
|
||||||
|
return showError("Unknown Error");
|
||||||
|
if (typeof json.kind !== "string" || typeof json.msg !== "string")
|
||||||
|
return showError("Unknown Error");
|
||||||
|
const pjson: { kind: string; msg: string } = json;
|
||||||
|
if (pjson.kind === "success") {
|
||||||
|
showSuccess("Updated image !");
|
||||||
|
return handleRoute();
|
||||||
|
} else {
|
||||||
|
console.log(`Failed to upload image: ${pjson.msg}`);
|
||||||
|
showError("Failed to change image");
|
||||||
|
}
|
||||||
|
} if (req.status === 413)
|
||||||
|
{
|
||||||
|
showError("Image too big");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
showError("Unknown Error");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async function route(url: string, _args: { [k: string]: string }) {
|
async function route(url: string, _args: { [k: string]: string }) {
|
||||||
setTitle("Edit Profile");
|
setTitle("Edit Profile");
|
||||||
return {
|
return {
|
||||||
|
|
@ -99,8 +138,7 @@ async function route(url: string, _args: { [k: string]: string }) {
|
||||||
|
|
||||||
let descWrapper =
|
let descWrapper =
|
||||||
app.querySelector<HTMLDivElement>("#descWrapper")!;
|
app.querySelector<HTMLDivElement>("#descWrapper")!;
|
||||||
let descBox =
|
let descBox = app.querySelector<HTMLInputElement>("#descBox")!;
|
||||||
app.querySelector<HTMLInputElement>("#descBox")!;
|
|
||||||
let descButton =
|
let descButton =
|
||||||
app.querySelector<HTMLButtonElement>("#descButton")!;
|
app.querySelector<HTMLButtonElement>("#descButton")!;
|
||||||
|
|
||||||
|
|
@ -126,6 +164,8 @@ async function route(url: string, _args: { [k: string]: string }) {
|
||||||
let totpWrapper =
|
let totpWrapper =
|
||||||
app.querySelector<HTMLDivElement>("#totpWrapper")!;
|
app.querySelector<HTMLDivElement>("#totpWrapper")!;
|
||||||
|
|
||||||
|
let imgBox = app.querySelector<HTMLDivElement>("#iconBox")!;
|
||||||
|
|
||||||
descBox.value = user.desc;
|
descBox.value = user.desc;
|
||||||
|
|
||||||
if (user.guest) {
|
if (user.guest) {
|
||||||
|
|
@ -138,10 +178,7 @@ async function route(url: string, _args: { [k: string]: string }) {
|
||||||
descButton,
|
descButton,
|
||||||
);
|
);
|
||||||
|
|
||||||
descButton.classList.add(
|
descButton.classList.add("bg-gray-700", "hover:bg-gray-700");
|
||||||
"bg-gray-700",
|
|
||||||
"hover:bg-gray-700",
|
|
||||||
);
|
|
||||||
descButton.disabled = true;
|
descButton.disabled = true;
|
||||||
descBox.disabled = true;
|
descBox.disabled = true;
|
||||||
|
|
||||||
|
|
@ -174,6 +211,7 @@ async function route(url: string, _args: { [k: string]: string }) {
|
||||||
passwordWrapper.hidden = false;
|
passwordWrapper.hidden = false;
|
||||||
|
|
||||||
accountTypeBox.innerText = "Normal";
|
accountTypeBox.innerText = "Normal";
|
||||||
|
setup_profile_image(imgBox, `/icons/${user.id}`);
|
||||||
} else if (
|
} else if (
|
||||||
!isNullish(user.selfInfo?.providerId) &&
|
!isNullish(user.selfInfo?.providerId) &&
|
||||||
!isNullish(user.selfInfo?.providerUser)
|
!isNullish(user.selfInfo?.providerUser)
|
||||||
|
|
@ -195,6 +233,7 @@ async function route(url: string, _args: { [k: string]: string }) {
|
||||||
totpWrapper.hidden = true;
|
totpWrapper.hidden = true;
|
||||||
|
|
||||||
accountTypeBox.innerText = "Provider";
|
accountTypeBox.innerText = "Provider";
|
||||||
|
setup_profile_image(imgBox, `/icons/${user.id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- Update UI ----
|
// ---- Update UI ----
|
||||||
|
|
@ -269,12 +308,13 @@ async function route(url: string, _args: { [k: string]: string }) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
descButton.onclick = async () => {
|
descButton.onclick = async () => {
|
||||||
let req = await client.changeDesc({ changeDescRequest: { desc: descBox.value } });
|
let req = await client.changeDesc({
|
||||||
|
changeDescRequest: { desc: descBox.value },
|
||||||
|
});
|
||||||
if (req.kind === "success") {
|
if (req.kind === "success") {
|
||||||
showSuccess("Successfully changed description");
|
showSuccess("Successfully changed description");
|
||||||
handleRoute();
|
handleRoute();
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
showError(`Failed to update`);
|
showError(`Failed to update`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
|
import client from "./api";
|
||||||
|
|
||||||
export function escapeHTML(str: string): string {
|
export function escapeHTML(str: string): string {
|
||||||
const p = document.createElement("p");
|
const p = document.createElement("p");
|
||||||
p.appendChild(document.createTextNode(str));
|
p.appendChild(document.createTextNode(str));
|
||||||
return p.innerHTML;
|
return p.innerHTML;
|
||||||
}
|
}
|
||||||
export function isNullish<T>(v: T | undefined | null): v is (null | undefined) {
|
export function isNullish<T>(v: T | undefined | null): v is null | undefined {
|
||||||
return v === null || v === undefined;
|
return v === null || v === undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -11,3 +13,22 @@ export function isNullish<T>(v: T | undefined | null): v is (null | undefined) {
|
||||||
export function ensureWindowState() {
|
export function ensureWindowState() {
|
||||||
window.__state = window.__state ?? {};
|
window.__state = window.__state ?? {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function updateFriendsList() {
|
||||||
|
window.__state = window.__state ?? {};
|
||||||
|
window.__state.friendList ??= [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
let req = await client.listFriend();
|
||||||
|
if (req.kind === "success") {
|
||||||
|
window.__state.friendList = req.payload.friends;
|
||||||
|
}
|
||||||
|
} catch (e: unknown) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getFriendList() {
|
||||||
|
ensureWindowState();
|
||||||
|
window.__state.friendList ??= [];
|
||||||
|
|
||||||
|
return window.__state.friendList;
|
||||||
|
}
|
||||||
|
|
|
||||||
10
nginx/conf/locations/icons.conf
Normal file
10
nginx/conf/locations/icons.conf
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
#forward the post request to the microservice
|
||||||
|
location /api/icons/ {
|
||||||
|
proxy_pass http://app-icons;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /icons/ {
|
||||||
|
root /volumes/;
|
||||||
|
default_type image/png;
|
||||||
|
add_header Cache-Control "max-age=30";
|
||||||
|
}
|
||||||
|
|
@ -10,5 +10,6 @@ COPY auth/package.json /build/auth/package.json
|
||||||
COPY chat/package.json /build/chat/package.json
|
COPY chat/package.json /build/chat/package.json
|
||||||
COPY tic-tac-toe/package.json /build/tic-tac-toe/package.json
|
COPY tic-tac-toe/package.json /build/tic-tac-toe/package.json
|
||||||
COPY user/package.json /build/user/package.json
|
COPY user/package.json /build/user/package.json
|
||||||
|
COPY icons/package.json /build/icons/package.json
|
||||||
|
|
||||||
RUN pnpm install -q --frozen-lockfile;
|
RUN pnpm install -q --frozen-lockfile;
|
||||||
|
|
|
||||||
|
|
@ -4,17 +4,19 @@ import { FastifyInstance, FastifyPluginAsync } from 'fastify';
|
||||||
import { Database as DbImpl } from './mixin/_base';
|
import { Database as DbImpl } from './mixin/_base';
|
||||||
import { IUserDb, UserImpl } from './mixin/user';
|
import { IUserDb, UserImpl } from './mixin/user';
|
||||||
import { IBlockedDb, BlockedImpl } from './mixin/blocked';
|
import { IBlockedDb, BlockedImpl } from './mixin/blocked';
|
||||||
|
import { IFriendsDb, FriendsImpl } from './mixin/friends';
|
||||||
import { ITicTacToeDb, TicTacToeImpl } from './mixin/tictactoe';
|
import { ITicTacToeDb, TicTacToeImpl } from './mixin/tictactoe';
|
||||||
import { IPongDb, PongImpl } from './mixin/pong';
|
import { IPongDb, PongImpl } from './mixin/pong';
|
||||||
import { ITournamentDb, TournamentImpl } from './mixin/tournament';
|
import { ITournamentDb, TournamentImpl } from './mixin/tournament';
|
||||||
|
|
||||||
Object.assign(DbImpl.prototype, UserImpl);
|
Object.assign(DbImpl.prototype, UserImpl);
|
||||||
Object.assign(DbImpl.prototype, BlockedImpl);
|
Object.assign(DbImpl.prototype, BlockedImpl);
|
||||||
|
Object.assign(DbImpl.prototype, FriendsImpl);
|
||||||
Object.assign(DbImpl.prototype, TicTacToeImpl);
|
Object.assign(DbImpl.prototype, TicTacToeImpl);
|
||||||
Object.assign(DbImpl.prototype, PongImpl);
|
Object.assign(DbImpl.prototype, PongImpl);
|
||||||
Object.assign(DbImpl.prototype, TournamentImpl);
|
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
|
// When using .decorate you have to specify added properties for Typescript
|
||||||
declare module 'fastify' {
|
declare module 'fastify' {
|
||||||
|
|
|
||||||
|
|
@ -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 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 --
|
-- TICTACTOE --
|
||||||
----------------
|
----------------
|
||||||
|
|
|
||||||
72
src/@shared/src/database/mixin/friends.ts
Normal file
72
src/@shared/src/database/mixin/friends.ts
Normal file
|
|
@ -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<IFriendsDb, keyof Database> = {
|
||||||
|
getFriendsUserFor(this: IFriendsDb, id: UserId): FriendsData[] {
|
||||||
|
const query = this.prepare('SELECT * FROM friends WHERE user = @id');
|
||||||
|
const data = query.all({ id }) as Partial<FriendsData>[];
|
||||||
|
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<FriendsData>[];
|
||||||
|
|
||||||
|
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>): 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;
|
||||||
|
}
|
||||||
BIN
src/auth/config/default.png
Normal file
BIN
src/auth/config/default.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
|
|
@ -2,6 +2,7 @@ import { FastifyPluginAsync } from 'fastify';
|
||||||
|
|
||||||
import { Static, Type } from 'typebox';
|
import { Static, Type } from 'typebox';
|
||||||
import { typeResponse, isNullish, MakeStaticResponse } from '@shared/utils';
|
import { typeResponse, isNullish, MakeStaticResponse } from '@shared/utils';
|
||||||
|
import * as fs from 'node:fs/promises';
|
||||||
|
|
||||||
export const GuestLoginRes = {
|
export const GuestLoginRes = {
|
||||||
'500': typeResponse('failed', [
|
'500': typeResponse('failed', [
|
||||||
|
|
@ -91,6 +92,7 @@ const route: FastifyPluginAsync = async (fastify, _opts): Promise<void> => {
|
||||||
'guestLogin.failed.generic.unknown',
|
'guestLogin.failed.generic.unknown',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
await fs.cp('/config/default.png', `/volumes/icons/${user.id}`);
|
||||||
return res.makeResponse(200, 'success', 'guestLogin.success', {
|
return res.makeResponse(200, 'success', 'guestLogin.success', {
|
||||||
token: this.signJwt('auth', user.id.toString()),
|
token: this.signJwt('auth', user.id.toString()),
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { FastifyPluginAsync } from 'fastify';
|
||||||
import { Static, Type } from 'typebox';
|
import { Static, Type } from 'typebox';
|
||||||
import { typeResponse, isNullish } from '@shared/utils';
|
import { typeResponse, isNullish } from '@shared/utils';
|
||||||
import * as oauth2 from '../../oauth2';
|
import * as oauth2 from '../../oauth2';
|
||||||
|
import * as fs from 'node:fs/promises';
|
||||||
|
|
||||||
|
|
||||||
export const WhoAmIRes = Type.Union([
|
export const WhoAmIRes = Type.Union([
|
||||||
|
|
@ -47,6 +48,9 @@ const route: FastifyPluginAsync = async (fastify, _opts): Promise<void> => {
|
||||||
user_name = `${orig}${Date.now()}`;
|
user_name = `${orig}${Date.now()}`;
|
||||||
}
|
}
|
||||||
u = await this.db.createOauth2User(user_name, provider.display_name, userinfo.unique_id);
|
u = await this.db.createOauth2User(user_name, provider.display_name, userinfo.unique_id);
|
||||||
|
if (u) {
|
||||||
|
await fs.cp('/config/default.png', `/volumes/icons/${u.id}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (isNullish(u)) {
|
if (isNullish(u)) {
|
||||||
return res.code(500).send('failed to fetch or create user...');
|
return res.code(500).send('failed to fetch or create user...');
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { FastifyPluginAsync } from 'fastify';
|
||||||
|
|
||||||
import { Static, Type } from 'typebox';
|
import { Static, Type } from 'typebox';
|
||||||
import { typeResponse, isNullish, MakeStaticResponse } from '@shared/utils';
|
import { typeResponse, isNullish, MakeStaticResponse } from '@shared/utils';
|
||||||
|
import * as fs from 'node:fs/promises';
|
||||||
|
|
||||||
const USERNAME_CHECK: RegExp = /^[a-zA-Z_0-9]+$/;
|
const USERNAME_CHECK: RegExp = /^[a-zA-Z_0-9]+$/;
|
||||||
|
|
||||||
|
|
@ -61,6 +62,7 @@ const route: FastifyPluginAsync = async (fastify, _opts): Promise<void> => {
|
||||||
}
|
}
|
||||||
const u = await this.db.createUser(name, user_name, password);
|
const u = await this.db.createUser(name, user_name, password);
|
||||||
if (isNullish(u)) { return res.makeResponse(500, 'failed', 'signin.failed.generic'); }
|
if (isNullish(u)) { return res.makeResponse(500, 'failed', 'signin.failed.generic'); }
|
||||||
|
await fs.cp('/config/default.png', `/volumes/icons/${u.id}`);
|
||||||
|
|
||||||
// every check has been passed, they are now logged in, using this token to say who they are...
|
// every check has been passed, they are now logged in, using this token to say who they are...
|
||||||
const userToken = this.signJwt('auth', u.id);
|
const userToken = this.signJwt('auth', u.id);
|
||||||
|
|
|
||||||
2
src/icons/.dockerignore
Normal file
2
src/icons/.dockerignore
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
/dist
|
||||||
|
/node_modules
|
||||||
21
src/icons/openapi.json
Normal file
21
src/icons/openapi.json
Normal file
|
|
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
35
src/icons/package.json
Normal file
35
src/icons/package.json
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
{
|
||||||
|
"type": "module",
|
||||||
|
"private": false,
|
||||||
|
"name": "icons",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "This project was bootstrapped with Fastify-CLI.",
|
||||||
|
"main": "app.ts",
|
||||||
|
"directories": {
|
||||||
|
"test": "test"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "npm run build && node dist/run.js",
|
||||||
|
"build": "vite build",
|
||||||
|
"build:prod": "vite build --outDir=/dist --minify=true --sourcemap=false",
|
||||||
|
"build:openapi": "VITE_ENTRYPOINT=src/openapi.ts vite build && node dist/openapi.cjs >openapi.json"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"@fastify/formbody": "^8.0.2",
|
||||||
|
"@fastify/multipart": "^9.3.0",
|
||||||
|
"fastify": "^5.6.2",
|
||||||
|
"fastify-plugin": "^5.1.0",
|
||||||
|
"file-type": "^21.3.0",
|
||||||
|
"sharp": "^0.34.5",
|
||||||
|
"typebox": "^1.0.69"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^22.19.3",
|
||||||
|
"rollup-plugin-node-externals": "^8.1.2",
|
||||||
|
"vite": "^7.3.0",
|
||||||
|
"vite-tsconfig-paths": "^5.1.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
33
src/icons/src/app.ts
Normal file
33
src/icons/src/app.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { FastifyPluginAsync } from 'fastify';
|
||||||
|
import * as db from '@shared/database';
|
||||||
|
import * as auth from '@shared/auth';
|
||||||
|
import * as swagger from '@shared/swagger';
|
||||||
|
import * as utils from '@shared/utils';
|
||||||
|
|
||||||
|
declare const __SERVICE_NAME: string;
|
||||||
|
|
||||||
|
// @ts-expect-error: import.meta.glob is a vite thing. Typescript doesn't know this...
|
||||||
|
const plugins = import.meta.glob('./plugins/**/*.ts', { eager: true });
|
||||||
|
// @ts-expect-error: import.meta.glob is a vite thing. Typescript doesn't know this...
|
||||||
|
const routes = import.meta.glob('./routes/**/*.ts', { eager: true });
|
||||||
|
|
||||||
|
const app: FastifyPluginAsync = async (fastify, opts): Promise<void> => {
|
||||||
|
void opts;
|
||||||
|
await fastify.register(utils.useMakeResponse);
|
||||||
|
await fastify.register(utils.useMonitoring);
|
||||||
|
await fastify.register(swagger.useSwagger, { service: __SERVICE_NAME });
|
||||||
|
await fastify.register(db.useDatabase as FastifyPluginAsync, {});
|
||||||
|
await fastify.register(auth.jwtPlugin as FastifyPluginAsync, {});
|
||||||
|
await fastify.register(auth.authPlugin as FastifyPluginAsync, {});
|
||||||
|
|
||||||
|
// Place here your custom code!
|
||||||
|
for (const plugin of Object.values(plugins)) {
|
||||||
|
void fastify.register(plugin as FastifyPluginAsync, {});
|
||||||
|
}
|
||||||
|
for (const route of Object.values(routes)) {
|
||||||
|
void fastify.register(route as FastifyPluginAsync, {});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default app;
|
||||||
|
export { app };
|
||||||
21
src/icons/src/openapi.ts
Normal file
21
src/icons/src/openapi.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
import f, { FastifyPluginAsync } from 'fastify';
|
||||||
|
import * as swagger from '@shared/swagger';
|
||||||
|
import * as auth from '@shared/auth';
|
||||||
|
|
||||||
|
declare const __SERVICE_NAME: string;
|
||||||
|
|
||||||
|
// @ts-expect-error: import.meta.glob is a vite thing. Typescript doesn't know this...
|
||||||
|
const routes = import.meta.glob('./routes/**/*.ts', { eager: true });
|
||||||
|
|
||||||
|
async function start() {
|
||||||
|
const fastify = f({ logger: false });
|
||||||
|
await fastify.register(auth.authPlugin, { onlySchema: true });
|
||||||
|
await fastify.register(swagger.useSwagger, { service: __SERVICE_NAME });
|
||||||
|
|
||||||
|
for (const route of Object.values(routes)) {
|
||||||
|
await fastify.register(route as FastifyPluginAsync, {});
|
||||||
|
}
|
||||||
|
await fastify.ready();
|
||||||
|
console.log(JSON.stringify(fastify.swagger(), undefined, 4));
|
||||||
|
}
|
||||||
|
start();
|
||||||
16
src/icons/src/plugins/README.md
Normal file
16
src/icons/src/plugins/README.md
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
# Plugins Folder
|
||||||
|
|
||||||
|
Plugins define behavior that is common to all the routes in your
|
||||||
|
application. Authentication, caching, templates, and all the other cross
|
||||||
|
cutting concerns should be handled by plugins placed in this folder.
|
||||||
|
|
||||||
|
Files in this folder are typically defined through the
|
||||||
|
[`fastify-plugin`](https://github.com/fastify/fastify-plugin) module,
|
||||||
|
making them non-encapsulated. They can define decorators and set hooks
|
||||||
|
that will then be used in the rest of your application.
|
||||||
|
|
||||||
|
Check out:
|
||||||
|
|
||||||
|
* [The hitchhiker's guide to plugins](https://fastify.dev/docs/latest/Guides/Plugins-Guide/)
|
||||||
|
* [Fastify decorators](https://fastify.dev/docs/latest/Reference/Decorators/).
|
||||||
|
* [Fastify lifecycle](https://fastify.dev/docs/latest/Reference/Lifecycle/).
|
||||||
97
src/icons/src/routes/set.ts
Normal file
97
src/icons/src/routes/set.ts
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
import { FastifyPluginAsync } from 'fastify';
|
||||||
|
import multipart from '@fastify/multipart';
|
||||||
|
import { MakeStaticResponse, typeResponse } from '@shared/utils';
|
||||||
|
import { fileTypeFromBuffer } from 'file-type';
|
||||||
|
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',
|
||||||
|
]),
|
||||||
|
};
|
||||||
|
|
||||||
|
export type IconSetRes = MakeStaticResponse<typeof IconSetRes>;
|
||||||
|
|
||||||
|
const validMimeTypes = new Set(['image/jpeg', 'image/png']);
|
||||||
|
|
||||||
|
async function resizeAndSaveImage(
|
||||||
|
imageBuffer: Buffer,
|
||||||
|
filename: string,
|
||||||
|
): Promise<void> {
|
||||||
|
const outputDir = '/volumes/icons/';
|
||||||
|
const outputPath = path.join(outputDir, filename);
|
||||||
|
|
||||||
|
// Ensure the directory exists
|
||||||
|
await fs.mkdir(outputDir, { recursive: true });
|
||||||
|
|
||||||
|
await sharp(imageBuffer)
|
||||||
|
.resize(512, 512, {
|
||||||
|
fit: 'cover',
|
||||||
|
})
|
||||||
|
.png()
|
||||||
|
.toFile(outputPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
const route: FastifyPluginAsync = async (fastify, _opts): Promise<void> => {
|
||||||
|
void _opts;
|
||||||
|
await fastify.register(multipart);
|
||||||
|
fastify.post(
|
||||||
|
'/api/icons/set',
|
||||||
|
{
|
||||||
|
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',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!validMimeTypes.has(file.mimetype)) {
|
||||||
|
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',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
resizeAndSaveImage(buf, userid);
|
||||||
|
return res.makeResponse(200, 'success', 'iconset.success');
|
||||||
|
}
|
||||||
|
catch (e: unknown) {
|
||||||
|
this.log.warn(e);
|
||||||
|
return res.makeResponse(
|
||||||
|
400,
|
||||||
|
'failure',
|
||||||
|
'iconset.failure.invalidFile',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default route;
|
||||||
21
src/icons/src/run.ts
Normal file
21
src/icons/src/run.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
// this sould only be used by the docker file !
|
||||||
|
|
||||||
|
import fastify, { FastifyInstance } from 'fastify';
|
||||||
|
import app from './app';
|
||||||
|
|
||||||
|
const start = async () => {
|
||||||
|
const f: FastifyInstance = fastify({ logger: { level: 'info' } });
|
||||||
|
process.on('SIGTERM', () => {
|
||||||
|
f.log.warn('Requested to shutdown');
|
||||||
|
process.exit(134);
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
await f.register(app, {});
|
||||||
|
await f.listen({ port: 80, host: '0.0.0.0' });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
f.log.error(err);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
start();
|
||||||
5
src/icons/tsconfig.json
Normal file
5
src/icons/tsconfig.json
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"extends": "../tsconfig.base.json",
|
||||||
|
"compilerOptions": {},
|
||||||
|
"include": ["src/**/*.ts"]
|
||||||
|
}
|
||||||
54
src/icons/vite.config.js
Normal file
54
src/icons/vite.config.js
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
import { defineConfig } from 'vite';
|
||||||
|
import tsconfigPaths from 'vite-tsconfig-paths';
|
||||||
|
import nodeExternals from 'rollup-plugin-node-externals';
|
||||||
|
import path from 'node:path';
|
||||||
|
import fs from 'node:fs';
|
||||||
|
|
||||||
|
function collectDeps(...pkgJsonPaths) {
|
||||||
|
const allDeps = new Set();
|
||||||
|
for (const pkgPath of pkgJsonPaths) {
|
||||||
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
||||||
|
for (const dep of Object.keys(pkg.dependencies || {})) {
|
||||||
|
allDeps.add(dep);
|
||||||
|
}
|
||||||
|
for (const peer of Object.keys(pkg.peerDependencies || {})) {
|
||||||
|
allDeps.add(peer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Array.from(allDeps);
|
||||||
|
}
|
||||||
|
|
||||||
|
const externals = collectDeps(
|
||||||
|
'./package.json',
|
||||||
|
'../@shared/package.json',
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
root: __dirname,
|
||||||
|
define: {
|
||||||
|
__SERVICE_NAME: '"icons"',
|
||||||
|
},
|
||||||
|
// service root
|
||||||
|
plugins: [tsconfigPaths(), nodeExternals()],
|
||||||
|
build: {
|
||||||
|
ssr: true,
|
||||||
|
outDir: 'dist',
|
||||||
|
emptyOutDir: true,
|
||||||
|
lib: {
|
||||||
|
entry: path.resolve(__dirname, process.env.VITE_ENTRYPOINT ?? 'src/run.ts'),
|
||||||
|
// adjust main entry
|
||||||
|
formats: ['cjs'],
|
||||||
|
// CommonJS for Node.js
|
||||||
|
fileName: () => 'index.js',
|
||||||
|
},
|
||||||
|
rollupOptions: {
|
||||||
|
external: externals,
|
||||||
|
},
|
||||||
|
target: 'node22',
|
||||||
|
// or whatever Node version you use
|
||||||
|
sourcemap: true,
|
||||||
|
minify: false,
|
||||||
|
// for easier debugging
|
||||||
|
},
|
||||||
|
});
|
||||||
320
src/openapi.json
320
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}": {
|
"/api/user/info/{user}": {
|
||||||
"get": {
|
"get": {
|
||||||
"operationId": "getUser",
|
"operationId": "getUser",
|
||||||
|
|
|
||||||
406
src/pnpm-lock.yaml
generated
406
src/pnpm-lock.yaml
generated
|
|
@ -141,6 +141,43 @@ importers:
|
||||||
specifier: ^5.1.4
|
specifier: ^5.1.4
|
||||||
version: 5.1.4(typescript@5.9.3)(vite@7.3.0(@types/node@22.19.3)(yaml@2.8.2))
|
version: 5.1.4(typescript@5.9.3)(vite@7.3.0(@types/node@22.19.3)(yaml@2.8.2))
|
||||||
|
|
||||||
|
icons:
|
||||||
|
dependencies:
|
||||||
|
'@fastify/formbody':
|
||||||
|
specifier: ^8.0.2
|
||||||
|
version: 8.0.2
|
||||||
|
'@fastify/multipart':
|
||||||
|
specifier: ^9.3.0
|
||||||
|
version: 9.3.0
|
||||||
|
fastify:
|
||||||
|
specifier: ^5.6.2
|
||||||
|
version: 5.6.2
|
||||||
|
fastify-plugin:
|
||||||
|
specifier: ^5.1.0
|
||||||
|
version: 5.1.0
|
||||||
|
file-type:
|
||||||
|
specifier: ^21.3.0
|
||||||
|
version: 21.3.0
|
||||||
|
sharp:
|
||||||
|
specifier: ^0.34.5
|
||||||
|
version: 0.34.5
|
||||||
|
typebox:
|
||||||
|
specifier: ^1.0.69
|
||||||
|
version: 1.0.69
|
||||||
|
devDependencies:
|
||||||
|
'@types/node':
|
||||||
|
specifier: ^22.19.3
|
||||||
|
version: 22.19.3
|
||||||
|
rollup-plugin-node-externals:
|
||||||
|
specifier: ^8.1.2
|
||||||
|
version: 8.1.2(rollup@4.54.0)
|
||||||
|
vite:
|
||||||
|
specifier: ^7.3.0
|
||||||
|
version: 7.3.0(@types/node@22.19.3)(yaml@2.8.2)
|
||||||
|
vite-tsconfig-paths:
|
||||||
|
specifier: ^5.1.4
|
||||||
|
version: 5.1.4(typescript@5.9.3)(vite@7.3.0(@types/node@22.19.3)(yaml@2.8.2))
|
||||||
|
|
||||||
pong:
|
pong:
|
||||||
dependencies:
|
dependencies:
|
||||||
fastify:
|
fastify:
|
||||||
|
|
@ -224,6 +261,12 @@ importers:
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
|
'@borewit/text-codec@0.2.1':
|
||||||
|
resolution: {integrity: sha512-k7vvKPbf7J2fZ5klGRD9AeKfUvojuZIQ3BT5u7Jfv+puwXkUBUT5PVyMDfJZpy30CBDXGMgw7fguK/lpOMBvgw==}
|
||||||
|
|
||||||
|
'@emnapi/runtime@1.8.1':
|
||||||
|
resolution: {integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==}
|
||||||
|
|
||||||
'@esbuild/aix-ppc64@0.27.2':
|
'@esbuild/aix-ppc64@0.27.2':
|
||||||
resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==}
|
resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
@ -424,15 +467,24 @@ packages:
|
||||||
'@fastify/ajv-compiler@4.0.5':
|
'@fastify/ajv-compiler@4.0.5':
|
||||||
resolution: {integrity: sha512-KoWKW+MhvfTRWL4qrhUwAAZoaChluo0m0vbiJlGMt2GXvL4LVPQEjt8kSpHI3IBq5Rez8fg+XeH3cneztq+C7A==}
|
resolution: {integrity: sha512-KoWKW+MhvfTRWL4qrhUwAAZoaChluo0m0vbiJlGMt2GXvL4LVPQEjt8kSpHI3IBq5Rez8fg+XeH3cneztq+C7A==}
|
||||||
|
|
||||||
|
'@fastify/busboy@3.2.0':
|
||||||
|
resolution: {integrity: sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA==}
|
||||||
|
|
||||||
'@fastify/cookie@11.0.2':
|
'@fastify/cookie@11.0.2':
|
||||||
resolution: {integrity: sha512-GWdwdGlgJxyvNv+QcKiGNevSspMQXncjMZ1J8IvuDQk0jvkzgWWZFNC2En3s+nHndZBGV8IbLwOI/sxCZw/mzA==}
|
resolution: {integrity: sha512-GWdwdGlgJxyvNv+QcKiGNevSspMQXncjMZ1J8IvuDQk0jvkzgWWZFNC2En3s+nHndZBGV8IbLwOI/sxCZw/mzA==}
|
||||||
|
|
||||||
|
'@fastify/deepmerge@3.1.0':
|
||||||
|
resolution: {integrity: sha512-lCVONBQINyNhM6LLezB6+2afusgEYR4G8xenMsfe+AT+iZ7Ca6upM5Ha8UkZuYSnuMw3GWl/BiPXnLMi/gSxuQ==}
|
||||||
|
|
||||||
'@fastify/error@4.2.0':
|
'@fastify/error@4.2.0':
|
||||||
resolution: {integrity: sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ==}
|
resolution: {integrity: sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ==}
|
||||||
|
|
||||||
'@fastify/fast-json-stringify-compiler@5.0.3':
|
'@fastify/fast-json-stringify-compiler@5.0.3':
|
||||||
resolution: {integrity: sha512-uik7yYHkLr6fxd8hJSZ8c+xF4WafPK+XzneQDPU+D10r5X19GW8lJcom2YijX2+qtFF1ENJlHXKFM9ouXNJYgQ==}
|
resolution: {integrity: sha512-uik7yYHkLr6fxd8hJSZ8c+xF4WafPK+XzneQDPU+D10r5X19GW8lJcom2YijX2+qtFF1ENJlHXKFM9ouXNJYgQ==}
|
||||||
|
|
||||||
|
'@fastify/formbody@8.0.2':
|
||||||
|
resolution: {integrity: sha512-84v5J2KrkXzjgBpYnaNRPqwgMsmY7ZDjuj0YVuMR3NXCJRCgKEZy/taSP1wUYGn0onfxJpLyRGDLa+NMaDJtnA==}
|
||||||
|
|
||||||
'@fastify/forwarded@3.0.1':
|
'@fastify/forwarded@3.0.1':
|
||||||
resolution: {integrity: sha512-JqDochHFqXs3C3Ml3gOY58zM7OqO9ENqPo0UqAjAjH8L01fRZqwX9iLeX34//kiJubF7r2ZQHtBRU36vONbLlw==}
|
resolution: {integrity: sha512-JqDochHFqXs3C3Ml3gOY58zM7OqO9ENqPo0UqAjAjH8L01fRZqwX9iLeX34//kiJubF7r2ZQHtBRU36vONbLlw==}
|
||||||
|
|
||||||
|
|
@ -442,6 +494,9 @@ packages:
|
||||||
'@fastify/merge-json-schemas@0.2.1':
|
'@fastify/merge-json-schemas@0.2.1':
|
||||||
resolution: {integrity: sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A==}
|
resolution: {integrity: sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A==}
|
||||||
|
|
||||||
|
'@fastify/multipart@9.3.0':
|
||||||
|
resolution: {integrity: sha512-NpeKipTOjjL1dA7SSlRMrOWWtrE8/0yKOmeudkdQoEaz4sVDJw5MVdZIahsWhvpc3YTN7f04f9ep/Y65RKoOWA==}
|
||||||
|
|
||||||
'@fastify/proxy-addr@5.1.0':
|
'@fastify/proxy-addr@5.1.0':
|
||||||
resolution: {integrity: sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw==}
|
resolution: {integrity: sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw==}
|
||||||
|
|
||||||
|
|
@ -473,6 +528,143 @@ packages:
|
||||||
resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
|
resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
|
||||||
engines: {node: '>=18.18'}
|
engines: {node: '>=18.18'}
|
||||||
|
|
||||||
|
'@img/colour@1.0.0':
|
||||||
|
resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
'@img/sharp-darwin-arm64@0.34.5':
|
||||||
|
resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
'@img/sharp-darwin-x64@0.34.5':
|
||||||
|
resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
'@img/sharp-libvips-darwin-arm64@1.2.4':
|
||||||
|
resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
'@img/sharp-libvips-darwin-x64@1.2.4':
|
||||||
|
resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-arm64@1.2.4':
|
||||||
|
resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-arm@1.2.4':
|
||||||
|
resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-ppc64@1.2.4':
|
||||||
|
resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==}
|
||||||
|
cpu: [ppc64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-riscv64@1.2.4':
|
||||||
|
resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==}
|
||||||
|
cpu: [riscv64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-s390x@1.2.4':
|
||||||
|
resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==}
|
||||||
|
cpu: [s390x]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-x64@1.2.4':
|
||||||
|
resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linuxmusl-arm64@1.2.4':
|
||||||
|
resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linuxmusl-x64@1.2.4':
|
||||||
|
resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-linux-arm64@0.34.5':
|
||||||
|
resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-linux-arm@0.34.5':
|
||||||
|
resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-linux-ppc64@0.34.5':
|
||||||
|
resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [ppc64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-linux-riscv64@0.34.5':
|
||||||
|
resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [riscv64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-linux-s390x@0.34.5':
|
||||||
|
resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [s390x]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-linux-x64@0.34.5':
|
||||||
|
resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-linuxmusl-arm64@0.34.5':
|
||||||
|
resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-linuxmusl-x64@0.34.5':
|
||||||
|
resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-wasm32@0.34.5':
|
||||||
|
resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [wasm32]
|
||||||
|
|
||||||
|
'@img/sharp-win32-arm64@0.34.5':
|
||||||
|
resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
'@img/sharp-win32-ia32@0.34.5':
|
||||||
|
resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [ia32]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
'@img/sharp-win32-x64@0.34.5':
|
||||||
|
resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
'@isaacs/balanced-match@4.0.1':
|
'@isaacs/balanced-match@4.0.1':
|
||||||
resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==}
|
resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==}
|
||||||
engines: {node: 20 || >=22}
|
engines: {node: 20 || >=22}
|
||||||
|
|
@ -605,6 +797,13 @@ packages:
|
||||||
'@socket.io/component-emitter@3.1.2':
|
'@socket.io/component-emitter@3.1.2':
|
||||||
resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==}
|
resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==}
|
||||||
|
|
||||||
|
'@tokenizer/inflate@0.4.1':
|
||||||
|
resolution: {integrity: sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
'@tokenizer/token@0.3.0':
|
||||||
|
resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==}
|
||||||
|
|
||||||
'@types/bcrypt@6.0.0':
|
'@types/bcrypt@6.0.0':
|
||||||
resolution: {integrity: sha512-/oJGukuH3D2+D+3H4JWLaAsJ/ji86dhRidzZ/Od7H/i8g+aCmvkeCc6Ni/f9uxGLSQVCRZkX2/lqEFG2BvWtlQ==}
|
resolution: {integrity: sha512-/oJGukuH3D2+D+3H4JWLaAsJ/ji86dhRidzZ/Od7H/i8g+aCmvkeCc6Ni/f9uxGLSQVCRZkX2/lqEFG2BvWtlQ==}
|
||||||
|
|
||||||
|
|
@ -1042,6 +1241,10 @@ packages:
|
||||||
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
|
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
|
||||||
engines: {node: '>=16.0.0'}
|
engines: {node: '>=16.0.0'}
|
||||||
|
|
||||||
|
file-type@21.3.0:
|
||||||
|
resolution: {integrity: sha512-8kPJMIGz1Yt/aPEwOsrR97ZyZaD1Iqm8PClb1nYFclUCkBi0Ma5IsYNQzvSFS9ib51lWyIw5mIT9rWzI/xjpzA==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
file-uri-to-path@1.0.0:
|
file-uri-to-path@1.0.0:
|
||||||
resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==}
|
resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==}
|
||||||
|
|
||||||
|
|
@ -1497,6 +1700,10 @@ packages:
|
||||||
sha1@1.1.1:
|
sha1@1.1.1:
|
||||||
resolution: {integrity: sha512-dZBS6OrMjtgVkopB1Gmo4RQCDKiZsqcpAQpkV/aaj+FCrCg8r4I4qMkDPQjBgLIxlmu9k4nUbWq6ohXahOneYA==}
|
resolution: {integrity: sha512-dZBS6OrMjtgVkopB1Gmo4RQCDKiZsqcpAQpkV/aaj+FCrCg8r4I4qMkDPQjBgLIxlmu9k4nUbWq6ohXahOneYA==}
|
||||||
|
|
||||||
|
sharp@0.34.5:
|
||||||
|
resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
|
||||||
shebang-command@2.0.0:
|
shebang-command@2.0.0:
|
||||||
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
|
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
@ -1587,6 +1794,10 @@ packages:
|
||||||
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
|
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
strtok3@10.3.4:
|
||||||
|
resolution: {integrity: sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
supports-color@7.2.0:
|
supports-color@7.2.0:
|
||||||
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
|
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
@ -1617,6 +1828,10 @@ packages:
|
||||||
resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
|
resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
|
||||||
engines: {node: '>=0.6'}
|
engines: {node: '>=0.6'}
|
||||||
|
|
||||||
|
token-types@6.1.2:
|
||||||
|
resolution: {integrity: sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==}
|
||||||
|
engines: {node: '>=14.16'}
|
||||||
|
|
||||||
ts-api-utils@2.3.0:
|
ts-api-utils@2.3.0:
|
||||||
resolution: {integrity: sha512-6eg3Y9SF7SsAvGzRHQvvc1skDAhwI4YQ32ui1scxD1Ccr0G5qIIbUBT3pFTKX8kmWIQClHobtUdNuaBgwdfdWg==}
|
resolution: {integrity: sha512-6eg3Y9SF7SsAvGzRHQvvc1skDAhwI4YQ32ui1scxD1Ccr0G5qIIbUBT3pFTKX8kmWIQClHobtUdNuaBgwdfdWg==}
|
||||||
engines: {node: '>=18.12'}
|
engines: {node: '>=18.12'}
|
||||||
|
|
@ -1633,6 +1848,9 @@ packages:
|
||||||
typescript:
|
typescript:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
tslib@2.8.1:
|
||||||
|
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
||||||
|
|
||||||
tunnel-agent@0.6.0:
|
tunnel-agent@0.6.0:
|
||||||
resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
|
resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
|
||||||
|
|
||||||
|
|
@ -1655,6 +1873,10 @@ packages:
|
||||||
engines: {node: '>=14.17'}
|
engines: {node: '>=14.17'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
uint8array-extras@1.5.0:
|
||||||
|
resolution: {integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
undici-types@6.21.0:
|
undici-types@6.21.0:
|
||||||
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
|
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
|
||||||
|
|
||||||
|
|
@ -1774,6 +1996,13 @@ packages:
|
||||||
|
|
||||||
snapshots:
|
snapshots:
|
||||||
|
|
||||||
|
'@borewit/text-codec@0.2.1': {}
|
||||||
|
|
||||||
|
'@emnapi/runtime@1.8.1':
|
||||||
|
dependencies:
|
||||||
|
tslib: 2.8.1
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@esbuild/aix-ppc64@0.27.2':
|
'@esbuild/aix-ppc64@0.27.2':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
|
@ -1906,17 +2135,26 @@ snapshots:
|
||||||
ajv-formats: 3.0.1(ajv@8.17.1)
|
ajv-formats: 3.0.1(ajv@8.17.1)
|
||||||
fast-uri: 3.1.0
|
fast-uri: 3.1.0
|
||||||
|
|
||||||
|
'@fastify/busboy@3.2.0': {}
|
||||||
|
|
||||||
'@fastify/cookie@11.0.2':
|
'@fastify/cookie@11.0.2':
|
||||||
dependencies:
|
dependencies:
|
||||||
cookie: 1.1.1
|
cookie: 1.1.1
|
||||||
fastify-plugin: 5.1.0
|
fastify-plugin: 5.1.0
|
||||||
|
|
||||||
|
'@fastify/deepmerge@3.1.0': {}
|
||||||
|
|
||||||
'@fastify/error@4.2.0': {}
|
'@fastify/error@4.2.0': {}
|
||||||
|
|
||||||
'@fastify/fast-json-stringify-compiler@5.0.3':
|
'@fastify/fast-json-stringify-compiler@5.0.3':
|
||||||
dependencies:
|
dependencies:
|
||||||
fast-json-stringify: 6.1.1
|
fast-json-stringify: 6.1.1
|
||||||
|
|
||||||
|
'@fastify/formbody@8.0.2':
|
||||||
|
dependencies:
|
||||||
|
fast-querystring: 1.1.2
|
||||||
|
fastify-plugin: 5.1.0
|
||||||
|
|
||||||
'@fastify/forwarded@3.0.1': {}
|
'@fastify/forwarded@3.0.1': {}
|
||||||
|
|
||||||
'@fastify/jwt@9.1.0':
|
'@fastify/jwt@9.1.0':
|
||||||
|
|
@ -1931,6 +2169,14 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
dequal: 2.0.3
|
dequal: 2.0.3
|
||||||
|
|
||||||
|
'@fastify/multipart@9.3.0':
|
||||||
|
dependencies:
|
||||||
|
'@fastify/busboy': 3.2.0
|
||||||
|
'@fastify/deepmerge': 3.1.0
|
||||||
|
'@fastify/error': 4.2.0
|
||||||
|
fastify-plugin: 5.1.0
|
||||||
|
secure-json-parse: 4.1.0
|
||||||
|
|
||||||
'@fastify/proxy-addr@5.1.0':
|
'@fastify/proxy-addr@5.1.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@fastify/forwarded': 3.0.1
|
'@fastify/forwarded': 3.0.1
|
||||||
|
|
@ -1982,6 +2228,102 @@ snapshots:
|
||||||
|
|
||||||
'@humanwhocodes/retry@0.4.3': {}
|
'@humanwhocodes/retry@0.4.3': {}
|
||||||
|
|
||||||
|
'@img/colour@1.0.0': {}
|
||||||
|
|
||||||
|
'@img/sharp-darwin-arm64@0.34.5':
|
||||||
|
optionalDependencies:
|
||||||
|
'@img/sharp-libvips-darwin-arm64': 1.2.4
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-darwin-x64@0.34.5':
|
||||||
|
optionalDependencies:
|
||||||
|
'@img/sharp-libvips-darwin-x64': 1.2.4
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-libvips-darwin-arm64@1.2.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-libvips-darwin-x64@1.2.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-arm64@1.2.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-arm@1.2.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-ppc64@1.2.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-riscv64@1.2.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-s390x@1.2.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-x64@1.2.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linuxmusl-arm64@1.2.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linuxmusl-x64@1.2.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-linux-arm64@0.34.5':
|
||||||
|
optionalDependencies:
|
||||||
|
'@img/sharp-libvips-linux-arm64': 1.2.4
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-linux-arm@0.34.5':
|
||||||
|
optionalDependencies:
|
||||||
|
'@img/sharp-libvips-linux-arm': 1.2.4
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-linux-ppc64@0.34.5':
|
||||||
|
optionalDependencies:
|
||||||
|
'@img/sharp-libvips-linux-ppc64': 1.2.4
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-linux-riscv64@0.34.5':
|
||||||
|
optionalDependencies:
|
||||||
|
'@img/sharp-libvips-linux-riscv64': 1.2.4
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-linux-s390x@0.34.5':
|
||||||
|
optionalDependencies:
|
||||||
|
'@img/sharp-libvips-linux-s390x': 1.2.4
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-linux-x64@0.34.5':
|
||||||
|
optionalDependencies:
|
||||||
|
'@img/sharp-libvips-linux-x64': 1.2.4
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-linuxmusl-arm64@0.34.5':
|
||||||
|
optionalDependencies:
|
||||||
|
'@img/sharp-libvips-linuxmusl-arm64': 1.2.4
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-linuxmusl-x64@0.34.5':
|
||||||
|
optionalDependencies:
|
||||||
|
'@img/sharp-libvips-linuxmusl-x64': 1.2.4
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-wasm32@0.34.5':
|
||||||
|
dependencies:
|
||||||
|
'@emnapi/runtime': 1.8.1
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-win32-arm64@0.34.5':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-win32-ia32@0.34.5':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-win32-x64@0.34.5':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@isaacs/balanced-match@4.0.1': {}
|
'@isaacs/balanced-match@4.0.1': {}
|
||||||
|
|
||||||
'@isaacs/brace-expansion@5.0.0':
|
'@isaacs/brace-expansion@5.0.0':
|
||||||
|
|
@ -2069,6 +2411,15 @@ snapshots:
|
||||||
|
|
||||||
'@socket.io/component-emitter@3.1.2': {}
|
'@socket.io/component-emitter@3.1.2': {}
|
||||||
|
|
||||||
|
'@tokenizer/inflate@0.4.1':
|
||||||
|
dependencies:
|
||||||
|
debug: 4.4.3
|
||||||
|
token-types: 6.1.2
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
'@tokenizer/token@0.3.0': {}
|
||||||
|
|
||||||
'@types/bcrypt@6.0.0':
|
'@types/bcrypt@6.0.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 22.19.3
|
'@types/node': 22.19.3
|
||||||
|
|
@ -2584,6 +2935,15 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
flat-cache: 4.0.1
|
flat-cache: 4.0.1
|
||||||
|
|
||||||
|
file-type@21.3.0:
|
||||||
|
dependencies:
|
||||||
|
'@tokenizer/inflate': 0.4.1
|
||||||
|
strtok3: 10.3.4
|
||||||
|
token-types: 6.1.2
|
||||||
|
uint8array-extras: 1.5.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
file-uri-to-path@1.0.0: {}
|
file-uri-to-path@1.0.0: {}
|
||||||
|
|
||||||
fill-range@7.1.1:
|
fill-range@7.1.1:
|
||||||
|
|
@ -3026,6 +3386,37 @@ snapshots:
|
||||||
charenc: 0.0.2
|
charenc: 0.0.2
|
||||||
crypt: 0.0.2
|
crypt: 0.0.2
|
||||||
|
|
||||||
|
sharp@0.34.5:
|
||||||
|
dependencies:
|
||||||
|
'@img/colour': 1.0.0
|
||||||
|
detect-libc: 2.1.2
|
||||||
|
semver: 7.7.3
|
||||||
|
optionalDependencies:
|
||||||
|
'@img/sharp-darwin-arm64': 0.34.5
|
||||||
|
'@img/sharp-darwin-x64': 0.34.5
|
||||||
|
'@img/sharp-libvips-darwin-arm64': 1.2.4
|
||||||
|
'@img/sharp-libvips-darwin-x64': 1.2.4
|
||||||
|
'@img/sharp-libvips-linux-arm': 1.2.4
|
||||||
|
'@img/sharp-libvips-linux-arm64': 1.2.4
|
||||||
|
'@img/sharp-libvips-linux-ppc64': 1.2.4
|
||||||
|
'@img/sharp-libvips-linux-riscv64': 1.2.4
|
||||||
|
'@img/sharp-libvips-linux-s390x': 1.2.4
|
||||||
|
'@img/sharp-libvips-linux-x64': 1.2.4
|
||||||
|
'@img/sharp-libvips-linuxmusl-arm64': 1.2.4
|
||||||
|
'@img/sharp-libvips-linuxmusl-x64': 1.2.4
|
||||||
|
'@img/sharp-linux-arm': 0.34.5
|
||||||
|
'@img/sharp-linux-arm64': 0.34.5
|
||||||
|
'@img/sharp-linux-ppc64': 0.34.5
|
||||||
|
'@img/sharp-linux-riscv64': 0.34.5
|
||||||
|
'@img/sharp-linux-s390x': 0.34.5
|
||||||
|
'@img/sharp-linux-x64': 0.34.5
|
||||||
|
'@img/sharp-linuxmusl-arm64': 0.34.5
|
||||||
|
'@img/sharp-linuxmusl-x64': 0.34.5
|
||||||
|
'@img/sharp-wasm32': 0.34.5
|
||||||
|
'@img/sharp-win32-arm64': 0.34.5
|
||||||
|
'@img/sharp-win32-ia32': 0.34.5
|
||||||
|
'@img/sharp-win32-x64': 0.34.5
|
||||||
|
|
||||||
shebang-command@2.0.0:
|
shebang-command@2.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
shebang-regex: 3.0.0
|
shebang-regex: 3.0.0
|
||||||
|
|
@ -3136,6 +3527,10 @@ snapshots:
|
||||||
|
|
||||||
strip-json-comments@3.1.1: {}
|
strip-json-comments@3.1.1: {}
|
||||||
|
|
||||||
|
strtok3@10.3.4:
|
||||||
|
dependencies:
|
||||||
|
'@tokenizer/token': 0.3.0
|
||||||
|
|
||||||
supports-color@7.2.0:
|
supports-color@7.2.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
has-flag: 4.0.0
|
has-flag: 4.0.0
|
||||||
|
|
@ -3172,6 +3567,12 @@ snapshots:
|
||||||
|
|
||||||
toidentifier@1.0.1: {}
|
toidentifier@1.0.1: {}
|
||||||
|
|
||||||
|
token-types@6.1.2:
|
||||||
|
dependencies:
|
||||||
|
'@borewit/text-codec': 0.2.1
|
||||||
|
'@tokenizer/token': 0.3.0
|
||||||
|
ieee754: 1.2.1
|
||||||
|
|
||||||
ts-api-utils@2.3.0(typescript@5.9.3):
|
ts-api-utils@2.3.0(typescript@5.9.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
typescript: 5.9.3
|
typescript: 5.9.3
|
||||||
|
|
@ -3180,6 +3581,9 @@ snapshots:
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
typescript: 5.9.3
|
typescript: 5.9.3
|
||||||
|
|
||||||
|
tslib@2.8.1:
|
||||||
|
optional: true
|
||||||
|
|
||||||
tunnel-agent@0.6.0:
|
tunnel-agent@0.6.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
safe-buffer: 5.2.1
|
safe-buffer: 5.2.1
|
||||||
|
|
@ -3203,6 +3607,8 @@ snapshots:
|
||||||
|
|
||||||
typescript@5.9.3: {}
|
typescript@5.9.3: {}
|
||||||
|
|
||||||
|
uint8array-extras@1.5.0: {}
|
||||||
|
|
||||||
undici-types@6.21.0: {}
|
undici-types@6.21.0: {}
|
||||||
|
|
||||||
undici-types@7.16.0:
|
undici-types@7.16.0:
|
||||||
|
|
|
||||||
|
|
@ -9,3 +9,4 @@ onlyBuiltDependencies:
|
||||||
- core-js
|
- core-js
|
||||||
- esbuild
|
- esbuild
|
||||||
- protobufjs
|
- protobufjs
|
||||||
|
- sharp
|
||||||
|
|
|
||||||
|
|
@ -160,8 +160,20 @@ class StateI {
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (user.currentGame !== null) {
|
||||||
this.dequeueUser(user.socket);
|
sock.emit('tournamentRegister', {
|
||||||
|
kind: 'failure',
|
||||||
|
msg: 'You are in game',
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.queue.has(user.id)) {
|
||||||
|
sock.emit('tournamentRegister', {
|
||||||
|
kind: 'failure',
|
||||||
|
msg: 'You are in queue',
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.tournament.addUser(user.id, name ?? udb.name);
|
this.tournament.addUser(user.id, name ?? udb.name);
|
||||||
sock.emit('tournamentRegister', {
|
sock.emit('tournamentRegister', {
|
||||||
kind: 'success',
|
kind: 'success',
|
||||||
|
|
@ -279,6 +291,14 @@ class StateI {
|
||||||
const gameId = newUUID() as unknown as GameId;
|
const gameId = newUUID() as unknown as GameId;
|
||||||
|
|
||||||
this.games.set(gameId, g);
|
this.games.set(gameId, g);
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!g.ready_checks[0] && !g.ready_checks[1]) {
|
||||||
|
this.fastify.log.info(
|
||||||
|
`paused game ${gameId} has been canceled`,
|
||||||
|
);
|
||||||
|
this.cleanupGame(gameId, g);
|
||||||
|
}
|
||||||
|
}, 1000 * 60);
|
||||||
this.fastify.log.info('new paused game \'' + gameId + '\'');
|
this.fastify.log.info('new paused game \'' + gameId + '\'');
|
||||||
return gameId;
|
return gameId;
|
||||||
}
|
}
|
||||||
|
|
@ -438,13 +458,19 @@ class StateI {
|
||||||
) {
|
) {
|
||||||
this.fastify.log.warn(
|
this.fastify.log.warn(
|
||||||
'user trying to connect to a game he\'s not part of: gameId:' +
|
'user trying to connect to a game he\'s not part of: gameId:' +
|
||||||
g_id + ' userId:' + sock.authUser.id);
|
g_id +
|
||||||
|
' userId:' +
|
||||||
|
sock.authUser.id,
|
||||||
|
);
|
||||||
return JoinRes.no;
|
return JoinRes.no;
|
||||||
}
|
}
|
||||||
if (game.userOnPage[0] === true && game.userOnPage[1] === true) {
|
if (game.userOnPage[0] === true && game.userOnPage[1] === true) {
|
||||||
this.fastify.log.warn(
|
this.fastify.log.warn(
|
||||||
'user trying to connect to a game he\'s already joined: gameId:' +
|
'user trying to connect to a game he\'s already joined: gameId:' +
|
||||||
g_id + ' userId:' + sock.authUser.id);
|
g_id +
|
||||||
|
' userId:' +
|
||||||
|
sock.authUser.id,
|
||||||
|
);
|
||||||
return JoinRes.no;
|
return JoinRes.no;
|
||||||
}
|
}
|
||||||
game.userOnPage[game.userLeft === sock.authUser.id ? 0 : 1] = true;
|
game.userOnPage[game.userLeft === sock.authUser.id ? 0 : 1] = true;
|
||||||
|
|
@ -573,7 +599,7 @@ class StateI {
|
||||||
game.local,
|
game.local,
|
||||||
);
|
);
|
||||||
this.fastify.log.info('SetGameOutcome !');
|
this.fastify.log.info('SetGameOutcome !');
|
||||||
if (!game.local) {
|
if (!game.local && game.ready_checks[0] && game.ready_checks[1]) {
|
||||||
const payload = { nextGame: chat_text };
|
const payload = { nextGame: chat_text };
|
||||||
try {
|
try {
|
||||||
const resp = await fetch('http://app-chat/broadcastNextGame', {
|
const resp = await fetch('http://app-chat/broadcastNextGame', {
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@ export class Tournament {
|
||||||
const [u1, u2] = matchup;
|
const [u1, u2] = matchup;
|
||||||
const gameId = newUUID() as PongGameId;
|
const gameId = newUUID() as PongGameId;
|
||||||
const game = State.initGame(null, gameId, u1, u2);
|
const game = State.initGame(null, gameId, u1, u2);
|
||||||
|
State.broadcastTourStatus(`A Tournament game between ${this.users.get(u1)?.name ?? 'the left player'} and ${this.users.get(u2)?.name ?? 'the right player'} will start ASAP`);
|
||||||
if (game) {
|
if (game) {
|
||||||
game.onEnd = () => this.gameEnd();
|
game.onEnd = () => this.gameEnd();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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}": {
|
"/api/user/info/{user}": {
|
||||||
"get": {
|
"get": {
|
||||||
"operationId": "getUser",
|
"operationId": "getUser",
|
||||||
|
|
|
||||||
44
src/user/src/routes/friendAdd.ts
Normal file
44
src/user/src/routes/friendAdd.ts
Normal file
|
|
@ -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<typeof AddFriendRes>;
|
||||||
|
|
||||||
|
const AddFriendParams = Type.Object({
|
||||||
|
user: Type.String(),
|
||||||
|
});
|
||||||
|
export type AddFriendParams = Static<typeof AddFriendParams>;
|
||||||
|
|
||||||
|
const route: FastifyPluginAsync = async (fastify, _opts): Promise<void> => {
|
||||||
|
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;
|
||||||
40
src/user/src/routes/friendList.ts
Normal file
40
src/user/src/routes/friendList.ts
Normal file
|
|
@ -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<typeof ListFriendRes>;
|
||||||
|
|
||||||
|
const RemoveFriendParams = Type.Object({
|
||||||
|
user: Type.String(),
|
||||||
|
});
|
||||||
|
export type RemoveFriendParams = Static<typeof RemoveFriendParams>;
|
||||||
|
|
||||||
|
const route: FastifyPluginAsync = async (fastify, _opts): Promise<void> => {
|
||||||
|
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;
|
||||||
44
src/user/src/routes/friendRemove.ts
Normal file
44
src/user/src/routes/friendRemove.ts
Normal file
|
|
@ -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<typeof RemoveFriendRes>;
|
||||||
|
|
||||||
|
const RemoveFriendParams = Type.Object({
|
||||||
|
user: Type.String(),
|
||||||
|
});
|
||||||
|
export type RemoveFriendParams = Static<typeof RemoveFriendParams>;
|
||||||
|
|
||||||
|
const route: FastifyPluginAsync = async (fastify, _opts): Promise<void> => {
|
||||||
|
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;
|
||||||
Loading…
Add table
Add a link
Reference in a new issue