refactor: foundational changes to support subdomain based cloud instances (#4704)

This commit is contained in:
James George 2025-02-23 22:54:28 -08:00 committed by GitHub
parent 23633a99a1
commit 49430528e5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 1227 additions and 356 deletions

View file

@ -62,8 +62,9 @@
"turn_off": "Turn off",
"turn_on": "Turn on",
"undo": "Undo",
"verify": "Verify",
"yes": "Yes",
"confirm": "Confirm",
"verify": "Verify",
"enable": "Enable",
"disable": "Disable"
},
@ -154,6 +155,12 @@
"whats_new": "What's new?",
"see_whats_new": "See whats new",
"wiki": "Wiki",
"collapse_sidebar": "Collapse Sidebar",
"continue_to_dashboard": "Continue to Dashboard",
"expand_sidebar": "Expand Sidebar",
"no_name": "No name",
"open_navigation": "Open Navigation",
"read_documentation": "Read Documentation",
"default": "default: {value}"
},
"auth": {
@ -415,7 +422,7 @@
"check_how_to_add_origin": "Check how you can add an origin",
"curl_invalid_format": "cURL is not formatted properly",
"danger_zone": "Danger zone",
"delete_account": "Your account is currently an owner in these workspaces:",
"delete_account": "Your account is currently the sole owner in these workspaces:",
"delete_account_description": "You must either remove yourself, transfer ownership, or delete these workspaces before you can delete your account.",
"delete_activity_log": "Failed to delete activity log",
"delete_all_activity_logs": "Failed to delete all activity logs",
@ -667,7 +674,9 @@
"profile": "Profile",
"realtime": "Realtime",
"rest": "REST",
"settings": "Settings"
"settings": "Settings",
"goto_app": "Goto App",
"authentication": "Authentication"
},
"preRequest": {
"javascript_code": "JavaScript Code",
@ -875,7 +884,25 @@
"light": "Light",
"system": "System",
"title": "Theme"
}
},
"action": "Action",
"clear_filter": "Clear Filter",
"confirm_request_deletion": "Confirm deletion of the selected shared request?",
"copy": "Copy",
"created_on": "Created On",
"delete": "Delete",
"email": "Email",
"filter": "Filter",
"filter_by_email": "Filter by email",
"id": "ID",
"load_list_error": "Unable to load shared requests list",
"no_requests": "No shared requests found",
"open_request": "Open Request",
"properties": "Properties",
"request": "Request",
"show_more": "Show more",
"title": "Shared Requests",
"url": "URL"
},
"shortcut": {
"general": {
@ -1081,6 +1108,111 @@
"unsubscribed_failed": "Failed to unsubscribe from topic: {topic}",
"unsubscribed_success": "Successfully unsubscribed from topic: {topic}",
"waiting_send_request": "Waiting to send request",
"add_user_failure": "Failed to add user to the workspace!!",
"add_user_success": "User is now a member of the workspace!!",
"admin_failure": "Failed to make user an admin!!",
"admin_success": "User is now an admin!!",
"and": "and",
"clear_selection": "Clear Selection",
"configure_auth": "Check out the documentation to configure auth providers.",
"confirm_admin_to_user": "Do you want to remove admin status from this user?",
"confirm_admins_to_users": "Do you want to remove admin status from selected users?",
"confirm_delete_infra_token": "Are you sure you want to delete the infra token {tokenLabel}?",
"confirm_delete_invite": "Do you want to revoke the selected invite?",
"confirm_delete_invites": "Do you want to revoke selected invites?",
"confirm_user_deletion": "Confirm user deletion?",
"confirm_users_deletion": "Do you want to delete selected users?",
"confirm_user_to_admin": "Do you want to make this user into an admin?",
"confirm_users_to_admin": "Do you want to make selected users into admins?",
"confirm_user_deactivation": "Confirm user deactivation?",
"confirm_logout": "Confirm Logout",
"created_on": "Created On",
"continue_email": "Continue with Email",
"continue_github": "Continue with Github",
"continue_google": "Continue with Google",
"continue_microsoft": "Continue with Microsoft",
"create_team_failure": "Failed to create workspace!!",
"create_team_success": "Workspace created successfully!!",
"data_sharing_failure": "Failed to update data sharing settings",
"delete_infra_token_failure": "Something went wrong while deleting the infra token",
"delete_invite_failure": "Failed to delete invite!!",
"delete_invites_failure": "Failed to delete selected invites!!",
"delete_invite_success": "Invite deleted successfully!!",
"delete_invites_success": "Selected invites deleted successfully!!",
"delete_request_failure": "Shared Request deletion failed!!",
"delete_request_success": "Shared Request deleted successfully!!",
"delete_team_failure": "Workspace deletion failed!!",
"delete_team_success": "Workspace deleted successfully!!",
"delete_some_users_failure": "Number of Users Not Deleted: {count}",
"delete_some_users_success": "Number of Users Deleted: {count}",
"delete_user_failed_only_one_admin": "Failed to delete user. There should be atleast one admin!!",
"delete_user_failure": "User deletion failed!!",
"delete_users_failure": "Failed to delete selected users!!",
"delete_user_success": "User deleted successfully!!",
"delete_users_success": "Selected users deleted successfully!!",
"email": "Email",
"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",
"generate_infra_token_failure": "Something went wrong while generating the infra token",
"github_signin_failure": "Failed to login with Github",
"google_signin_failure": "Failed to login with Google",
"infra_token_label_short": "Infra Token Label character length is too short!!",
"invalid_email": "Please enter a valid email address",
"link_copied_to_clipboard": "Link copied to clipboard",
"logged_out": "Logged out",
"login_as_admin": "and login with an admin account.",
"login_using_email": "Please ask the user to check their email or share the link below",
"login_using_link": "Please ask the user to login using the link below",
"logout": "Logout",
"magic_link_sign_in": "Click on the link to sign in.",
"magic_link_success": "We sent a magic link to",
"microsoft_signin_failure": "Failed to login with Microsoft",
"newsletter_failure": "Unable to update newsletter settings",
"non_admin_logged_in": "Logged in as non admin user.",
"non_admin_login": "You are logged in. But you're not an admin",
"owner_not_present": "Atleast one owner should be present in the team!!",
"privacy_policy": "Privacy Policy",
"reenter_email": "Re-enter email",
"remove_admin_failure": "Failed to remove admin status!!",
"remove_admin_failure_only_one_admin": "Failed to remove admin status. There should be at least one admin!!",
"remove_admin_success": "Admin status removed!!",
"remove_admin_from_users_failure": "Failed to remove admin status from selected users!!",
"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!!",
"remove_invitee_success": "Removal of invitee is successfull!!",
"remove_member_failure": "Member couldn't be removed!!",
"remove_member_success": "Member removed successfully!!",
"rename_team_failure": "Failed to rename workspace!!",
"rename_team_success": "Workspace renamed successfully!",
"rename_user_failure": "Failed to rename user!!",
"rename_user_success": "User renamed successfully!!",
"require_auth_provider": "You need to set atleast one authentication provider to log in.",
"role_update_failed": "Roles updation has failed!!",
"role_update_success": "Roles updated successfully!!",
"selected": "{count} selected",
"self_host_docs": "Self Host Documentation",
"send_magic_link": "Send magic link",
"setup_failure": "Setup has failed!!",
"setup_success": "Setup completed successfully!!",
"sign_in_agreement": "By signing in, you are agreeing to our",
"sign_in_options": "All sign in option",
"sign_out": "Sign out",
"something_went_wrong": "Something went wrong",
"team_name_too_short": "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!!",
"users_to_admin_failure": "Failed to elevate selected users to admin status!!",
"loading_workspaces": "Loading workspaces",
"loading_collections_in_workspace": "Loading collections in workspace"
},
@ -1190,7 +1322,8 @@
"invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.",
"copy_invite_link": "Copy Invite Link",
"search_title": "Team Requests",
"user_not_found": "User not found in the instance."
"user_not_found": "User not found in the instance.",
"invite_members": "Invite Members"
},
"team_environment": {
"deleted": "Environment Deleted",
@ -1303,5 +1436,279 @@
"generate_or_modify_test_script_input_placeholder": "Enter a prompt to generate or modify the test script",
"modify_test_script_error": "Failed to modify test script",
"modify_prerequest_error": "Failed to modify pre-request script"
},
"configs": {
"auth_providers": {
"callback_url": "CALLBACK URL",
"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",
"update_failure": "Failed to update authentication provider configurations!!"
},
"confirm_changes": "Hoppscotch server must restart to reflect the new changes. Confirm changes made to the server configurations?",
"input_empty": "Please fill all the fields before updating the configurations",
"data_sharing": {
"title": "Data Sharing",
"description": "Help improve Hoppscotch by sharing anonymous data",
"enable": "Enable Data Sharing",
"secondary_title": "Data Sharing Configurations",
"see_shared": "See what is shared",
"toggle_description": "Share anonymous data",
"update_failure": "Failed to update data sharing configurations!!"
},
"load_error": "Unable to load server configurations",
"mail_configs": {
"address_from": "MAILER FROM ADDRESS",
"custom_smtp_configs": "Use Custom SMTP Configurations",
"description": " Configure the smtp configurations",
"enable_email_auth": "Enable Email based authentication",
"enable_smtp": "Enable SMTP",
"host": "MAILER HOST",
"password": "MAILER PASSWORD",
"port": "MAILER PORT",
"secure": "MAILER SECURE",
"smtp_url": "MAILER SMTP URL",
"tls_reject_unauthorized": "TLS REJECT UNAUTHORIZED",
"title": "SMTP Configurations",
"toggle_failure": "Failed to toggle smtp!!",
"update_failure": "Failed to update smtp configurations!!",
"user": "MAILER USER"
},
"reset": {
"confirm_reset": "Hoppscotch server must restart to reflect the new changes. Confirm the reset of server configurations?",
"description": "Default configurations will be loaded as specified in the environment file",
"failure": "Failed to reset configurations!!",
"title": "Reset Configurations",
"info": "Reset server configurations"
},
"restart": {
"description": "{duration} seconds remaining before this page reloads automatically",
"initiate": "Initiating server restart...",
"title": "Server is restarting"
},
"save_changes": "Save Changes",
"title": "Configurations",
"update_failure": "Failed to update server configurations",
"restrict_access": "Restrict Access",
"site_protection": {
"control_access": "Control who can access Hoppscotch app",
"description": "Customize how visitors access your Hoppscotch app using site protection settings.",
"enable": "Enable site protection",
"note": "Users visiting the Hoppscotch app will see the login page, mandatory login is required to access the app. Admin approval is still required for authorization",
"update_failure": "Failed to update site protection configurations!!"
},
"domain_whitelisting": {
"add_domain": "Add New Domain",
"description": "Users with email ID registered in whitelisted domains does not require explicit approval from admin to access the Hoppscotch app",
"enable": "Enable domain whitelisting",
"enter_domain": "Enter domain",
"title": "Whitelisted Domains",
"toggle_failure": "Failed to toggle domain whitelisting",
"update_failure": "Failed to update domain whitelisting configurations!!"
},
"oidc_configs": {
"auth_url": "Auth URL",
"callback_url": "Callback URL",
"client_id": "Client ID",
"client_secret": "Client Secret",
"description": "Configure OIDC configurations",
"enable": "Enable OIDC based authentication",
"issuer": "Issuer",
"provider_name": "Provider Name",
"scope": "Scope",
"title": "OIDC Configurations",
"token_url": "Token URL",
"update_failure": "Failed to update OIDC configurations!!",
"user_info_url": "User Info URL"
},
"saml": {
"audience": "Audience",
"callback_url": "Callback URL",
"certificate": "Certificate",
"description": "Configure SAML configurations",
"enable": "Enable SAML based authentication",
"entry_point": "Entry Point",
"issuer": "Issuer",
"title": "SAML Configurations",
"update_failure": "Failed to update SAML SSO configurations!!",
"want_assertions_signed": "Sign Assertions",
"want_response_signed": "Sign Response"
}
},
"data_sharing": {
"description": "Share anonymous data usage to improve Hoppscotch",
"enable": "Enable Data Sharing",
"see_shared": "See what is shared",
"toggle_description": "Share data and make Hoppscotch better",
"title": "Make Hoppscotch Better",
"welcome": "Welcome to"
},
"infra_tokens": {
"copy_token_warning": "Make sure to copy your infra token now. You won't be able to see it again!",
"deletion_success": "The infra token {label} has been deleted",
"empty": "Infra tokens are empty",
"expired": "Expired",
"expiration_label": "Expiration",
"expires_on": "Expires on",
"generate_modal_title": "New Infra Token",
"generate_new_token": "Generate new token",
"generate_token": "Generate Token",
"invalid_label": "Please provide a label for the token",
"last_used_on": "Last used on",
"no_expiration": "No expiration",
"no_expiration_verbose": "This token will never expire!",
"section_description": "Manage your Hoppscotch users through APIs with Infra tokens",
"section_title": "Infra Tokens",
"tab_title": "Infra Tokens",
"token_expires_on": "This token will expire on",
"token_purpose": "Enter a label to identify this token"
},
"metrics": {
"dashboard": "Dashboard",
"no_metrics": "No metrics found",
"total_collections": "Total Collections",
"total_requests": "Total Requests",
"total_teams": "Total Workspaces",
"total_users": "Total Users"
},
"newsletter": {
"description": "Get updates about our latest news",
"subscribe": "Subscribe",
"title": "Stay in Touch",
"toggle_description": "Get updates about the latest at Hoppscotch",
"unsubscribe": "Unsubscribe"
},
"teams": {
"add_member": "Add Member",
"add_members": "Add Members",
"add_new": "Add New",
"admin": "Admin",
"admin_Email": "Admin Email",
"admin_id": "Admin ID",
"cancel": "Cancel",
"confirm_team_deletion": "Confirm deletion of the workspace?",
"copy": "Copy",
"create_team": "Create Workspace",
"date": "Date",
"delete_team": "Delete Workspace",
"details": "Details",
"edit": "Edit",
"editor": "EDITOR",
"editor_description": "Editors can add, edit, and delete requests and collections.",
"email": "Workspace owner email",
"email_address": "Email Address",
"email_title": "Email",
"empty_name": "Team name cannot be empty!!",
"error": "Something went wrong. Please try again later.",
"id": "Workspace ID",
"invited_email": "Invitee Email",
"invited_on": "Invited On",
"invites": "Invites",
"load_info_error": "Unable to load Workspace info",
"load_list_error": "Unable to Load Workspace List",
"members": "Number of members",
"no_invite": "No invites",
"no_invite_description": "Invite your team to start collaborating",
"owner": "OWNER",
"owner_description": " Owners can add, edit, and delete requests, collections and workspace members.",
"permissions": "Permissions",
"name": "Workspace Name",
"no_members": "No members in this workspace. Add members to this workspace to collaborate",
"no_pending_invites": "No pending invites",
"no_teams": "No workspaces found..",
"no_teams_description": "Create a workspace to collaborate with your team",
"pending_invites": "Pending invites",
"roles": "Roles",
"roles_description": "Roles are used to control access to the shared collections.",
"remove": "Remove",
"rename": "Rename",
"save": "Save",
"save_changes": "Save Changes",
"send_invite": "Send Invite",
"show_more": "Show more",
"team_details": "Workspace details",
"team_members": "Members",
"team_members_tab": "Workspace members",
"teams": "Workspace",
"uid": "UID",
"unnamed": "(Unnamed Workspace)",
"viewer": "VIEWER",
"viewer_description": "Viewers can only view and use requests",
"valid_name": "Please enter a valid workspace name",
"valid_owner_email": "Please enter a valid owner email"
},
"users": {
"add_user": "Add User",
"admin": "Admin",
"admin_id": "Admin ID",
"cancel": "Cancel",
"created_on": "Created On",
"copy_invite_link": "Copy Invite Link",
"copy_link": "Copy Link",
"date": "Date",
"delete": "Delete",
"delete_user": "Delete User",
"delete_users": "Delete Users",
"details": "Details",
"edit": "Edit",
"email": "Email",
"email_address": "Email Address",
"empty_name": "Name cannot be empty!!",
"id": "User ID",
"invalid_user": "Invalid User",
"invite_load_list_error": "Unable to Load Invited Users List",
"invite_user": "Invite User",
"invited_by": "Invited By",
"invited_on": "Invited On",
"invited_users": "Invited Users",
"invitee_email": "Invitee Email",
"last_active_on": "Last Active",
"load_info_error": "Unable to load user info",
"load_list_error": "Unable to Load Users List",
"make_admin": "Make Admin",
"name": "Name",
"new_user_added": "New User Added",
"no_invite": "No pending invites found",
"no_invite_description": "No pending invites! Start inviting your teammates to Hoppscotch",
"no_shared_requests": "No shared requests created by the user",
"no_users": "No users found",
"not_available": "Not Available",
"not_found": "User not found",
"pending_invites": "Pending Invites",
"remove_admin_privilege": "Remove Admin Privilege",
"remove_admin_status": "Remove Admin Status",
"rename": "Rename",
"revoke_invitation": "Revoke Invitation",
"searchbar_placeholder": "Search by name or email..",
"send_invite": "Send Invite",
"show_more": "Show more",
"uid": "UID",
"unnamed": "(Unnamed User)",
"user_not_found": "User not found in the infra!!",
"users": "Users",
"valid_email": "Please enter a valid email address",
"deactivate": "Deactivate",
"deactivate_user": "Deactivate User"
},
"organization": {
"login_to_continue_description": "You need to be logged in to join an organization instance.",
"create_an_organization": "Create an organization",
"deactivate_user_failure": "User deactivation failed!!",
"deactivate_user_success": "User deactivated successfully!!",
"delete_account_description": "This will delete all data associated with your Hoppscotch account, including this and any other organizations you are part of.",
"delete_account": "Delete Hoppscotch Account",
"user_deletion_failed_sole_admin": "The user is the sole admin of one or more organization instances. Please demote the user before attempting deletion.",
"user_deletion_failed_sole_team_owner": "The user is the sole team owner on one or more organization instances. Please transfer the ownership or delete the relevant workspaces before attempting deletion."
},
"billing": {
"confirm": {
"update_seat_count": "Are you sure you want to update the seat count to {newSeatCount}?",
"update_billing_cycle": "Are you sure you want to update the billing cycle to {newBillingCycle}?"
},
"cancel_subscription": "Cancel subscription"
}
}

View file

@ -7,19 +7,34 @@ export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
'(chore': fix broken runner for user collection)
'(feat': collection runner config in modal)
'(fix': run again function)
AccessTokens: typeof import('./components/accessTokens/index.vue')['default']
AccessTokensGenerateModal: typeof import('./components/accessTokens/GenerateModal.vue')['default']
AccessTokensList: typeof import('./components/accessTokens/List.vue')['default']
AccessTokensOverview: typeof import('./components/accessTokens/Overview.vue')['default']
AdminAppHeader: typeof import('./../../hoppscotch-web/src/components/admin/app/Header.vue')['default']
AdminAppLogin: typeof import('./../../hoppscotch-web/src/components/admin/app/Login.vue')['default']
AdminAppLogout: typeof import('./../../hoppscotch-web/src/components/admin/app/Logout.vue')['default']
AdminAppSidebar: typeof import('./../../hoppscotch-web/src/components/admin/app/Sidebar.vue')['default']
AdminAuthenticationAccessControl: typeof import('./../../hoppscotch-web/src/components/admin/authentication/AccessControl.vue')['default']
AdminAuthenticationOidcConfiguration: typeof import('./../../hoppscotch-web/src/components/admin/authentication/OidcConfiguration.vue')['default']
AdminAuthenticationProvider: typeof import('./../../hoppscotch-web/src/components/admin/authentication/Provider.vue')['default']
AdminAuthenticationSamlConfiguration: typeof import('./../../hoppscotch-web/src/components/admin/authentication/SamlConfiguration.vue')['default']
AdminAuthenticationServerRestart: typeof import('./../../hoppscotch-web/src/components/admin/authentication/ServerRestart.vue')['default']
AdminDashboardMetricsCard: typeof import('./../../hoppscotch-web/src/components/admin/dashboard/MetricsCard.vue')['default']
AdminTeamsAdd: typeof import('./../../hoppscotch-web/src/components/admin/teams/Add.vue')['default']
AdminTeamsDetails: typeof import('./../../hoppscotch-web/src/components/admin/teams/Details.vue')['default']
AdminTeamsInvite: typeof import('./../../hoppscotch-web/src/components/admin/teams/Invite.vue')['default']
AdminTeamsMembers: typeof import('./../../hoppscotch-web/src/components/admin/teams/Members.vue')['default']
AdminTeamsPendingInvites: typeof import('./../../hoppscotch-web/src/components/admin/teams/PendingInvites.vue')['default']
AdminUiAutoResetIcon: typeof import('./../../hoppscotch-web/src/components/admin/ui/AutoResetIcon.vue')['default']
AdminUsersDetails: typeof import('./../../hoppscotch-web/src/components/admin/users/Details.vue')['default']
AdminUsersInviteModal: typeof import('./../../hoppscotch-web/src/components/admin/users/InviteModal.vue')['default']
AdminUsersSuccessInviteModal: typeof import('./../../hoppscotch-web/src/components/admin/users/SuccessInviteModal.vue')['default']
AiexperimentsMergeView: typeof import('./components/aiexperiments/MergeView.vue')['default']
AiexperimentsModifyBodyModal: typeof import('./components/aiexperiments/ModifyBodyModal.vue')['default']
AiexperimentsModifyPreRequestModal: typeof import('./components/aiexperiments/ModifyPreRequestModal.vue')['default']
AiexperimentsModifyTestScriptModal: typeof import('./components/aiexperiments/ModifyTestScriptModal.vue')['default']
AppActionHandler: typeof import('./components/app/ActionHandler.vue')['default']
AppAnnouncement: (typeof import("./components/app/Announcement.vue"))["default"]
AppBanner: typeof import('./components/app/Banner.vue')['default']
AppContextMenu: typeof import('./components/app/ContextMenu.vue')['default']
AppDeveloperOptions: typeof import('./components/app/DeveloperOptions.vue')['default']
@ -48,8 +63,6 @@ declare module 'vue' {
AppSpotlightSearch: typeof import('./components/app/SpotlightSearch.vue')['default']
AppSupport: typeof import('./components/app/Support.vue')['default']
AppWhatsNewDialog: typeof import('./components/app/WhatsNewDialog.vue')['default']
ButtonPrimary: (typeof import("./../../hoppscotch-ui/src/components/button/Primary.vue"))["default"]
ButtonSecondary: (typeof import("./../../hoppscotch-ui/src/components/button/Secondary.vue"))["default"]
Collections: typeof import('./components/collections/index.vue')['default']
CollectionsAdd: typeof import('./components/collections/Add.vue')['default']
CollectionsAddFolder: typeof import('./components/collections/AddFolder.vue')['default']
@ -75,11 +88,11 @@ declare module 'vue' {
CollectionsMyCollections: typeof import('./components/collections/MyCollections.vue')['default']
CollectionsProperties: typeof import('./components/collections/Properties.vue')['default']
CollectionsRequest: typeof import('./components/collections/Request.vue')['default']
CollectionsRunner: (typeof import("./components/collections/Runner.vue"))["default"]
CollectionsSaveRequest: typeof import('./components/collections/SaveRequest.vue')['default']
CollectionsTeamCollections: typeof import('./components/collections/TeamCollections.vue')['default']
CookiesAllModal: typeof import('./components/cookies/AllModal.vue')['default']
CookiesEditCookie: typeof import('./components/cookies/EditCookie.vue')['default']
CustomEmbeds: typeof import('./../../hoppscotch-web/src/components/CustomEmbeds.vue')['default']
Embeds: typeof import('./components/embeds/index.vue')['default']
EmbedsHeader: typeof import('./components/embeds/Header.vue')['default']
EmbedsRequest: typeof import('./components/embeds/Request.vue')['default']
@ -94,23 +107,20 @@ declare module 'vue' {
EnvironmentsTeams: typeof import('./components/environments/teams/index.vue')['default']
EnvironmentsTeamsDetails: typeof import('./components/environments/teams/Details.vue')['default']
EnvironmentsTeamsEnvironment: typeof import('./components/environments/teams/Environment.vue')['default']
ErrorPage: typeof import('./../../hoppscotch-web/src/components/ErrorPage.vue')['default']
FirebaseLogin: typeof import('./components/firebase/Login.vue')['default']
FirebaseLogout: typeof import('./components/firebase/Logout.vue')['default']
GraphqlArgument: typeof import('./components/graphql/Argument.vue')['default']
GraphqlArguments: typeof import('./components/graphql/Arguments.vue')['default']
GraphqlAuthorization: typeof import('./components/graphql/Authorization.vue')['default']
GraphqlDefaultValue: typeof import('./components/graphql/DefaultValue.vue')['default']
GraphqlDeprecationReason: typeof import('./components/graphql/DeprecationReason.vue')['default']
GraphqlDirective: typeof import('./components/graphql/Directive.vue')['default']
GraphqlDirectives: typeof import('./components/graphql/Directives.vue')['default']
GraphqlDoc: typeof import('./components/graphql/Doc.vue')['default']
GraphqlDocExplorer: typeof import('./components/graphql/DocExplorer.vue')['default']
GraphqlEnumValues: typeof import('./components/graphql/EnumValues.vue')['default']
GraphqlExplorerSection: typeof import('./components/graphql/ExplorerSection.vue')['default']
GraphqlField: typeof import('./components/graphql/Field.vue')['default']
GraphqlFieldDocumentation: typeof import('./components/graphql/FieldDocumentation.vue')['default']
GraphqlFieldLink: typeof import('./components/graphql/FieldLink.vue')['default']
GraphqlFieldOld: typeof import('./components/graphql/FieldOld.vue')['default']
GraphqlFields: typeof import('./components/graphql/Fields.vue')['default']
GraphqlHeaders: typeof import('./components/graphql/Headers.vue')['default']
GraphqlImplementsInterfaces: typeof import('./components/graphql/ImplementsInterfaces.vue')['default']
@ -119,8 +129,6 @@ declare module 'vue' {
GraphqlRequestOptions: typeof import('./components/graphql/RequestOptions.vue')['default']
GraphqlRequestTab: typeof import('./components/graphql/RequestTab.vue')['default']
GraphqlResponse: typeof import('./components/graphql/Response.vue')['default']
GraphqlSchemaBrowser: typeof import('./components/graphql/SchemaBrowser.vue')['default']
GraphqlSchemaDoc: typeof import('./components/graphql/SchemaDoc.vue')['default']
GraphqlSchemaDocumentation: typeof import('./components/graphql/SchemaDocumentation.vue')['default']
GraphqlSchemaSearch: typeof import('./components/graphql/SchemaSearch.vue')['default']
GraphqlSidebar: typeof import('./components/graphql/Sidebar.vue')['default']
@ -129,18 +137,17 @@ declare module 'vue' {
GraphqlType: typeof import('./components/graphql/Type.vue')['default']
GraphqlTypeDocumentation: typeof import('./components/graphql/TypeDocumentation.vue')['default']
GraphqlTypeLink: typeof import('./components/graphql/TypeLink.vue')['default']
GraphqlTypeLinkNew: typeof import('./components/graphql/TypeLink.vue')['default']
GraphqlVariable: typeof import('./components/graphql/Variable.vue')['default']
History: typeof import('./components/history/index.vue')['default']
HistoryGraphqlCard: typeof import('./components/history/graphql/Card.vue')['default']
HistoryPersonal: typeof import('./components/history/Personal.vue')['default']
HistoryRestCard: typeof import('./components/history/rest/Card.vue')['default']
HoppButtonPrimary: typeof import('@hoppscotch/ui')['HoppButtonPrimary']
HoppButtonSecondary: typeof import('@hoppscotch/ui')['HoppButtonSecondary']
HoppSmartAnchor: typeof import('@hoppscotch/ui')['HoppSmartAnchor']
HoppSmartAutoComplete: (typeof import("@hoppscotch/ui"))["HoppSmartAutoComplete"]
HoppSmartAutoComplete: typeof import('@hoppscotch/ui')['HoppSmartAutoComplete']
HoppSmartCheckbox: typeof import('@hoppscotch/ui')['HoppSmartCheckbox']
HoppSmartConfirmModal: typeof import('@hoppscotch/ui')['HoppSmartConfirmModal']
HoppSmartExpand: (typeof import("@hoppscotch/ui"))["HoppSmartExpand"]
HoppSmartFileChip: typeof import('@hoppscotch/ui')['HoppSmartFileChip']
HoppSmartInput: typeof import('@hoppscotch/ui')['HoppSmartInput']
HoppSmartIntersection: typeof import('@hoppscotch/ui')['HoppSmartIntersection']
@ -156,13 +163,12 @@ declare module 'vue' {
HoppSmartSlideOver: typeof import('@hoppscotch/ui')['HoppSmartSlideOver']
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']
HoppSmartTree: typeof import('@hoppscotch/ui')['HoppSmartTree']
HoppSmartWindow: typeof import('@hoppscotch/ui')['HoppSmartWindow']
HoppSmartWindows: typeof import('@hoppscotch/ui')['HoppSmartWindows']
HoppTestEnv: (typeof import("@hoppscotch/ui"))["HoppTestEnv"]
HoppTestRunnerModal: (typeof import("@hoppscotch/ui"))["HoppTestRunnerModal"]
HttpAuthorization: typeof import('./components/http/Authorization.vue')['default']
HttpAuthorizationAkamaiEG: typeof import('./components/http/authorization/AkamaiEG.vue')['default']
HttpAuthorizationApiKey: typeof import('./components/http/authorization/ApiKey.vue')['default']
@ -178,7 +184,6 @@ declare module 'vue' {
HttpBodyParameters: typeof import('./components/http/BodyParameters.vue')['default']
HttpCodegen: typeof import('./components/http/Codegen.vue')['default']
HttpCodegenModal: typeof import('./components/http/CodegenModal.vue')['default']
HttpCollectionRunner: (typeof import("./components/http/CollectionRunner.vue"))["default"]
HttpExampleLenseBodyRenderer: typeof import('./components/http/example/LenseBodyRenderer.vue')['default']
HttpExampleResponse: typeof import('./components/http/example/Response.vue')['default']
HttpExampleResponseMeta: typeof import('./components/http/example/ResponseMeta.vue')['default']
@ -187,7 +192,6 @@ declare module 'vue' {
HttpHeaders: typeof import('./components/http/Headers.vue')['default']
HttpImportCurl: typeof import('./components/http/ImportCurl.vue')['default']
HttpKeyValue: typeof import('./components/http/KeyValue.vue')['default']
HttpOAuth2Authorization: (typeof import("./components/http/OAuth2Authorization.vue"))["default"]
HttpParameters: typeof import('./components/http/Parameters.vue')['default']
HttpPreRequestScript: typeof import('./components/http/PreRequestScript.vue')['default']
HttpRawBody: typeof import('./components/http/RawBody.vue')['default']
@ -199,7 +203,6 @@ declare module 'vue' {
HttpResponse: typeof import('./components/http/Response.vue')['default']
HttpResponseInterface: typeof import('./components/http/ResponseInterface.vue')['default']
HttpResponseMeta: typeof import('./components/http/ResponseMeta.vue')['default']
HttpRunner: (typeof import("./components/http/Runner.vue"))["default"]
HttpSaveResponseName: typeof import('./components/http/SaveResponseName.vue')['default']
HttpSidebar: typeof import('./components/http/Sidebar.vue')['default']
HttpTabHead: typeof import('./components/http/TabHead.vue')['default']
@ -214,23 +217,19 @@ declare module 'vue' {
HttpTestResultReport: typeof import('./components/http/TestResultReport.vue')['default']
HttpTestResultRequest: typeof import('./components/http/test/ResultRequest.vue')['default']
HttpTestRunner: typeof import('./components/http/test/Runner.vue')['default']
HttpTestRunnerConfig: typeof import('./components/http/test/RunnerConfig.vue')['default']
HttpTestRunnerMeta: typeof import('./components/http/test/RunnerMeta.vue')['default']
HttpTestRunnerModal: typeof import('./components/http/test/RunnerModal.vue')['default']
HttpTestRunnerResult: typeof import('./components/http/test/RunnerResult.vue')['default']
HttpTests: typeof import('./components/http/Tests.vue')['default']
HttpTestSelector: (typeof import("./components/http/test/Selector.vue"))["default"]
HttpTestSelectRequest: (typeof import("./components/http/test/SelectRequest.vue"))["default"]
HttpTestTestResult: typeof import('./components/http/test/TestResult.vue')['default']
HttpURLEncodedParams: typeof import('./components/http/URLEncodedParams.vue')['default']
IconLucideActivity: typeof import('~icons/lucide/activity')['default']
IconLucideAlertCircle: (typeof import("~icons/lucide/alert-circle"))["default"]
IconLucideAlertTriangle: typeof import('~icons/lucide/alert-triangle')['default']
IconLucideArrowLeft: typeof import('~icons/lucide/arrow-left')['default']
IconLucideArrowUpRight: typeof import('~icons/lucide/arrow-up-right')['default']
IconLucideBrush: (typeof import("~icons/lucide/brush"))["default"]
IconLucideBrush: typeof import('~icons/lucide/brush')['default']
IconLucideCheck: typeof import('~icons/lucide/check')['default']
IconLucideCheckCircle: typeof import('~icons/lucide/check-circle')['default']
IconLucideChevronLeft: typeof import('~icons/lucide/chevron-left')['default']
IconLucideChevronRight: typeof import('~icons/lucide/chevron-right')['default']
IconLucideCircleCheck: typeof import('~icons/lucide/circle-check')['default']
IconLucideGlobe: typeof import('~icons/lucide/globe')['default']
@ -240,12 +239,11 @@ declare module 'vue' {
IconLucideLayers: typeof import('~icons/lucide/layers')['default']
IconLucideListEnd: typeof import('~icons/lucide/list-end')['default']
IconLucideMinus: typeof import('~icons/lucide/minus')['default']
IconLucidePlay: (typeof import("~icons/lucide/play"))["default"]
IconLucidePlaySquare: (typeof import("~icons/lucide/play-square"))["default"]
IconLucidePlusCircle: typeof import('~icons/lucide/plus-circle')['default']
IconLucideRss: typeof import('~icons/lucide/rss')['default']
IconLucideSearch: typeof import('~icons/lucide/search')['default']
IconLucideTriangleAlert: typeof import('~icons/lucide/triangle-alert')['default']
IconLucideUser: typeof import('~icons/lucide/user')['default']
IconLucideUsers: typeof import('~icons/lucide/users')['default']
IconLucideVerified: typeof import('~icons/lucide/verified')['default']
IconLucideX: typeof import('~icons/lucide/x')['default']
@ -275,8 +273,7 @@ declare module 'vue' {
LensesRenderersVideoLensRenderer: typeof import('./components/lenses/renderers/VideoLensRenderer.vue')['default']
LensesRenderersXMLLensRenderer: typeof import('./components/lenses/renderers/XMLLensRenderer.vue')['default']
LensesResponseBodyRenderer: typeof import('./components/lenses/ResponseBodyRenderer.vue')['default']
ProfileShortcode: (typeof import("./components/profile/Shortcode.vue"))["default"]
ProfileShortcodes: (typeof import("./components/profile/Shortcodes.vue"))["default"]
Newsletter: typeof import('./../../hoppscotch-web/src/components/Newsletter.vue')['default']
ProfileUserDelete: typeof import('./components/profile/UserDelete.vue')['default']
RealtimeCommunication: typeof import('./components/realtime/Communication.vue')['default']
RealtimeConnectionConfig: typeof import('./components/realtime/ConnectionConfig.vue')['default']
@ -291,43 +288,14 @@ declare module 'vue' {
ShareCustomizeModal: typeof import('./components/share/CustomizeModal.vue')['default']
ShareModal: typeof import('./components/share/Modal.vue')['default']
ShareRequest: typeof import('./components/share/Request.vue')['default']
ShareRequestModal: (typeof import("./components/share/RequestModal.vue"))["default"]
ShareShareRequestModal: (typeof import("./components/share/ShareRequestModal.vue"))["default"]
ShareTemplatesButton: typeof import('./components/share/templates/Button.vue')['default']
ShareTemplatesEmbeds: typeof import('./components/share/templates/Embeds.vue')['default']
ShareTemplatesLink: typeof import('./components/share/templates/Link.vue')['default']
SmartAccentModePicker: typeof import('./components/smart/AccentModePicker.vue')['default']
SmartAnchor: (typeof import("./../../hoppscotch-ui/src/components/smart/Anchor.vue"))["default"]
SmartAutoComplete: (typeof import("./../../hoppscotch-ui/src/components/smart/AutoComplete.vue"))["default"]
SmartChangeLanguage: typeof import('./components/smart/ChangeLanguage.vue')['default']
SmartCheckbox: (typeof import("./../../hoppscotch-ui/src/components/smart/Checkbox.vue"))["default"]
SmartColorModePicker: typeof import('./components/smart/ColorModePicker.vue')['default']
SmartConfirmModal: (typeof import("./../../hoppscotch-ui/src/components/smart/ConfirmModal.vue"))["default"]
SmartEncodingPicker: typeof import('./components/smart/EncodingPicker.vue')['default']
SmartEnvInput: typeof import('./components/smart/EnvInput.vue')['default']
SmartExpand: (typeof import("./../../hoppscotch-ui/src/components/smart/Expand.vue"))["default"]
SmartFileChip: (typeof import("./../../hoppscotch-ui/src/components/smart/FileChip.vue"))["default"]
SmartInput: (typeof import("./../../hoppscotch-ui/src/components/smart/Input.vue"))["default"]
SmartIntersection: (typeof import("./../../hoppscotch-ui/src/components/smart/Intersection.vue"))["default"]
SmartItem: (typeof import("./../../hoppscotch-ui/src/components/smart/Item.vue"))["default"]
SmartLink: (typeof import("./../../hoppscotch-ui/src/components/smart/Link.vue"))["default"]
SmartModal: (typeof import("./../../hoppscotch-ui/src/components/smart/Modal.vue"))["default"]
SmartPicture: (typeof import("./../../hoppscotch-ui/src/components/smart/Picture.vue"))["default"]
SmartPlaceholder: (typeof import("./../../hoppscotch-ui/src/components/smart/Placeholder.vue"))["default"]
SmartProgressRing: (typeof import("./../../hoppscotch-ui/src/components/smart/ProgressRing.vue"))["default"]
SmartRadio: (typeof import("./../../hoppscotch-ui/src/components/smart/Radio.vue"))["default"]
SmartRadioGroup: (typeof import("./../../hoppscotch-ui/src/components/smart/RadioGroup.vue"))["default"]
SmartSelectWrapper: (typeof import("./../../hoppscotch-ui/src/components/smart/SelectWrapper.vue"))["default"]
SmartSlideOver: (typeof import("./../../hoppscotch-ui/src/components/smart/SlideOver.vue"))["default"]
SmartSpinner: (typeof import("./../../hoppscotch-ui/src/components/smart/Spinner.vue"))["default"]
SmartTab: (typeof import("./../../hoppscotch-ui/src/components/smart/Tab.vue"))["default"]
SmartTable: (typeof import("./../../hoppscotch-ui/src/components/smart/Table.vue"))["default"]
SmartTabs: (typeof import("./../../hoppscotch-ui/src/components/smart/Tabs.vue"))["default"]
SmartToggle: (typeof import("./../../hoppscotch-ui/src/components/smart/Toggle.vue"))["default"]
SmartTree: (typeof import("./../../hoppscotch-ui/src/components/smart/Tree.vue"))["default"]
SmartTreeBranch: (typeof import("./../../hoppscotch-ui/src/components/smart/TreeBranch.vue"))["default"]
SmartWindow: (typeof import("./../../hoppscotch-ui/src/components/smart/Window.vue"))["default"]
SmartWindows: (typeof import("./../../hoppscotch-ui/src/components/smart/Windows.vue"))["default"]
TabPrimary: typeof import('./components/tab/Primary.vue')['default']
TabSecondary: typeof import('./components/tab/Secondary.vue')['default']
Teams: typeof import('./components/teams/index.vue')['default']
@ -338,6 +306,8 @@ declare module 'vue' {
TeamsModal: typeof import('./components/teams/Modal.vue')['default']
TeamsTeam: typeof import('./components/teams/Team.vue')['default']
Tippy: typeof import('vue-tippy')['Tippy']
UserDeactivate: typeof import('./../../hoppscotch-web/src/components/UserDeactivate.vue')['default']
UserDeletionSoleTeamOwnerInfo: typeof import('./../../hoppscotch-web/src/components/UserDeletionSoleTeamOwnerInfo.vue')['default']
WorkspaceCurrent: typeof import('./components/workspace/Current.vue')['default']
WorkspaceSelector: typeof import('./components/workspace/Selector.vue')['default']
}

View file

@ -1,5 +1,11 @@
<template>
<AppPaneLayout layout-id="embed-primary" :is-embed="true">
<component
:is="platform.ui?.additionalEmbedsComponent"
v-if="showCustomEmbedsView"
v-bind="props"
/>
<AppPaneLayout v-else layout-id="embed-primary" :is-embed="true">
<template #primary>
<EmbedsHeader :shared-request-u-r-l="sharedRequestURL" />
<div class="flex flex-col flex-1">
@ -28,6 +34,7 @@ import { computed, useModel } from "vue"
import { ref } from "vue"
import { HoppTab } from "~/services/tab"
import { HoppRequestDocument } from "~/helpers/rest/document"
import { platform } from "~/platform"
import { RESTOptionTabs } from "../http/RequestOptions.vue"
const props = defineProps<{
@ -40,11 +47,12 @@ const tab = useModel(props, "modelTab")
const selectedOptionTab = ref<RESTOptionTabs>(props.properties[0])
const shortcodeBaseURL =
import.meta.env.VITE_SHORTCODE_BASE_URL ?? "https://hopp.sh"
const shortcodeBaseURL = computed(
() => import.meta.env.VITE_SHORTCODE_BASE_URL ?? "https://hopp.sh"
)
const sharedRequestURL = computed(() => {
return `${shortcodeBaseURL}/r/${props.sharedRequestID}`
return `${shortcodeBaseURL.value}/e/${props.sharedRequestID}`
})
const tabRequestVariables = computed(() => {
@ -55,4 +63,14 @@ const tabRequestVariables = computed(() => {
sourceEnv: "RequestVariable",
}))
})
const showCustomEmbedsView = computed(() => {
const { organization, ui } = platform
return (
organization &&
!organization.isDefaultCloudInstance &&
ui?.additionalEmbedsComponent
)
})
</script>

View file

@ -24,6 +24,7 @@ import { useToast } from "@composables/toast"
import { useI18n } from "@composables/i18n"
import { platform } from "~/platform"
import { defineActionHandler } from "~/helpers/actions"
import { useRouter } from "vue-router"
defineProps({
outline: {
@ -45,11 +46,26 @@ const logoutItem = ref<HTMLButtonElement>()
const toast = useToast()
const t = useI18n()
const router = useRouter()
const logout = async () => {
try {
await platform.auth.signOutUser()
toast.success(`${t("auth.logged_out")}`)
const { organization } = platform
if (!organization) {
return
}
if (!organization.isDefaultCloudInstance) {
router.push({
name: "orgs-login-required",
})
window.location.reload()
}
} catch (e) {
console.error(e)
toast.error(`${t("error.something_went_wrong")}`)

View file

@ -1,10 +1,10 @@
<template>
<section class="p-4">
<h4 class="font-semibold text-secondaryDark">
{{ t("settings.delete_account") }}
{{ deleteAccountLabel }}
</h4>
<div class="my-1 mb-4 text-secondaryLight">
{{ t("settings.delete_account_description") }}
{{ deleteAccountDescription }}
</div>
<HoppButtonSecondary
filled
@ -16,7 +16,7 @@
<HoppSmartModal
v-if="showDeleteAccountModal"
dialog
:title="t('settings.delete_account')"
:title="deleteAccountLabel"
@close="showDeleteAccountModal = false"
>
<template #body>
@ -36,6 +36,12 @@
<ul class="my-4 ml-8 list-disc space-y-2">
<li v-for="team in myTeams" :key="team.id">
{{ team.name }}
<component
:is="platform.ui.additionalUserDeletionSoleTeamOwnerInfo"
v-if="platform.ui?.additionalUserDeletionSoleTeamOwnerInfo"
:team="team"
/>
</li>
</ul>
<span class="font-semibold">
@ -51,7 +57,7 @@
{{ t("error.danger_zone") }}
</h2>
<div class="font-medium text-secondaryDark">
{{ t("settings.delete_account_description") }}
{{ deleteAccountDescription }}
</div>
</div>
<div class="flex flex-col">
@ -101,13 +107,13 @@
<script setup lang="ts">
import { pipe } from "fp-ts/function"
import * as TE from "fp-ts/TaskEither"
import { ref, watch } from "vue"
import { GQLError, runGQLQuery } from "~/helpers/backend/GQLClient"
import { GQLError } from "~/helpers/backend/GQLClient"
import * as E from "fp-ts/Either"
import { computed, ref, watch } from "vue"
import { useRouter } from "vue-router"
import { useI18n } from "~/composables/i18n"
import { GetMyTeamsQuery } from "~/helpers/backend/graphql"
import { useToast } from "~/composables/toast"
import { GetMyTeamsDocument, GetMyTeamsQuery } from "~/helpers/backend/graphql"
import { deleteUser } from "~/helpers/backend/mutations/Profile"
import { platform } from "~/platform"
@ -119,6 +125,7 @@ const showDeleteAccountModal = ref(false)
const userVerificationInput = ref("")
const loading = ref(true)
const myTeams = ref<GetMyTeamsQuery["myTeams"]>([])
watch(showDeleteAccountModal, (isModalOpen) => {
@ -127,12 +134,23 @@ watch(showDeleteAccountModal, (isModalOpen) => {
}
})
const deleteAccountLabel = computed(() =>
platform.organization
? t("organization.delete_account")
: t("settings.delete_account")
)
const deleteAccountDescription = computed(() =>
platform.organization
? t("organization.delete_account_description")
: t("settings.delete_account_description")
)
const fetchMyTeams = async () => {
loading.value = true
const result = await runGQLQuery({
query: GetMyTeamsDocument,
variables: {},
})
const result = await platform.backend.getUserTeams(undefined, true)
loading.value = false
if (E.isLeft(result)) {
@ -175,6 +193,16 @@ const getErrorMessage = (err: GQLError<string>) => {
return t("error.network_error")
}
const { error } = err
if (error.includes("user/is_sole_admin")) {
return t("organization.user_deletion_failed_sole_admin")
}
if (error.includes("user/is_owner")) {
return t("organization.user_deletion_failed_sole_team_owner")
}
return t("error.something_went_wrong")
}
</script>

View file

@ -198,7 +198,7 @@
<script lang="ts" setup>
import { useVModel } from "@vueuse/core"
import { computed, ref } from "vue"
import { computed, onMounted, ref } from "vue"
import { PropType } from "vue"
import { useI18n } from "~/composables/i18n"
import IconMonitor from "~icons/lucide/monitor"
@ -206,6 +206,7 @@ import IconSun from "~icons/lucide/sun"
import IconMoon from "~icons/lucide/moon"
import { TippyComponent } from "vue-tippy"
import { HoppRESTRequest } from "@hoppscotch/data"
import { platform } from "~/platform"
const t = useI18n()
@ -306,6 +307,25 @@ type EmbedOption = {
}[]
theme: "light" | "dark" | "system"
}
const organizationDomain = ref<string>("")
onMounted(async () => {
const { organization } = platform
if (!organization || organization.isDefaultCloudInstance) {
return
}
const orgInfo = await organization.getOrgInfo()
if (orgInfo) {
const { orgDomain } = orgInfo
organizationDomain.value = orgDomain
}
})
const embedThemeIcon = computed(() => {
if (embedOptions.value.theme === "system") {
return IconMonitor
@ -375,11 +395,19 @@ const linkVariants: LinkVariant[] = [
},
]
const shortcodeBaseURL =
import.meta.env.VITE_SHORTCODE_BASE_URL ?? "https://hopp.sh"
const shortcodeBaseURL = computed(() => {
const { organization } = platform
if (!organization || organization.isDefaultCloudInstance) {
return import.meta.env.VITE_SHORTCODE_BASE_URL ?? "https://hopp.sh"
}
const rootDomain = organization.getRootDomain()
return `https://${organizationDomain.value}.${rootDomain}`
})
const copyEmbed = () => {
return `<iframe src="${shortcodeBaseURL}/e/${props.request?.id}" title="Hoppscotch Embed" style="width: 100%; height: 480px; border-radius: 4px; border: 1px solid rgba(0, 0, 0, 0.1);"></iframe>`
return `<iframe src="${shortcodeBaseURL.value}/e/${props.request?.id}" title="Hoppscotch Embed" style="width: 100%; height: 480px; border-radius: 4px; border: 1px solid rgba(0, 0, 0, 0.1);"></iframe>`
}
const copyButton = (
@ -396,18 +424,18 @@ const copyButton = (
}
if (type === "markdown") {
return `[![Run in Hoppscotch](${shortcodeBaseURL}/${badge})](${shortcodeBaseURL}/r/${props.request?.id})`
return `[![Run in Hoppscotch](${shortcodeBaseURL.value}/${badge})](${shortcodeBaseURL.value}/r/${props.request?.id})`
}
return `<a href="${shortcodeBaseURL}/r/${props.request?.id}"><img src="${shortcodeBaseURL}/${badge}" alt="Run in Hoppscotch" /></a>`
return `<a href="${shortcodeBaseURL.value}/r/${props.request?.id}"><img src="${shortcodeBaseURL.value}/${badge}" alt="Run in Hoppscotch" /></a>`
}
const copyLink = (variationID: string) => {
if (variationID === "link1") {
return `${shortcodeBaseURL}/r/${props.request?.id}`
return `${shortcodeBaseURL.value}/r/${props.request?.id}`
} else if (variationID === "link2") {
return `<a href="${shortcodeBaseURL}/r/${props.request?.id}">Run in Hoppscotch</a>`
return `<a href="${shortcodeBaseURL.value}/r/${props.request?.id}">Run in Hoppscotch</a>`
}
return `[Run in Hoppscotch](${shortcodeBaseURL}/r/${props.request?.id})`
return `[Run in Hoppscotch](${shortcodeBaseURL.value}/r/${props.request?.id})`
}
const copyContent = ({

View file

@ -11,8 +11,9 @@
</template>
<script lang="ts" setup>
import { computed } from "vue"
import { computed, onMounted, ref } from "vue"
import { useI18n } from "~/composables/i18n"
import { platform } from "~/platform"
const t = useI18n()
@ -21,12 +22,38 @@ const props = defineProps<{
label?: string | undefined
}>()
const shortcodeBaseURL =
import.meta.env.VITE_SHORTCODE_BASE_URL ?? "https://hopp.sh"
const organizationDomain = ref<string>("")
onMounted(async () => {
const { organization } = platform
if (!organization || organization.isDefaultCloudInstance) {
return
}
const orgInfo = await organization.getOrgInfo()
if (orgInfo) {
const { orgDomain } = orgInfo
organizationDomain.value = orgDomain
}
})
const shortcodeBaseURL = computed(() => {
const { organization } = platform
if (!organization || organization.isDefaultCloudInstance) {
return import.meta.env.VITE_SHORTCODE_BASE_URL ?? "https://hopp.sh"
}
const rootDomain = organization.getRootDomain()
return `https://${organizationDomain.value}.${rootDomain}`
})
const text = computed(() => {
return props.label
? t(props.label)
: `${shortcodeBaseURL}/r/${props.link ?? "xxxx"}`
: `${shortcodeBaseURL.value}/r/${props.link ?? "xxxx"}`
})
</script>

View file

@ -390,46 +390,46 @@
</template>
<script setup lang="ts">
import { watch, ref, reactive, computed, Ref, onMounted } from "vue"
import * as T from "fp-ts/Task"
import * as E from "fp-ts/Either"
import { useGQLQuery } from "@composables/graphql"
import * as A from "fp-ts/Array"
import * as E from "fp-ts/Either"
import * as O from "fp-ts/Option"
import * as T from "fp-ts/Task"
import { flow, pipe } from "fp-ts/function"
import { Email, EmailCodec } from "../../helpers/backend/types/Email"
import { computed, onMounted, reactive, ref, Ref, watch } from "vue"
import { GQLError } from "~/helpers/backend/GQLClient"
import {
TeamInvitationAddedDocument,
TeamInvitationRemovedDocument,
TeamMemberRole,
GetPendingInvitesDocument,
GetPendingInvitesQuery,
GetPendingInvitesQueryVariables,
TeamInvitationAddedDocument,
TeamInvitationRemovedDocument,
TeamMemberRole,
} from "../../helpers/backend/graphql"
import {
createTeamInvitation,
CreateTeamInvitationErrors,
revokeTeamInvitation,
} from "../../helpers/backend/mutations/TeamInvitation"
import { GQLError } from "~/helpers/backend/GQLClient"
import { useGQLQuery } from "@composables/graphql"
import { Email, EmailCodec } from "../../helpers/backend/types/Email"
import { useI18n } from "@composables/i18n"
import { useToast } from "@composables/toast"
import { useColorMode } from "~/composables/theming"
import IconTrash from "~icons/lucide/trash"
import IconPlus from "~icons/lucide/plus"
import IconAlertTriangle from "~icons/lucide/alert-triangle"
import IconMailCheck from "~icons/lucide/mail-check"
import IconCircleDot from "~icons/lucide/circle-dot"
import IconCircle from "~icons/lucide/circle"
import IconArrowLeft from "~icons/lucide/arrow-left"
import IconCopy from "~icons/lucide/copy"
import IconCheck from "~icons/lucide/check"
import { TippyComponent } from "vue-tippy"
import { refAutoReset } from "@vueuse/core"
import { TippyComponent } from "vue-tippy"
import { copyToClipboard } from "~/helpers/utils/clipboard"
import { platform } from "~/platform"
import IconAlertTriangle from "~icons/lucide/alert-triangle"
import IconArrowLeft from "~icons/lucide/arrow-left"
import IconCheck from "~icons/lucide/check"
import IconCircle from "~icons/lucide/circle"
import IconCircleDot from "~icons/lucide/circle-dot"
import IconCopy from "~icons/lucide/copy"
import IconMailCheck from "~icons/lucide/mail-check"
import IconPlus from "~icons/lucide/plus"
import IconTrash from "~icons/lucide/trash"
const copyIcons: Record<string, Ref<typeof IconCopy | typeof IconCheck>> = {}
const getCopyIcon = (id: string) => {
@ -463,6 +463,8 @@ const emit = defineEmits<{
const inviteMethod = ref<"email" | "link">("email")
let organizationDomain = ""
onMounted(async () => {
const getIsSMTPEnabled = platform.infra?.getIsSMTPEnabled
@ -473,6 +475,18 @@ onMounted(async () => {
inviteMethod.value = res.right ? "email" : "link"
}
}
const { organization } = platform
if (!organization) {
return
}
if (!organization.isDefaultCloudInstance) {
const orgInfo = await organization.getOrgInfo()
organizationDomain = orgInfo?.orgDomain ?? ""
}
})
const pendingInvites = useGQLQuery<
@ -566,9 +580,18 @@ const removeNewInvitee = (id: number) => {
}
const copyInviteLink = (invitationID: string) => {
copyToClipboard(
`${import.meta.env.VITE_BASE_URL}/join-team?id=${invitationID}`
)
let inviteLink = ""
const { organization } = platform
if (organization && !organization.isDefaultCloudInstance) {
const rootDomain = organization?.getRootDomain()
inviteLink = `https://${organizationDomain}.${rootDomain}/join-team?id=${invitationID}`
} else {
inviteLink = `${import.meta.env.VITE_BASE_URL}/join-team?id=${invitationID}`
}
copyToClipboard(inviteLink)
getCopyIcon(invitationID).value = IconCheck
}

View file

@ -65,6 +65,14 @@
<icon-lucide-help-circle class="svg-icons mb-4" />
{{ t("error.something_went_wrong") }}
</div>
<div v-if="showCreateOrganizationCTA" class="flex flex-col">
<hr />
<HoppButtonPrimary
:label="t('organization.create_an_organization')"
to="/orgs"
/>
</div>
</div>
<TeamsAdd
:show="showModalAdd"
@ -152,6 +160,12 @@ const isActiveWorkspace = computed(() => (id: string) => {
return workspace.value.teamID === id
})
const showCreateOrganizationCTA = computed(() => {
const { organization } = platform
return organization?.isDefaultCloudInstance ?? false
})
const switchToTeamWorkspace = (team: GetMyTeamsQuery["myTeams"][number]) => {
REMEMBERED_TEAM_ID.value = team.id
workspaceService.changeWorkspace({

View file

@ -1,9 +1,7 @@
import { HoppRESTRequest } from "@hoppscotch/data"
import { platform } from "~/platform"
import { runMutation } from "../GQLClient"
import {
CreateShortcodeDocument,
CreateShortcodeMutation,
CreateShortcodeMutationVariables,
DeleteShortcodeDocument,
DeleteShortcodeMutation,
DeleteShortcodeMutationVariables,
@ -17,14 +15,7 @@ type DeleteShortcodeErrors = "shortcode/not_found"
export const createShortcode = (
request: HoppRESTRequest,
properties?: string
) =>
runMutation<CreateShortcodeMutation, CreateShortcodeMutationVariables, "">(
CreateShortcodeDocument,
{
request: JSON.stringify(request),
properties,
}
)
) => platform.backend.createShortcode(request, properties)
export const deleteShortcode = (code: string) =>
runMutation<

View file

@ -3,9 +3,6 @@ import * as TE from "fp-ts/TaskEither"
import { runMutation } from "../GQLClient"
import { TeamName } from "../types/TeamName"
import {
CreateTeamDocument,
CreateTeamMutation,
CreateTeamMutationVariables,
DeleteTeamDocument,
DeleteTeamMutation,
DeleteTeamMutationVariables,
@ -23,6 +20,7 @@ import {
UpdateTeamMemberRoleMutation,
UpdateTeamMemberRoleMutationVariables,
} from "../graphql"
import { platform } from "~/platform"
type DeleteTeamErrors =
| "team/not_required_role"
@ -52,17 +50,12 @@ type RemoveTeamMemberErrors =
| "team/invalid_id"
| "team/not_required_role"
export const createTeam = (name: TeamName) =>
pipe(
runMutation<
CreateTeamMutation,
CreateTeamMutationVariables,
CreateTeamErrors
>(CreateTeamDocument, {
name,
}),
export const createTeam = (name: TeamName) => {
return pipe(
platform.backend.createTeam<CreateTeamErrors>(name),
TE.map(({ createTeam }) => createTeam)
)
}
export const deleteTeam = (teamID: string) =>
runMutation<

View file

@ -1,13 +1,8 @@
import { pipe } from "fp-ts/function"
import * as TE from "fp-ts/TaskEither"
import { platform } from "~/platform"
import { runMutation } from "../GQLClient"
import {
AcceptTeamInvitationDocument,
AcceptTeamInvitationMutation,
AcceptTeamInvitationMutationVariables,
CreateTeamInvitationDocument,
CreateTeamInvitationMutation,
CreateTeamInvitationMutationVariables,
RevokeTeamInvitationDocument,
RevokeTeamInvitationMutation,
RevokeTeamInvitationMutationVariables,
@ -36,19 +31,16 @@ export const createTeamInvitation = (
inviteeEmail: Email,
inviteeRole: TeamMemberRole,
teamID: string
) =>
pipe(
runMutation<
CreateTeamInvitationMutation,
CreateTeamInvitationMutationVariables,
CreateTeamInvitationErrors
>(CreateTeamInvitationDocument, {
) => {
return pipe(
platform.backend.createTeamInvitation<CreateTeamInvitationErrors>(
inviteeEmail,
inviteeRole,
teamID,
}),
teamID
),
TE.map((x) => x.createTeamInvitation)
)
}
export const revokeTeamInvitation = (inviteID: string) =>
runMutation<
@ -60,10 +52,4 @@ export const revokeTeamInvitation = (inviteID: string) =>
})
export const acceptTeamInvitation = (inviteID: string) =>
runMutation<
AcceptTeamInvitationMutation,
AcceptTeamInvitationMutationVariables,
AcceptTeamInvitationErrors
>(AcceptTeamInvitationDocument, {
inviteID,
})
platform.backend.acceptTeamInvitation<AcceptTeamInvitationErrors>(inviteID)

View file

@ -1,20 +1,16 @@
import * as E from "fp-ts/Either"
import { BehaviorSubject, Subscription } from "rxjs"
import { Subscription as WSubscription } from "wonka"
import {
GQLError,
runAuthOnlyGQLSubscription,
runGQLQuery,
} from "../backend/GQLClient"
import { GQLError, runAuthOnlyGQLSubscription } from "../backend/GQLClient"
import {
GetUserShortcodesQuery,
GetUserShortcodesDocument,
ShortcodeCreatedDocument,
ShortcodeDeletedDocument,
ShortcodeUpdatedDocument,
} from "../backend/graphql"
import { BACKEND_PAGE_SIZE } from "../backend/helpers"
import { Shortcode } from "./Shortcode"
import { platform } from "~/platform"
export default class ShortcodeListAdapter {
error$: BehaviorSubject<GQLError<string> | null>
@ -95,12 +91,8 @@ export default class ShortcodeListAdapter {
? this.shortcodes$.value[this.shortcodes$.value.length - 1].id
: undefined
const result = await runGQLQuery({
query: GetUserShortcodesDocument,
variables: {
cursor: lastCodeID,
},
})
const result = await platform.backend.getUserShortcodes(lastCodeID)
if (E.isLeft(result)) {
this.error$.next(result.left)
console.error(result.left)

View file

@ -1,8 +1,8 @@
import * as E from "fp-ts/Either"
import { BehaviorSubject } from "rxjs"
import { GQLError, runGQLQuery } from "../backend/GQLClient"
import { GetMyTeamsDocument, GetMyTeamsQuery } from "../backend/graphql"
import { platform } from "~/platform"
import { GQLError } from "../backend/GQLClient"
import { GetMyTeamsQuery } from "../backend/graphql"
const BACKEND_PAGE_SIZE = 10
const POLL_DURATION = 10000
@ -68,13 +68,10 @@ export default class TeamListAdapter {
const results: GetMyTeamsQuery["myTeams"] = []
while (true) {
const result = await runGQLQuery({
query: GetMyTeamsDocument,
variables: {
cursor:
results.length > 0 ? results[results.length - 1].id : undefined,
},
})
const cursor =
results.length > 0 ? results[results.length - 1].id : undefined
const result = await platform.backend.getUserTeams(cursor)
if (E.isLeft(result)) {
this.error$.next(result.left)

View file

@ -7,8 +7,9 @@
</template>
<script lang="ts">
import { defineComponent } from "vue"
import { useI18n } from "@composables/i18n"
import { defineComponent } from "vue"
import { useRoute } from "vue-router"
import { initializeApp } from "~/helpers/app"
import { platform } from "~/platform"
@ -16,6 +17,7 @@ export default defineComponent({
setup() {
return {
t: useI18n(),
route: useRoute(),
}
},
data() {
@ -28,6 +30,19 @@ export default defineComponent({
initializeApp()
},
async mounted() {
const { redirect, ...queryParams } = this.route.query
if (redirect && Object.keys(queryParams).length) {
const url = new URL(("https://" + redirect) as string)
Object.entries(queryParams).forEach(([key, value]) => {
url.searchParams.set(key, value as string)
})
window.location.href = url.href
return
}
this.signingInWithEmail = true
try {

View file

@ -141,28 +141,23 @@
</div>
</template>
<script lang="ts">
import { computed, defineComponent } from "vue"
import { useRoute } from "vue-router"
<script lang="ts" setup>
import * as E from "fp-ts/Either"
import * as TE from "fp-ts/TaskEither"
import { pipe } from "fp-ts/function"
import { GQLError } from "~/helpers/backend/GQLClient"
import { useGQLQuery } from "@composables/graphql"
import {
GetInviteDetailsDocument,
GetInviteDetailsQuery,
GetInviteDetailsQueryVariables,
} from "~/helpers/backend/graphql"
import { acceptTeamInvitation } from "~/helpers/backend/mutations/TeamInvitation"
import { initializeApp } from "~/helpers/app"
import { platform } from "~/platform"
import { computed, onBeforeMount, onMounted, ref } from "vue"
import { useRoute } from "vue-router"
import { onLoggedIn } from "@composables/auth"
import { useReadonlyStream } from "@composables/stream"
import { useToast } from "@composables/toast"
import { invokeAction } from "@helpers/actions"
import { useI18n } from "~/composables/i18n"
import { initializeApp } from "~/helpers/app"
import { GQLError } from "~/helpers/backend/GQLClient"
import { acceptTeamInvitation } from "~/helpers/backend/mutations/TeamInvitation"
import { platform } from "~/platform"
import IconHome from "~icons/lucide/home"
import { invokeAction } from "~/helpers/actions"
type GetInviteDetailsError =
| "team_invite/not_valid_viewer"
@ -171,115 +166,94 @@ type GetInviteDetailsError =
| "team_invite/email_do_not_match"
| "team_invite/already_member"
export default defineComponent({
layout: "empty",
// Data properties
const invalidLink = ref(false)
const loading = ref(false)
const revokedLink = ref(false)
const inviteID = ref("")
const joinTeamSuccess = ref(false)
setup() {
const route = useRoute()
const route = useRoute()
const inviteDetails = useGQLQuery<
GetInviteDetailsQuery,
GetInviteDetailsQueryVariables,
GetInviteDetailsError
>({
query: GetInviteDetailsDocument,
variables: {
inviteID: route.query.id as string,
},
defer: true,
})
const inviteDetails = platform.backend.getInviteDetails<GetInviteDetailsError>(
route.query.id as string
)
onLoggedIn(() => {
if (typeof route.query.id === "string") {
inviteDetails.execute({
inviteID: route.query.id,
})
}
})
const probableUser = useReadonlyStream(
platform.auth.getProbableUserStream(),
platform.auth.getProbableUser()
)
const currentUser = useReadonlyStream(
platform.auth.getCurrentUserStream(),
platform.auth.getCurrentUser()
)
const loadingCurrentUser = computed(() => {
if (!probableUser.value) return false
else if (!currentUser.value) return true
return false
})
return {
E,
inviteDetails,
loadingCurrentUser,
currentUser,
toast: useToast(),
t: useI18n(),
IconHome,
invokeAction,
}
},
data() {
return {
invalidLink: false,
loading: false,
revokedLink: false,
inviteID: "",
joinTeamSuccess: false,
}
},
beforeMount() {
initializeApp()
},
mounted() {
if (typeof this.$route.query.id === "string") {
this.inviteID = this.$route.query.id
}
this.invalidLink = !this.inviteID
// TODO: check revokeTeamInvitation
// TODO: check login user already a member
},
methods: {
joinTeam() {
this.loading = true
pipe(
acceptTeamInvitation(this.inviteID),
TE.matchW(
() => {
this.loading = false
this.toast.error(`${this.t("error.something_went_wrong")}`)
},
() => {
this.joinTeamSuccess = true
this.loading = false
}
)
)()
},
getErrorMessage(error: GQLError<GetInviteDetailsError>) {
if (error.type === "network_error") {
return this.t("error.network_error")
}
switch (error.error) {
case "team_invite/not_valid_viewer":
return this.t("team.not_valid_viewer")
case "team_invite/not_found":
return this.t("team.not_found")
case "team_invite/no_invite_found":
return this.t("team.no_invite_found")
case "team_invite/already_member":
return this.t("team.already_member")
case "team_invite/email_do_not_match":
return this.t("team.email_do_not_match")
default:
return this.t("error.something_went_wrong")
}
},
},
// Lifecycle hooks
onBeforeMount(() => {
initializeApp()
})
onMounted(async () => {
if (typeof route.query.id === "string") {
inviteID.value = route.query.id
}
invalidLink.value = !inviteID.value
// TODO: check revokeTeamInvitation
// TODO: check if the logged-in user is already a member
})
onLoggedIn(async () => {
if (typeof route.query.id === "string") {
inviteDetails.execute({
inviteID: route.query.id,
})
}
})
const probableUser = useReadonlyStream(
platform.auth.getProbableUserStream(),
platform.auth.getProbableUser()
)
const currentUser = useReadonlyStream(
platform.auth.getCurrentUserStream(),
platform.auth.getCurrentUser()
)
const loadingCurrentUser = computed(() => {
if (!probableUser.value) return false
if (!currentUser.value) return true
return false
})
const toast = useToast()
const t = useI18n()
const joinTeam = () => {
loading.value = true
pipe(
acceptTeamInvitation(inviteID.value),
TE.matchW(
() => {
loading.value = false
toast.error(`${t("error.something_went_wrong")}`)
},
() => {
joinTeamSuccess.value = true
loading.value = false
}
)
)()
}
const getErrorMessage = (error: GQLError<GetInviteDetailsError>) => {
if (error.type === "network_error") {
return t("error.network_error")
}
switch (error.error) {
case "team_invite/not_valid_viewer":
return t("team.not_valid_viewer")
case "team_invite/not_found":
return t("team.not_found")
case "team_invite/no_invite_found":
return t("team.no_invite_found")
case "team_invite/already_member":
return t("team.already_member")
case "team_invite/email_do_not_match":
return t("team.email_do_not_match")
default:
return t("error.something_went_wrong")
}
}
</script>

View file

@ -1,46 +1,46 @@
<template>
<div class="flex flex-col items-center justify-between">
<div
v-if="invalidLink"
class="flex flex-1 flex-col items-center justify-center"
<!-- Shortcodes are behind login on subdomain based cloud instances -->
<template
v-if="
platform.organization && !platform.organization.isDefaultCloudInstance
"
>
<icon-lucide-alert-triangle class="svg-icons mb-2 opacity-75" />
<h1 class="heading text-center">
{{ t("error.invalid_link") }}
</h1>
<p class="mt-2 text-center">
{{ t("error.invalid_link_description") }}
</p>
<p class="mt-4">
<HoppButtonSecondary
to="/"
:icon="IconHome"
filled
:label="t('app.home')"
/>
</p>
</div>
<div v-else class="flex flex-1 flex-col items-center justify-center p-4">
<div
v-if="sharedRequestDetails.loading"
v-if="loadingCurrentUser"
class="flex flex-1 flex-col items-center justify-center p-4"
>
<HoppSmartSpinner />
<HoppSmartSpinner class="mb-4" />
</div>
<div v-else>
<HoppSmartPlaceholder
v-else-if="currentUser === null"
:src="`/images/states/${colorMode.value}/login.svg`"
:alt="t('empty.shared_requests_logout')"
:text="t('empty.shared_requests_logout')"
>
<template #body>
<HoppButtonPrimary
:label="t('auth.login')"
@click="invokeAction('modals.login.toggle')"
/>
</template>
</HoppSmartPlaceholder>
<template v-else>
<div
v-if="
!sharedRequestDetails.loading && E.isLeft(sharedRequestDetails.data)
"
class="flex flex-col items-center p-4"
v-if="invalidLink"
class="flex flex-1 flex-col items-center justify-center"
>
<icon-lucide-alert-triangle class="svg-icons mb-2 opacity-75" />
<h1 class="heading text-center">
{{ t("error.invalid_link") }}
</h1>
<p class="mt-2 text-center">
{{ t("error.invalid_link_description") }}
</p>
<p class="mt-4">
<HoppButtonSecondary
to="/"
@ -48,32 +48,153 @@
filled
:label="t('app.home')"
/>
<HoppButtonSecondary
:icon="IconRefreshCW"
:label="t('app.reload')"
filled
@click="reloadApplication"
/>
</p>
</div>
<div
v-if="
!sharedRequestDetails.loading &&
E.isRight(sharedRequestDetails.data)
"
v-else
class="flex flex-1 flex-col items-center justify-center p-4"
>
<h1 class="heading">
{{ t("state.loading") }}
</h1>
<div
v-if="sharedRequestDetails.loading"
class="flex flex-1 flex-col items-center justify-center p-4"
>
<HoppSmartSpinner />
</div>
<div v-else>
<div
v-if="
!sharedRequestDetails.loading &&
E.isLeft(sharedRequestDetails.data)
"
class="flex flex-col items-center p-4"
>
<icon-lucide-alert-triangle class="svg-icons mb-2 opacity-75" />
<h1 class="heading text-center">
{{ t("error.invalid_link") }}
</h1>
<p class="mt-2 text-center">
{{ t("error.invalid_link_description") }}
</p>
<p class="mt-4">
<HoppButtonSecondary
to="/"
:icon="IconHome"
filled
:label="t('app.home')"
/>
<HoppButtonSecondary
:icon="IconRefreshCW"
:label="t('app.reload')"
filled
@click="reloadApplication"
/>
</p>
</div>
<div
v-if="
!sharedRequestDetails.loading &&
E.isRight(sharedRequestDetails.data)
"
class="flex flex-1 flex-col items-center justify-center p-4"
>
<h1 class="heading">
{{ t("state.loading") }}
</h1>
</div>
</div>
</div>
</template>
</template>
<template v-else>
<div
v-if="invalidLink"
class="flex flex-1 flex-col items-center justify-center"
>
<icon-lucide-alert-triangle class="svg-icons mb-2 opacity-75" />
<h1 class="heading text-center">
{{ t("error.invalid_link") }}
</h1>
<p class="mt-2 text-center">
{{ t("error.invalid_link_description") }}
</p>
<p class="mt-4">
<HoppButtonSecondary
to="/"
:icon="IconHome"
filled
:label="t('app.home')"
/>
</p>
</div>
<div v-else class="flex flex-1 flex-col items-center justify-center p-4">
<div
v-if="sharedRequestDetails.loading"
class="flex flex-1 flex-col items-center justify-center p-4"
>
<HoppSmartSpinner />
</div>
<div v-else>
<div
v-if="
!sharedRequestDetails.loading &&
E.isLeft(sharedRequestDetails.data)
"
class="flex flex-col items-center p-4"
>
<icon-lucide-alert-triangle class="svg-icons mb-2 opacity-75" />
<h1 class="heading text-center">
{{ t("error.invalid_link") }}
</h1>
<p class="mt-2 text-center">
{{ t("error.invalid_link_description") }}
</p>
<p class="mt-4">
<HoppButtonSecondary
to="/"
:icon="IconHome"
filled
:label="t('app.home')"
/>
<HoppButtonSecondary
:icon="IconRefreshCW"
:label="t('app.reload')"
filled
@click="reloadApplication"
/>
</p>
</div>
<div
v-if="
!sharedRequestDetails.loading &&
E.isRight(sharedRequestDetails.data)
"
class="flex flex-1 flex-col items-center justify-center p-4"
>
<h1 class="heading">
{{ t("state.loading") }}
</h1>
</div>
</div>
</div>
</div>
</template>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref, watch } from "vue"
import { computed, onMounted, ref, watch } from "vue"
import { useRoute, useRouter } from "vue-router"
import * as E from "fp-ts/Either"
import { safelyExtractRESTRequest } from "@hoppscotch/data"
@ -91,6 +212,10 @@ import { getDefaultRESTRequest } from "~/helpers/rest/default"
import { platform } from "~/platform"
import { RESTTabService } from "~/services/tab/rest"
import { useService } from "dioc/vue"
import { invokeAction } from "~/helpers/actions"
import { useColorMode } from "~/composables/theming"
import { useReadonlyStream } from "~/composables/stream"
import { until } from "@vueuse/core"
const route = useRoute()
const router = useRouter()
@ -113,6 +238,34 @@ const sharedRequestDetails = useGQLQuery<
},
})
const currentUser = useReadonlyStream(
platform.auth.getCurrentUserStream(),
platform.auth.getCurrentUser()
)
const probableUser = useReadonlyStream(
platform.auth.getProbableUserStream(),
platform.auth.getProbableUser()
)
const colorMode = useColorMode()
const loadingCurrentUser = computed(() => {
if (!probableUser.value) return false
else if (!currentUser.value) return true
return false
})
watch(
() => currentUser.value,
() => {
if (typeof route.params.id === "string") {
sharedRequestID.value = route.params.id
sharedRequestDetails.execute()
}
invalidLink.value = !sharedRequestID.value
}
)
watch(
() => sharedRequestDetails.data,
() => addRequestToTab()
@ -149,7 +302,17 @@ const reloadApplication = () => {
window.location.reload()
}
onMounted(() => {
onMounted(async () => {
const { organization } = platform
if (organization && !organization.isDefaultCloudInstance) {
await until(loadingCurrentUser).toMatch((val) => !val)
if (!currentUser.value) {
return
}
}
if (typeof route.params.id === "string") {
sharedRequestID.value = route.params.id
sharedRequestDetails.execute()

View file

@ -0,0 +1,72 @@
import { HoppRESTRequest } from "@hoppscotch/data"
import * as TE from "fp-ts/TaskEither"
import * as E from "fp-ts/lib/Either"
import { GQLError } from "~/helpers/backend/GQLClient"
import {
CreateShortcodeMutation,
GetInviteDetailsQuery,
GetInviteDetailsQueryVariables,
GetMyTeamsQuery,
GetUserShortcodesQuery,
TeamMemberRole,
} from "~/helpers/backend/graphql"
import { useGQLQuery } from "~/composables/graphql"
import { Email } from "~/helpers/backend/types/Email"
import { TeamName } from "~/helpers/backend/types/TeamName"
import {
AcceptTeamInvitationMutation,
CreateTeamInvitationMutation,
CreateTeamMutation,
} from "../helpers/backend/graphql"
export type BackendPlatformDef = {
// Read actions via GQL queries
getInviteDetails: <GetInviteDetailsError extends string>(
inviteID: string
) => ReturnType<
typeof useGQLQuery<
GetInviteDetailsQuery,
GetInviteDetailsQueryVariables,
GetInviteDetailsError
>
>
getUserShortcodes: (
cursor?: string
) => Promise<E.Either<GQLError<"">, GetUserShortcodesQuery>>
// Sample use case for `matchAllTeams` would be at the cloud platform level where the list of teams across instances is fetched
// and not limited to a single instance.
getUserTeams: (
cursor?: string,
matchAllTeams?: boolean
) => Promise<E.Either<GQLError<"">, GetMyTeamsQuery>>
// Write actions via GQL mutations
createTeam: <CreateTeamErrors extends string>(
name: TeamName
) => TE.TaskEither<GQLError<CreateTeamErrors>, CreateTeamMutation>
createTeamInvitation: <CreateTeamInvitationErrors extends string>(
inviteeEmail: Email,
inviteeRole: TeamMemberRole,
teamID: string
) => TE.TaskEither<
GQLError<CreateTeamInvitationErrors>,
CreateTeamInvitationMutation
>
acceptTeamInvitation: <AcceptTeamInvitationErrors extends string>(
inviteID: string
) => TE.TaskEither<
GQLError<AcceptTeamInvitationErrors>,
AcceptTeamInvitationMutation
>
createShortcode: (
request: HoppRESTRequest,
properties?: string
) => TE.TaskEither<GQLError<string>, CreateShortcodeMutation>
}

View file

@ -15,6 +15,8 @@ import { LimitsPlatformDef } from "./limits"
import { SettingsPlatformDef } from "./settings"
import { SpotlightPlatformDef } from "./spotlight"
import { UIPlatformDef } from "./ui"
import { BackendPlatformDef } from "./backend"
import { OrganizationPlatformDef } from "./organization"
export type PlatformDef = {
ui?: UIPlatformDef
@ -58,6 +60,8 @@ export type PlatformDef = {
limits?: LimitsPlatformDef
infra?: InfraPlatformDef
experiments?: ExperimentsPlatformDef
backend: BackendPlatformDef
organization?: OrganizationPlatformDef
}
export let platform: PlatformDef

View file

@ -0,0 +1,9 @@
export type OrganizationPlatformDef = {
isDefaultCloudInstance: boolean
getOrgInfo: () => Promise<{
orgID: string
orgDomain: string
} | null>
getRootDomain: () => string
initiateOnboarding: () => void
}

View file

@ -0,0 +1,132 @@
import { useGQLQuery } from "~/composables/graphql"
import { TeamName } from "~/helpers/backend/types/TeamName"
import { BackendPlatformDef } from "~/platform/backend"
import { HoppRESTRequest } from "@hoppscotch/data"
import { runGQLQuery, runMutation } from "~/helpers/backend/GQLClient"
import { Email } from "~/helpers/backend/types/Email"
import {
AcceptTeamInvitationDocument,
AcceptTeamInvitationMutation,
AcceptTeamInvitationMutationVariables,
CreateShortcodeDocument,
CreateShortcodeMutation,
CreateShortcodeMutationVariables,
CreateTeamDocument,
CreateTeamInvitationDocument,
CreateTeamInvitationMutation,
CreateTeamInvitationMutationVariables,
CreateTeamMutation,
CreateTeamMutationVariables,
GetInviteDetailsDocument,
GetInviteDetailsQuery,
GetInviteDetailsQueryVariables,
GetMyTeamsDocument,
GetMyTeamsQuery,
GetMyTeamsQueryVariables,
GetUserShortcodesDocument,
GetUserShortcodesQuery,
GetUserShortcodesQueryVariables,
TeamMemberRole,
} from "../../helpers/backend/graphql"
const getInviteDetails = <GetInviteDetailsError extends string>(
inviteID: string
) => {
return useGQLQuery<
GetInviteDetailsQuery,
GetInviteDetailsQueryVariables,
GetInviteDetailsError
>({
query: GetInviteDetailsDocument,
variables: {
inviteID,
},
defer: true,
})
}
const getUserShortcodes = (cursor?: string) => {
return runGQLQuery<
GetUserShortcodesQuery,
GetUserShortcodesQueryVariables,
""
>({
query: GetUserShortcodesDocument,
variables: {
cursor,
},
})
}
const getUserTeams = (cursor?: string) => {
return runGQLQuery<GetMyTeamsQuery, GetMyTeamsQueryVariables, "">({
query: GetMyTeamsDocument,
variables: {
cursor,
},
})
}
export const createTeam = <CreateTeamErrors extends string>(name: TeamName) => {
return runMutation<
CreateTeamMutation,
CreateTeamMutationVariables,
CreateTeamErrors
>(CreateTeamDocument, {
name,
})
}
export const createTeamInvitation = <CreateTeamInvitationErrors extends string>(
inviteeEmail: Email,
inviteeRole: TeamMemberRole,
teamID: string
) => {
return runMutation<
CreateTeamInvitationMutation,
CreateTeamInvitationMutationVariables,
CreateTeamInvitationErrors
>(CreateTeamInvitationDocument, {
inviteeEmail,
inviteeRole,
teamID,
})
}
export const acceptTeamInvitation = <AcceptTeamInvitationErrors extends string>(
inviteID: string
) => {
return runMutation<
AcceptTeamInvitationMutation,
AcceptTeamInvitationMutationVariables,
AcceptTeamInvitationErrors
>(AcceptTeamInvitationDocument, {
inviteID,
})
}
export const createShortcode = (
request: HoppRESTRequest,
properties?: string
) => {
return runMutation<
CreateShortcodeMutation,
CreateShortcodeMutationVariables,
""
>(CreateShortcodeDocument, {
request: JSON.stringify(request),
properties,
})
}
export const def: BackendPlatformDef = {
getInviteDetails,
getUserShortcodes,
getUserTeams,
createTeam,
createTeamInvitation,
acceptTeamInvitation,
createShortcode,
}

View file

@ -58,4 +58,16 @@ export type UIPlatformDef = {
* Custom sidebar header item to be shown in the sidebar header
*/
additionalSidebarHeaderItem?: Component
/**
* More info shown in the danger zone section while attempting user deletion
* Sample use case includes displaying the instance information on cloud instances
*/
additionalUserDeletionSoleTeamOwnerInfo?: Component
/**
* Customize embeds appearance at the platform level
* Sample use case includes bringing embeds behind auth on sub domain based cloud instances
*/
additionalEmbedsComponent?: Component
}

View file

@ -25,15 +25,14 @@ import { Container, Service } from "dioc"
import * as E from "fp-ts/Either"
import MiniSearch from "minisearch"
import IconCheckCircle from "~/components/app/spotlight/entry/IconSelected.vue"
import { runGQLQuery } from "~/helpers/backend/GQLClient"
import { GetMyTeamsDocument, GetMyTeamsQuery } from "~/helpers/backend/graphql"
import { GetMyTeamsQuery } from "~/helpers/backend/graphql"
import { platform } from "~/platform"
import { WorkspaceService } from "~/services/workspace.service"
import IconEdit from "~icons/lucide/edit"
import IconTrash2 from "~icons/lucide/trash-2"
import IconUser from "~icons/lucide/user"
import IconUserPlus from "~icons/lucide/user-plus"
import IconUsers from "~icons/lucide/users"
import { WorkspaceService } from "~/services/workspace.service"
type Doc = {
text: string | string[]
@ -180,13 +179,10 @@ export class SwitchWorkspaceSpotlightSearcherService
const results: GetMyTeamsQuery["myTeams"] = []
const result = await runGQLQuery({
query: GetMyTeamsDocument,
variables: {
cursor:
results.length > 0 ? results[results.length - 1].id : undefined,
},
})
const cursor =
results.length > 0 ? results[results.length - 1].id : undefined
const result = await platform.backend.getUserTeams(cursor)
if (E.isRight(result)) results.push(...result.right.myTeams)
resolve(results)

View file

@ -4,6 +4,7 @@ import { def as environmentsDef } from "./platform/environments/environments.pla
import { def as collectionsDef } from "./platform/collections/collections.platform"
import { def as settingsDef } from "./platform/settings/settings.platform"
import { def as historyDef } from "./platform/history/history.platform"
import { def as backendDef } from "@hoppscotch/common/platform/std/backend"
import { proxyInterceptor } from "@hoppscotch/common/platform/std/interceptors/proxy"
import { NativeInterceptorService } from "./platform/interceptors/native"
import { nextTick, ref, watch } from "vue"
@ -63,6 +64,7 @@ const headerPaddingTop = ref("0px")
cookiesEnabled: true,
promptAsUsingCookies: false,
},
backend: backendDef,
})
watch(

View file

@ -4,6 +4,7 @@ import { def as environmentsDef } from "./platform/environments/environments.pla
import { def as collectionsDef } from "./platform/collections/collections.platform"
import { def as settingsDef } from "./platform/settings/settings.platform"
import { def as historyDef } from "./platform/history/history.platform"
import { def as backendDef } from "@hoppscotch/common/platform/std/backend"
import { browserInterceptor } from "@hoppscotch/common/platform/std/interceptors/browser"
import { proxyInterceptor } from "@hoppscotch/common/platform/std/interceptors/proxy"
import { ExtensionInspectorService } from "@hoppscotch/common/platform/std/inspections/extension.inspector"
@ -47,4 +48,5 @@ createHoppApp("#app", {
collectionImportSizeLimit: 50,
},
infra: InfraPlatform,
backend: backendDef,
})