From b7a3ae231b64051e277597ab42312f6459fae1d4 Mon Sep 17 00:00:00 2001 From: Mir Arif Hasan Date: Wed, 15 May 2024 13:07:35 +0600 Subject: [PATCH 01/16] HSB-431 fix: email case sensitive in email provider (#4042) * feat: code level email insensitivity added * test: fix broken test case * chore: updated testcase for findUserByEmail in user module --------- Co-authored-by: Balu Babu --- .../src/admin/admin.service.spec.ts | 6 +++++- .../src/admin/admin.service.ts | 12 +++++++++--- .../src/shortcode/shortcode.service.ts | 5 ++++- .../team-invitation/team-invitation.service.ts | 9 +++++---- .../src/user/user.service.spec.ts | 4 ++-- .../src/user/user.service.ts | 18 +++++++++--------- 6 files changed, 34 insertions(+), 20 deletions(-) diff --git a/packages/hoppscotch-backend/src/admin/admin.service.spec.ts b/packages/hoppscotch-backend/src/admin/admin.service.spec.ts index a7ebc068..3cee03e9 100644 --- a/packages/hoppscotch-backend/src/admin/admin.service.spec.ts +++ b/packages/hoppscotch-backend/src/admin/admin.service.spec.ts @@ -121,6 +121,7 @@ describe('AdminService', () => { NOT: { inviteeEmail: { in: [dbAdminUsers[0].email], + mode: 'insensitive', }, }, }, @@ -229,7 +230,10 @@ describe('AdminService', () => { expect(mockPrisma.invitedUsers.deleteMany).toHaveBeenCalledWith({ where: { - inviteeEmail: { in: [invitedUsers[0].inviteeEmail] }, + inviteeEmail: { + in: [invitedUsers[0].inviteeEmail], + mode: 'insensitive', + }, }, }); expect(result).toEqualRight(true); diff --git a/packages/hoppscotch-backend/src/admin/admin.service.ts b/packages/hoppscotch-backend/src/admin/admin.service.ts index 0bed6613..982b7c79 100644 --- a/packages/hoppscotch-backend/src/admin/admin.service.ts +++ b/packages/hoppscotch-backend/src/admin/admin.service.ts @@ -89,12 +89,17 @@ export class AdminService { adminEmail: string, inviteeEmail: string, ) { - if (inviteeEmail == adminEmail) return E.left(DUPLICATE_EMAIL); + if (inviteeEmail.toLowerCase() == adminEmail.toLowerCase()) { + return E.left(DUPLICATE_EMAIL); + } if (!validateEmail(inviteeEmail)) return E.left(INVALID_EMAIL); const alreadyInvitedUser = await this.prisma.invitedUsers.findFirst({ where: { - inviteeEmail: inviteeEmail, + inviteeEmail: { + equals: inviteeEmail, + mode: 'insensitive', + }, }, }); if (alreadyInvitedUser != null) return E.left(USER_ALREADY_INVITED); @@ -159,7 +164,7 @@ export class AdminService { try { await this.prisma.invitedUsers.deleteMany({ where: { - inviteeEmail: { in: inviteeEmails }, + inviteeEmail: { in: inviteeEmails, mode: 'insensitive' }, }, }); return E.right(true); @@ -189,6 +194,7 @@ export class AdminService { NOT: { inviteeEmail: { in: userEmailObjs.map((user) => user.email), + mode: 'insensitive', }, }, }, diff --git a/packages/hoppscotch-backend/src/shortcode/shortcode.service.ts b/packages/hoppscotch-backend/src/shortcode/shortcode.service.ts index e6a02fdb..e8642305 100644 --- a/packages/hoppscotch-backend/src/shortcode/shortcode.service.ts +++ b/packages/hoppscotch-backend/src/shortcode/shortcode.service.ts @@ -299,7 +299,10 @@ export class ShortcodeService implements UserDataHandler, OnModuleInit { where: userEmail ? { User: { - email: userEmail, + email: { + equals: userEmail, + mode: 'insensitive', + }, }, } : undefined, diff --git a/packages/hoppscotch-backend/src/team-invitation/team-invitation.service.ts b/packages/hoppscotch-backend/src/team-invitation/team-invitation.service.ts index f7749070..db292749 100644 --- a/packages/hoppscotch-backend/src/team-invitation/team-invitation.service.ts +++ b/packages/hoppscotch-backend/src/team-invitation/team-invitation.service.ts @@ -75,12 +75,13 @@ export class TeamInvitationService { if (!isEmailValid) return E.left(INVALID_EMAIL); try { - const teamInvite = await this.prisma.teamInvitation.findUniqueOrThrow({ + const teamInvite = await this.prisma.teamInvitation.findFirstOrThrow({ where: { - teamID_inviteeEmail: { - inviteeEmail: inviteeEmail, - teamID: teamID, + inviteeEmail: { + equals: inviteeEmail, + mode: 'insensitive', }, + teamID, }, }); diff --git a/packages/hoppscotch-backend/src/user/user.service.spec.ts b/packages/hoppscotch-backend/src/user/user.service.spec.ts index b5093831..26f56780 100644 --- a/packages/hoppscotch-backend/src/user/user.service.spec.ts +++ b/packages/hoppscotch-backend/src/user/user.service.spec.ts @@ -149,7 +149,7 @@ beforeEach(() => { describe('UserService', () => { describe('findUserByEmail', () => { test('should successfully return a valid user given a valid email', async () => { - mockPrisma.user.findUniqueOrThrow.mockResolvedValueOnce(user); + mockPrisma.user.findFirst.mockResolvedValueOnce(user); const result = await userService.findUserByEmail( 'dwight@dundermifflin.com', @@ -158,7 +158,7 @@ describe('UserService', () => { }); test('should return a null user given a invalid email', async () => { - mockPrisma.user.findUniqueOrThrow.mockRejectedValueOnce('NotFoundError'); + mockPrisma.user.findFirst.mockResolvedValueOnce(null); const result = await userService.findUserByEmail('jim@dundermifflin.com'); expect(result).resolves.toBeNone; diff --git a/packages/hoppscotch-backend/src/user/user.service.ts b/packages/hoppscotch-backend/src/user/user.service.ts index 26018894..6a53f8a9 100644 --- a/packages/hoppscotch-backend/src/user/user.service.ts +++ b/packages/hoppscotch-backend/src/user/user.service.ts @@ -62,16 +62,16 @@ export class UserService { * @returns Option of found User */ async findUserByEmail(email: string): Promise> { - try { - const user = await this.prisma.user.findUniqueOrThrow({ - where: { - email: email, + const user = await this.prisma.user.findFirst({ + where: { + email: { + equals: email, + mode: 'insensitive', }, - }); - return O.some(user); - } catch (error) { - return O.none; - } + }, + }); + if (!user) return O.none; + return O.some(user); } /** From 5c214a8da03830cacd9598c23fcd963e0cdb2b49 Mon Sep 17 00:00:00 2001 From: Nivedin <53208152+nivedin@users.noreply.github.com> Date: Mon, 3 Jun 2024 14:28:58 +0530 Subject: [PATCH 02/16] fix: persist interceptor state for logged out user (#4060) * fix: persist interceptor state for loged out user * refactor: update interceptor section in settings page * chore: update settings page interceptor section UI * chore: minoir ui and code refactor --- .../src/components/app/Interceptor.vue | 14 ++- .../src/components/settings/Extension.vue | 32 ------ .../src/components/settings/Proxy.vue | 33 +------ .../src/newstore/settings.ts | 97 ++++++++----------- .../hoppscotch-common/src/pages/settings.vue | 6 ++ 5 files changed, 66 insertions(+), 116 deletions(-) diff --git a/packages/hoppscotch-common/src/components/app/Interceptor.vue b/packages/hoppscotch-common/src/components/app/Interceptor.vue index 42777031..d66e1408 100644 --- a/packages/hoppscotch-common/src/components/app/Interceptor.vue +++ b/packages/hoppscotch-common/src/components/app/Interceptor.vue @@ -1,6 +1,6 @@ diff --git a/packages/hoppscotch-common/src/components/settings/Proxy.vue b/packages/hoppscotch-common/src/components/settings/Proxy.vue index e8c2ddd1..270706b0 100644 --- a/packages/hoppscotch-common/src/components/settings/Proxy.vue +++ b/packages/hoppscotch-common/src/components/settings/Proxy.vue @@ -8,16 +8,6 @@ :label="t('app.proxy_privacy_policy')" />. -
-
- - {{ t("settings.proxy_use_toggle") }} - -
-
+ interceptorService.currentInterceptorID.value === + proxyInterceptor.interceptorID +) const clearIcon = refAutoReset( IconRotateCCW, diff --git a/packages/hoppscotch-common/src/newstore/settings.ts b/packages/hoppscotch-common/src/newstore/settings.ts index 778de8d4..eb582f2f 100644 --- a/packages/hoppscotch-common/src/newstore/settings.ts +++ b/packages/hoppscotch-common/src/newstore/settings.ts @@ -1,8 +1,6 @@ import { cloneDeep, defaultsDeep, has } from "lodash-es" import { Observable } from "rxjs" import { distinctUntilChanged, pluck } from "rxjs/operators" -import { nextTick } from "vue" -import { platform } from "~/platform" import type { KeysMatching } from "~/types/ts-utils" import DispatchingStore, { defineDispatchers } from "./DispatchingStore" @@ -70,63 +68,52 @@ export type SettingsDef = { HAS_OPENED_SPOTLIGHT: boolean } -export const getDefaultSettings = (): SettingsDef => { - const defaultSettings: SettingsDef = { - syncCollections: true, - syncHistory: true, - syncEnvironments: true, +export const getDefaultSettings = (): SettingsDef => ({ + syncCollections: true, + syncHistory: true, + syncEnvironments: true, - WRAP_LINES: { - httpRequestBody: true, - httpResponseBody: true, - httpHeaders: true, - httpParams: true, - httpUrlEncoded: true, - httpPreRequest: true, - httpTest: true, - httpRequestVariables: true, - graphqlQuery: true, - graphqlResponseBody: true, - graphqlHeaders: false, - graphqlVariables: false, - graphqlSchema: true, - importCurl: true, - codeGen: true, - cookie: true, - }, + WRAP_LINES: { + httpRequestBody: true, + httpResponseBody: true, + httpHeaders: true, + httpParams: true, + httpUrlEncoded: true, + httpPreRequest: true, + httpTest: true, + httpRequestVariables: true, + graphqlQuery: true, + graphqlResponseBody: true, + graphqlHeaders: false, + graphqlVariables: false, + graphqlSchema: true, + importCurl: true, + codeGen: true, + cookie: true, + }, - CURRENT_INTERCEPTOR_ID: "", + // Set empty because interceptor module will set the default value + CURRENT_INTERCEPTOR_ID: "", - // TODO: Interceptor related settings should move under the interceptor systems - PROXY_URL: "https://proxy.hoppscotch.io/", - URL_EXCLUDES: { - auth: true, - httpUser: true, - httpPassword: true, - bearerToken: true, - oauth2Token: true, - }, - THEME_COLOR: "indigo", - BG_COLOR: "system", - TELEMETRY_ENABLED: true, - EXPAND_NAVIGATION: false, - SIDEBAR: true, - SIDEBAR_ON_LEFT: false, - COLUMN_LAYOUT: true, + // TODO: Interceptor related settings should move under the interceptor systems + PROXY_URL: "https://proxy.hoppscotch.io/", + URL_EXCLUDES: { + auth: true, + httpUser: true, + httpPassword: true, + bearerToken: true, + oauth2Token: true, + }, + THEME_COLOR: "indigo", + BG_COLOR: "system", + TELEMETRY_ENABLED: true, + EXPAND_NAVIGATION: false, + SIDEBAR: true, + SIDEBAR_ON_LEFT: false, + COLUMN_LAYOUT: true, - HAS_OPENED_SPOTLIGHT: false, - } - - // Wait for platform to initialize before setting CURRENT_INTERCEPTOR_ID - nextTick(() => { - applySetting( - "CURRENT_INTERCEPTOR_ID", - platform?.interceptors.default || "browser" - ) - }) - - return defaultSettings -} + HAS_OPENED_SPOTLIGHT: false, +}) type ApplySettingPayload = { [K in keyof SettingsDef]: { diff --git a/packages/hoppscotch-common/src/pages/settings.vue b/packages/hoppscotch-common/src/pages/settings.vue index 2285e68f..cab99e31 100644 --- a/packages/hoppscotch-common/src/pages/settings.vue +++ b/packages/hoppscotch-common/src/pages/settings.vue @@ -98,6 +98,12 @@

+
+

+ {{ t("settings.interceptor") }} +

+ +

{{ settings.entryTitle(t) }} From d0350ec789c8a84b6571f6f5eac87be2c495bbc0 Mon Sep 17 00:00:00 2001 From: Nivedin <53208152+nivedin@users.noreply.github.com> Date: Mon, 3 Jun 2024 14:29:48 +0530 Subject: [PATCH 03/16] fix: add previous value as optional in test schema env diff (#4071) * fix: add previous value as optional * refactor: remove method chaining for consistency --------- Co-authored-by: jamesgeorge007 --- .../src/services/persistence/validation-schemas/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/hoppscotch-common/src/services/persistence/validation-schemas/index.ts b/packages/hoppscotch-common/src/services/persistence/validation-schemas/index.ts index 5efdf765..36dc5162 100644 --- a/packages/hoppscotch-common/src/services/persistence/validation-schemas/index.ts +++ b/packages/hoppscotch-common/src/services/persistence/validation-schemas/index.ts @@ -400,7 +400,7 @@ const HoppTestResultSchema = z (x) => "secret" in x && !x.secret ).and( z.object({ - previousValue: z.string(), + previousValue: z.optional(z.string()), }) ) ), @@ -415,7 +415,7 @@ const HoppTestResultSchema = z (x) => "secret" in x && !x.secret ).and( z.object({ - previousValue: z.string(), + previousValue: z.optional(z.string()), }) ) ), From bece13e6b0987da85d42292fa92c5e91c5c6d01f Mon Sep 17 00:00:00 2001 From: Nivedin <53208152+nivedin@users.noreply.github.com> Date: Mon, 3 Jun 2024 14:30:31 +0530 Subject: [PATCH 04/16] fix: precision lost when json is beautified (#4086) --- packages/hoppscotch-common/src/components/http/RawBody.vue | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/hoppscotch-common/src/components/http/RawBody.vue b/packages/hoppscotch-common/src/components/http/RawBody.vue index a3bffd95..8ccc5f31 100644 --- a/packages/hoppscotch-common/src/components/http/RawBody.vue +++ b/packages/hoppscotch-common/src/components/http/RawBody.vue @@ -89,6 +89,7 @@ import { readFileAsText } from "~/helpers/functional/files" import xmlFormat from "xml-formatter" import { useNestedSetting } from "~/composables/settings" import { toggleNestedSetting } from "~/newstore/settings" +import * as LJSON from "lossless-json" type PossibleContentTypes = Exclude< ValidContentTypes, @@ -187,8 +188,8 @@ const prettifyRequestBody = () => { let prettifyBody = "" try { if (body.value.contentType.endsWith("json")) { - const jsonObj = JSON.parse(rawParamsBody.value as string) - prettifyBody = JSON.stringify(jsonObj, null, 2) + const jsonObj = LJSON.parse(rawParamsBody.value as string) + prettifyBody = LJSON.stringify(jsonObj, undefined, 2) as string } else if (body.value.contentType === "application/xml") { prettifyBody = prettifyXML(rawParamsBody.value as string) } From b601a2f55f04a15316c3b2d9634f6bbb8c3947a1 Mon Sep 17 00:00:00 2001 From: Joel Jacob Stephen <70131076+JoelJacobStephen@users.noreply.github.com> Date: Mon, 3 Jun 2024 14:31:44 +0530 Subject: [PATCH 05/16] fix(sh-admin): saving changes to server configurations post a failed attempt will require a page reload in dashboard (#4081) * fix: resolved an issue with server restart component * refactor: early return if any mutation fails when initiating server restart * fix: ensure further attempts go through after a failed reset configs action --------- Co-authored-by: jamesgeorge007 --- .../hoppscotch-sh-admin/src/components.d.ts | 6 ---- .../src/components/settings/Reset.vue | 6 +++- .../src/components/settings/ServerRestart.vue | 30 ++++++++++++++----- .../src/pages/settings.vue | 1 + 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/packages/hoppscotch-sh-admin/src/components.d.ts b/packages/hoppscotch-sh-admin/src/components.d.ts index eaaf88bf..d26357c8 100644 --- a/packages/hoppscotch-sh-admin/src/components.d.ts +++ b/packages/hoppscotch-sh-admin/src/components.d.ts @@ -17,7 +17,6 @@ declare module '@vue/runtime-core' { HoppButtonPrimary: typeof import('@hoppscotch/ui')['HoppButtonPrimary'] HoppButtonSecondary: typeof import('@hoppscotch/ui')['HoppButtonSecondary'] HoppSmartAnchor: typeof import('@hoppscotch/ui')['HoppSmartAnchor'] - HoppSmartAutoComplete: typeof import('@hoppscotch/ui')['HoppSmartAutoComplete'] HoppSmartConfirmModal: typeof import('@hoppscotch/ui')['HoppSmartConfirmModal'] HoppSmartInput: typeof import('@hoppscotch/ui')['HoppSmartInput'] HoppSmartItem: typeof import('@hoppscotch/ui')['HoppSmartItem'] @@ -26,14 +25,9 @@ declare module '@vue/runtime-core' { HoppSmartPicture: typeof import('@hoppscotch/ui')['HoppSmartPicture'] HoppSmartSpinner: typeof import('@hoppscotch/ui')['HoppSmartSpinner'] HoppSmartTab: typeof import('@hoppscotch/ui')['HoppSmartTab'] - HoppSmartTable: typeof import('@hoppscotch/ui')['HoppSmartTable'] HoppSmartTabs: typeof import('@hoppscotch/ui')['HoppSmartTabs'] HoppSmartToggle: typeof import('@hoppscotch/ui')['HoppSmartToggle'] - IconLucideArrowLeft: typeof import('~icons/lucide/arrow-left')['default'] - IconLucideChevronDown: typeof import('~icons/lucide/chevron-down')['default'] IconLucideInbox: typeof import('~icons/lucide/inbox')['default'] - IconLucideSearch: typeof import('~icons/lucide/search')['default'] - IconLucideUser: typeof import('~icons/lucide/user')['default'] SettingsAuthProvider: typeof import('./components/settings/AuthProvider.vue')['default'] SettingsConfigurations: typeof import('./components/settings/Configurations.vue')['default'] SettingsDataSharing: typeof import('./components/settings/DataSharing.vue')['default'] diff --git a/packages/hoppscotch-sh-admin/src/components/settings/Reset.vue b/packages/hoppscotch-sh-admin/src/components/settings/Reset.vue index ddfa3538..cc6e2b1b 100644 --- a/packages/hoppscotch-sh-admin/src/components/settings/Reset.vue +++ b/packages/hoppscotch-sh-admin/src/components/settings/Reset.vue @@ -24,7 +24,11 @@

- + (); + // Mutations to update or reset server configurations and audit logs const resetInfraConfigsMutation = useMutation(ResetInfraConfigsDocument); const updateInfraConfigsMutation = useMutation(UpdateInfraConfigsDocument); @@ -73,28 +77,40 @@ const startCountdown = () => { }, 1000); }; +const triggerComponentUnMount = () => emit('mutationFailure'); + // Call relevant mutations on component mount and initiate server restart onMounted(async () => { - let success = true; - if (props.reset) { - success = await resetInfraConfigs(resetInfraConfigsMutation); - if (!success) return; + const resetInfraConfigsResult = await resetInfraConfigs( + resetInfraConfigsMutation + ); + + if (!resetInfraConfigsResult) { + return triggerComponentUnMount(); + } } else { const infraResult = await updateInfraConfigs(updateInfraConfigsMutation); - if (!infraResult) return; + if (!infraResult) { + return triggerComponentUnMount(); + } const authResult = await updateAuthProvider( updateAllowedAuthProviderMutation ); - if (!authResult) return; + + if (!authResult) { + return triggerComponentUnMount(); + } const dataSharingResult = await updateDataSharingConfigs( toggleDataSharingMutation ); - if (!dataSharingResult) return; + if (!dataSharingResult) { + return triggerComponentUnMount(); + } } restart.value = true; diff --git a/packages/hoppscotch-sh-admin/src/pages/settings.vue b/packages/hoppscotch-sh-admin/src/pages/settings.vue index 23cddb6e..78393541 100644 --- a/packages/hoppscotch-sh-admin/src/pages/settings.vue +++ b/packages/hoppscotch-sh-admin/src/pages/settings.vue @@ -37,6 +37,7 @@ Date: Mon, 3 Jun 2024 12:05:32 +0300 Subject: [PATCH 06/16] locale: update tr i18n translations (#4057) * Update tr.json i18n * Update tr.json i18n * Update tr.json l10n * Update tr.json l10n based on en.json * Update tr.json * Update tr.json --- packages/hoppscotch-common/locales/tr.json | 126 +++++++++++++++------ 1 file changed, 90 insertions(+), 36 deletions(-) diff --git a/packages/hoppscotch-common/locales/tr.json b/packages/hoppscotch-common/locales/tr.json index e61da251..3bfa3e92 100644 --- a/packages/hoppscotch-common/locales/tr.json +++ b/packages/hoppscotch-common/locales/tr.json @@ -17,15 +17,17 @@ "dismiss": "Boşver", "dont_save": "Don't save", "download_file": "Dosyayı Indir", - "drag_to_reorder": "Drag to reorder", + "drag_to_reorder": "Yeniden sıralamak için sürükleyin", "duplicate": "Klonla", "edit": "Düzenle", - "filter": "Filter", + "filter": "Filtre", "go_back": "Geri git", - "go_forward": "Go forward", + "go_forward": "İleri git", "group_by": "Group by", + "hide_secret": "Hide secret", "label": "Etiket", "learn_more": "Daha fazla bilgi edin", + "download_here": "Download here", "less": "Daha az", "more": "Daha fazla", "new": "Yeni", @@ -42,8 +44,9 @@ "scroll_to_top": "Scroll to top", "search": "Arama", "send": "Gönder", - "share": "Share", + "share": "Paylaş", "start": "Başla", + "show_secret": "Show secret", "starting": "Starting", "stop": "Dur", "to_close": "kapatmak için", @@ -101,16 +104,18 @@ "auth": { "account_exists": "Farklı kimlik bilgilerine sahip hesap var - Her iki hesabı birbirine bağlamak için giriş yapın", "all_sign_in_options": "Tüm oturum açma seçenekleri", + "continue_with_auth_provider": "{provider} ile devam et", "continue_with_email": "E-posta ile devam et", - "continue_with_github": "GitHub hesabı ile devam et", - "continue_with_google": "Google hesabı ile devam et", - "continue_with_microsoft": "Microsoft hesabı ile devam et", + "continue_with_github": "GitHub ile devam et", + "continue_with_github_enterprise": "GitHub Enterprise ile devam et", + "continue_with_google": "Google ile devam et", + "continue_with_microsoft": "Microsoft ile devam et", "email": "E-posta", "logged_out": "Çıkış yapıldı", - "login": "Giriş yap", + "login": "Giriş", "login_success": "Başarıyla giriş yapıldı", "login_to_hoppscotch": "Hoppscotch'a giriş yapın", - "logout": "Çıkış yap", + "logout": "Çıkış", "re_enter_email": "E-mail adresinizi yeniden girin", "send_magic_link": "Sihirli bir bağlantı gönder", "sync": "Senkronizasyon", @@ -135,20 +140,42 @@ "redirect_no_token_endpoint": "No Token Endpoint Defined", "something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect", "something_went_wrong_on_token_generation": "Something went wrong on token generation", - "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed" + "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed", + "grant_type": "Grant Type", + "grant_type_auth_code": "Authorization Code", + "token_fetched_successfully": "Token fetched successfully", + "token_fetch_failed": "Failed to fetch token", + "validation_failed": "Validation Failed, please check the form fields", + "label_authorization_endpoint": "Authorization Endpoint", + "label_client_id": "Client ID", + "label_client_secret": "Client Secret", + "label_code_challenge": "Code Challenge", + "label_code_challenge_method": "Code Challenge Method", + "label_code_verifier": "Code Verifier", + "label_scopes": "Scopes", + "label_token_endpoint": "Token Endpoint", + "label_use_pkce": "Use PKCE", + "label_implicit": "Implicit", + "label_password": "Password", + "label_username": "Username", + "label_auth_code": "Authorization Code", + "label_client_credentials": "Client Credentials" }, "pass_key_by": "Şunla anahtar ekleyin", + "pass_by_query_params_label": "Query Parameters", + "pass_by_headers_label": "Headers", "password": "Parola", "save_to_inherit": "Please save this request in any collection to inherit the authorization", "token": "Jeton", - "type": "Yetki türü", + "type": "Yetki Türü", "username": "Kullanıcı adı" }, "collection": { "created": "Koleksiyon oluşturuldu", "different_parent": "Cannot reorder collection with different parent", "edit": "Koleksiyonu düzenle", - "import_or_create": "Import or create a collection", + "import_or_create": "Koleksiyon oluşturun veya içe aktarın", + "import_collection":"Koleksiyonu İçe Aktar", "invalid_name": "Lütfen koleksiyon için geçerli bir ad girin", "invalid_root_move": "Collection already in the root", "moved": "Başarıyla taşındı", @@ -158,15 +185,13 @@ "new": "Yeni koleksiyon", "order_changed": "Collection Order Updated", "properties": "Collection Properties", - "properties_updated": "Collection Properties Updated", + "properties_updated": "Koleksiyon Özellikleri Güncellendi", "renamed": "Koleksiyon yeniden adlandırıldı", "request_in_use": "Kullanımda istek", "save_as": "Farklı kaydet", "save_to_collection": "Save to Collection", "select": "Bir koleksiyon Seçin", - "select_location": "Konum seçin", - "select_team": "Bir takım seçin", - "team_collections": "Takım koleksiyonları" + "select_location": "Konum seçin" }, "confirm": { "close_unsaved_tab": "Are you sure you want to close this tab?", @@ -237,14 +262,15 @@ "pending_invites": "Bu takım için bekleyen bir istek yok", "profile": "Bu profili görüntülemek için giriş yapın", "protocols": "Protokoller boş", + "request_variables": "This request does not have any request variables", "schema": "Bir GraphQL uç noktasına bağlanma", + "secret_environments": "Secrets are not synced to Hoppscotch", "shared_requests": "Shared requests are empty", "shared_requests_logout": "Login to view your shared requests or create a new one", "subscription": "Subscriptions are empty", "team_name": "Takım adı boş", "teams": "Takımlar boş", - "tests": "Bu istek için test yok", - "shortcodes": "Shortcodes are empty" + "tests": "Bu istek için test yok" }, "environment": { "add_to_global": "Globale ekle", @@ -260,24 +286,27 @@ "import_or_create": "Import or create a environment", "invalid_name": "Lütfen ortam için geçerli bir ad girin", "list": "Environment variables", - "my_environments": "My Environments", + "my_environments": "Ortamlarım", "name": "Name", "nested_overflow": "İç içe ortam değişkenleri 10 düzeyle sınırlıdır", "new": "Yeni ortam", - "no_active_environment": "No active environment", + "no_active_environment": "Aktif ortam yok", "no_environment": "Ortam yok", "no_environment_description": "Hiçbir ortam seçilmedi. Aşağıdaki değişkenlerle ne yapacağınızı seçin.", "quick_peek": "Environment Quick Peek", "replace_with_variable": "Replace with variable", "scope": "Scope", + "secrets": "Secrets", + "secret_value": "Secret value", "select": "Ortam seçin", "set": "Set environment", "set_as_environment": "Set as environment", "team_environments": "Team Environments", "title": "Ortamlar", "updated": "Ortam güncellendi", - "value": "Value", - "variable": "Variable", + "value": "Değer", + "variable": "Değişken", + "variables": "Değişkenler", "variable_list": "Değişken listesi" }, "error": { @@ -289,6 +318,7 @@ "danger_zone": "Danger zone", "delete_account": "Your account is currently an owner in these teams:", "delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.", + "empty_profile_name": "Profile name cannot be empty", "empty_req_name": "Boş İstek Adı", "f12_details": "(Ayrıntılar için F12)", "gql_prettify_invalid_query": "Geçersiz bir sorgu güzelleştirilemedi, sorgu sözdizimi hatalarını çözüp tekrar deneyin", @@ -296,7 +326,8 @@ "incorrect_email": "Geçersiz e-posta", "invalid_link": "Geçersiz bağlantı", "invalid_link_description": "Tıkladığınız linkin süresi geçmiş veya geçersiz", - "json_parsing_failed": "Invalid JSON", + "invalid_embed_link": "The embed does not exist or is invalid.", + "json_parsing_failed": "Geçersiz JSON", "json_prettify_invalid_body": "Geçersiz bir gövde güzelleştirilemedi, JSON sözdizimi hatalarını çözüp tekrar deneyin", "network_error": "Görünene göre bir ağ hatası var. Lütfen tekrar deneyin.", "network_fail": "İstek gönderilemedi", @@ -307,22 +338,26 @@ "page_not_found": "This page could not be found", "please_install_extension": "Please install the extension and add origin to the extension.", "proxy_error": "Proxy error", + "same_profile_name": "Updated profile name is same as the current profile name", "script_fail": "Ön istek komut dosyası çalıştırılamadı", "something_went_wrong": "Bir şeyler yanlış gitti", - "test_script_fail": "Could not execute post-request script" + "test_script_fail": "Could not execute post-request script", + "reading_files": "Error while reading one or more files." }, "export": { "as_json": "JSON olarak dışa aktar", "create_secret_gist": "Gizli Gist oluştur", + "create_secret_gist_tooltip_text": "Export as secret Gist", "failed": "Something went wrong while exporting", - "gist_created": "Gist oluşturuldu", "require_github": "Gizli Gist oluşturmak için GitHub ile giriş yapın", + "secret_gist_success": "Successfully exported as secret Gist", + "success": "Successfully exported", "title": "Dışarı Aktar" }, "filter": { - "all": "All", - "none": "None", - "starred": "Starred" + "all": "Tümü", + "none": "Hiçbiri", + "starred": "Yıldızlı" }, "folder": { "created": "Klasör oluşturuldu", @@ -339,7 +374,8 @@ "mutations": "Mutasyonlar", "schema": "Şema", "subscriptions": "Abonelikler", - "switch_connection": "Switch connection" + "switch_connection": "Switch connection", + "url_placeholder": "Enter a GraphQL endpoint URL" }, "graphql_collections": { "title": "GraphQL Collections" @@ -379,6 +415,8 @@ "environments_from_gist": "Import From Gist", "environments_from_gist_description": "Import Hoppscotch Environments From Gist", "failed": "İçe aktarılamadı", + "file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of 10MB. Only the first {files} selected will be imported", + "file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of 10MB. Please select another file.", "from_file": "Import from File", "from_gist": "Gist'ten içe aktar", "from_gist_description": "Gist ile içe aktar", @@ -405,12 +443,15 @@ "json_description": "Import collections from a Hoppscotch Collections JSON file", "postman_environment": "Postman Environment", "postman_environment_description": "Import Postman Environment from a JSON file", + "success": "Successfully imported", "title": "İçe aktar" }, "inspections": { "description": "Inspect possible errors", "environment": { "add_environment": "Add to Environment", + "add_environment_value": "Add value", + "empty_value": "Environment value is empty for the variable '{variable}' ", "not_found": "Environment variable “{environment}” not found." }, "header": { @@ -546,6 +587,7 @@ "raw_body": "Ham istek gövdesi", "rename": "Rename Request", "renamed": "Yeniden adlandırılmış istek", + "request_variables": "Request variables", "run": "Çalıştır", "save": "Kaydet", "save_as": "Farklı kaydet", @@ -557,9 +599,9 @@ "title": "İstek", "type": "İstek türü", "url": "URL", + "url_placeholder": "Enter a URL or paste a cURL command", "variables": "Değişkenler", - "view_my_links": "View my links", - "copy_link": "Bağlantıyı kopyala" + "view_my_links": "View my links" }, "response": { "audio": "Audio", @@ -611,7 +653,7 @@ "profile_description": "Profil detaylarını güncelle", "profile_email": "E-posta adresi", "profile_name": "Profil ismi", - "proxy": "vekil", + "proxy": "Proxy", "proxy_url": "Proxy URL'si", "proxy_use_toggle": "İstek göndermek için proxy ara yazılımını kullanın", "read_the": "Oku", @@ -704,8 +746,7 @@ "send_request": "İstek gönder", "share_request": "Share Request", "show_code": "Generate code snippet", - "title": "İstek", - "copy_request_link": "İstek bağlantısını kopyala" + "title": "İstek" }, "response": { "copy": "Copy response to clipboard", @@ -726,6 +767,11 @@ "more": "Daha fazla göster", "sidebar": "Kenar çubuğunu göster" }, + "site_protection": { + "error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status", + "login_to_continue": "Login to continue", + "login_to_continue_description": "You need to be logged in to access this Hoppscotch Enterprise Instance." + }, "socketio": { "communication": "İletişim", "connection_not_authorized": "This SocketIO connection does not use any authentication.", @@ -763,6 +809,13 @@ "invite": "Invite your friends to Hoppscotch", "title": "Miscellaneous" }, + "phrases": { + "create_environment": "Create environment", + "create_workspace": "Create workspace", + "import_collections": "Import collections", + "share_request": "Share request", + "try": "Try" + }, "request": { "save_as_new": "Save as new request", "select_method": "Select method", @@ -867,7 +920,6 @@ "forum": "Sorular sorun ve cevaplar alın", "github": "Bizi Github'da takip edin", "shortcuts": "Uygulamaya daha hızlı göz atın", - "team": "Takımla iletişim kurun", "title": "Destek", "twitter": "Bizi Twitter'da takip edin" }, @@ -888,6 +940,7 @@ "queries": "Sorgular", "query": "Sorgu", "schema": "Schema", + "share_tab_request": "Share tab request", "shared_requests": "Shared Requests", "socketio": "Socket.IO", "sse": "SSE", @@ -917,7 +970,6 @@ "invite_tooltip": "İnsanları bu çalışma alanına davet edin", "invited_to_team": "{owner} seni {team} takımına davet etti.", "join": "Davet kabul edildi", - "join_beta": "Takımlara erişmek için beta programına katılın.", "join_team": "{team}'e katıl", "joined_team": "{team} takımına katıldın", "joined_team_description": "Artık bu takımın bir üyesisin", @@ -946,6 +998,7 @@ "permissions": "İzinler", "same_target_destination": "Same target and destination", "saved": "Takım kaydedildi", + "search_title": "Team Requests", "select_a_team": "Takım seç", "success_invites": "Success invites", "title": "Başlık", @@ -976,6 +1029,7 @@ }, "workspace": { "change": "Change workspace", + "other_workspaces": "My Workspaces", "personal": "My Workspace", "team": "Team Workspace", "title": "Workspaces" From 5805826994ac982ef9705de75d08488efaabcd1b Mon Sep 17 00:00:00 2001 From: Joel Jacob Stephen <70131076+JoelJacobStephen@users.noreply.github.com> Date: Mon, 3 Jun 2024 17:17:46 +0530 Subject: [PATCH 07/16] refactor(sh-admin): improved error handling and dynamic user actions in admin dashboard (#4044) * feat: new helper functions for better error management * refactor: new i18n strings * refactor: better error handling in invite modal and members component * refactor: better user management * refactor: better error handling in config handler * refactor: updated logic of dynamic action row * refactor: better naming for computed properties * feat: new error message when an admin tries to invite himself * refactor: updated error message when user is already invited * refactor: reverted i18n string for user already invited back to the old string * refactor: removed show prop from invite modal * refactor: improved implementation for getting the compiled error messages * feat: new error message when email inputted is of an invalid format * refactor: minor optimization --------- Co-authored-by: jamesgeorge007 --- packages/hoppscotch-sh-admin/locales/en.json | 4 +- .../hoppscotch-sh-admin/src/components.d.ts | 83 ++++++++++--------- .../src/components/teams/Members.vue | 9 +- .../src/components/users/InviteModal.vue | 10 --- .../src/composables/useConfigHandler.ts | 8 +- .../hoppscotch-sh-admin/src/helpers/errors.ts | 56 ++++++++++++- .../src/helpers/userManagement.ts | 18 ++-- .../src/pages/users/index.vue | 39 ++++++--- 8 files changed, 154 insertions(+), 73 deletions(-) diff --git a/packages/hoppscotch-sh-admin/locales/en.json b/packages/hoppscotch-sh-admin/locales/en.json index 68ce20b6..95189969 100644 --- a/packages/hoppscotch-sh-admin/locales/en.json +++ b/packages/hoppscotch-sh-admin/locales/en.json @@ -14,6 +14,7 @@ "client_id": "CLIENT ID", "client_secret": "CLIENT SECRET", "description": "Configure authentication providers for your server", + "provider_not_specified": "Please enable at least one authentication provider", "scope": "SCOPE", "tenant": "TENANT", "title": "Authentication Providers", @@ -146,6 +147,7 @@ "email_failure": "Failed to send invitation", "email_signin_failure": "Failed to login with Email", "email_success": "Email invitation sent successfully", + "emails_cannot_be_same": "You cannot invite yourself, please choose a different email address!!", "enter_team_email": "Please enter email of workspace owner!!", "error": "Something went wrong", "error_auth_providers": "Unable to load auth providers", @@ -171,6 +173,7 @@ "remove_admin_from_users_success": "Admin status removed from selected users!!", "remove_admin_to_delete_user": "Remove admin privilege to delete the user!!", "remove_owner_to_delete_user": "Remove team ownership status to delete the user!!", + "remove_owner_failure_only_one_owner": "Failed to remove member. There should be atleast one owner in a team!!", "remove_admin_for_deletion": "Remove admin status before attempting deletion!!", "remove_owner_for_deletion": "One or more users are team owners. Update ownership before deletion!!", "remove_invitee_failure": "Removal of invitee failed!!", @@ -193,7 +196,6 @@ "sign_in_options": "All sign in option", "sign_out": "Sign out", "team_name_too_short": "Workspace name should be atleast 6 characters long!!", - "team_name_long": "Workspace name should be atleast 6 characters long!!", "user_already_invited": "Failed to send invite. User is already invited!!", "user_not_found": "User not found in the infra!!", "users_to_admin_success": "Selected users are elevated to admin status!!", diff --git a/packages/hoppscotch-sh-admin/src/components.d.ts b/packages/hoppscotch-sh-admin/src/components.d.ts index d26357c8..271876ac 100644 --- a/packages/hoppscotch-sh-admin/src/components.d.ts +++ b/packages/hoppscotch-sh-admin/src/components.d.ts @@ -1,50 +1,51 @@ // generated by unplugin-vue-components // We suggest you to commit this file into source control // Read more: https://github.com/vuejs/core/pull/3399 -import '@vue/runtime-core' +import '@vue/runtime-core'; -export {} +export {}; declare module '@vue/runtime-core' { export interface GlobalComponents { - AppHeader: typeof import('./components/app/Header.vue')['default'] - AppLogin: typeof import('./components/app/Login.vue')['default'] - AppLogout: typeof import('./components/app/Logout.vue')['default'] - AppModal: typeof import('./components/app/Modal.vue')['default'] - AppSidebar: typeof import('./components/app/Sidebar.vue')['default'] - AppToast: typeof import('./components/app/Toast.vue')['default'] - DashboardMetricsCard: typeof import('./components/dashboard/MetricsCard.vue')['default'] - HoppButtonPrimary: typeof import('@hoppscotch/ui')['HoppButtonPrimary'] - HoppButtonSecondary: typeof import('@hoppscotch/ui')['HoppButtonSecondary'] - HoppSmartAnchor: typeof import('@hoppscotch/ui')['HoppSmartAnchor'] - HoppSmartConfirmModal: typeof import('@hoppscotch/ui')['HoppSmartConfirmModal'] - HoppSmartInput: typeof import('@hoppscotch/ui')['HoppSmartInput'] - HoppSmartItem: typeof import('@hoppscotch/ui')['HoppSmartItem'] - HoppSmartLink: typeof import('@hoppscotch/ui')['HoppSmartLink'] - HoppSmartModal: typeof import('@hoppscotch/ui')['HoppSmartModal'] - HoppSmartPicture: typeof import('@hoppscotch/ui')['HoppSmartPicture'] - HoppSmartSpinner: typeof import('@hoppscotch/ui')['HoppSmartSpinner'] - HoppSmartTab: typeof import('@hoppscotch/ui')['HoppSmartTab'] - HoppSmartTabs: typeof import('@hoppscotch/ui')['HoppSmartTabs'] - HoppSmartToggle: typeof import('@hoppscotch/ui')['HoppSmartToggle'] - IconLucideInbox: typeof import('~icons/lucide/inbox')['default'] - SettingsAuthProvider: typeof import('./components/settings/AuthProvider.vue')['default'] - SettingsConfigurations: typeof import('./components/settings/Configurations.vue')['default'] - SettingsDataSharing: typeof import('./components/settings/DataSharing.vue')['default'] - SettingsReset: typeof import('./components/settings/Reset.vue')['default'] - SettingsServerRestart: typeof import('./components/settings/ServerRestart.vue')['default'] - SettingsSmtpConfiguration: typeof import('./components/settings/SmtpConfiguration.vue')['default'] - SetupDataSharingAndNewsletter: typeof import('./components/setup/DataSharingAndNewsletter.vue')['default'] - TeamsAdd: typeof import('./components/teams/Add.vue')['default'] - TeamsDetails: typeof import('./components/teams/Details.vue')['default'] - TeamsInvite: typeof import('./components/teams/Invite.vue')['default'] - TeamsMembers: typeof import('./components/teams/Members.vue')['default'] - TeamsPendingInvites: typeof import('./components/teams/PendingInvites.vue')['default'] - Tippy: typeof import('vue-tippy')['Tippy'] - UiAutoResetIcon: typeof import('./components/ui/AutoResetIcon.vue')['default'] - UsersDetails: typeof import('./components/users/Details.vue')['default'] - UsersInviteModal: typeof import('./components/users/InviteModal.vue')['default'] - UsersSharedRequests: typeof import('./components/users/SharedRequests.vue')['default'] + AppHeader: typeof import('./components/app/Header.vue')['default']; + AppLogin: typeof import('./components/app/Login.vue')['default']; + AppLogout: typeof import('./components/app/Logout.vue')['default']; + AppModal: typeof import('./components/app/Modal.vue')['default']; + AppSidebar: typeof import('./components/app/Sidebar.vue')['default']; + AppToast: typeof import('./components/app/Toast.vue')['default']; + DashboardMetricsCard: typeof import('./components/dashboard/MetricsCard.vue')['default']; + HoppButtonPrimary: typeof import('@hoppscotch/ui')['HoppButtonPrimary']; + HoppButtonSecondary: typeof import('@hoppscotch/ui')['HoppButtonSecondary']; + HoppSmartAnchor: typeof import('@hoppscotch/ui')['HoppSmartAnchor']; + HoppSmartConfirmModal: typeof import('@hoppscotch/ui')['HoppSmartConfirmModal']; + HoppSmartInput: typeof import('@hoppscotch/ui')['HoppSmartInput']; + HoppSmartItem: typeof import('@hoppscotch/ui')['HoppSmartItem']; + HoppSmartLink: typeof import('@hoppscotch/ui')['HoppSmartLink']; + HoppSmartModal: typeof import('@hoppscotch/ui')['HoppSmartModal']; + HoppSmartPicture: typeof import('@hoppscotch/ui')['HoppSmartPicture']; + HoppSmartPlaceholder: typeof import('@hoppscotch/ui')['HoppSmartPlaceholder']; + HoppSmartSelectWrapper: typeof import('@hoppscotch/ui')['HoppSmartSelectWrapper']; + HoppSmartSpinner: typeof import('@hoppscotch/ui')['HoppSmartSpinner']; + HoppSmartTab: typeof import('@hoppscotch/ui')['HoppSmartTab']; + HoppSmartTabs: typeof import('@hoppscotch/ui')['HoppSmartTabs']; + HoppSmartToggle: typeof import('@hoppscotch/ui')['HoppSmartToggle']; + IconLucideInbox: typeof import('~icons/lucide/inbox')['default']; + SettingsAuthProvider: typeof import('./components/settings/AuthProvider.vue')['default']; + SettingsConfigurations: typeof import('./components/settings/Configurations.vue')['default']; + SettingsDataSharing: typeof import('./components/settings/DataSharing.vue')['default']; + SettingsReset: typeof import('./components/settings/Reset.vue')['default']; + SettingsServerRestart: typeof import('./components/settings/ServerRestart.vue')['default']; + SettingsSmtpConfiguration: typeof import('./components/settings/SmtpConfiguration.vue')['default']; + SetupDataSharingAndNewsletter: typeof import('./components/setup/DataSharingAndNewsletter.vue')['default']; + TeamsAdd: typeof import('./components/teams/Add.vue')['default']; + TeamsDetails: typeof import('./components/teams/Details.vue')['default']; + TeamsInvite: typeof import('./components/teams/Invite.vue')['default']; + TeamsMembers: typeof import('./components/teams/Members.vue')['default']; + TeamsPendingInvites: typeof import('./components/teams/PendingInvites.vue')['default']; + Tippy: typeof import('vue-tippy')['Tippy']; + UiAutoResetIcon: typeof import('./components/ui/AutoResetIcon.vue')['default']; + UsersDetails: typeof import('./components/users/Details.vue')['default']; + UsersInviteModal: typeof import('./components/users/InviteModal.vue')['default']; + UsersSharedRequests: typeof import('./components/users/SharedRequests.vue')['default']; } - } diff --git a/packages/hoppscotch-sh-admin/src/components/teams/Members.vue b/packages/hoppscotch-sh-admin/src/components/teams/Members.vue index a9d93395..d2d7e9af 100644 --- a/packages/hoppscotch-sh-admin/src/components/teams/Members.vue +++ b/packages/hoppscotch-sh-admin/src/components/teams/Members.vue @@ -168,6 +168,7 @@ import { useRoute } from 'vue-router'; import { useI18n } from '~/composables/i18n'; import { useToast } from '~/composables/toast'; import { useClientHandler } from '~/composables/useClientHandler'; +import { getCompiledErrorMessage } from '~/helpers/errors'; import IconChevronDown from '~icons/lucide/chevron-down'; import IconCircle from '~icons/lucide/circle'; import IconCircleDot from '~icons/lucide/circle-dot'; @@ -353,7 +354,13 @@ const removeExistingTeamMember = async (userID: string, index: number) => { team.value.id )(); if (removeTeamMemberResult.error) { - toast.error(t('state.remove_member_failure')); + const compiledErrorMessage = getCompiledErrorMessage( + removeTeamMemberResult.error.message + ); + + compiledErrorMessage + ? toast.error(compiledErrorMessage) + : toast.error(t('state.remove_member_failure')); } else { team.value.teamMembers = team.value.teamMembers?.filter( (member: any) => member.user.uid !== userID diff --git a/packages/hoppscotch-sh-admin/src/components/users/InviteModal.vue b/packages/hoppscotch-sh-admin/src/components/users/InviteModal.vue index 36c536d4..dee511f1 100644 --- a/packages/hoppscotch-sh-admin/src/components/users/InviteModal.vue +++ b/packages/hoppscotch-sh-admin/src/components/users/InviteModal.vue @@ -1,6 +1,5 @@