diff --git a/docker-compose.yml b/docker-compose.yml index 45f7af3..33f54fb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -48,33 +48,6 @@ services: gelf-address: "udp://127.0.0.1:12201" 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 # ############### diff --git a/frontend/src/api/generated/.openapi-generator/FILES b/frontend/src/api/generated/.openapi-generator/FILES index ed23b0c..dbddaec 100644 --- a/frontend/src/api/generated/.openapi-generator/FILES +++ b/frontend/src/api/generated/.openapi-generator/FILES @@ -4,11 +4,15 @@ index.ts models/ChangeDisplayName200Response.ts models/ChangeDisplayName400Response.ts models/ChangeDisplayNameRequest.ts +models/ChangePassword200Response.ts +models/ChangePassword400Response.ts +models/ChangePassword401Response.ts +models/ChangePassword500Response.ts +models/ChangePasswordRequest.ts models/ChatTest200Response.ts models/ChatTest200ResponsePayload.ts models/DisableOtp200Response.ts models/DisableOtp400Response.ts -models/DisableOtp401Response.ts models/DisableOtp500Response.ts models/EnableOtp200Response.ts models/EnableOtp200ResponsePayload.ts diff --git a/frontend/src/api/generated/apis/OpenapiOtherApi.ts b/frontend/src/api/generated/apis/OpenapiOtherApi.ts index 9609421..444695b 100644 --- a/frontend/src/api/generated/apis/OpenapiOtherApi.ts +++ b/frontend/src/api/generated/apis/OpenapiOtherApi.ts @@ -18,10 +18,14 @@ import type { ChangeDisplayName200Response, ChangeDisplayName400Response, ChangeDisplayNameRequest, + ChangePassword200Response, + ChangePassword400Response, + ChangePassword401Response, + ChangePassword500Response, + ChangePasswordRequest, ChatTest200Response, DisableOtp200Response, DisableOtp400Response, - DisableOtp401Response, DisableOtp500Response, EnableOtp200Response, EnableOtp400Response, @@ -60,14 +64,22 @@ import { ChangeDisplayName400ResponseToJSON, ChangeDisplayNameRequestFromJSON, ChangeDisplayNameRequestToJSON, + ChangePassword200ResponseFromJSON, + ChangePassword200ResponseToJSON, + ChangePassword400ResponseFromJSON, + ChangePassword400ResponseToJSON, + ChangePassword401ResponseFromJSON, + ChangePassword401ResponseToJSON, + ChangePassword500ResponseFromJSON, + ChangePassword500ResponseToJSON, + ChangePasswordRequestFromJSON, + ChangePasswordRequestToJSON, ChatTest200ResponseFromJSON, ChatTest200ResponseToJSON, DisableOtp200ResponseFromJSON, DisableOtp200ResponseToJSON, DisableOtp400ResponseFromJSON, DisableOtp400ResponseToJSON, - DisableOtp401ResponseFromJSON, - DisableOtp401ResponseToJSON, DisableOtp500ResponseFromJSON, DisableOtp500ResponseToJSON, EnableOtp200ResponseFromJSON, @@ -134,6 +146,10 @@ export interface ChangeDisplayNameOperationRequest { changeDisplayNameRequest: ChangeDisplayNameRequest; } +export interface ChangePasswordOperationRequest { + changePasswordRequest: ChangePasswordRequest; +} + export interface GetUserRequest { user: GetUserUserParameter; } @@ -161,7 +177,7 @@ export class OpenapiOtherApi extends runtime.BaseAPI { /** */ - async changeDisplayNameRaw(requestParameters: ChangeDisplayNameOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + async changeDisplayNameRaw(requestParameters: ChangeDisplayNameOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { if (requestParameters['changeDisplayNameRequest'] == null) { throw new runtime.RequiredError( 'changeDisplayNameRequest', @@ -200,7 +216,7 @@ export class OpenapiOtherApi extends runtime.BaseAPI { } if (response.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 // 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 { + async changeDisplayName(requestParameters: ChangeDisplayNameOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { const response = await this.changeDisplayNameRaw(requestParameters, initOverrides); return await response.value(); } + /** + */ + async changePasswordRaw(requestParameters: ChangePasswordOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + 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 { + const response = await this.changePasswordRaw(requestParameters, initOverrides); + return await response.value(); + } + /** */ async chatTestRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { @@ -259,7 +335,7 @@ export class OpenapiOtherApi extends runtime.BaseAPI { /** */ - async disableOtpRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + async disableOtpRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { const queryParameters: any = {}; const headerParameters: runtime.HTTPHeaders = {}; @@ -288,7 +364,7 @@ export class OpenapiOtherApi extends runtime.BaseAPI { } if (response.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) { // Object response for status 500 @@ -302,7 +378,7 @@ export class OpenapiOtherApi extends runtime.BaseAPI { /** */ - async disableOtp(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + async disableOtp(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { const response = await this.disableOtpRaw(initOverrides); return await response.value(); } diff --git a/frontend/src/api/generated/models/ChangePassword200Response.ts b/frontend/src/api/generated/models/ChangePassword200Response.ts new file mode 100644 index 0000000..0df6906 --- /dev/null +++ b/frontend/src/api/generated/models/ChangePassword200Response.ts @@ -0,0 +1,93 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * @fastify/swagger + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 9.6.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface 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'], + }; +} + diff --git a/frontend/src/api/generated/models/ChangePassword400Response.ts b/frontend/src/api/generated/models/ChangePassword400Response.ts new file mode 100644 index 0000000..1606223 --- /dev/null +++ b/frontend/src/api/generated/models/ChangePassword400Response.ts @@ -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'], + }; +} + diff --git a/frontend/src/api/generated/models/ChangePassword401Response.ts b/frontend/src/api/generated/models/ChangePassword401Response.ts new file mode 100644 index 0000000..7e460c7 --- /dev/null +++ b/frontend/src/api/generated/models/ChangePassword401Response.ts @@ -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'], + }; +} + diff --git a/frontend/src/api/generated/models/ChangePassword500Response.ts b/frontend/src/api/generated/models/ChangePassword500Response.ts new file mode 100644 index 0000000..2d345f7 --- /dev/null +++ b/frontend/src/api/generated/models/ChangePassword500Response.ts @@ -0,0 +1,93 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * @fastify/swagger + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 9.6.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface 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'], + }; +} + diff --git a/frontend/src/api/generated/models/ChangePasswordRequest.ts b/frontend/src/api/generated/models/ChangePasswordRequest.ts new file mode 100644 index 0000000..d54b3e1 --- /dev/null +++ b/frontend/src/api/generated/models/ChangePasswordRequest.ts @@ -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'], + }; +} + diff --git a/frontend/src/api/generated/models/EnableOtp401Response.ts b/frontend/src/api/generated/models/EnableOtp401Response.ts index 4db1df0..58b0f2f 100644 --- a/frontend/src/api/generated/models/EnableOtp401Response.ts +++ b/frontend/src/api/generated/models/EnableOtp401Response.ts @@ -13,13 +13,6 @@ */ import { mapValues } from '../runtime'; -import type { DisableOtp401Response } from './DisableOtp401Response'; -import { - DisableOtp401ResponseFromJSON, - DisableOtp401ResponseFromJSONTyped, - DisableOtp401ResponseToJSON, - DisableOtp401ResponseToJSONTyped, -} from './DisableOtp401Response'; import type { EnableOtp401ResponseAnyOf } from './EnableOtp401ResponseAnyOf'; import { EnableOtp401ResponseAnyOfFromJSON, @@ -27,6 +20,13 @@ import { EnableOtp401ResponseAnyOfToJSON, EnableOtp401ResponseAnyOfToJSONTyped, } from './EnableOtp401ResponseAnyOf'; +import type { ChangePassword401Response } from './ChangePassword401Response'; +import { + ChangePassword401ResponseFromJSON, + ChangePassword401ResponseFromJSONTyped, + ChangePassword401ResponseToJSON, + ChangePassword401ResponseToJSONTyped, +} from './ChangePassword401Response'; /** * diff --git a/frontend/src/api/generated/models/StatusOtp401Response.ts b/frontend/src/api/generated/models/StatusOtp401Response.ts index 10bde87..b89208a 100644 --- a/frontend/src/api/generated/models/StatusOtp401Response.ts +++ b/frontend/src/api/generated/models/StatusOtp401Response.ts @@ -13,13 +13,13 @@ */ import { mapValues } from '../runtime'; -import type { DisableOtp401Response } from './DisableOtp401Response'; +import type { ChangePassword401Response } from './ChangePassword401Response'; import { - DisableOtp401ResponseFromJSON, - DisableOtp401ResponseFromJSONTyped, - DisableOtp401ResponseToJSON, - DisableOtp401ResponseToJSONTyped, -} from './DisableOtp401Response'; + ChangePassword401ResponseFromJSON, + ChangePassword401ResponseFromJSONTyped, + ChangePassword401ResponseToJSON, + ChangePassword401ResponseToJSONTyped, +} from './ChangePassword401Response'; /** * diff --git a/frontend/src/api/generated/models/index.ts b/frontend/src/api/generated/models/index.ts index f00b24e..af6e1a0 100644 --- a/frontend/src/api/generated/models/index.ts +++ b/frontend/src/api/generated/models/index.ts @@ -3,11 +3,15 @@ export * from './ChangeDisplayName200Response'; export * from './ChangeDisplayName400Response'; export * from './ChangeDisplayNameRequest'; +export * from './ChangePassword200Response'; +export * from './ChangePassword400Response'; +export * from './ChangePassword401Response'; +export * from './ChangePassword500Response'; +export * from './ChangePasswordRequest'; export * from './ChatTest200Response'; export * from './ChatTest200ResponsePayload'; export * from './DisableOtp200Response'; export * from './DisableOtp400Response'; -export * from './DisableOtp401Response'; export * from './DisableOtp500Response'; export * from './EnableOtp200Response'; export * from './EnableOtp200ResponsePayload'; diff --git a/frontend/src/pages/profile/profile.ts b/frontend/src/pages/profile/profile.ts index 4b91b84..7241eeb 100644 --- a/frontend/src/pages/profile/profile.ts +++ b/frontend/src/pages/profile/profile.ts @@ -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 page from "./profile.html?raw"; import { updateUser } from "@app/auth"; @@ -159,7 +159,7 @@ async function route(url: string, _args: { [k: string]: string }) { } else if (!isNullish(user.selfInfo?.loginName)) { loginNameWrapper.hidden = false; loginNameBox.innerText = user.selfInfo.loginName; - totpWrapper.hidden =false; + totpWrapper.hidden = false; passwordWrapper.hidden = false; accountTypeBox.innerText = "Normal"; @@ -244,6 +244,19 @@ async function route(url: string, _args: { [k: string]: string }) { 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 refreshTotpUI(); diff --git a/src/auth/openapi.json b/src/auth/openapi.json index 7fd431f..7a08ce2 100644 --- a/src/auth/openapi.json +++ b/src/auth/openapi.json @@ -8,6 +8,140 @@ "schemas": {} }, "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": { "put": { "operationId": "disableOtp", diff --git a/src/auth/src/routes/changePassword.ts b/src/auth/src/routes/changePassword.ts new file mode 100644 index 0000000..7d87d9c --- /dev/null +++ b/src/auth/src/routes/changePassword.ts @@ -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; + +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; + +const route: FastifyPluginAsync = async (fastify, _opts): Promise => { + 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; diff --git a/src/icons/.dockerignore b/src/icons/.dockerignore deleted file mode 100644 index c925c21..0000000 --- a/src/icons/.dockerignore +++ /dev/null @@ -1,2 +0,0 @@ -/dist -/node_modules diff --git a/src/icons/entrypoint.sh b/src/icons/entrypoint.sh deleted file mode 100644 index 4a3dac4..0000000 --- a/src/icons/entrypoint.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -set -e -set -x -# do anything here - -cp -r /extra /files - -# run the CMD [ ... ] from the dockerfile -exec "$@" diff --git a/src/icons/extra/.gitkeep b/src/icons/extra/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/icons/package.json b/src/icons/package.json deleted file mode 100644 index bce9ed7..0000000 --- a/src/icons/package.json +++ /dev/null @@ -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" - } -} diff --git a/src/icons/src/app.ts b/src/icons/src/app.ts deleted file mode 100644 index 7c3b2f0..0000000 --- a/src/icons/src/app.ts +++ /dev/null @@ -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 _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 }; diff --git a/src/icons/src/plugins/README.md b/src/icons/src/plugins/README.md deleted file mode 100644 index 1e61ee5..0000000 --- a/src/icons/src/plugins/README.md +++ /dev/null @@ -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/). diff --git a/src/icons/src/plugins/sensible.ts b/src/icons/src/plugins/sensible.ts deleted file mode 100644 index 8c2093c..0000000 --- a/src/icons/src/plugins/sensible.ts +++ /dev/null @@ -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(async (fastify) => { - fastify.register(sensible); -}); diff --git a/src/icons/src/routes/set.ts b/src/icons/src/routes/set.ts deleted file mode 100644 index 8063719..0000000 --- a/src/icons/src/routes/set.ts +++ /dev/null @@ -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 _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: `/` - // 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; - diff --git a/src/icons/src/run.ts b/src/icons/src/run.ts deleted file mode 100644 index d3d410f..0000000 --- a/src/icons/src/run.ts +++ /dev/null @@ -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(); diff --git a/src/icons/tsconfig.json b/src/icons/tsconfig.json deleted file mode 100644 index e6d24e2..0000000 --- a/src/icons/tsconfig.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "../tsconfig.base.json", - "compilerOptions": {}, - "include": ["src/**/*.ts"] -} diff --git a/src/icons/vite.config.js b/src/icons/vite.config.js deleted file mode 100644 index 2c172a5..0000000 --- a/src/icons/vite.config.js +++ /dev/null @@ -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 - }, -}); diff --git a/src/openapi.json b/src/openapi.json index d9b52b6..42cbe84 100644 --- a/src/openapi.json +++ b/src/openapi.json @@ -21,6 +21,143 @@ } ], "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": { "put": { "operationId": "disableOtp",