feat: openssl based hoppscotch-relay for request forwarding (#4442)

This commit is contained in:
Shreyas
2024-10-24 14:20:51 +05:30
committed by GitHub
parent deedf35bf0
commit f52219bb95
28 changed files with 2284 additions and 859 deletions

View File

@@ -0,0 +1,181 @@
<template>
<HoppSmartModal
v-if="show"
dialog
:title="t('agent.ca_certs')"
@close="emit('hide-modal')"
>
<template #body>
<div class="flex flex-col space-y-4">
<ul
v-if="certificates.length > 0"
class="mx-4 border border-dividerDark rounded"
>
<li
v-for="(certificate, index) in certificates"
:key="index"
class="flex border-dividerDark px-2 items-center justify-between"
:class="{ 'border-t border-dividerDark': index !== 0 }"
>
<div class="truncate">
{{ certificate.filename }}
</div>
<div class="flex items-center">
<HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:icon="certificate.enabled ? IconCheckCircle : IconCircle"
:title="
certificate.enabled
? t('action.turn_off')
: t('action.turn_on')
"
color="green"
@click="toggleEntryEnabled(index)"
/>
<HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:icon="IconTrash"
:title="t('action.remove')"
@click="deleteEntry(index)"
/>
</div>
</li>
</ul>
<HoppButtonSecondary
class="mx-4"
:icon="IconPlus"
:label="t('agent.add_cert_file')"
:loading="selectedFiles && selectedFiles!.length > 0"
filled
outline
@click="openFilePicker"
/>
<p class="text-center text-secondaryLight">
Hoppscotch supports .crt, .cer or .pem files containing one or more
certificates.
</p>
</div>
</template>
<template #footer>
<div class="flex space-x-2">
<HoppButtonPrimary :label="t('action.save')" @click="save" />
<HoppButtonSecondary
:label="t('action.cancel')"
filled
outline
@click="emit('hide-modal')"
/>
</div>
</template>
</HoppSmartModal>
</template>
<!-- TODO: i18n -->
<script setup lang="ts">
import IconPlus from "~icons/lucide/plus"
import IconCheckCircle from "~icons/lucide/check-circle"
import IconCircle from "~icons/lucide/circle"
import IconTrash from "~icons/lucide/trash"
import { useService } from "dioc/vue"
import { ref, watch } from "vue"
import { useFileDialog } from "@vueuse/core"
import { cloneDeep } from "lodash-es"
import { useI18n } from "@composables/i18n"
import {
CACertificateEntry,
AgentInterceptorService,
} from "~/platform/std/interceptors/agent"
import { useToast } from "@composables/toast"
import { hasValidExtension } from "~/helpers/utils/file-extension"
const t = useI18n()
const toast = useToast()
const props = defineProps<{
show: boolean
}>()
const emit = defineEmits<{
(e: "hide-modal"): void
}>()
const nativeInterceptorService = useService(AgentInterceptorService)
const certificates = ref<CACertificateEntry[]>([])
const {
files: selectedFiles,
open: openFilePicker,
reset: resetFilePicker,
onChange: onSelectedFilesChange,
} = useFileDialog({
multiple: true,
})
const ALLOWED_EXTENSIONS = [".crt", ".cer", ".pem"]
function isValidCertType(filename: string): boolean {
return hasValidExtension(filename, ALLOWED_EXTENSIONS)
}
// When files are selected, add them to the list of certificates and reset the file list
onSelectedFilesChange(async (files) => {
if (files) {
const addedCertificates: CACertificateEntry[] = []
for (let i = 0; i < files.length; i++) {
const file = files[i]
if (!isValidCertType(file.name)) {
toast.error(t("error.invalid_file_type", { filename: file.name }))
continue
}
const data = new Uint8Array(await file.arrayBuffer())
addedCertificates.push({
filename: file.name,
enabled: true,
certificate: data,
})
}
certificates.value.push(...addedCertificates)
resetFilePicker()
}
})
// When the modal is shown, clone the certificates from the service,
// We only write to the service when the user clicks on save
watch(
() => props.show,
(show) => {
if (show) {
certificates.value = cloneDeep(
nativeInterceptorService.caCertificates.value
)
} else {
resetFilePicker()
}
}
)
function save() {
nativeInterceptorService.caCertificates.value = certificates.value
emit("hide-modal")
}
function deleteEntry(index: number) {
certificates.value.splice(index, 1)
}
function toggleEntryEnabled(index: number) {
certificates.value[index].enabled = !certificates.value[index].enabled
}
</script>

View File

@@ -9,37 +9,29 @@
</div>
<div class="flex space-x-4">
<!--
<HoppButtonSecondary
:icon="IconLucideFileBadge"
:label="'CA Certificates'"
outline
@click="showCACertificatesModal = true"
/>
-->
<!--
<HoppButtonSecondary
:icon="IconLucideFileKey"
:label="t('agent.client_certs')"
outline
@click="showClientCertificatesModal = true"
/>
-->
</div>
<!--
<ModalsNativeCACertificates
<InterceptorsAgentModalNativeCACertificates
:show="showCACertificatesModal"
@hide-modal="showCACertificatesModal = false"
/>
-->
<!--
<InterceptorsAgentModalNativeClientCertificates
:show="showClientCertificatesModal"
@hide-modal="showClientCertificatesModal = false"
/>
-->
<div class="pt-4 space-y-4">
<div class="flex items-center">
@@ -68,7 +60,7 @@
<script setup lang="ts">
import { computed, ref } from "vue"
import { useI18n } from "@composables/i18n"
// import IconLucideFileKey from "~icons/lucide/file-key"
import IconLucideFileKey from "~icons/lucide/file-key"
import { useService } from "dioc/vue"
import {
RequestDef,
@@ -84,8 +76,8 @@ const agentInterceptorService = useService(AgentInterceptorService)
const allowSSLVerification = agentInterceptorService.validateCerts
// const showCACertificatesModal = ref(false)
// const showClientCertificatesModal = ref(false)
const showCACertificatesModal = ref(false)
const showClientCertificatesModal = ref(false)
const allowProxy = ref(false)
const proxyURL = ref("")