This is an automated email from the ASF dual-hosted git repository. marat pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-karavan.git
commit cf741583da96e8325622049583df26dca79f8ec6 Author: Marat Gubaidullin <[email protected]> AuthorDate: Wed Jun 19 17:32:17 2024 -0400 Cleanup --- .../api/{AuthResource.java => PublicResource.java} | 42 +-------- .../src/main/webui/src/api/ErrorEventBus.ts | 25 +++++ karavan-app/src/main/webui/src/api/KaravanApi.tsx | 104 +++++++++------------ .../src/main/webui/src/api/ProjectService.ts | 26 ++++-- .../main/webui/src/config/ConfigurationPage.tsx | 2 +- karavan-app/src/main/webui/src/project/devmode.css | 39 ++++++++ .../src/project/trace/RunnerInfoTraceNode.tsx | 76 +++++++++++++++ .../src/main/webui/src/services/ServicesPage.tsx | 3 +- .../src/main/webui/src/shared/error/Error.ts | 11 --- .../main/webui/src/shared/error/ErrorResponse.ts | 15 --- .../src/shared/error/UseResponseErrorHandler.ts | 43 --------- .../src/main/webui/src/templates/TemplatesPage.tsx | 2 +- karavan-app/src/main/webui/src/util/StringUtils.ts | 35 ++++++- 13 files changed, 241 insertions(+), 182 deletions(-) diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/api/AuthResource.java b/karavan-app/src/main/java/org/apache/camel/karavan/api/PublicResource.java similarity index 59% rename from karavan-app/src/main/java/org/apache/camel/karavan/api/AuthResource.java rename to karavan-app/src/main/java/org/apache/camel/karavan/api/PublicResource.java index 1561486c..aa503c12 100644 --- a/karavan-app/src/main/java/org/apache/camel/karavan/api/AuthResource.java +++ b/karavan-app/src/main/java/org/apache/camel/karavan/api/PublicResource.java @@ -23,15 +23,12 @@ import jakarta.ws.rs.core.Response; import org.apache.camel.karavan.KaravanStartupLoader; import org.apache.camel.karavan.kubernetes.KubernetesStatusService; import org.eclipse.microprofile.config.ConfigProvider; -import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.health.HealthCheckResponse; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.util.*; @Path("/public") -public class AuthResource { +public class PublicResource { @Inject KaravanStartupLoader karavanStartupLoader; @@ -39,43 +36,6 @@ public class AuthResource { @Inject KubernetesStatusService kubernetesStatusService; - @ConfigProperty(name = "quarkus.security.users.embedded.realm-name", defaultValue = "") - Optional<String> realm; - - @ConfigProperty(name = "quarkus.security.users.embedded.users") - Optional<Map<String,String>> users; - - public static String getMd5Hash(String input) throws NoSuchAlgorithmException { - MessageDigest md = MessageDigest.getInstance("MD5"); - byte[] digest = md.digest(input.getBytes()); - StringBuilder sb = new StringBuilder(); - for (byte b : digest) { - sb.append(String.format("%02x", b)); - } - return sb.toString(); - } - - @Path("/auth") - @POST - @Produces(MediaType.APPLICATION_JSON) - @Consumes(MediaType.APPLICATION_FORM_URLENCODED) - public Response authenticateUser(@FormParam("username") String username, @FormParam("password") String password) { - try { - if (users.isPresent() && users.get().containsKey(username)) { - var pwdStored = users.get().get(username); - var pwdReceived = new String(Base64.getDecoder().decode(password)); - var pwdString = username + ":" + realm.orElse("") + ":" + pwdReceived; - String pwdToCheck = getMd5Hash(pwdString); - if (Objects.equals(pwdToCheck, pwdStored)) { - return Response.ok().build(); - } - } - return Response.status(Response.Status.FORBIDDEN).entity("Incorrect Username and/or Password!").build(); - } catch (Exception e) { - return Response.status(Response.Status.FORBIDDEN).entity(e.getMessage()).build(); - } - } - @GET @Path("/auth") @Produces(MediaType.TEXT_PLAIN) diff --git a/karavan-app/src/main/webui/src/api/ErrorEventBus.ts b/karavan-app/src/main/webui/src/api/ErrorEventBus.ts new file mode 100644 index 00000000..17df7fb2 --- /dev/null +++ b/karavan-app/src/main/webui/src/api/ErrorEventBus.ts @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import {Subject} from 'rxjs'; + +const apiErrors = new Subject<any>(); + +export const ErrorEventBus = { + + sendApiError: (error: any) => apiErrors.next(error), + onApiError: () => apiErrors.asObservable(), +} diff --git a/karavan-app/src/main/webui/src/api/KaravanApi.tsx b/karavan-app/src/main/webui/src/api/KaravanApi.tsx index 4cf61d0d..0ca8f6c9 100644 --- a/karavan-app/src/main/webui/src/api/KaravanApi.tsx +++ b/karavan-app/src/main/webui/src/api/KaravanApi.tsx @@ -29,6 +29,7 @@ import {SsoApi} from "./SsoApi"; import {v4 as uuidv4} from "uuid"; import {useAppConfigStore} from "./ProjectStore"; import {EventBus} from "../designer/utils/EventBus"; +import {ErrorEventBus} from "./ErrorEventBus"; const USER_ID_KEY = 'KARAVAN_USER_ID'; axios.defaults.headers.common['Accept'] = 'application/json'; @@ -46,9 +47,7 @@ export class KaravanApi { } static getUserId(): string { - if (KaravanApi.me?.userName !== undefined) { - return KaravanApi.me?.userName; - } else { + if (KaravanApi.authType === 'public') { const userId = localStorage.getItem(USER_ID_KEY); if (userId !== null && userId !== undefined) { return userId; @@ -57,11 +56,12 @@ export class KaravanApi { localStorage.setItem(USER_ID_KEY, newId); return newId; } + } else { + return KaravanApi.me?.userName; } } static setAuthType(authType: string) { - console.log("SetAuthType", authType) KaravanApi.authType = authType; switch (authType) { case "public": { @@ -124,30 +124,6 @@ export class KaravanApi { }); } - static async auth(username: string, password: string, after: (ok: boolean, res: any) => void) { - instance.post('/public/auth', - {username: username, password: Buffer.from(password).toString('base64')}, - {headers: {'content-type': 'application/x-www-form-urlencoded'}}) - .then(res => { - if (res.status === 200) { - KaravanApi.basicToken = Buffer.from(username + ":" + password).toString('base64'); - KaravanApi.setBasicAuthentication(); - KaravanApi.getMe(user => { - after(true, res); - useAppConfigStore.setState({isAuthorized: true}) - }) - } else if (res.status === 401) { - useAppConfigStore.setState({isAuthorized: false}) - KaravanApi.basicToken = ''; - after(false, res); - } - }).catch(err => { - KaravanApi.basicToken = ''; - useAppConfigStore.setState({isAuthorized: false}) - after(false, err); - }); - } - static async getReadiness(after: (readiness: any) => void) { axios.get('/public/readiness', {headers: {'Accept': 'application/json'}}) .then(res => { @@ -157,8 +133,7 @@ export class KaravanApi { after(undefined); } }).catch(err => { - console.log(err.message); - after(undefined); + ErrorEventBus.sendApiError(err); }); } @@ -169,7 +144,7 @@ export class KaravanApi { after(res.data); } }).catch(err => { - console.log(err); + ErrorEventBus.sendApiError(err); }); } @@ -183,7 +158,7 @@ export class KaravanApi { after(res.data); } }).catch(err => { - console.log(err); + ErrorEventBus.sendApiError(err); }); } @@ -195,7 +170,7 @@ export class KaravanApi { after(res.data); } }).catch(err => { - console.log(err); + ErrorEventBus.sendApiError(err); }); } @@ -206,7 +181,7 @@ export class KaravanApi { after(res.data); } }).catch(err => { - console.log(err); + ErrorEventBus.sendApiError(err); }); } @@ -217,7 +192,7 @@ export class KaravanApi { after(res.data); } }).catch(err => { - console.log(err); + ErrorEventBus.sendApiError(err); }); } @@ -228,7 +203,7 @@ export class KaravanApi { after(res.data); } }).catch(err => { - console.log(err); + ErrorEventBus.sendApiError(err); }); } @@ -241,7 +216,7 @@ export class KaravanApi { after(undefined); } }).catch(err => { - console.log(err); + ErrorEventBus.sendApiError(err); }); } @@ -252,7 +227,7 @@ export class KaravanApi { after(res.data); } }).catch(err => { - console.log(err); + ErrorEventBus.sendApiError(err); }); } @@ -263,7 +238,7 @@ export class KaravanApi { after(res.data); } }).catch(err => { - console.log(err); + ErrorEventBus.sendApiError(err); }); } @@ -274,7 +249,7 @@ export class KaravanApi { after(res.data.map((p: Partial<Project> | undefined) => new Project(p))); } }).catch(err => { - console.log(err); + ErrorEventBus.sendApiError(err); }); } @@ -333,7 +308,7 @@ export class KaravanApi { after(res.data); } }).catch((err: any) => { - console.log(err); + ErrorEventBus.sendApiError(err); EventBus.sendAlert("Error", err.message, "danger") }); } @@ -345,7 +320,7 @@ export class KaravanApi { after(res.data); } }).catch(err => { - console.log(err); + ErrorEventBus.sendApiError(err); }); } @@ -356,7 +331,7 @@ export class KaravanApi { after(res.data); } }).catch(err => { - console.log(err); + ErrorEventBus.sendApiError(err); }); } @@ -367,7 +342,7 @@ export class KaravanApi { after(res.data); } }).catch(err => { - console.log(err); + ErrorEventBus.sendApiError(err); }); } @@ -429,7 +404,7 @@ export class KaravanApi { after(res.data); } }).catch(err => { - console.log(err); + ErrorEventBus.sendApiError(err); }); } @@ -440,7 +415,7 @@ export class KaravanApi { after(res.data); } }).catch(err => { - console.log(err); + ErrorEventBus.sendApiError(err); }); } @@ -514,7 +489,7 @@ export class KaravanApi { after(res.data); } }).catch(err => { - console.log(err); + ErrorEventBus.sendApiError(err); }); } @@ -525,7 +500,7 @@ export class KaravanApi { after(res.data); } }).catch(err => { - console.log(err); + ErrorEventBus.sendApiError(err); }); } @@ -536,7 +511,7 @@ export class KaravanApi { after(res.data); } }).catch(err => { - console.log(err); + ErrorEventBus.sendApiError(err); }); } @@ -547,7 +522,7 @@ export class KaravanApi { after(res.data); } }).catch(err => { - console.log(err); + ErrorEventBus.sendApiError(err); }); } @@ -567,7 +542,7 @@ export class KaravanApi { after(res.data); } }).catch(err => { - console.log(err); + ErrorEventBus.sendApiError(err); }); } @@ -578,7 +553,7 @@ export class KaravanApi { after(res.data); } }).catch(err => { - console.log(err); + ErrorEventBus.sendApiError(err); }); } @@ -630,7 +605,7 @@ export class KaravanApi { after(res.data); } }).catch(err => { - console.log(err); + ErrorEventBus.sendApiError(err); }); } @@ -641,7 +616,7 @@ export class KaravanApi { after(res.data); } }).catch(err => { - console.log(err); + ErrorEventBus.sendApiError(err); }); } @@ -652,7 +627,7 @@ export class KaravanApi { after(); } }).catch(err => { - console.log(err); + ErrorEventBus.sendApiError(err); }); } @@ -663,7 +638,7 @@ export class KaravanApi { after(res.data); } }).catch(err => { - console.log(err); + ErrorEventBus.sendApiError(err); }); } @@ -674,7 +649,7 @@ export class KaravanApi { after(res.data); } }).catch(err => { - console.log(err); + ErrorEventBus.sendApiError(err); }); } @@ -703,7 +678,18 @@ export class KaravanApi { after(res.data); } }).catch(err => { - console.log(err); + ErrorEventBus.sendApiError(err); + }); + } + + static async getKameletsForProject(projectId: string, after: (yaml: string) => void) { + instance.get('/ui/kamelet/' + projectId, {headers: {'Accept': 'text/plain'}}) + .then(res => { + if (res.status === 200) { + after(res.data); + } + }).catch(err => { + ErrorEventBus.sendApiError(err); }); } @@ -714,7 +700,7 @@ export class KaravanApi { after(JSON.stringify(res.data)); } }).catch(err => { - console.log(err); + ErrorEventBus.sendApiError(err); }); } } diff --git a/karavan-app/src/main/webui/src/api/ProjectService.ts b/karavan-app/src/main/webui/src/api/ProjectService.ts index f32e12fa..bce8a5fa 100644 --- a/karavan-app/src/main/webui/src/api/ProjectService.ts +++ b/karavan-app/src/main/webui/src/api/ProjectService.ts @@ -25,7 +25,7 @@ import { useStatusesStore, useFileStore, useLogStore, useProjectsStore, - useProjectStore, useDevModeStore, useAppConfigStore + useProjectStore, useDevModeStore } from './ProjectStore'; import {ProjectEventBus} from './ProjectEventBus'; import {EventBus} from "../designer/utils/EventBus"; @@ -98,7 +98,6 @@ export class ProjectService { } public static pushProject(project: Project, commitMessage: string) { - useAppConfigStore.setState({notificationFetcherId: Math.random().toString()}); const params = { 'projectId': project.projectId, 'message': commitMessage, @@ -128,18 +127,28 @@ export class ProjectService { }); } - public static reloadKamelets() { - KaravanApi.getKamelets(yamls => { - const kamelets: string[] = []; - yamls.split(/\n?---\n?/).map(c => c.trim()).forEach(z => kamelets.push(z)); - KameletApi.saveKamelets(kamelets, true); - }) + static afterKameletsLoad(yamls: string): void { + const kamelets: string[] = []; + yamls.split(/\n?---\n?/).map(c => c.trim()).forEach(z => kamelets.push(z)); + KameletApi.saveKamelets(kamelets, true); + } + public static reloadKamelets(projectId?: string) { + if (projectId) { + KaravanApi.getKameletsForProject(projectId, ProjectService.afterKameletsLoad); + useFilesStore.getState().files + ?.filter(f => f.name.endsWith('.kamelet.yaml')) + .map(f => f.name.replace('.kamelet.yaml', '')) + .forEach(name => KameletApi.saveCustomKameletName(name)) + } else { + KaravanApi.getKamelets(ProjectService.afterKameletsLoad) + } KaravanApi.getFiles("kamelets", (files: ProjectFile[]) => { files.map(f => f.name.replace('.kamelet.yaml', '')) .forEach(name => KameletApi.saveCustomKameletName(name)) }); } + public static updateFile(file: ProjectFile, active: boolean) { KaravanApi.putProjectFile(file, res => { if (res.status === 200) { @@ -282,6 +291,7 @@ export class ProjectService { }); KaravanApi.getFiles(projectId, (files: ProjectFile[]) => { useFilesStore.setState({files: files}); + ProjectService.reloadKamelets(projectId); }); KaravanApi.getFilesDiff(projectId, (diff: any) => { diff --git a/karavan-app/src/main/webui/src/config/ConfigurationPage.tsx b/karavan-app/src/main/webui/src/config/ConfigurationPage.tsx index 80fd439b..000b1718 100644 --- a/karavan-app/src/main/webui/src/config/ConfigurationPage.tsx +++ b/karavan-app/src/main/webui/src/config/ConfigurationPage.tsx @@ -79,7 +79,7 @@ export const ConfigurationPage = (props: Props) => { </PageSection> <PageSection className="tools-section" padding={{default: 'noPadding'}}> <Flex direction={{default: "column"}} spaceItems={{default: "spaceItemsNone"}}> - <FlexItem className="knowledge-tabs"> + <FlexItem> <Tabs activeKey={tab} onSelect={(event, tabIndex) => setTab(tabIndex)}> <Tab eventKey="statuses" title="Statuses"/> <Tab eventKey="secrets" title="Secrets" isDisabled/> diff --git a/karavan-app/src/main/webui/src/project/devmode.css b/karavan-app/src/main/webui/src/project/devmode.css new file mode 100644 index 00000000..fea70e8e --- /dev/null +++ b/karavan-app/src/main/webui/src/project/devmode.css @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.karavan .refresher { + width: 34px; + height: 34px; +} + +.karavan .refresher .spinner { + position: absolute; + width: 34px; + height: 34px; +} + +.karavan .refresher .button { + position: absolute; + width: 34px; + height: 34px; + padding: 0; + font-size: 16px; +} + +.karavan .refresher .button .pf-v5-c-button__icon { + margin: 0; +} \ No newline at end of file diff --git a/karavan-app/src/main/webui/src/project/trace/RunnerInfoTraceNode.tsx b/karavan-app/src/main/webui/src/project/trace/RunnerInfoTraceNode.tsx new file mode 100644 index 00000000..73181663 --- /dev/null +++ b/karavan-app/src/main/webui/src/project/trace/RunnerInfoTraceNode.tsx @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React from 'react'; +import { + CodeBlock, CodeBlockCode, DataList, DataListCell, DataListItem, DataListItemCells, DataListItemRow, DataListWrapModifier, + DescriptionList, + DescriptionListDescription, + DescriptionListGroup, + DescriptionListTerm, Panel, PanelHeader, PanelMain, PanelMainBody +} from '@patternfly/react-core'; +import '../../designer/karavan.css'; + +interface Props { + trace: any +} + +export function RunnerInfoTraceNode (props: Props) { + + const type = props.trace?.message?.body?.type; + const body = props.trace?.message?.body?.value; + const headers: any[] = [...props.trace?.message?.headers]; + return ( + <Panel isScrollable> + <PanelMain tabIndex={0}> + <PanelHeader> + <DescriptionList isHorizontal> + <DescriptionListGroup> + <DescriptionListTerm>Headers</DescriptionListTerm> + </DescriptionListGroup> + <DataList aria-label="Compact data list example" isCompact> + {headers.map((header: any, index: number) => ( + <DataListItem key={header[0] + "-" + index} aria-labelledby="compact-item1"> + <DataListItemRow> + <DataListItemCells + dataListCells={[ + <DataListCell key="uid" >{header.key}</DataListCell>, + <DataListCell key="type">{header.type}</DataListCell>, + <DataListCell key="routeId" wrapModifier={DataListWrapModifier.truncate}> + {header.value} + </DataListCell>, + ]} + /> + </DataListItemRow> + </DataListItem>))} + </DataList> + <DescriptionListGroup> + <DescriptionListTerm>Body</DescriptionListTerm> + <DescriptionListDescription> + {type} + </DescriptionListDescription> + </DescriptionListGroup> + </DescriptionList> + </PanelHeader> + <PanelMainBody style={{padding: "0"}}> + <CodeBlock title="Body"> + <CodeBlockCode id="code-content">{body}</CodeBlockCode> + </CodeBlock> + </PanelMainBody> + </PanelMain> + </Panel> + ); +} diff --git a/karavan-app/src/main/webui/src/services/ServicesPage.tsx b/karavan-app/src/main/webui/src/services/ServicesPage.tsx index 725b2124..252fa667 100644 --- a/karavan-app/src/main/webui/src/services/ServicesPage.tsx +++ b/karavan-app/src/main/webui/src/services/ServicesPage.tsx @@ -32,7 +32,6 @@ import { } from '@patternfly/react-core'; import '../designer/karavan.css'; import RefreshIcon from '@patternfly/react-icons/dist/esm/icons/sync-alt-icon'; -import PlusIcon from '@patternfly/react-icons/dist/esm/icons/plus-icon'; import { Td, Th, @@ -143,7 +142,7 @@ export function ServicesPage () { <PageSection className="tools-section" padding={{default: 'noPadding'}}> <MainToolbar title={title()} tools={getTools()}/> </PageSection> - <PageSection isFilled className="kamelets-page"> + <PageSection isFilled className="scrolled-page"> {getServicesTable()} </PageSection> <ProjectLogPanel/> diff --git a/karavan-app/src/main/webui/src/shared/error/Error.ts b/karavan-app/src/main/webui/src/shared/error/Error.ts deleted file mode 100644 index b0e0dac3..00000000 --- a/karavan-app/src/main/webui/src/shared/error/Error.ts +++ /dev/null @@ -1,11 +0,0 @@ -export class Error { - field: string = ''; - message: string = ''; - code: string = ''; - - constructor(field: string, message: string, code: string) { - this.field = field; - this.message = message; - this.code = code; - } -} \ No newline at end of file diff --git a/karavan-app/src/main/webui/src/shared/error/ErrorResponse.ts b/karavan-app/src/main/webui/src/shared/error/ErrorResponse.ts deleted file mode 100644 index 70728e36..00000000 --- a/karavan-app/src/main/webui/src/shared/error/ErrorResponse.ts +++ /dev/null @@ -1,15 +0,0 @@ -import {Error} from "./Error"; - -export class ErrorResponse { - status: number = 0; - error: string = ''; - message: string = ''; - errors: Array<Error> = []; - - constructor(status: number, error: string, message: string, errors: Array<Error>) { - this.status = status; - this.error = error; - this.message = message; - this.errors = errors; - } -} \ No newline at end of file diff --git a/karavan-app/src/main/webui/src/shared/error/UseResponseErrorHandler.ts b/karavan-app/src/main/webui/src/shared/error/UseResponseErrorHandler.ts deleted file mode 100644 index 7e8c557a..00000000 --- a/karavan-app/src/main/webui/src/shared/error/UseResponseErrorHandler.ts +++ /dev/null @@ -1,43 +0,0 @@ -import {useState} from 'react'; -import {ErrorResponse} from "./ErrorResponse"; -import {Error} from "./Error"; -import {AxiosError} from "axios"; - -export function useResponseErrorHandler(errorResponseToFormFields: Map<string, string>, setError: any) { - const [globalErrors, setGlobalErrors] = useState<string[]>([]); - - function registerResponseErrors(axiosError: AxiosError) { - const errorResponse: ErrorResponse = axiosError.response?.data as ErrorResponse; - // Register field errors if field errors were returned - if (errorResponse.errors) { - // Reset global error - setGlobalErrors([]); - - // Register all field errors - errorResponse.errors?.forEach((error: Error) => { - // If field name was found in mapping table, register it to the form error - if (errorResponseToFormFields.get(error.field)) { - setError(errorResponseToFormFields.get(error.field), { - type: 'custom', - message: error.message - }); - } - // If field name was not found in mapping table, register it to the global error - else { - // Make copy as state shouldn't be mutated - setGlobalErrors(prevGlobalErrors => [...prevGlobalErrors, error.message]); - } - }); - } - // Register global error if no field errors were returned - else { - setGlobalErrors([errorResponse.message]); - } - } - - function resetGlobalErrors() { - setGlobalErrors([]); - } - - return [globalErrors, registerResponseErrors, resetGlobalErrors] as const; -} diff --git a/karavan-app/src/main/webui/src/templates/TemplatesPage.tsx b/karavan-app/src/main/webui/src/templates/TemplatesPage.tsx index f13242f1..c63df7b0 100644 --- a/karavan-app/src/main/webui/src/templates/TemplatesPage.tsx +++ b/karavan-app/src/main/webui/src/templates/TemplatesPage.tsx @@ -117,7 +117,7 @@ export function TemplatesPage () { <PageSection className="tools-section" padding={{default: 'noPadding'}}> <MainToolbar title={title()} tools={getTools()}/> </PageSection> - <PageSection isFilled className="kamelets-page"> + <PageSection isFilled className="scrolled-page"> {getProjectsTable()} </PageSection> </PageSection> diff --git a/karavan-app/src/main/webui/src/util/StringUtils.ts b/karavan-app/src/main/webui/src/util/StringUtils.ts index ced025d4..ec04e358 100644 --- a/karavan-app/src/main/webui/src/util/StringUtils.ts +++ b/karavan-app/src/main/webui/src/util/StringUtils.ts @@ -23,4 +23,37 @@ export function getPathParams(input: string): string[] { export function getShortCommit(commitId: string): string { return commitId ? commitId?.substring(0, 7) : "-"; -} \ No newline at end of file +} + +export function hasLowercase(password: string): boolean { + const pattern = /[a-z]/; + return pattern.test(password); +} + +export function hasUppercase(password: string): boolean { + const pattern = /[A-Z]/; + return pattern.test(password); +} + +export function hasDigit(password: string): boolean { + const pattern = /\d/; + return pattern.test(password); +} + +export function hasSpecialCharacter(password: string): boolean { + const pattern = /[@$!%*?&]/; + return pattern.test(password); +} + +export function hasMinimumLength(password: string, minLength: number = 8): boolean { + return password.length >= minLength; +} + + +export function isValidPassword(password: string): boolean { + return hasLowercase(password) && + hasUppercase(password) && + hasDigit(password) && + hasSpecialCharacter(password) && + hasMinimumLength(password); +}
