web/satellite/vuetify-poc: implement Manage Passphrase dialog

This change implements the Manage Passphrase dialog in the Vuetify
project. Within it, users can create, switch, or clear the current
passphrase.

Resolves #6284

Change-Id: I2ca87e62b59c0cefd13bf36005c9301b35f12255
This commit is contained in:
Jeremy Wharton 2023-09-18 15:24:28 -05:00
parent 471111122b
commit 950672ca6c
29 changed files with 722 additions and 138 deletions

View File

@ -1,3 +0,0 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.99999 0.900024C13.4735 0.900024 17.1 4.52652 17.1 9.00002C17.1 13.4735 13.4735 17.1 8.99999 17.1C4.52649 17.1 0.899994 13.4735 0.899994 9.00002C0.899994 4.52652 4.52649 0.900024 8.99999 0.900024ZM8.99999 2.38502C5.34663 2.38502 2.38499 5.34666 2.38499 9.00002C2.38499 12.6534 5.34663 15.615 8.99999 15.615C12.6534 15.615 15.615 12.6534 15.615 9.00002C15.615 5.34666 12.6534 2.38502 8.99999 2.38502ZM9.6746 6.20904L9.6749 6.22099L9.67486 8.30072H11.6934C12.1076 8.30072 12.4472 8.62922 12.461 9.04322C12.4742 9.43988 12.1633 9.77213 11.7667 9.78533L11.7547 9.78563L9.67486 9.78572L9.67499 11.8539C9.67499 12.264 9.34257 12.5964 8.93249 12.5964C8.53242 12.5964 8.20626 12.28 8.19058 11.8838L8.18999 11.8539L8.18986 9.78572H6.12179C5.71172 9.78572 5.37929 9.4533 5.37929 9.04322C5.37929 8.64316 5.6957 8.31699 6.09193 8.30131L6.12179 8.30072H8.18986L8.18999 6.28235C8.18999 5.86811 8.51849 5.5285 8.93249 5.51473C9.32915 5.50153 9.6614 5.81238 9.6746 6.20904Z" fill="#56606D"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -74,7 +74,10 @@
:ref="stepInfos[CreateAccessStep.EnterNewPassphrase].ref"
:passphrase-type="CreateAccessStep.EnterNewPassphrase"
@passphrase-changed="newPass => passphrase = newPass"
/>
>
This passphrase will be used to encrypt all the files you upload using this access grant.
You will need it to access these files in the future.
</enter-passphrase-step>
</v-window-item>
<v-window-item :value="CreateAccessStep.PassphraseGenerated">
@ -82,7 +85,10 @@
:ref="stepInfos[CreateAccessStep.PassphraseGenerated].ref"
:name="name"
@passphrase-changed="newPass => passphrase = newPass"
/>
>
This passphrase will be used to encrypt all the files you upload using this access grant.
You will need it to access these files in the future.
</passphrase-generated-step>
</v-window-item>
<v-window-item :value="CreateAccessStep.ConfirmDetails">
@ -164,14 +170,13 @@
</template>
<script setup lang="ts">
import { Component, Ref, computed, ref, watch, WatchStopHandle, onMounted } from 'vue';
import { Component, Ref, computed, ref, watch, WatchStopHandle } from 'vue';
import {
VCol,
VRow,
VBtn,
VDialog,
VCard,
VSheet,
VCardItem,
VCardTitle,
VDivider,
@ -188,17 +193,18 @@ import { useConfigStore } from '@/store/modules/configStore';
import { useAnalyticsStore } from '@/store/modules/analyticsStore';
import { useNotify } from '@/utils/hooks';
import { AccessType, PassphraseOption, Permission, CreateAccessStep, STEP_ICON_AND_TITLE } from '@/types/createAccessGrant';
import { AccessGrantEndDate, ACCESS_TYPE_LINKS, CreateAccessStepComponent } from '@poc/types/createAccessGrant';
import { AccessGrantEndDate, ACCESS_TYPE_LINKS } from '@poc/types/createAccessGrant';
import { LocalData } from '@/utils/localData';
import { AccessGrant, EdgeCredentials } from '@/types/accessGrants';
import { AnalyticsErrorEventSource, AnalyticsEvent } from '@/utils/constants/analyticsEventNames';
import { DialogStepComponent } from '@poc/types/common';
import CreateNewAccessStep from '@poc/components/dialogs/createAccessSteps/CreateNewAccessStep.vue';
import ChoosePermissionsStep from '@poc/components/dialogs/createAccessSteps/ChoosePermissionsStep.vue';
import AccessEncryptionStep from '@poc/components/dialogs/createAccessSteps/AccessEncryptionStep.vue';
import EncryptionInfoStep from '@poc/components/dialogs/createAccessSteps/EncryptionInfoStep.vue';
import EnterPassphraseStep from '@poc/components/dialogs/createAccessSteps/EnterPassphraseStep.vue';
import PassphraseGeneratedStep from '@poc/components/dialogs/createAccessSteps/PassphraseGeneratedStep.vue';
import EnterPassphraseStep from '@poc/components/dialogs/commonPassphraseSteps/EnterPassphraseStep.vue';
import PassphraseGeneratedStep from '@poc/components/dialogs/commonPassphraseSteps/PassphraseGeneratedStep.vue';
import ConfirmDetailsStep from '@poc/components/dialogs/createAccessSteps/ConfirmDetailsStep.vue';
import AccessCreatedStep from '@poc/components/dialogs/createAccessSteps/AccessCreatedStep.vue';
import CLIAccessCreatedStep from '@poc/components/dialogs/createAccessSteps/CLIAccessCreatedStep.vue';
@ -207,7 +213,7 @@ import S3CredentialsCreatedStep from '@poc/components/dialogs/createAccessSteps/
type CreateAccessLocation = CreateAccessStep | null | (() => (CreateAccessStep | null));
class StepInfo {
public ref: Ref<CreateAccessStepComponent | null> = ref<CreateAccessStepComponent | null>(null);
public ref: Ref<DialogStepComponent | null> = ref<DialogStepComponent | null>(null);
public prev: Ref<CreateAccessStep | null>;
public next: Ref<CreateAccessStep | null>;
public nextText: Ref<string>;
@ -401,9 +407,10 @@ async function nextStep(): Promise<void> {
return;
}
info.ref.value?.onExit?.('next');
const next = info.next.value;
if (!next) {
info.ref.value?.onExit?.();
model.value = false;
return;
}
@ -414,7 +421,11 @@ async function nextStep(): Promise<void> {
* Navigates to the previous step.
*/
function prevStep(): void {
const prev = stepInfos[step.value].prev.value;
const info = stepInfos[step.value];
info.ref.value?.onExit?.('prev');
const prev = info.prev.value;
if (!prev) return;
step.value = prev;
}
@ -422,11 +433,9 @@ function prevStep(): void {
/**
* Initializes the current step when it has changed.
*/
watch(step, (newStep, oldStep) => {
watch(step, newStep => {
if (!innerContent.value) return;
stepInfos[oldStep].ref.value?.onExit?.();
// Window items are lazy loaded, so the component may not exist yet
let unwatch: WatchStopHandle | null = null;
let unwatchImmediately = false;

View File

@ -0,0 +1,276 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<v-dialog v-model="model" width="414px" transition="fade-transition">
<v-card ref="innerContent" rounded="xlg">
<v-card-item class="pl-7 py-4 pos-relative">
<template #prepend>
<img class="d-block" :src="stepInfo[step].ref.value?.iconSrc || LockIcon">
</template>
<v-card-title class="font-weight-bold">
{{ stepInfo[step].ref.value?.title }}
</v-card-title>
<template #append>
<v-btn icon="$close" variant="text" size="small" color="default" @click="model = false" />
</template>
</v-card-item>
<v-divider />
<v-window v-model="step" class="overflow-y-auto">
<v-window-item :value="ManageProjectPassphraseStep.ManageOptions">
<manage-options-step
:ref="stepInfo[ManageProjectPassphraseStep.ManageOptions].ref"
@option-click="newStep => step = newStep"
/>
</v-window-item>
<v-window-item :value="ManageProjectPassphraseStep.Create">
<create-step :ref="stepInfo[ManageProjectPassphraseStep.Create].ref" />
</v-window-item>
<v-window-item :value="ManageProjectPassphraseStep.EncryptionPassphrase">
<encryption-passphrase-step
:ref="stepInfo[ManageProjectPassphraseStep.EncryptionPassphrase].ref"
@select-option="newOpt => passphraseOption = newOpt"
/>
</v-window-item>
<v-window-item :value="ManageProjectPassphraseStep.EnterPassphrase">
<enter-passphrase-step
:ref="stepInfo[ManageProjectPassphraseStep.EnterPassphrase].ref"
ack-required
@passphrase-changed="newPass => passphrase = newPass"
>
Please note that Storj does not know or store your encryption passphrase.
If you lose it, you will not be able to recover your files.
</enter-passphrase-step>
</v-window-item>
<v-window-item :value="ManageProjectPassphraseStep.PassphraseGenerated">
<passphrase-generated-step
:ref="stepInfo[ManageProjectPassphraseStep.PassphraseGenerated].ref"
:name="projectName"
@passphrase-changed="newPass => passphrase = newPass"
>
Please note that Storj does not know or store your encryption passphrase.
If you lose it, you will not be able to recover your files.
</passphrase-generated-step>
</v-window-item>
<v-window-item :value="ManageProjectPassphraseStep.Success">
<success-step
:ref="stepInfo[ManageProjectPassphraseStep.Success].ref"
:passphrase="passphrase"
:option="passphraseOption"
/>
</v-window-item>
<v-window-item :value="ManageProjectPassphraseStep.Switch">
<enter-passphrase-step
:ref="stepInfo[ManageProjectPassphraseStep.Switch].ref"
title="Switch Passphrase"
set-on-next
>
Switch passphrase to view existing data that is uploaded with a different passphrase, or upload new data.
Please note that you won't see the previous data once you switch passphrases.
</enter-passphrase-step>
</v-window-item>
<v-window-item :value="ManageProjectPassphraseStep.Clear">
<clear-step :ref="stepInfo[ManageProjectPassphraseStep.Clear].ref" />
</v-window-item>
<!-- This is required to prevent the above item from sliding in the wrong direction when Back is clicked. -->
<v-window-item />
</v-window>
<v-divider />
<v-card-actions class="pa-7">
<v-row>
<v-col v-if="stepInfo[step].prev">
<v-btn
v-if="stepInfo[step].prev"
variant="outlined"
color="default"
prepend-icon="mdi-chevron-left"
block
@click="onBackClick"
>
Back
</v-btn>
</v-col>
<v-col v-else-if="stepInfo[step].showCancelButton">
<v-btn variant="outlined" color="default" block @click="model = false">
Cancel
</v-btn>
</v-col>
<v-col v-if="stepInfo[step].next || stepInfo[step].showNextButton">
<v-btn
color="primary"
variant="flat"
block
append-icon="mdi-chevron-right"
@click="onNextClick"
>
Continue
</v-btn>
</v-col>
</v-row>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script setup lang="ts">
import { Component, Ref, WatchStopHandle, computed, ref, watch } from 'vue';
import {
VDialog,
VCard,
VCardItem,
VCardTitle,
VWindow,
VWindowItem,
VDivider,
VCardActions,
VRow,
VCol,
VBtn,
} from 'vuetify/components';
import { ManageProjectPassphraseStep, PassphraseOption } from '@poc/types/managePassphrase';
import { useProjectsStore } from '@/store/modules/projectsStore';
import { DialogStepComponent } from '@poc/types/common';
import ManageOptionsStep from '@poc/components/dialogs/managePassphraseSteps/ManageOptionsStep.vue';
import CreateStep from '@poc/components/dialogs/managePassphraseSteps/CreateStep.vue';
import EncryptionPassphraseStep from '@poc/components/dialogs/managePassphraseSteps/EncryptionPassphraseStep.vue';
import EnterPassphraseStep from '@poc/components/dialogs/commonPassphraseSteps/EnterPassphraseStep.vue';
import PassphraseGeneratedStep from '@poc/components/dialogs/commonPassphraseSteps/PassphraseGeneratedStep.vue';
import SuccessStep from '@poc/components/dialogs/managePassphraseSteps/SuccessStep.vue';
import ClearStep from '@poc/components/dialogs/managePassphraseSteps/ClearStep.vue';
import LockIcon from '@/../static/images/accessGrants/newCreateFlow/accessEncryption.svg';
class StepInfo {
public ref: Ref<DialogStepComponent | null> = ref<DialogStepComponent | null>(null);
constructor(
public prev: ManageProjectPassphraseStep | null = null,
public next: ManageProjectPassphraseStep | (() => ManageProjectPassphraseStep) | null = null,
public showCancelButton: boolean = true,
public showNextButton: boolean = true,
) {}
}
const props = defineProps<{
modelValue: boolean,
}>();
const model = computed<boolean>({
get: () => props.modelValue,
set: value => emit('update:modelValue', value),
});
const emit = defineEmits<{
'update:modelValue': [value: boolean];
}>();
const projectsStore = useProjectsStore();
const innerContent = ref<Component | null>(null);
const step = ref<ManageProjectPassphraseStep>(ManageProjectPassphraseStep.ManageOptions);
const passphraseOption = ref<PassphraseOption>(PassphraseOption.EnterPassphrase);
const passphrase = ref<string>('');
const projectName = computed<string>(() => projectsStore.state.selectedProject.name);
const stepInfo: Record<ManageProjectPassphraseStep, StepInfo> = {
[ManageProjectPassphraseStep.ManageOptions]: new StepInfo(null, null, true, false),
[ManageProjectPassphraseStep.Create]: new StepInfo(
ManageProjectPassphraseStep.ManageOptions,
ManageProjectPassphraseStep.EncryptionPassphrase,
),
[ManageProjectPassphraseStep.EncryptionPassphrase]: new StepInfo(
ManageProjectPassphraseStep.Create,
() => passphraseOption.value === PassphraseOption.GeneratePassphrase
? ManageProjectPassphraseStep.PassphraseGenerated
: ManageProjectPassphraseStep.EnterPassphrase,
),
[ManageProjectPassphraseStep.PassphraseGenerated]: new StepInfo(
ManageProjectPassphraseStep.EncryptionPassphrase,
ManageProjectPassphraseStep.Success,
),
[ManageProjectPassphraseStep.EnterPassphrase]: new StepInfo(
ManageProjectPassphraseStep.EncryptionPassphrase,
ManageProjectPassphraseStep.Success,
),
[ManageProjectPassphraseStep.Success]: new StepInfo(null, null, false),
[ManageProjectPassphraseStep.Switch]: new StepInfo(ManageProjectPassphraseStep.ManageOptions),
[ManageProjectPassphraseStep.Clear]: new StepInfo(ManageProjectPassphraseStep.ManageOptions),
};
function onBackClick(): void {
const info = stepInfo[step.value];
info.ref.value?.onExit?.('prev');
if (info.prev !== null) {
step.value = info.prev;
return;
}
model.value = false;
}
function onNextClick(): void {
const info = stepInfo[step.value];
if (info.ref.value?.validate?.() === false) return;
info.ref.value?.onExit?.('next');
const next = typeof info.next === 'function' ? info.next() : info.next;
if (next !== null) {
step.value = next;
return;
}
model.value = false;
}
/**
* Initializes a step when it has been entered.
*/
watch(step, newStep => {
if (!innerContent.value) return;
// Window items are lazy loaded, so the component may not exist yet
let unwatch: WatchStopHandle | null = null;
let unwatchImmediately = false;
unwatch = watch(
() => stepInfo[newStep].ref.value,
stepComp => {
if (!stepComp) return;
stepComp.onEnter?.();
if (unwatch) {
unwatch();
return;
}
unwatchImmediately = true;
},
{ immediate: true },
);
if (unwatchImmediately) unwatch();
});
watch(innerContent, comp => {
if (comp) return;
step.value = ManageProjectPassphraseStep.ManageOptions;
});
</script>

View File

@ -0,0 +1,86 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<v-form ref="form" class="pa-8" @submit.prevent>
<v-row>
<v-col v-if="$slots.default" cols="12">
<slot />
</v-col>
<v-col cols="12">
<v-text-field
v-model="passphrase"
label="Encryption Passphrase"
:append-inner-icon="isPassphraseVisible ? 'mdi-eye-off' : 'mdi-eye'"
:type="isPassphraseVisible ? 'text' : 'password'"
variant="outlined"
:hide-details="false"
:rules="[ RequiredRule ]"
@click:append-inner="isPassphraseVisible = !isPassphraseVisible"
/>
</v-col>
<v-col v-if="ackRequired" cols="12">
<v-checkbox
density="compact"
color="primary"
label="Yes, I saved my encryption passphrase."
:hide-details="false"
:rules="[ RequiredRule ]"
/>
</v-col>
</v-row>
</v-form>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue';
import { VForm, VRow, VCol, VTextField, VCheckbox } from 'vuetify/components';
import { RequiredRule, DialogStepComponent } from '@poc/types/common';
import { useBucketsStore } from '@/store/modules/bucketsStore';
import { EdgeCredentials } from '@/types/accessGrants';
import { useNotify } from '@/utils/hooks';
const bucketsStore = useBucketsStore();
const notify = useNotify();
const form = ref<VForm | null>(null);
const passphrase = ref<string>('');
const isPassphraseVisible = ref<boolean>(false);
const props = withDefaults(defineProps<{
title: string;
setOnNext: boolean;
ackRequired: boolean;
}>(), {
title: 'Enter New Passphrase',
setOnNext: false,
ackRequired: false,
});
const emit = defineEmits<{
'passphraseChanged': [passphrase: string];
}>();
watch(passphrase, value => emit('passphraseChanged', value));
defineExpose<DialogStepComponent>({
title: props.title,
validate: () => {
form.value?.validate();
return !!form.value?.isValid;
},
onExit: to => {
if (!props.setOnNext || to !== 'next') return;
bucketsStore.setEdgeCredentials(new EdgeCredentials());
bucketsStore.setPassphrase(passphrase.value);
bucketsStore.setPromptForPassphrase(false);
notify.success('Passphrase switched.');
},
});
</script>

View File

@ -4,11 +4,10 @@
<template>
<v-form ref="form" class="pa-8">
<v-row>
<v-col cols="12">
This passphrase will be used to encrypt all the files you upload using this access grant.
You will need it to access these files in the future.
<v-col v-if="$slots.default" cols="12">
<slot />
</v-col>
<save-buttons :access-name="name" :items="[ passphrase ]" file-name-base="passphrase" />
<save-buttons :name="name" :items="[ passphrase ]" type="passphrase" />
<v-divider class="my-3" />
<v-col cols="12">
@ -38,11 +37,12 @@ import { ref } from 'vue';
import { generateMnemonic } from 'bip39-english';
import { VForm, VRow, VCol, VCheckbox, VDivider } from 'vuetify/components';
import { RequiredRule } from '@poc/types/common';
import { CreateAccessStepComponent } from '@poc/types/createAccessGrant';
import { RequiredRule, DialogStepComponent } from '@poc/types/common';
import TextOutputArea from '@poc/components/dialogs/createAccessSteps/TextOutputArea.vue';
import SaveButtons from '@poc/components/dialogs/createAccessSteps/SaveButtons.vue';
import SaveButtons from '@poc/components/dialogs/commonPassphraseSteps/SaveButtons.vue';
import Icon from '@/../static/images/accessGrants/newCreateFlow/passphraseGenerated.svg';
const props = defineProps<{
name: string;
@ -57,8 +57,9 @@ const isTooltipDisabled = ref<boolean>(false);
const passphrase: string = generateMnemonic();
defineExpose<CreateAccessStepComponent>({
defineExpose<DialogStepComponent>({
title: 'Passphrase Generated',
iconSrc: Icon,
onEnter: () => {
emit('passphraseChanged', passphrase);
isTooltipDisabled.value = false;

View File

@ -32,15 +32,15 @@
import { ref, computed } from 'vue';
import { VCol, VBtn } from 'vuetify/components';
import { SaveButtonsItem } from '@poc/types/createAccessGrant';
import { SaveButtonsItem } from '@poc/types/common';
import { Download } from '@/utils/download';
import { useAnalyticsStore } from '@/store/modules/analyticsStore';
import { AnalyticsEvent } from '@/utils/constants/analyticsEventNames';
const props = defineProps<{
items: SaveButtonsItem[];
accessName: string;
fileNameBase: string;
name: string;
type: string;
}>();
const successDuration = 2000;
@ -71,7 +71,7 @@ function onCopy(): void {
function onDownload(): void {
Download.file(
props.items.map(item => typeof item === 'string' ? item : `${item.name}:\n${item.value}`).join('\n\n'),
`Storj-${props.fileNameBase}-${props.accessName}-${new Date().toISOString()}.txt`,
`Storj-${props.type}-${props.name}-${new Date().toISOString()}.txt`,
);
analyticsStore.eventTriggered(AnalyticsEvent.DOWNLOAD_TXT_CLICKED);

View File

@ -7,7 +7,7 @@
<v-col cols="12">
Copy or save the Access Grant as it will only appear once.
</v-col>
<save-buttons :items="[ accessGrant ]" :access-name="name" file-name-base="access" />
<save-buttons :items="[ accessGrant ]" :name="name" type="access" />
<v-divider class="my-3" />
<v-col cols="12">
@ -21,10 +21,10 @@
import { ref } from 'vue';
import { VRow, VCol, VDivider } from 'vuetify/components';
import { CreateAccessStepComponent } from '@poc/types/createAccessGrant';
import { DialogStepComponent } from '@poc/types/common';
import TextOutputArea from '@poc/components/dialogs/createAccessSteps/TextOutputArea.vue';
import SaveButtons from '@poc/components/dialogs/createAccessSteps/SaveButtons.vue';
import SaveButtons from '@poc/components/dialogs/commonPassphraseSteps/SaveButtons.vue';
const props = defineProps<{
name: string;
@ -34,7 +34,7 @@ const props = defineProps<{
const output = ref<InstanceType<typeof TextOutputArea> | null>(null);
const isTooltipDisabled = ref<boolean>(false);
defineExpose<CreateAccessStepComponent>({
defineExpose<DialogStepComponent>({
title: 'Access Created',
onEnter: () => isTooltipDisabled.value = false,
onExit: () => isTooltipDisabled.value = true,

View File

@ -102,9 +102,8 @@ import {
} from 'vuetify/components';
import { PassphraseOption } from '@/types/createAccessGrant';
import { CreateAccessStepComponent } from '@poc/types/createAccessGrant';
import { useBucketsStore } from '@/store/modules/bucketsStore';
import { RequiredRule, ValidationRule } from '@poc/types/common';
import { ValidationRule, DialogStepComponent } from '@poc/types/common';
import InfoTooltip from '@poc/components/dialogs/createAccessSteps/InfoTooltip.vue';
@ -141,7 +140,7 @@ const passphraseRules = computed<ValidationRule<string>[]>(() => {
return [ v => !required || !!v || 'Required' ];
});
defineExpose<CreateAccessStepComponent>({
defineExpose<DialogStepComponent>({
title: 'Access Encryption',
validate: () => {
form.value?.validate();

View File

@ -7,7 +7,7 @@
<v-col cols="12">
Copy or save the satellite address and API key as they will only appear once.
</v-col>
<save-buttons :items="saveItems" :access-name="name" file-name-base="CLI-access" />
<save-buttons :items="saveItems" :name="name" type="CLI-access" />
<v-divider class="my-3" />
<v-col cols="12">
@ -35,10 +35,10 @@ import { ref, computed } from 'vue';
import { VRow, VCol, VDivider } from 'vuetify/components';
import { useConfigStore } from '@/store/modules/configStore';
import { CreateAccessStepComponent, SaveButtonsItem } from '@poc/types/createAccessGrant';
import { SaveButtonsItem, DialogStepComponent } from '@poc/types/common';
import TextOutputArea from '@poc/components/dialogs/createAccessSteps/TextOutputArea.vue';
import SaveButtons from '@poc/components/dialogs/createAccessSteps/SaveButtons.vue';
import SaveButtons from '@poc/components/dialogs/commonPassphraseSteps/SaveButtons.vue';
const props = defineProps<{
name: string;
@ -56,7 +56,7 @@ const saveItems = computed<SaveButtonsItem[]>(() => [
{ name: 'API Key', value: props.apiKey },
]);
defineExpose<CreateAccessStepComponent>({
defineExpose<DialogStepComponent>({
title: 'CLI Access Created',
onEnter: () => isTooltipDisabled.value = false,
onExit: () => isTooltipDisabled.value = true,

View File

@ -147,10 +147,10 @@ import {
import { VDatePicker } from 'vuetify/labs/components';
import { Permission } from '@/types/createAccessGrant';
import { AccessGrantEndDate, CreateAccessStepComponent } from '@poc/types/createAccessGrant';
import { AccessGrantEndDate } from '@poc/types/createAccessGrant';
import { useBucketsStore } from '@/store/modules/bucketsStore';
import { SHORT_MONTHS_NAMES } from '@/utils/constants/date';
import { ValidationRule, RequiredRule } from '@poc/types/common';
import { ValidationRule, RequiredRule, DialogStepComponent } from '@poc/types/common';
type EndDateListItem = AccessGrantEndDate | { divider: true };
@ -267,7 +267,7 @@ function onDatePickerSubmit(): void {
isDatePicker.value = false;
}
defineExpose<CreateAccessStepComponent>({
defineExpose<DialogStepComponent>({
title: 'Access Permissions',
validate: () => {
form.value?.validate();

View File

@ -27,7 +27,8 @@ import { computed } from 'vue';
import { VRow, VCol, VDivider } from 'vuetify/components';
import { Permission, AccessType } from '@/types/createAccessGrant';
import { AccessGrantEndDate, CreateAccessStepComponent } from '@poc/types/createAccessGrant';
import { AccessGrantEndDate } from '@poc/types/createAccessGrant';
import { DialogStepComponent } from '@poc/types/common';
interface Item {
title: string;
@ -55,7 +56,7 @@ const items = computed<Item[]>(() => {
];
});
defineExpose<CreateAccessStepComponent>({
defineExpose<DialogStepComponent>({
title: 'Confirm Details',
});
</script>

View File

@ -51,9 +51,9 @@ import { computed, ref, watch, WritableComputedRef } from 'vue';
import { VForm, VRow, VCol, VTextField, VCheckbox, VInput } from 'vuetify/components';
import { AccessType } from '@/types/createAccessGrant';
import { ACCESS_TYPE_LINKS, CreateAccessStepComponent } from '@poc/types/createAccessGrant';
import { ACCESS_TYPE_LINKS } from '@poc/types/createAccessGrant';
import { useAccessGrantsStore } from '@/store/modules/accessGrantsStore';
import { RequiredRule, ValidationRule } from '@poc/types/common';
import { RequiredRule, ValidationRule, DialogStepComponent } from '@poc/types/common';
import InfoTooltip from '@poc/components/dialogs/createAccessSteps/InfoTooltip.vue';
@ -127,7 +127,7 @@ const nameRules: ValidationRule<string>[] = [
v => !agStore.state.allAGNames.includes(v) || 'This name is already in use',
];
defineExpose<CreateAccessStepComponent>({
defineExpose<DialogStepComponent>({
title: 'Create New Access',
validate: () => {
form.value?.validate();

View File

@ -28,12 +28,11 @@ import { ref } from 'vue';
import { VForm, VRow, VCol, VCheckbox } from 'vuetify/components';
import { LocalData } from '@/utils/localData';
import { RequiredRule } from '@poc/types/common';
import { CreateAccessStepComponent } from '@poc/types/createAccessGrant';
import { RequiredRule, DialogStepComponent } from '@poc/types/common';
const form = ref<VForm | null>(null);
defineExpose<CreateAccessStepComponent>({
defineExpose<DialogStepComponent>({
title: 'Encryption Information',
validate: () => {
form.value?.validate();

View File

@ -1,53 +0,0 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<v-form ref="form" class="pa-8" @submit.prevent>
<v-row>
<v-col cols="12">
This passphrase will be used to encrypt all the files you upload using this access grant.
You will need it to access these files in the future.
</v-col>
<v-col cols="12">
<v-text-field
v-model="passphrase"
label="Encryption Passphrase"
:append-inner-icon="isPassphraseVisible ? 'mdi-eye-off' : 'mdi-eye'"
:type="isPassphraseVisible ? 'text' : 'password'"
variant="outlined"
:hide-details="false"
:rules="[ RequiredRule ]"
@click:append-inner="isPassphraseVisible = !isPassphraseVisible"
/>
</v-col>
</v-row>
</v-form>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue';
import { VForm, VRow, VCol, VTextField } from 'vuetify/components';
import { CreateAccessStepComponent } from '@poc/types/createAccessGrant';
import { RequiredRule } from '@poc/types/common';
const form = ref<VForm | null>(null);
const passphrase = ref<string>('');
const isPassphraseVisible = ref<boolean>(false);
const emit = defineEmits<{
'passphraseChanged': [passphrase: string];
}>();
watch(passphrase, value => emit('passphraseChanged', value));
defineExpose<CreateAccessStepComponent>({
title: 'Enter New Passphrase',
validate: () => {
form.value?.validate();
return !!form.value?.isValid;
},
});
</script>

View File

@ -7,7 +7,7 @@
<v-col cols="12">
Copy or save the S3 credentials as they will only appear once.
</v-col>
<save-buttons :items="saveItems" :access-name="name" file-name-base="S3-credentials" />
<save-buttons :items="saveItems" :name="name" type="S3-credentials" />
<v-divider class="my-3" />
<v-col cols="12">
@ -27,10 +27,10 @@
import { ref, computed } from 'vue';
import { VRow, VCol, VDivider } from 'vuetify/components';
import { SaveButtonsItem } from '@poc/types/createAccessGrant';
import { SaveButtonsItem } from '@poc/types/common';
import TextOutputArea from '@poc/components/dialogs/createAccessSteps/TextOutputArea.vue';
import SaveButtons from '@poc/components/dialogs/createAccessSteps/SaveButtons.vue';
import SaveButtons from '@poc/components/dialogs/commonPassphraseSteps/SaveButtons.vue';
const props = defineProps<{
name: string;

View File

@ -0,0 +1,27 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<div class="pa-8">
By choosing to clear your passphrase for this session, your data will become locked
while you can use the rest of the dashboard.
</div>
</template>
<script setup lang="ts">
import { DialogStepComponent } from '@poc/types/common';
import { useNotify } from '@/utils/hooks';
import { useBucketsStore } from '@/store/modules/bucketsStore';
const bucketsStore = useBucketsStore();
const notify = useNotify();
defineExpose<DialogStepComponent>({
title: 'Clear My Passphrase',
onExit: to => {
if (to !== 'next') return;
bucketsStore.clearS3Data();
notify.success('Passphrase cleared.');
},
});
</script>

View File

@ -0,0 +1,17 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<div class="pa-8">
Creating a new passphrase allows you to upload data separately from
the data uploaded with the current encryption passphrase.
</div>
</template>
<script setup lang="ts">
import { DialogStepComponent } from '@poc/types/common';
defineExpose<DialogStepComponent>({
title: 'Create a New Passphrase',
});
</script>

View File

@ -0,0 +1,44 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<div class="pa-8 pb-4">
<v-row>
<v-col cols="12">
The encryption passphrase will be used to encrypt the files you upload in this project.
You can generate a new encryption passphrase, or enter your own.
</v-col>
<v-col cols="12">
<p class="text-subtitle-2 font-weight-bold mb-2">Encryption Passphrase</p>
<v-radio-group v-model="passphraseOption" hide-details="auto">
<v-radio label="Enter a new passphrase" :value="PassphraseOption.EnterPassphrase" />
<v-radio label="Generate a 12-word passphrase" :value="PassphraseOption.GeneratePassphrase" />
</v-radio-group>
</v-col>
</v-row>
</div>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue';
import { VRow, VCol, VRadioGroup, VRadio } from 'vuetify/components';
import { PassphraseOption } from '@poc/types/managePassphrase';
import { DialogStepComponent } from '@poc/types/common';
const emit = defineEmits<{
'selectOption': [option: PassphraseOption];
}>();
const passphraseOption = ref<PassphraseOption | null>(null);
watch(passphraseOption, value => value !== null && emit('selectOption', value));
defineExpose<DialogStepComponent>({
title: 'Encryption Passphrase',
onEnter: () => {
if (passphraseOption.value !== null) return;
passphraseOption.value = PassphraseOption.EnterPassphrase;
},
});
</script>

View File

@ -0,0 +1,66 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<div class="pa-4">
<v-sheet v-for="item in items" :key="item.next" class="bg-surface-emphasis ma-4" border rounded>
<v-list-item class="py-2 px-5" link @click="emit('optionClick', item.next)">
<div class="d-flex flex-row align-center">
<component :is="item.icon" />
<div class="mx-4">
<p class="font-weight-bold mb-1">{{ item.title }}</p>
<p class="text-caption">{{ item.subtitle }}</p>
</div>
<v-spacer />
<v-icon size="32" icon="mdi-chevron-right" color="primary" />
</div>
</v-list-item>
</v-sheet>
</div>
</template>
<script setup lang="ts">
import { Component } from 'vue';
import { VSheet, VListItem, VSpacer, VIcon } from 'vuetify/components';
import { ManageProjectPassphraseStep } from '@poc/types/managePassphrase';
import { DialogStepComponent } from '@poc/types/common';
import IconCirclePlus from '@poc/components/icons/IconCirclePlus.vue';
import IconSwitch from '@poc/components/icons/IconSwitch.vue';
import IconLock from '@poc/components/icons/IconLock.vue';
type Item = {
icon: Component;
title: string;
subtitle: string;
next: ManageProjectPassphraseStep;
};
const items: Item[] = [
{
icon: IconCirclePlus,
title: 'Create a new passphrase',
subtitle: 'Allows you to upload data with a different passphrase.',
next: ManageProjectPassphraseStep.Create,
}, {
icon: IconSwitch,
title: 'Switch active passphrase',
subtitle: 'View and upload data using another passphrase.',
next: ManageProjectPassphraseStep.Switch,
}, {
icon: IconLock,
title: 'Clear saved passphrase',
subtitle: 'Lock your data and clear passphrase from this session.',
next: ManageProjectPassphraseStep.Clear,
},
];
const emit = defineEmits<{
'optionClick': [option: ManageProjectPassphraseStep];
}>();
defineExpose<DialogStepComponent>({
title: 'Manage Passphrase',
});
</script>

View File

@ -0,0 +1,42 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<div class="pa-8">
Your encryption passphrase is ready to use.
Now you can upload files into your buckets securely using an encryption passphrase only you know.
</div>
</template>
<script setup lang="ts">
import { DialogStepComponent } from '@poc/types/common';
import { useAnalyticsStore } from '@/store/modules/analyticsStore';
import { useBucketsStore } from '@/store/modules/bucketsStore';
import { PassphraseOption } from '@poc/types/managePassphrase';
import { AnalyticsEvent } from '@/utils/constants/analyticsEventNames';
import { EdgeCredentials } from '@/types/accessGrants';
import Icon from '@/../static/images/accessGrants/newCreateFlow/accessCreated.svg';
const analyticsStore = useAnalyticsStore();
const bucketsStore = useBucketsStore();
const props = defineProps<{
passphrase: string;
option: PassphraseOption;
}>();
defineExpose<DialogStepComponent>({
title: 'Success',
iconSrc: Icon,
onEnter: () => {
analyticsStore.eventTriggered(AnalyticsEvent.PASSPHRASE_CREATED, {
method: props.option === PassphraseOption.EnterPassphrase ? 'enter' : 'generate',
});
bucketsStore.setEdgeCredentials(new EdgeCredentials());
bucketsStore.setPassphrase(props.passphrase);
bucketsStore.setPromptForPassphrase(false);
},
});
</script>

View File

@ -0,0 +1,8 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<svg width="32" height="32" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 1C14.9706 1 19 5.02944 19 10C19 14.9706 14.9706 19 10 19C5.02944 19 1 14.9706 1 10C1 5.02944 5.02944 1 10 1ZM10 2.65C5.94071 2.65 2.65 5.94071 2.65 10C2.65 14.0593 5.94071 17.35 10 17.35C14.0593 17.35 17.35 14.0593 17.35 10C17.35 5.94071 14.0593 2.65 10 2.65ZM10.7496 6.8989L10.7499 6.91218L10.7499 9.223H12.9926C13.4529 9.223 13.8302 9.58799 13.8456 10.048C13.8602 10.4887 13.5148 10.8579 13.0741 10.8726L13.0608 10.8729L10.7499 10.873L10.75 13.171C10.75 13.6266 10.3806 13.996 9.925 13.996C9.48048 13.996 9.11807 13.6444 9.10066 13.2042L9.1 13.171L9.09985 10.873H6.802C6.34637 10.873 5.977 10.5036 5.977 10.048C5.977 9.60348 6.32857 9.24107 6.76882 9.22366L6.802 9.223H9.09985L9.1 6.98036C9.1 6.5201 9.46499 6.14276 9.925 6.12745C10.3657 6.11279 10.7349 6.45818 10.7496 6.8989Z" fill="currentColor" />
</svg>
</template>

View File

@ -0,0 +1,8 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<svg width="32" height="32" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.84232 1C13.4949 1 16.4628 3.93138 16.5218 7.56989L16.5227 7.68036L16.5228 9.10213C16.849 9.3342 17.1125 9.63454 17.3056 9.99551L17.3472 10.0757C17.5632 10.5047 17.6798 10.9704 17.6846 12.1098V15.3616C17.6846 16.6268 17.5529 17.0855 17.3056 17.5481C17.0582 18.0106 16.6952 18.3736 16.2327 18.6209L16.1525 18.6626C15.7235 18.8785 15.2578 18.9952 14.1184 18.9999L5.63838 19C4.37323 19 3.91446 18.8683 3.45195 18.6209C2.98943 18.3736 2.62644 18.0106 2.37908 17.5481L2.35051 17.4936C2.12618 17.0572 2.00491 16.596 2 15.4337V12.1819C2 10.9168 2.13173 10.458 2.37908 9.99551C2.57213 9.63454 2.83561 9.3342 3.16187 9.10213L3.16196 7.68036C3.16196 3.9909 6.15286 1 9.84232 1ZM14.0463 10.1867L5.57301 10.1869L5.40618 10.1882C4.68354 10.1973 4.45951 10.2472 4.22685 10.3716C4.05069 10.4658 3.92225 10.5943 3.82804 10.7704L3.80192 10.8216C3.69649 11.039 3.65187 11.2848 3.64433 11.9555L3.64315 12.1819L3.64329 15.427L3.64463 15.5938C3.6537 16.3165 3.70361 16.5405 3.82804 16.7731C3.92225 16.9493 4.05069 17.0777 4.22685 17.172L4.27801 17.1981C4.4954 17.3035 4.74126 17.3481 5.41198 17.3557L5.63838 17.3568L14.1632 17.3566C14.974 17.3524 15.2141 17.3023 15.4578 17.172C15.634 17.0777 15.7624 16.9493 15.8566 16.7731L15.8827 16.722C15.9882 16.5046 16.0328 16.2587 16.0403 15.588L16.0415 15.3616L16.0412 12.065C16.037 11.2542 15.9869 11.0141 15.8566 10.7704C15.7624 10.5943 15.634 10.4658 15.4578 10.3716L15.4066 10.3455C15.1892 10.2401 14.9434 10.1954 14.2727 10.1879L14.0463 10.1867ZM9.9917 11.9793C10.7342 11.9793 11.3361 12.5812 11.3361 13.3237C11.3361 13.7567 11.1313 14.1419 10.8134 14.3878L10.8133 14.8921C10.8133 15.3459 10.4454 15.7137 9.9917 15.7137C9.53796 15.7137 9.17013 15.3459 9.17013 14.8921L9.17018 14.3879C8.85212 14.1421 8.6473 13.7568 8.6473 13.3237C8.6473 12.5812 9.24921 11.9793 9.9917 11.9793ZM9.84232 2.64315C7.08817 2.64315 4.85027 4.85351 4.80579 7.59706L4.80512 7.68036L4.80502 8.56969C5.02091 8.55366 5.27096 8.54493 5.56628 8.54371L14.0463 8.54357C14.3733 8.54357 14.6465 8.55237 14.8798 8.5697L14.8795 7.68036C14.8795 4.89839 12.6243 2.64315 9.84232 2.64315Z" fill="currentColor" />
</svg>
</template>

View File

@ -0,0 +1,8 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<svg width="32" height="32" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.78991 9.49399L4.76561 9.47064L1.24538 5.9504C0.926188 5.63121 0.918403 5.11854 1.22202 4.78991L1.24538 4.76562L4.76561 1.24538C5.09278 0.918208 5.62323 0.918208 5.9504 1.24538C6.26959 1.56457 6.27737 2.07724 5.97375 2.40586L5.9504 2.43016L3.86032 4.52038L18.1403 4.52024C18.603 4.52024 18.9781 4.89532 18.9781 5.35801C18.9781 5.80941 18.6211 6.17743 18.174 6.19511L18.1403 6.19577L3.86032 6.19592L5.9504 8.28585C6.26959 8.60504 6.27737 9.11771 5.97375 9.44634L5.9504 9.47064C5.63121 9.78983 5.11854 9.79761 4.78991 9.49399ZM14.0496 18.4855C13.7304 18.1663 13.7226 17.6537 14.0262 17.325L14.0496 17.3007L16.1399 15.2108L1.85969 15.2107C1.39701 15.2107 1.02193 14.8356 1.02193 14.3729C1.02193 13.9215 1.37893 13.5535 1.826 13.5358L1.85969 13.5351L16.1399 13.5352L14.0496 11.445C13.7304 11.1259 13.7226 10.6132 14.0262 10.2846L14.0496 10.2603C14.3688 9.94107 14.8815 9.93329 15.2101 10.2369L15.2344 10.2603L18.7546 13.7805C19.0738 14.0997 19.0816 14.6124 18.778 14.941L18.7546 14.9653L15.2344 18.4855C14.9072 18.8127 14.3768 18.8127 14.0496 18.4855Z" fill="currentColor" />
</svg>
</template>

View File

@ -110,7 +110,7 @@
<v-divider class="my-2" />
<!-- Manage Passphrase -->
<v-list-item link class="mt-1" rounded="lg">
<v-list-item link class="mt-1" rounded="lg" @click="isManagePassphraseDialogShown = true">
<template #prepend>
<IconPassphrase />
</template>
@ -261,6 +261,7 @@
</v-navigation-drawer>
<create-project-dialog v-model="isCreateProjectDialogShown" />
<manage-passphrase-dialog v-model="isManagePassphraseDialogShown" />
</template>
<script setup lang="ts">
@ -298,6 +299,7 @@ import IconForum from '@poc/components/icons/IconForum.vue';
import IconSupport from '@poc/components/icons/IconSupport.vue';
import IconResources from '@poc/components/icons/IconResources.vue';
import CreateProjectDialog from '@poc/components/dialogs/CreateProjectDialog.vue';
import ManagePassphraseDialog from '@poc/components/dialogs/ManagePassphraseDialog.vue';
const analyticsStore = useAnalyticsStore();
const projectsStore = useProjectsStore();
@ -312,6 +314,7 @@ const model = computed<boolean>({
});
const isCreateProjectDialogShown = ref<boolean>(false);
const isManagePassphraseDialogShown = ref<boolean>(false);
/**
* Returns the selected project from the store.

View File

@ -32,6 +32,7 @@ export default createVuetify({
error: '#FF458B',
error2: '#FF0149',
surface: '#FFF',
'surface-emphasis': '#FAFAFB',
purple: '#7B61FF',
blue6: '#091c45',
blue5: '#0218A7',
@ -53,6 +54,7 @@ export default createVuetify({
error: '#FF458B',
error2: '#FF0149',
surface: '#0d1116',
'surface-emphasis': '#1E2426',
purple: '#7B61FF',
blue6: '#091c45',
blue5: '#2196f3',

View File

@ -5,3 +5,16 @@ export type ValidationRule<T> = string | boolean | ((value: T) => string | boole
export function RequiredRule(value: unknown): string | boolean {
return (Array.isArray(value) ? !!value.length : !!value) || 'Required';
}
export interface DialogStepComponent {
title: string;
iconSrc?: string;
onEnter?: () => void;
onExit?: (to: 'next' | 'prev') => void;
validate?: () => boolean;
}
export type SaveButtonsItem = string | {
name: string;
value: string;
};

View File

@ -13,15 +13,3 @@ export interface AccessGrantEndDate {
title: string;
date: Date | null;
}
export interface CreateAccessStepComponent {
title: string;
onEnter?: () => void;
onExit?: () => void;
validate?: () => boolean;
}
export type SaveButtonsItem = string | {
name: string;
value: string;
};

View File

@ -0,0 +1,18 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
export enum ManageProjectPassphraseStep {
ManageOptions = 1,
Create,
EncryptionPassphrase,
PassphraseGenerated,
EnterPassphrase,
Success,
Switch,
Clear,
}
export enum PassphraseOption {
GeneratePassphrase,
EnterPassphrase,
}

View File

@ -18,7 +18,7 @@
<v-btn
color="primary"
min-width="120"
:disabled="isContentDisabled"
:disabled="!isInitialized"
v-bind="props"
>
<browser-snackbar-component :on-cancel="() => { snackbar = false }" />
@ -27,7 +27,7 @@
</v-btn>
</template>
<v-list class="pa-2">
<v-list-item rounded="lg" @click.stop="buttonFileUpload">
<v-list-item rounded="lg" :disabled="!isInitialized" @click.stop="buttonFileUpload">
<template #prepend>
<IconFile />
</template>
@ -38,7 +38,7 @@
<v-divider class="my-2" />
<v-list-item class="mt-1" rounded="lg" @click.stop="buttonFolderUpload">
<v-list-item class="mt-1" rounded="lg" :disabled="!isInitialized" @click.stop="buttonFolderUpload">
<template #prepend>
<icon-folder />
</template>
@ -71,7 +71,7 @@
variant="outlined"
color="default"
class="mx-4"
:disabled="isContentDisabled"
:disabled="!isInitialized"
>
<icon-folder />
New Folder
@ -80,14 +80,10 @@
</v-row>
</v-col>
<browser-table-component :loading="isLoading" :force-empty="isContentDisabled" />
<browser-table-component :loading="isFetching" :force-empty="!isInitialized" />
</v-container>
<enter-bucket-passphrase-dialog
v-if="!isLoading"
v-model="isBucketPassphraseDialogOpen"
@passphrase-entered="initObjectStore"
/>
<enter-bucket-passphrase-dialog v-model="isBucketPassphraseDialogOpen" @passphrase-entered="initObjectStore" />
</template>
<script setup lang="ts">
@ -136,11 +132,11 @@ const notify = useNotify();
const folderInput = ref<HTMLInputElement>();
const fileInput = ref<HTMLInputElement>();
const menu = ref<boolean>(false);
const isLoading = ref<boolean>(true);
const isFetching = ref<boolean>(true);
const isInitialized = ref<boolean>(false);
const isDragging = ref<boolean>(false);
const isContentDisabled = ref<boolean>(true);
const snackbar = ref<boolean>(false);
const isBucketPassphraseDialogOpen = ref<boolean>(bucketsStore.state.promptForPassphrase);
const isBucketPassphraseDialogOpen = ref<boolean>(false);
/**
* Returns the name of the selected bucket.
@ -193,7 +189,7 @@ function initObjectStore(): void {
bucket: bucketName.value,
browserRoot: '', // unused
});
isContentDisabled.value = false;
isInitialized.value = true;
}
/**
@ -230,12 +226,37 @@ watch(() => route.params.browserPath, browserPath => {
bucketsStore.setFileComponentPath(filePath);
}, { immediate: true });
watch(() => bucketsStore.state.passphrase, async newPass => {
if (isBucketPassphraseDialogOpen.value) return;
const bucketsURL = `/projects/${projectId.value}/buckets`;
if (!newPass) {
router.push(bucketsURL);
return;
}
isInitialized.value = false;
try {
await bucketsStore.setS3Client(projectId.value);
obStore.reinit({
endpoint: edgeCredentials.value.endpoint,
accessKey: edgeCredentials.value.accessKeyId,
secretKey: edgeCredentials.value.secretKey,
});
await router.push(`${bucketsURL}/${bucketsStore.state.fileComponentBucketName}`);
isInitialized.value = true;
} catch (error) {
error.message = `Error setting S3 client. ${error.message}`;
notify.notifyError(error, AnalyticsErrorEventSource.UPLOAD_FILE_VIEW);
router.push(bucketsURL);
}
});
/**
* Initializes file browser.
*/
onMounted(async () => {
const dashboardURL = `/projects/${projectId.value}/dashboard`;
try {
await bucketsStore.getAllBucketsNames(projectId.value);
} catch (error) {
@ -245,13 +266,17 @@ onMounted(async () => {
}
if (bucketsStore.state.allBucketNames.indexOf(bucketName.value) === -1) {
router.push(dashboardURL);
router.push(`/projects/${projectId.value}/dashboard`);
return;
}
if (!isPromptForPassphrase.value) initObjectStore();
if (isPromptForPassphrase.value) {
isBucketPassphraseDialogOpen.value = true;
} else {
initObjectStore();
}
isLoading.value = false;
isFetching.value = false;
});
</script>