removed icon server and finished profile page

This commit is contained in:
Maieul BOYER 2025-12-10 17:06:53 +01:00
parent dbe323c77e
commit 763d0c38cc
No known key found for this signature in database
26 changed files with 881 additions and 325 deletions

View file

@ -48,33 +48,6 @@ services:
gelf-address: "udp://127.0.0.1:12201" gelf-address: "udp://127.0.0.1:12201"
tag: "{{.Name}}" tag: "{{.Name}}"
###############
# ICONS #
###############
icons:
build:
context: ./src/
args:
- SERVICE=icons
#- EXTRA_FILES=icons/extra
container_name: icons
restart: always
networks:
- transcendance-network
volumes:
- images-volume:/volumes/store
- sqlite-volume:/volumes/database
environment:
- JWT_SECRET=KRUGKIDROVUWG2ZAMJZG653OEBTG66BANJ2W24DTEBXXMZLSEB2GQZJANRQXU6JA
- USER_ICONS_STORE=/volumes/store
- DATABASE_DIR=/volumes/database
logging:
driver: gelf
options:
gelf-address: "udp://127.0.0.1:12201"
tag: "{{.Name}}"
############### ###############
# AUTH # # AUTH #
############### ###############

View file

@ -4,11 +4,15 @@ index.ts
models/ChangeDisplayName200Response.ts models/ChangeDisplayName200Response.ts
models/ChangeDisplayName400Response.ts models/ChangeDisplayName400Response.ts
models/ChangeDisplayNameRequest.ts models/ChangeDisplayNameRequest.ts
models/ChangePassword200Response.ts
models/ChangePassword400Response.ts
models/ChangePassword401Response.ts
models/ChangePassword500Response.ts
models/ChangePasswordRequest.ts
models/ChatTest200Response.ts models/ChatTest200Response.ts
models/ChatTest200ResponsePayload.ts models/ChatTest200ResponsePayload.ts
models/DisableOtp200Response.ts models/DisableOtp200Response.ts
models/DisableOtp400Response.ts models/DisableOtp400Response.ts
models/DisableOtp401Response.ts
models/DisableOtp500Response.ts models/DisableOtp500Response.ts
models/EnableOtp200Response.ts models/EnableOtp200Response.ts
models/EnableOtp200ResponsePayload.ts models/EnableOtp200ResponsePayload.ts

View file

@ -18,10 +18,14 @@ import type {
ChangeDisplayName200Response, ChangeDisplayName200Response,
ChangeDisplayName400Response, ChangeDisplayName400Response,
ChangeDisplayNameRequest, ChangeDisplayNameRequest,
ChangePassword200Response,
ChangePassword400Response,
ChangePassword401Response,
ChangePassword500Response,
ChangePasswordRequest,
ChatTest200Response, ChatTest200Response,
DisableOtp200Response, DisableOtp200Response,
DisableOtp400Response, DisableOtp400Response,
DisableOtp401Response,
DisableOtp500Response, DisableOtp500Response,
EnableOtp200Response, EnableOtp200Response,
EnableOtp400Response, EnableOtp400Response,
@ -60,14 +64,22 @@ import {
ChangeDisplayName400ResponseToJSON, ChangeDisplayName400ResponseToJSON,
ChangeDisplayNameRequestFromJSON, ChangeDisplayNameRequestFromJSON,
ChangeDisplayNameRequestToJSON, ChangeDisplayNameRequestToJSON,
ChangePassword200ResponseFromJSON,
ChangePassword200ResponseToJSON,
ChangePassword400ResponseFromJSON,
ChangePassword400ResponseToJSON,
ChangePassword401ResponseFromJSON,
ChangePassword401ResponseToJSON,
ChangePassword500ResponseFromJSON,
ChangePassword500ResponseToJSON,
ChangePasswordRequestFromJSON,
ChangePasswordRequestToJSON,
ChatTest200ResponseFromJSON, ChatTest200ResponseFromJSON,
ChatTest200ResponseToJSON, ChatTest200ResponseToJSON,
DisableOtp200ResponseFromJSON, DisableOtp200ResponseFromJSON,
DisableOtp200ResponseToJSON, DisableOtp200ResponseToJSON,
DisableOtp400ResponseFromJSON, DisableOtp400ResponseFromJSON,
DisableOtp400ResponseToJSON, DisableOtp400ResponseToJSON,
DisableOtp401ResponseFromJSON,
DisableOtp401ResponseToJSON,
DisableOtp500ResponseFromJSON, DisableOtp500ResponseFromJSON,
DisableOtp500ResponseToJSON, DisableOtp500ResponseToJSON,
EnableOtp200ResponseFromJSON, EnableOtp200ResponseFromJSON,
@ -134,6 +146,10 @@ export interface ChangeDisplayNameOperationRequest {
changeDisplayNameRequest: ChangeDisplayNameRequest; changeDisplayNameRequest: ChangeDisplayNameRequest;
} }
export interface ChangePasswordOperationRequest {
changePasswordRequest: ChangePasswordRequest;
}
export interface GetUserRequest { export interface GetUserRequest {
user: GetUserUserParameter; user: GetUserUserParameter;
} }
@ -161,7 +177,7 @@ export class OpenapiOtherApi extends runtime.BaseAPI {
/** /**
*/ */
async changeDisplayNameRaw(requestParameters: ChangeDisplayNameOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<ChangeDisplayName200Response | ChangeDisplayName400Response | DisableOtp401Response>> { async changeDisplayNameRaw(requestParameters: ChangeDisplayNameOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<ChangeDisplayName200Response | ChangeDisplayName400Response | ChangePassword401Response>> {
if (requestParameters['changeDisplayNameRequest'] == null) { if (requestParameters['changeDisplayNameRequest'] == null) {
throw new runtime.RequiredError( throw new runtime.RequiredError(
'changeDisplayNameRequest', 'changeDisplayNameRequest',
@ -200,7 +216,7 @@ export class OpenapiOtherApi extends runtime.BaseAPI {
} }
if (response.status === 401) { if (response.status === 401) {
// Object response for status 401 // Object response for status 401
return new runtime.JSONApiResponse(response, (jsonValue) => DisableOtp401ResponseFromJSON(jsonValue)); return new runtime.JSONApiResponse(response, (jsonValue) => ChangePassword401ResponseFromJSON(jsonValue));
} }
// CHANGED: Throw error if status code is not handled by any of the defined responses // 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 // This ensures all code paths return a value and provides clear error messages for unexpected status codes
@ -210,11 +226,71 @@ export class OpenapiOtherApi extends runtime.BaseAPI {
/** /**
*/ */
async changeDisplayName(requestParameters: ChangeDisplayNameOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<ChangeDisplayName200Response | ChangeDisplayName400Response | DisableOtp401Response> { async changeDisplayName(requestParameters: ChangeDisplayNameOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<ChangeDisplayName200Response | ChangeDisplayName400Response | ChangePassword401Response> {
const response = await this.changeDisplayNameRaw(requestParameters, initOverrides); const response = await this.changeDisplayNameRaw(requestParameters, initOverrides);
return await response.value(); return await response.value();
} }
/**
*/
async changePasswordRaw(requestParameters: ChangePasswordOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<ChangePassword200Response | ChangePassword400Response | ChangePassword401Response | ChangePassword500Response>> {
if (requestParameters['changePasswordRequest'] == null) {
throw new runtime.RequiredError(
'changePasswordRequest',
'Required parameter "changePasswordRequest" was null or undefined when calling changePassword().'
);
}
const queryParameters: any = {};
const headerParameters: runtime.HTTPHeaders = {};
headerParameters['Content-Type'] = 'application/json';
let urlPath = `/api/auth/changePassword`;
const response = await this.request({
path: urlPath,
method: 'POST',
headers: headerParameters,
query: queryParameters,
body: ChangePasswordRequestToJSON(requestParameters['changePasswordRequest']),
}, 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) => ChangePassword200ResponseFromJSON(jsonValue));
}
if (response.status === 400) {
// Object response for status 400
return new runtime.JSONApiResponse(response, (jsonValue) => ChangePassword400ResponseFromJSON(jsonValue));
}
if (response.status === 401) {
// Object response for status 401
return new runtime.JSONApiResponse(response, (jsonValue) => ChangePassword401ResponseFromJSON(jsonValue));
}
if (response.status === 500) {
// Object response for status 500
return new runtime.JSONApiResponse(response, (jsonValue) => ChangePassword500ResponseFromJSON(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, 400, 401, 500`);
}
/**
*/
async changePassword(requestParameters: ChangePasswordOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<ChangePassword200Response | ChangePassword400Response | ChangePassword401Response | ChangePassword500Response> {
const response = await this.changePasswordRaw(requestParameters, initOverrides);
return await response.value();
}
/** /**
*/ */
async chatTestRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<ChatTest200Response | StatusOtp401Response>> { async chatTestRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<ChatTest200Response | StatusOtp401Response>> {
@ -259,7 +335,7 @@ export class OpenapiOtherApi extends runtime.BaseAPI {
/** /**
*/ */
async disableOtpRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<DisableOtp200Response | DisableOtp400Response | DisableOtp401Response | DisableOtp500Response>> { async disableOtpRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<DisableOtp200Response | DisableOtp400Response | ChangePassword401Response | DisableOtp500Response>> {
const queryParameters: any = {}; const queryParameters: any = {};
const headerParameters: runtime.HTTPHeaders = {}; const headerParameters: runtime.HTTPHeaders = {};
@ -288,7 +364,7 @@ export class OpenapiOtherApi extends runtime.BaseAPI {
} }
if (response.status === 401) { if (response.status === 401) {
// Object response for status 401 // Object response for status 401
return new runtime.JSONApiResponse(response, (jsonValue) => DisableOtp401ResponseFromJSON(jsonValue)); return new runtime.JSONApiResponse(response, (jsonValue) => ChangePassword401ResponseFromJSON(jsonValue));
} }
if (response.status === 500) { if (response.status === 500) {
// Object response for status 500 // Object response for status 500
@ -302,7 +378,7 @@ export class OpenapiOtherApi extends runtime.BaseAPI {
/** /**
*/ */
async disableOtp(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<DisableOtp200Response | DisableOtp400Response | DisableOtp401Response | DisableOtp500Response> { async disableOtp(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<DisableOtp200Response | DisableOtp400Response | ChangePassword401Response | DisableOtp500Response> {
const response = await this.disableOtpRaw(initOverrides); const response = await this.disableOtpRaw(initOverrides);
return await response.value(); return await response.value();
} }

View 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 ChangePassword200Response
*/
export interface ChangePassword200Response {
/**
*
* @type {string}
* @memberof ChangePassword200Response
*/
kind: ChangePassword200ResponseKindEnum;
/**
*
* @type {string}
* @memberof ChangePassword200Response
*/
msg: ChangePassword200ResponseMsgEnum;
}
/**
* @export
*/
export const ChangePassword200ResponseKindEnum = {
Success: 'success'
} as const;
export type ChangePassword200ResponseKindEnum = typeof ChangePassword200ResponseKindEnum[keyof typeof ChangePassword200ResponseKindEnum];
/**
* @export
*/
export const ChangePassword200ResponseMsgEnum = {
ChangePasswordSuccess: 'changePassword.success'
} as const;
export type ChangePassword200ResponseMsgEnum = typeof ChangePassword200ResponseMsgEnum[keyof typeof ChangePassword200ResponseMsgEnum];
/**
* Check if a given object implements the ChangePassword200Response interface.
*/
export function instanceOfChangePassword200Response(value: object): value is ChangePassword200Response {
if (!('kind' in value) || value['kind'] === undefined) return false;
if (!('msg' in value) || value['msg'] === undefined) return false;
return true;
}
export function ChangePassword200ResponseFromJSON(json: any): ChangePassword200Response {
return ChangePassword200ResponseFromJSONTyped(json, false);
}
export function ChangePassword200ResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): ChangePassword200Response {
if (json == null) {
return json;
}
return {
'kind': json['kind'],
'msg': json['msg'],
};
}
export function ChangePassword200ResponseToJSON(json: any): ChangePassword200Response {
return ChangePassword200ResponseToJSONTyped(json, false);
}
export function ChangePassword200ResponseToJSONTyped(value?: ChangePassword200Response | null, ignoreDiscriminator: boolean = false): any {
if (value == null) {
return value;
}
return {
'kind': value['kind'],
'msg': value['msg'],
};
}

View file

@ -0,0 +1,95 @@
/* 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 ChangePassword400Response
*/
export interface ChangePassword400Response {
/**
*
* @type {string}
* @memberof ChangePassword400Response
*/
kind: ChangePassword400ResponseKindEnum;
/**
*
* @type {string}
* @memberof ChangePassword400Response
*/
msg: ChangePassword400ResponseMsgEnum;
}
/**
* @export
*/
export const ChangePassword400ResponseKindEnum = {
Failed: 'failed'
} as const;
export type ChangePassword400ResponseKindEnum = typeof ChangePassword400ResponseKindEnum[keyof typeof ChangePassword400ResponseKindEnum];
/**
* @export
*/
export const ChangePassword400ResponseMsgEnum = {
ChangePasswordFailedToolong: 'changePassword.failed.toolong',
ChangePasswordFailedTooshort: 'changePassword.failed.tooshort',
ChangePasswordFailedInvalid: 'changePassword.failed.invalid'
} as const;
export type ChangePassword400ResponseMsgEnum = typeof ChangePassword400ResponseMsgEnum[keyof typeof ChangePassword400ResponseMsgEnum];
/**
* Check if a given object implements the ChangePassword400Response interface.
*/
export function instanceOfChangePassword400Response(value: object): value is ChangePassword400Response {
if (!('kind' in value) || value['kind'] === undefined) return false;
if (!('msg' in value) || value['msg'] === undefined) return false;
return true;
}
export function ChangePassword400ResponseFromJSON(json: any): ChangePassword400Response {
return ChangePassword400ResponseFromJSONTyped(json, false);
}
export function ChangePassword400ResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): ChangePassword400Response {
if (json == null) {
return json;
}
return {
'kind': json['kind'],
'msg': json['msg'],
};
}
export function ChangePassword400ResponseToJSON(json: any): ChangePassword400Response {
return ChangePassword400ResponseToJSONTyped(json, false);
}
export function ChangePassword400ResponseToJSONTyped(value?: ChangePassword400Response | null, ignoreDiscriminator: boolean = false): any {
if (value == null) {
return value;
}
return {
'kind': value['kind'],
'msg': value['msg'],
};
}

View file

@ -0,0 +1,96 @@
/* 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 ChangePassword401Response
*/
export interface ChangePassword401Response {
/**
*
* @type {string}
* @memberof ChangePassword401Response
*/
kind: ChangePassword401ResponseKindEnum;
/**
*
* @type {string}
* @memberof ChangePassword401Response
*/
msg: ChangePassword401ResponseMsgEnum;
}
/**
* @export
*/
export const ChangePassword401ResponseKindEnum = {
NotLoggedIn: 'notLoggedIn'
} as const;
export type ChangePassword401ResponseKindEnum = typeof ChangePassword401ResponseKindEnum[keyof typeof ChangePassword401ResponseKindEnum];
/**
* @export
*/
export const ChangePassword401ResponseMsgEnum = {
AuthNoCookie: 'auth.noCookie',
AuthInvalidKind: 'auth.invalidKind',
AuthNoUser: 'auth.noUser',
AuthInvalid: 'auth.invalid'
} as const;
export type ChangePassword401ResponseMsgEnum = typeof ChangePassword401ResponseMsgEnum[keyof typeof ChangePassword401ResponseMsgEnum];
/**
* Check if a given object implements the ChangePassword401Response interface.
*/
export function instanceOfChangePassword401Response(value: object): value is ChangePassword401Response {
if (!('kind' in value) || value['kind'] === undefined) return false;
if (!('msg' in value) || value['msg'] === undefined) return false;
return true;
}
export function ChangePassword401ResponseFromJSON(json: any): ChangePassword401Response {
return ChangePassword401ResponseFromJSONTyped(json, false);
}
export function ChangePassword401ResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): ChangePassword401Response {
if (json == null) {
return json;
}
return {
'kind': json['kind'],
'msg': json['msg'],
};
}
export function ChangePassword401ResponseToJSON(json: any): ChangePassword401Response {
return ChangePassword401ResponseToJSONTyped(json, false);
}
export function ChangePassword401ResponseToJSONTyped(value?: ChangePassword401Response | null, ignoreDiscriminator: boolean = false): any {
if (value == null) {
return value;
}
return {
'kind': value['kind'],
'msg': value['msg'],
};
}

View 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 ChangePassword500Response
*/
export interface ChangePassword500Response {
/**
*
* @type {string}
* @memberof ChangePassword500Response
*/
kind: ChangePassword500ResponseKindEnum;
/**
*
* @type {string}
* @memberof ChangePassword500Response
*/
msg: ChangePassword500ResponseMsgEnum;
}
/**
* @export
*/
export const ChangePassword500ResponseKindEnum = {
Failed: 'failed'
} as const;
export type ChangePassword500ResponseKindEnum = typeof ChangePassword500ResponseKindEnum[keyof typeof ChangePassword500ResponseKindEnum];
/**
* @export
*/
export const ChangePassword500ResponseMsgEnum = {
ChangePasswordFailedGeneric: 'changePassword.failed.generic'
} as const;
export type ChangePassword500ResponseMsgEnum = typeof ChangePassword500ResponseMsgEnum[keyof typeof ChangePassword500ResponseMsgEnum];
/**
* Check if a given object implements the ChangePassword500Response interface.
*/
export function instanceOfChangePassword500Response(value: object): value is ChangePassword500Response {
if (!('kind' in value) || value['kind'] === undefined) return false;
if (!('msg' in value) || value['msg'] === undefined) return false;
return true;
}
export function ChangePassword500ResponseFromJSON(json: any): ChangePassword500Response {
return ChangePassword500ResponseFromJSONTyped(json, false);
}
export function ChangePassword500ResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): ChangePassword500Response {
if (json == null) {
return json;
}
return {
'kind': json['kind'],
'msg': json['msg'],
};
}
export function ChangePassword500ResponseToJSON(json: any): ChangePassword500Response {
return ChangePassword500ResponseToJSONTyped(json, false);
}
export function ChangePassword500ResponseToJSONTyped(value?: ChangePassword500Response | null, ignoreDiscriminator: boolean = false): any {
if (value == null) {
return value;
}
return {
'kind': value['kind'],
'msg': value['msg'],
};
}

View file

@ -0,0 +1,66 @@
/* 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 ChangePasswordRequest
*/
export interface ChangePasswordRequest {
/**
*
* @type {string}
* @memberof ChangePasswordRequest
*/
newPassword: string;
}
/**
* Check if a given object implements the ChangePasswordRequest interface.
*/
export function instanceOfChangePasswordRequest(value: object): value is ChangePasswordRequest {
if (!('newPassword' in value) || value['newPassword'] === undefined) return false;
return true;
}
export function ChangePasswordRequestFromJSON(json: any): ChangePasswordRequest {
return ChangePasswordRequestFromJSONTyped(json, false);
}
export function ChangePasswordRequestFromJSONTyped(json: any, ignoreDiscriminator: boolean): ChangePasswordRequest {
if (json == null) {
return json;
}
return {
'newPassword': json['new_password'],
};
}
export function ChangePasswordRequestToJSON(json: any): ChangePasswordRequest {
return ChangePasswordRequestToJSONTyped(json, false);
}
export function ChangePasswordRequestToJSONTyped(value?: ChangePasswordRequest | null, ignoreDiscriminator: boolean = false): any {
if (value == null) {
return value;
}
return {
'new_password': value['newPassword'],
};
}

View file

@ -13,13 +13,6 @@
*/ */
import { mapValues } from '../runtime'; import { mapValues } from '../runtime';
import type { DisableOtp401Response } from './DisableOtp401Response';
import {
DisableOtp401ResponseFromJSON,
DisableOtp401ResponseFromJSONTyped,
DisableOtp401ResponseToJSON,
DisableOtp401ResponseToJSONTyped,
} from './DisableOtp401Response';
import type { EnableOtp401ResponseAnyOf } from './EnableOtp401ResponseAnyOf'; import type { EnableOtp401ResponseAnyOf } from './EnableOtp401ResponseAnyOf';
import { import {
EnableOtp401ResponseAnyOfFromJSON, EnableOtp401ResponseAnyOfFromJSON,
@ -27,6 +20,13 @@ import {
EnableOtp401ResponseAnyOfToJSON, EnableOtp401ResponseAnyOfToJSON,
EnableOtp401ResponseAnyOfToJSONTyped, EnableOtp401ResponseAnyOfToJSONTyped,
} from './EnableOtp401ResponseAnyOf'; } from './EnableOtp401ResponseAnyOf';
import type { ChangePassword401Response } from './ChangePassword401Response';
import {
ChangePassword401ResponseFromJSON,
ChangePassword401ResponseFromJSONTyped,
ChangePassword401ResponseToJSON,
ChangePassword401ResponseToJSONTyped,
} from './ChangePassword401Response';
/** /**
* *

View file

@ -13,13 +13,13 @@
*/ */
import { mapValues } from '../runtime'; import { mapValues } from '../runtime';
import type { DisableOtp401Response } from './DisableOtp401Response'; import type { ChangePassword401Response } from './ChangePassword401Response';
import { import {
DisableOtp401ResponseFromJSON, ChangePassword401ResponseFromJSON,
DisableOtp401ResponseFromJSONTyped, ChangePassword401ResponseFromJSONTyped,
DisableOtp401ResponseToJSON, ChangePassword401ResponseToJSON,
DisableOtp401ResponseToJSONTyped, ChangePassword401ResponseToJSONTyped,
} from './DisableOtp401Response'; } from './ChangePassword401Response';
/** /**
* *

View file

@ -3,11 +3,15 @@
export * from './ChangeDisplayName200Response'; export * from './ChangeDisplayName200Response';
export * from './ChangeDisplayName400Response'; export * from './ChangeDisplayName400Response';
export * from './ChangeDisplayNameRequest'; export * from './ChangeDisplayNameRequest';
export * from './ChangePassword200Response';
export * from './ChangePassword400Response';
export * from './ChangePassword401Response';
export * from './ChangePassword500Response';
export * from './ChangePasswordRequest';
export * from './ChatTest200Response'; export * from './ChatTest200Response';
export * from './ChatTest200ResponsePayload'; export * from './ChatTest200ResponsePayload';
export * from './DisableOtp200Response'; export * from './DisableOtp200Response';
export * from './DisableOtp400Response'; export * from './DisableOtp400Response';
export * from './DisableOtp401Response';
export * from './DisableOtp500Response'; export * from './DisableOtp500Response';
export * from './EnableOtp200Response'; export * from './EnableOtp200Response';
export * from './EnableOtp200ResponsePayload'; export * from './EnableOtp200ResponsePayload';

View file

@ -1,4 +1,4 @@
import { addRoute, handleRoute, navigateTo, setTitle } from "@app/routing"; import { addRoute, getRoute, handleRoute, navigateTo, setTitle } from "@app/routing";
import { showError, showSuccess } from "@app/toast"; import { showError, showSuccess } from "@app/toast";
import page from "./profile.html?raw"; import page from "./profile.html?raw";
import { updateUser } from "@app/auth"; import { updateUser } from "@app/auth";
@ -159,7 +159,7 @@ async function route(url: string, _args: { [k: string]: string }) {
} else if (!isNullish(user.selfInfo?.loginName)) { } else if (!isNullish(user.selfInfo?.loginName)) {
loginNameWrapper.hidden = false; loginNameWrapper.hidden = false;
loginNameBox.innerText = user.selfInfo.loginName; loginNameBox.innerText = user.selfInfo.loginName;
totpWrapper.hidden =false; totpWrapper.hidden = false;
passwordWrapper.hidden = false; passwordWrapper.hidden = false;
accountTypeBox.innerText = "Normal"; accountTypeBox.innerText = "Normal";
@ -244,6 +244,19 @@ async function route(url: string, _args: { [k: string]: string }) {
showError(`Failed to update: ${req.msg}`); showError(`Failed to update: ${req.msg}`);
} }
}; };
passwordButton.onclick = async () => {
let req = await client.changePassword({
changePasswordRequest: {
newPassword: passwordBox.value,
},
});
if (req.kind === "success") {
showSuccess("Successfully changed password");
handleRoute();
} else {
showError(`Failed to update: ${req.msg}`);
}
};
// Initialize UI state // Initialize UI state
refreshTotpUI(); refreshTotpUI();

View file

@ -8,6 +8,140 @@
"schemas": {} "schemas": {}
}, },
"paths": { "paths": {
"/api/auth/changePassword": {
"post": {
"operationId": "changePassword",
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"new_password"
],
"properties": {
"new_password": {
"type": "string"
}
}
}
}
},
"required": true
},
"responses": {
"200": {
"description": "Default Response",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"kind",
"msg"
],
"properties": {
"kind": {
"enum": [
"success"
]
},
"msg": {
"enum": [
"changePassword.success"
]
}
}
}
}
}
},
"400": {
"description": "Default Response",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"kind",
"msg"
],
"properties": {
"kind": {
"enum": [
"failed"
]
},
"msg": {
"enum": [
"changePassword.failed.toolong",
"changePassword.failed.tooshort",
"changePassword.failed.invalid"
]
}
}
}
}
}
},
"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"
]
}
}
}
}
}
},
"500": {
"description": "Default Response",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"kind",
"msg"
],
"properties": {
"kind": {
"enum": [
"failed"
]
},
"msg": {
"enum": [
"changePassword.failed.generic"
]
}
}
}
}
}
}
}
}
},
"/api/auth/disableOtp": { "/api/auth/disableOtp": {
"put": { "put": {
"operationId": "disableOtp", "operationId": "disableOtp",

View file

@ -0,0 +1,44 @@
import { FastifyPluginAsync } from 'fastify';
import { Static, Type } from 'typebox';
import { typeResponse, MakeStaticResponse } from '@shared/utils';
const ChangePasswordReq = Type.Object({
new_password: Type.String(),
});
type ChangePasswordReq = Static<typeof ChangePasswordReq>;
const ChangePasswordRes = {
'500': typeResponse('failed',
'changePassword.failed.generic'),
'400': typeResponse('failed', [
'changePassword.failed.toolong',
'changePassword.failed.tooshort',
'changePassword.failed.invalid',
]),
'200': typeResponse('success', 'changePassword.success'),
};
type ChangePasswordRes = MakeStaticResponse<typeof ChangePasswordRes>;
const route: FastifyPluginAsync = async (fastify, _opts): Promise<void> => {
void _opts;
fastify.post<{ Body: ChangePasswordReq }>(
'/api/auth/changePassword',
{ schema: { body: ChangePasswordReq, response: ChangePasswordRes, operationId: 'changePassword' }, config: { requireAuth: true } },
async function(req, res) {
const password = req.body.new_password;
if (password.length < 8) { return res.makeResponse(400, 'failed', 'changePassword.failed.tooshort'); }
if (password.length > 64) { return res.makeResponse(400, 'failed', 'changePassword.failed.toolong'); }
// password is good too !
await this.db.setUserPassword(req.authUser!.id, password);
return res.makeResponse(200, 'success', 'changePassword.success');
},
);
};
export default route;

View file

@ -1,2 +0,0 @@
/dist
/node_modules

View file

@ -1,10 +0,0 @@
#!/bin/sh
set -e
set -x
# do anything here
cp -r /extra /files
# run the CMD [ ... ] from the dockerfile
exec "$@"

View file

@ -1,37 +0,0 @@
{
"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"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@fastify/autoload": "^6.3.1",
"@fastify/formbody": "^8.0.2",
"@fastify/multipart": "^9.3.0",
"@fastify/sensible": "^6.0.4",
"@fastify/static": "^8.3.0",
"fastify": "^5.6.2",
"fastify-cli": "^7.4.1",
"fastify-plugin": "^5.1.0",
"raw-body": "^3.0.2",
"sharp": "^0.34.5"
},
"devDependencies": {
"@types/node": "^22.19.2",
"rollup-plugin-node-externals": "^8.1.2",
"vite": "^7.2.7",
"vite-tsconfig-paths": "^5.1.4"
}
}

View file

@ -1,54 +0,0 @@
import { FastifyPluginAsync } from 'fastify';
import fastifyFormBody from '@fastify/formbody';
import fastifyMultipart from '@fastify/multipart';
import { mkdir } from 'node:fs/promises';
import fp from 'fastify-plugin';
import * as db from '@shared/database';
import * as utils from '@shared/utils';
import { authPlugin, jwtPlugin } from '@shared/auth';
// @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 });
// When using .decorate you have to specify added properties for Typescript
declare module 'fastify' {
export interface FastifyInstance {
image_store: string;
}
}
const app: FastifyPluginAsync = async (
fastify,
_opts,
): Promise<void> => {
void _opts;
// 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, {});
}
await fastify.register(utils.useMonitoring);
await fastify.register(db.useDatabase as FastifyPluginAsync, {});
await fastify.register(authPlugin as FastifyPluginAsync, {});
await fastify.register(jwtPlugin as FastifyPluginAsync, {});
void fastify.register(fastifyFormBody, {});
void fastify.register(fastifyMultipart, {});
// The use of fastify-plugin is required to be able
// to export the decorators to the outer scope
void fastify.register(fp(async (fastify2) => {
const image_store = process.env.USER_ICONS_STORE ?? '/tmp/icons';
fastify2.decorate('image_store', image_store);
await mkdir(fastify2.image_store, { recursive: true });
}));
};
export default app;
export { app };

View file

@ -1,16 +0,0 @@
# 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/).

View file

@ -1,11 +0,0 @@
import fp from 'fastify-plugin';
import sensible, { FastifySensibleOptions } from '@fastify/sensible';
/**
* This plugins adds some utilities to handle http errors
*
* @see https://github.com/fastify/fastify-sensible
*/
export default fp<FastifySensibleOptions>(async (fastify) => {
fastify.register(sensible);
});

View file

@ -1,51 +0,0 @@
import { FastifyPluginAsync } from 'fastify';
import { join } from 'node:path';
import { open } from 'node:fs/promises';
import sharp from 'sharp';
import rawBody from 'raw-body';
import { isNullish } from '@shared/utils';
const route: FastifyPluginAsync = async (fastify, _opts): Promise<void> => {
void _opts;
// await fastify.register(authMethod, {});
// here we register plugins that will be active for the current fastify instance (aka everything in this function)
// we register a route handler for: `/<USERID_HERE>`
// it sets some configuration options, and set the actual function that will handle the request
fastify.addContentTypeParser('*', function(request, payload, done) {
done(null);
});
fastify.post<{ Params: { userid: string } }>('/:userid', async function(request, reply) {
const buffer = await rawBody(request.raw);
// this is how we get the `:userid` part of things
const userid: string | undefined = (request.params)['userid'];
if (isNullish(userid)) {
return await reply.code(403);
}
const image_store: string = fastify.getDecorator('image_store');
const image_path = join(image_store, userid);
try {
const img = sharp(buffer);
img.resize({
height: 128,
width: 128,
fit: 'fill',
});
const data = await img.png({ compressionLevel: 6 }).toBuffer();
const image_file = await open(image_path, 'w', 0o666);
await image_file.write(data);
await image_file.close();
}
catch (e) {
fastify.log.error(`Error: ${e}`);
reply.code(400);
return { status: 'error' };
}
});
};
export default route;

View file

@ -1,35 +0,0 @@
// this sould only be used by the docker file !
import fastify, { FastifyInstance } from 'fastify';
import app from './app';
const start = async () => {
const envToLogger = {
development: {
transport: {
target: 'pino-pretty',
options: {
translateTime: 'HH:MM:ss Z',
ignore: 'pid,hostname',
},
},
},
production: true,
test: false,
};
const f: FastifyInstance = fastify({ logger: envToLogger.development });
process.on('SIGTERM', () => {
f.log.info('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();

View file

@ -1,5 +0,0 @@
{
"extends": "../tsconfig.base.json",
"compilerOptions": {},
"include": ["src/**/*.ts"]
}

View file

@ -1,51 +0,0 @@
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,
// service root
plugins: [tsconfigPaths(), nodeExternals()],
build: {
ssr: true,
outDir: 'dist',
emptyOutDir: true,
lib: {
entry: path.resolve(__dirname, '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: false,
minify: true,
// for easier debugging
},
});

View file

@ -21,6 +21,143 @@
} }
], ],
"paths": { "paths": {
"/api/auth/changePassword": {
"post": {
"operationId": "changePassword",
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"new_password"
],
"properties": {
"new_password": {
"type": "string"
}
}
}
}
},
"required": true
},
"responses": {
"200": {
"description": "Default Response",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"kind",
"msg"
],
"properties": {
"kind": {
"enum": [
"success"
]
},
"msg": {
"enum": [
"changePassword.success"
]
}
}
}
}
}
},
"400": {
"description": "Default Response",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"kind",
"msg"
],
"properties": {
"kind": {
"enum": [
"failed"
]
},
"msg": {
"enum": [
"changePassword.failed.toolong",
"changePassword.failed.tooshort",
"changePassword.failed.invalid"
]
}
}
}
}
}
},
"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"
]
}
}
}
}
}
},
"500": {
"description": "Default Response",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"kind",
"msg"
],
"properties": {
"kind": {
"enum": [
"failed"
]
},
"msg": {
"enum": [
"changePassword.failed.generic"
]
}
}
}
}
}
}
},
"tags": [
"openapi_other"
]
}
},
"/api/auth/disableOtp": { "/api/auth/disableOtp": {
"put": { "put": {
"operationId": "disableOtp", "operationId": "disableOtp",