satellite/{web, console}: removed old object flow
Removed old flow along with a feature flag Issue: https://github.com/storj/storj/issues/5166 Change-Id: I85cfabbf94e910c9d5efb01ef850f6b888a13f71
This commit is contained in:
parent
58a9c55f36
commit
149b59069f
@ -92,7 +92,6 @@ type Config struct {
|
||||
LinksharingURL string `help:"url link for linksharing requests" default:"https://link.storjshare.io" devDefault:"http://localhost:8001"`
|
||||
PathwayOverviewEnabled bool `help:"indicates if the overview onboarding step should render with pathways" default:"true"`
|
||||
NewProjectDashboard bool `help:"indicates if new project dashboard should be used" default:"true"`
|
||||
NewObjectsFlow bool `help:"indicates if new objects flow should be used" default:"true"`
|
||||
NewAccessGrantFlow bool `help:"indicates if new access grant flow should be used" default:"true"`
|
||||
NewBillingScreen bool `help:"indicates if new billing screens should be used" default:"false"`
|
||||
GeneratedAPIEnabled bool `help:"indicates if generated console api should be used" default:"false"`
|
||||
@ -454,7 +453,6 @@ func (server *Server) appHandler(w http.ResponseWriter, r *http.Request) {
|
||||
NewProjectDashboard bool
|
||||
DefaultPaidStorageLimit memory.Size
|
||||
DefaultPaidBandwidthLimit memory.Size
|
||||
NewObjectsFlow bool
|
||||
NewAccessGrantFlow bool
|
||||
NewBillingScreen bool
|
||||
InactivityTimerEnabled bool
|
||||
@ -499,7 +497,6 @@ func (server *Server) appHandler(w http.ResponseWriter, r *http.Request) {
|
||||
data.LoginHcaptchaEnabled = server.config.Captcha.Login.Hcaptcha.Enabled
|
||||
data.LoginHcaptchaSiteKey = server.config.Captcha.Login.Hcaptcha.SiteKey
|
||||
data.NewProjectDashboard = server.config.NewProjectDashboard
|
||||
data.NewObjectsFlow = server.config.NewObjectsFlow
|
||||
data.NewAccessGrantFlow = server.config.NewAccessGrantFlow
|
||||
data.NewBillingScreen = server.config.NewBillingScreen
|
||||
data.InactivityTimerEnabled = server.config.Session.InactivityTimerEnabled
|
||||
|
3
scripts/testdata/satellite-config.yaml.lock
vendored
3
scripts/testdata/satellite-config.yaml.lock
vendored
@ -208,9 +208,6 @@ compensation.withheld-percents: 75,75,75,50,50,50,25,25,25,0,0,0,0,0,0
|
||||
# indicates if new billing screens should be used
|
||||
# console.new-billing-screen: false
|
||||
|
||||
# indicates if new objects flow should be used
|
||||
# console.new-objects-flow: true
|
||||
|
||||
# indicates if new project dashboard should be used
|
||||
# console.new-project-dashboard: true
|
||||
|
||||
|
@ -34,7 +34,6 @@ func configureSatellite(log *zap.Logger, index int, config *satellite.Config) {
|
||||
if dir := os.Getenv("STORJ_TEST_SATELLITE_WEB"); dir != "" {
|
||||
config.Console.StaticDir = dir
|
||||
}
|
||||
config.Console.NewObjectsFlow = true
|
||||
config.Console.NewAccessGrantFlow = true
|
||||
config.Console.NewProjectDashboard = true
|
||||
config.Console.CouponCodeBillingUIEnabled = true
|
||||
|
@ -34,7 +34,6 @@
|
||||
<meta name="new-project-dashboard" content="{{ .NewProjectDashboard }}">
|
||||
<meta name="default-paid-storage-limit" content="{{ .DefaultPaidStorageLimit }}">
|
||||
<meta name="default-paid-bandwidth-limit" content="{{ .DefaultPaidBandwidthLimit }}">
|
||||
<meta name="new-objects-flow" content="{{ .NewObjectsFlow }}">
|
||||
<meta name="new-access-grant-flow" content="{{ .NewAccessGrantFlow }}">
|
||||
<meta name="new-billing-screen" content="{{ .NewBillingScreen }}">
|
||||
<meta name="inactivity-timer-enabled" content="{{ .InactivityTimerEnabled }}">
|
||||
|
@ -40,7 +40,6 @@ export default class App extends Vue {
|
||||
const couponCodeBillingUIEnabled = MetaUtils.getMetaContent('coupon-code-billing-ui-enabled');
|
||||
const couponCodeSignupUIEnabled = MetaUtils.getMetaContent('coupon-code-signup-ui-enabled');
|
||||
const isNewProjectDashboard = MetaUtils.getMetaContent('new-project-dashboard');
|
||||
const isNewObjectsFlow = MetaUtils.getMetaContent('new-objects-flow');
|
||||
|
||||
if (satelliteName) {
|
||||
this.$store.dispatch(APP_STATE_ACTIONS.SET_SATELLITE_NAME, satelliteName);
|
||||
@ -72,10 +71,6 @@ export default class App extends Vue {
|
||||
if (isNewProjectDashboard) {
|
||||
this.$store.dispatch(APP_STATE_ACTIONS.SET_PROJECT_DASHBOARD_STATUS, isNewProjectDashboard === 'true');
|
||||
}
|
||||
|
||||
if (isNewObjectsFlow) {
|
||||
this.$store.dispatch(APP_STATE_ACTIONS.SET_OBJECTS_FLOW_STATUS, isNewObjectsFlow === 'true');
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -357,13 +357,6 @@ export default class FileBrowser extends Vue {
|
||||
return this.$store.state.objectsModule.fileComponentBucketName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns objects flow status from store.
|
||||
*/
|
||||
private get isNewObjectsFlow(): string {
|
||||
return this.$store.state.appStateModule.isNewObjectsFlow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a boolean signifying whether the upload display is allowed to be shown.
|
||||
*/
|
||||
@ -391,8 +384,7 @@ export default class FileBrowser extends Vue {
|
||||
*/
|
||||
public async created(): Promise<void> {
|
||||
if (!this.bucket) {
|
||||
const path = this.isNewObjectsFlow ? RouteConfig.Buckets.with(RouteConfig.BucketsManagement).path :
|
||||
RouteConfig.Buckets.with(RouteConfig.EncryptData).path;
|
||||
const path = RouteConfig.Buckets.with(RouteConfig.BucketsManagement).path;
|
||||
|
||||
this.analytics.pageVisit(path);
|
||||
await this.$router.push(path);
|
||||
|
@ -1,408 +0,0 @@
|
||||
// Copyright (C) 2021 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<div class="encrypt-container">
|
||||
<EncryptIcon />
|
||||
<h1 class="encrypt-container__title" aria-roledescription="enc-title">Encryption passphrase</h1>
|
||||
<p class="encrypt-container__info">
|
||||
The encryption passphrase is used to encrypt and access the data that you upload to Storj.
|
||||
</p>
|
||||
<div class="encrypt-container__functional">
|
||||
<div class="encrypt-container__functional__header">
|
||||
<p class="encrypt-container__functional__header__gen" :class="{ active: isGenerate }" @click="setToGenerate">
|
||||
Generate a new passphrase
|
||||
</p>
|
||||
<div class="encrypt-container__functional__header__right" :class="{ active: !isGenerate }">
|
||||
<p
|
||||
class="encrypt-container__functional__header__right__enter"
|
||||
:class="{ active: !isGenerate }"
|
||||
aria-roledescription="enter-passphrase-label"
|
||||
@click="setToEnter"
|
||||
>
|
||||
Enter your own passphrase
|
||||
</p>
|
||||
<VInfo class="encrypt-container__functional__header__right__info-button">
|
||||
<template #icon>
|
||||
<InfoIcon class="encrypt-container__functional__header__right__info-button__image" :class="{ active: !isGenerate }" />
|
||||
</template>
|
||||
<template #message>
|
||||
<p class="encrypt-container__functional__header__right__info-button__message">
|
||||
We strongly encourage you to use a mnemonic phrase, which is automatically generated on
|
||||
the client-side for you. Alternatively, you can enter your own passphrase.
|
||||
</p>
|
||||
</template>
|
||||
</VInfo>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="isGenerate" class="encrypt-container__functional__generate">
|
||||
<p class="encrypt-container__functional__generate__value">{{ passphrase }}</p>
|
||||
<VButton
|
||||
class="encrypt-container__functional__generate__button"
|
||||
label="Copy"
|
||||
width="66px"
|
||||
height="30px"
|
||||
:is-blue-white="true"
|
||||
:is-uppercase="true"
|
||||
:on-press="onCopyClick"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="encrypt-container__functional__enter">
|
||||
<VInput
|
||||
label="Your Passphrase"
|
||||
placeholder="Enter a passphrase here..."
|
||||
:error="enterError"
|
||||
role-description="passphrase"
|
||||
is-password
|
||||
:disabled="isLoading"
|
||||
@setData="setPassphrase"
|
||||
/>
|
||||
</div>
|
||||
<h2 class="encrypt-container__functional__warning-title" aria-roledescription="warning-title">
|
||||
Save your encryption passphrase
|
||||
</h2>
|
||||
<p class="encrypt-container__functional__warning-msg">
|
||||
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.
|
||||
</p>
|
||||
<p class="encrypt-container__functional__download" @click="onDownloadClick">Download as a text file</p>
|
||||
<VCheckbox
|
||||
class="encrypt-container__functional__checkbox"
|
||||
label="I understand, and I have saved the passphrase."
|
||||
:is-checkbox-error="isCheckboxError"
|
||||
@setData="setSavingConfirmation"
|
||||
/>
|
||||
</div>
|
||||
<div class="encrypt-container__buttons">
|
||||
<VButton
|
||||
v-if="isNewObjectsFlow"
|
||||
class="encrypt-container__buttons__back"
|
||||
label="< Back"
|
||||
height="64px"
|
||||
border-radius="62px"
|
||||
:is-blue-white="true"
|
||||
:on-press="onBackClick"
|
||||
:is-disabled="isLoading"
|
||||
/>
|
||||
<VButton
|
||||
label="Next >"
|
||||
height="64px"
|
||||
border-radius="62px"
|
||||
:on-press="onNextButtonClick"
|
||||
:is-disabled="isLoading || !isSavingConfirmed"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||
import { generateMnemonic } from 'bip39';
|
||||
|
||||
import { LocalData, UserIDPassSalt } from '@/utils/localData';
|
||||
import { Download } from '@/utils/download';
|
||||
|
||||
import VButton from '@/components/common/VButton.vue';
|
||||
import VInfo from '@/components/common/VInfo.vue';
|
||||
import VInput from '@/components/common/VInput.vue';
|
||||
import VCheckbox from '@/components/common/VCheckbox.vue';
|
||||
|
||||
import EncryptIcon from '@/../static/images/objects/encrypt.svg';
|
||||
import InfoIcon from '@/../static/images/common/smallGreyInfo.svg';
|
||||
|
||||
// @vue/component
|
||||
@Component({
|
||||
components: {
|
||||
EncryptIcon,
|
||||
InfoIcon,
|
||||
VInfo,
|
||||
VButton,
|
||||
VInput,
|
||||
VCheckbox,
|
||||
},
|
||||
})
|
||||
export default class GeneratePassphrase extends Vue {
|
||||
@Prop({ default: () => () => null })
|
||||
public readonly onNextClick: () => unknown;
|
||||
@Prop({ default: () => () => null })
|
||||
public readonly onBackClick: () => unknown;
|
||||
@Prop({ default: () => () => null })
|
||||
public readonly setParentPassphrase: (passphrase: string) => void;
|
||||
@Prop({ default: false })
|
||||
public readonly isLoading: boolean;
|
||||
|
||||
public isGenerate = true;
|
||||
public enterError = '';
|
||||
public passphrase = '';
|
||||
public isSavingConfirmed = false;
|
||||
public isCheckboxError = false;
|
||||
|
||||
/**
|
||||
* Lifecycle hook after initial render.
|
||||
* Chooses correct state and generates mnemonic.
|
||||
*/
|
||||
public mounted(): void {
|
||||
const idPassSalt: UserIDPassSalt | null = LocalData.getUserIDPassSalt();
|
||||
if (idPassSalt && idPassSalt.userId === this.$store.getters.user.id) {
|
||||
this.isGenerate = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.passphrase = generateMnemonic();
|
||||
this.setParentPassphrase(this.passphrase);
|
||||
}
|
||||
|
||||
public setSavingConfirmation(value: boolean): void {
|
||||
this.isSavingConfirmed = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds on copy button click logic.
|
||||
* Copies passphrase to clipboard.
|
||||
*/
|
||||
public onCopyClick(): void {
|
||||
this.$copyText(this.passphrase);
|
||||
this.$notify.success('Passphrase was copied successfully');
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds on download button click logic.
|
||||
* Downloads encryption passphrase as a txt file.
|
||||
*/
|
||||
public onDownloadClick(): void {
|
||||
if (!this.passphrase) {
|
||||
this.enterError = 'Can\'t be empty!';
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const fileName = 'StorjEncryptionPassphrase.txt';
|
||||
|
||||
Download.file(this.passphrase, fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets passphrase from child component.
|
||||
*/
|
||||
public setPassphrase(passphrase: string): void {
|
||||
if (this.enterError) this.enterError = '';
|
||||
|
||||
this.passphrase = passphrase;
|
||||
this.setParentPassphrase(this.passphrase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets view state to enter passphrase.
|
||||
*/
|
||||
public setToEnter(): void {
|
||||
this.passphrase = '';
|
||||
this.setParentPassphrase(this.passphrase);
|
||||
this.isGenerate = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets view state to generate passphrase.
|
||||
*/
|
||||
public setToGenerate(): void {
|
||||
if (this.enterError) this.enterError = '';
|
||||
|
||||
this.passphrase = generateMnemonic();
|
||||
this.setParentPassphrase(this.passphrase);
|
||||
this.isGenerate = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds on next button click logic.
|
||||
*/
|
||||
public async onNextButtonClick(): Promise<void> {
|
||||
if (!this.passphrase) {
|
||||
this.enterError = 'Can\'t be empty!';
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.isSavingConfirmed) {
|
||||
this.isCheckboxError = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await this.onNextClick();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns objects flow status from store.
|
||||
*/
|
||||
public get isNewObjectsFlow(): string {
|
||||
return this.$store.state.appStateModule.isNewObjectsFlow;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.encrypt-container {
|
||||
font-family: 'font_regular', sans-serif;
|
||||
padding: 40px 60px 60px;
|
||||
max-width: 500px;
|
||||
background: #fcfcfc;
|
||||
box-shadow: 0 0 32px rgb(0 0 0 / 4%);
|
||||
border-radius: 20px;
|
||||
margin: 30px auto 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
&__title {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 36px;
|
||||
line-height: 56px;
|
||||
letter-spacing: 1px;
|
||||
color: #14142b;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
&__info {
|
||||
font-size: 16px;
|
||||
line-height: 28px;
|
||||
letter-spacing: 0.75px;
|
||||
color: #1b2533;
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
max-width: 420px;
|
||||
}
|
||||
|
||||
&__functional {
|
||||
border: 1px solid #e6e9ef;
|
||||
border-radius: 10px;
|
||||
padding: 20px 0;
|
||||
|
||||
&__header {
|
||||
width: calc(100% - 50px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 25px;
|
||||
border-bottom: 1px solid #e6e9ef;
|
||||
|
||||
&__gen {
|
||||
font-family: 'font_medium', sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 17px;
|
||||
color: #a9b5c1;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 4px solid #fff;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&__right {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 4px solid #fff;
|
||||
cursor: pointer;
|
||||
|
||||
&__enter {
|
||||
font-family: 'font_medium', sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 17px;
|
||||
color: #a9b5c1;
|
||||
margin-right: 10px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&__info-button {
|
||||
|
||||
&__image {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&__message {
|
||||
color: #586c86;
|
||||
font-size: 16px;
|
||||
line-height: 21px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__generate {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 16px 22px;
|
||||
background: #eff0f7;
|
||||
border-radius: 10px;
|
||||
margin: 25px 25px 0;
|
||||
|
||||
&__value {
|
||||
font-size: 14px;
|
||||
line-height: 25px;
|
||||
color: #384b65;
|
||||
}
|
||||
|
||||
&__button {
|
||||
margin-left: 32px;
|
||||
min-width: 66px;
|
||||
}
|
||||
}
|
||||
|
||||
&__enter {
|
||||
margin: 25px 25px 0;
|
||||
}
|
||||
|
||||
&__download {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 19px;
|
||||
color: #0068dc;
|
||||
cursor: pointer;
|
||||
margin: 20px 25px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
&__warning-title {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 19px;
|
||||
color: #1b2533;
|
||||
margin: 25px 25px 10px;
|
||||
}
|
||||
|
||||
&__warning-msg {
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: #1b2533;
|
||||
margin: 0 25px;
|
||||
}
|
||||
|
||||
&__checkbox {
|
||||
margin: 0 25px;
|
||||
}
|
||||
}
|
||||
|
||||
&__buttons {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 30px;
|
||||
|
||||
&__back {
|
||||
margin-right: 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.active {
|
||||
color: var(--c-blue-3);
|
||||
border-color: var(--c-blue-3);
|
||||
}
|
||||
|
||||
.active svg rect {
|
||||
fill: var(--c-blue-3);
|
||||
}
|
||||
|
||||
:deep(.info__box__message) {
|
||||
min-width: 440px;
|
||||
}
|
||||
</style>
|
@ -348,7 +348,7 @@ export default class MobileNavigation extends Vue {
|
||||
private get isBucketsView(): boolean {
|
||||
const currentRoute = this.$route.path;
|
||||
|
||||
return currentRoute.includes(RouteConfig.BucketsManagement.path) || currentRoute.includes(RouteConfig.EncryptData.path);
|
||||
return currentRoute.includes(RouteConfig.BucketsManagement.path);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -285,7 +285,7 @@ export default class ProjectSelection extends Vue {
|
||||
private get isBucketsView(): boolean {
|
||||
const currentRoute = this.$route.path;
|
||||
|
||||
return currentRoute.includes(RouteConfig.BucketsManagement.path) || currentRoute.includes(RouteConfig.EncryptData.path);
|
||||
return currentRoute.includes(RouteConfig.BucketsManagement.path);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -87,9 +87,7 @@ export default class BucketDetails extends Vue {
|
||||
this.$store.dispatch(OBJECTS_ACTIONS.SET_FILE_COMPONENT_BUCKET_NAME, this.bucket?.name);
|
||||
|
||||
if (this.$route.params.backRoute === RouteConfig.BucketsManagement.name) {
|
||||
this.isNewObjectsFlow
|
||||
? this.$store.commit(APP_STATE_MUTATIONS.TOGGLE_OPEN_BUCKET_MODAL_SHOWN)
|
||||
: this.$router.push(RouteConfig.Buckets.with(RouteConfig.EncryptData).path);
|
||||
this.$store.commit(APP_STATE_MUTATIONS.TOGGLE_OPEN_BUCKET_MODAL_SHOWN);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -97,13 +95,6 @@ export default class BucketDetails extends Vue {
|
||||
this.analytics.pageVisit(RouteConfig.Buckets.with(RouteConfig.UploadFile).path);
|
||||
this.$router.push(RouteConfig.Buckets.with(RouteConfig.UploadFile).path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns objects flow status from store.
|
||||
*/
|
||||
private get isNewObjectsFlow(): string {
|
||||
return this.$store.state.appStateModule.isNewObjectsFlow;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -60,17 +60,6 @@
|
||||
</template>
|
||||
</v-table>
|
||||
<EncryptionBanner v-if="!isServerSideEncryptionBannerHidden" :hide="hideBanner" />
|
||||
<ObjectsPopup
|
||||
v-if="isCreatePopupVisible"
|
||||
:on-click="onCreateBucketClick"
|
||||
title="Create Bucket"
|
||||
sub-title="Buckets are simply containers that store objects and their metadata within a project."
|
||||
button-label="Create Bucket"
|
||||
:error-message="errorMessage"
|
||||
:is-loading="isRequestProcessing"
|
||||
@setName="setCreateBucketName"
|
||||
@close="hideCreateBucketPopup"
|
||||
/>
|
||||
<ObjectsPopup
|
||||
v-if="isDeletePopupVisible"
|
||||
:on-click="onDeleteBucketClick"
|
||||
@ -127,11 +116,9 @@ export default class BucketsView extends Vue {
|
||||
private readonly FILE_BROWSER_AG_NAME: string = 'Web file browser API key';
|
||||
private worker: Worker;
|
||||
private grantWithPermissions = '';
|
||||
private createBucketName = '';
|
||||
private deleteBucketName = '';
|
||||
|
||||
public isLoading = true;
|
||||
public isCreatePopupVisible = false;
|
||||
public isDeletePopupVisible = false;
|
||||
public isRequestProcessing = false;
|
||||
public errorMessage = '';
|
||||
@ -182,13 +169,8 @@ export default class BucketsView extends Vue {
|
||||
}
|
||||
|
||||
if (!this.bucketsPage.buckets.length && !wasDemoBucketCreated) {
|
||||
if (this.isNewObjectsFlow) {
|
||||
this.analytics.pageVisit(RouteConfig.Buckets.with(RouteConfig.BucketCreation).path);
|
||||
await this.$router.push(RouteConfig.Buckets.with(RouteConfig.BucketCreation).path);
|
||||
return;
|
||||
}
|
||||
|
||||
await this.createDemoBucket();
|
||||
this.analytics.pageVisit(RouteConfig.Buckets.with(RouteConfig.BucketCreation).path);
|
||||
await this.$router.push(RouteConfig.Buckets.with(RouteConfig.BucketCreation).path);
|
||||
}
|
||||
} catch (error) {
|
||||
await this.$notify.error(`Failed to setup Buckets view. ${error.message}`);
|
||||
@ -270,40 +252,7 @@ export default class BucketsView extends Vue {
|
||||
|
||||
public onNewBucketButtonClick(): void {
|
||||
this.analytics.pageVisit(RouteConfig.Buckets.with(RouteConfig.BucketCreation).path);
|
||||
this.isNewObjectsFlow
|
||||
? this.$router.push(RouteConfig.Buckets.with(RouteConfig.BucketCreation).path)
|
||||
: this.showCreateBucketPopup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds create bucket click logic.
|
||||
*/
|
||||
public async onCreateBucketClick(): Promise<void> {
|
||||
if (this.isRequestProcessing) return;
|
||||
|
||||
if (!this.isBucketNameValid(this.createBucketName)) return;
|
||||
|
||||
this.isRequestProcessing = true;
|
||||
|
||||
try {
|
||||
if (!this.edgeCredentials.accessKeyId) {
|
||||
await this.setAccess();
|
||||
}
|
||||
await this.$store.dispatch(OBJECTS_ACTIONS.CREATE_BUCKET, this.createBucketName);
|
||||
await this.fetchBuckets();
|
||||
this.createBucketName = '';
|
||||
this.hideCreateBucketPopup();
|
||||
} catch (error) {
|
||||
const BUCKET_ALREADY_EXISTS_ERROR = 'BucketAlreadyExists';
|
||||
|
||||
if (error.name === BUCKET_ALREADY_EXISTS_ERROR) {
|
||||
await this.$notify.error('Bucket with provided name already exists.');
|
||||
} else {
|
||||
await this.$notify.error(error.message);
|
||||
}
|
||||
} finally {
|
||||
this.isRequestProcessing = false;
|
||||
}
|
||||
this.$router.push(RouteConfig.Buckets.with(RouteConfig.BucketCreation).path);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -404,30 +353,6 @@ export default class BucketsView extends Vue {
|
||||
this.deleteBucketName = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes create bucket popup visible.
|
||||
*/
|
||||
public showCreateBucketPopup(): void {
|
||||
this.createBucketName = '';
|
||||
this.isCreatePopupVisible = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides create bucket popup.
|
||||
*/
|
||||
public hideCreateBucketPopup(): void {
|
||||
this.errorMessage = '';
|
||||
this.isCreatePopupVisible = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set create bucket name form input.
|
||||
*/
|
||||
public setCreateBucketName(name: string): void {
|
||||
this.errorMessage = '';
|
||||
this.createBucketName = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides server-side encryption banner.
|
||||
*/
|
||||
@ -441,10 +366,7 @@ export default class BucketsView extends Vue {
|
||||
*/
|
||||
public openBucket(bucketName: string): void {
|
||||
this.$store.dispatch(OBJECTS_ACTIONS.SET_FILE_COMPONENT_BUCKET_NAME, bucketName);
|
||||
this.analytics.pageVisit(RouteConfig.Buckets.with(RouteConfig.EncryptData).path);
|
||||
this.isNewObjectsFlow
|
||||
? this.$store.commit(APP_STATE_MUTATIONS.TOGGLE_OPEN_BUCKET_MODAL_SHOWN)
|
||||
: this.$router.push(RouteConfig.Buckets.with(RouteConfig.EncryptData).path);
|
||||
this.$store.commit(APP_STATE_MUTATIONS.TOGGLE_OPEN_BUCKET_MODAL_SHOWN);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -454,13 +376,6 @@ export default class BucketsView extends Vue {
|
||||
return this.$store.state.bucketUsageModule.page;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns objects flow status from store.
|
||||
*/
|
||||
private get isNewObjectsFlow(): string {
|
||||
return this.$store.state.appStateModule.isNewObjectsFlow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns selected project id from store.
|
||||
*/
|
||||
|
@ -1,317 +0,0 @@
|
||||
// Copyright (C) 2021 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<div class="encrypt">
|
||||
<div class="encrypt__msg-container">
|
||||
<p class="encrypt__msg-container__title" aria-roledescription="objects-title">
|
||||
The object browser uses
|
||||
<a
|
||||
class="encrypt__msg-container__title__link"
|
||||
href="https://docs.storj.io/dcs/concepts/encryption-key/design-decision-server-side-encryption"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
server side encryption.
|
||||
</a>
|
||||
</p>
|
||||
<p class="encrypt__msg-container__text">
|
||||
If you want to use our product with only end-to-end encryption, you may want to use our
|
||||
<span class="encrypt__msg-container__text__link" @click="navigateToCLIFlow">command line solution.</span>
|
||||
</p>
|
||||
</div>
|
||||
<GeneratePassphrase
|
||||
:on-next-click="onNextClick"
|
||||
:on-back-click="onBackClick"
|
||||
:set-parent-passphrase="setPassphrase"
|
||||
:is-loading="isLoading"
|
||||
/>
|
||||
<div class="encrypt__faq">
|
||||
<h2 class="encrypt__faq__title">FAQ</h2>
|
||||
<FAQBullet
|
||||
title="Why do I need a passphrase to upload?"
|
||||
text="One very important design consideration is that data stored on Storj is encrypted. That means
|
||||
only you have the encryption passphrase for your data. The service doesn't ever have access to or store
|
||||
your encryption passphrase. If you lose your passphrase, you will be unable to recover your data."
|
||||
/>
|
||||
<FAQBullet
|
||||
title="What if I enter a wrong passphrase?"
|
||||
text="There is no wrong passphrase because every passphrase can have access to different files. Entering
|
||||
a new or different passphrase won’t have any effect on the existing files. If you enter your passphrase
|
||||
and don’t see the existing files, it’s most likely you entered a new passphrase and that’s why you can’t
|
||||
see the encrypted data stored with a different passphrase."
|
||||
/>
|
||||
<FAQBullet
|
||||
title="Why I have to enter a passphrase for every bucket?"
|
||||
text="In general, the best practice is to use one encryption passphrase per bucket. If an object with
|
||||
the same path and object name uploaded by two uplinks with encryption keys derived from the same
|
||||
encryption passphrase, the most recent upload will over-write the older object."
|
||||
/>
|
||||
<FAQBullet
|
||||
title="Why there is no “remember passphrase”?"
|
||||
text="There is no wrong passphrase because every passphrase can have access to different files. Entering
|
||||
a new or different passphrase won’t have any effect on the existing files. If you enter a passphrase and
|
||||
don’t see your existing files, try to enter your passphrase again."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
import pbkdf2 from 'pbkdf2';
|
||||
|
||||
import { RouteConfig } from '@/router';
|
||||
import { OBJECTS_ACTIONS } from '@/store/modules/objects';
|
||||
import { LocalData } from '@/utils/localData';
|
||||
import { EdgeCredentials } from '@/types/accessGrants';
|
||||
import { ACCESS_GRANTS_ACTIONS } from '@/store/modules/accessGrants';
|
||||
import { PROJECTS_ACTIONS } from '@/store/modules/projects';
|
||||
import { APP_STATE_MUTATIONS } from '@/store/mutationConstants';
|
||||
import { MetaUtils } from '@/utils/meta';
|
||||
import { AnalyticsHttpApi } from '@/api/analytics';
|
||||
|
||||
import GeneratePassphrase from '@/components/common/GeneratePassphrase.vue';
|
||||
import FAQBullet from '@/components/objects/FAQBullet.vue';
|
||||
|
||||
// @vue/component
|
||||
@Component({
|
||||
components: {
|
||||
GeneratePassphrase,
|
||||
FAQBullet,
|
||||
},
|
||||
})
|
||||
export default class EncryptData extends Vue {
|
||||
private worker: Worker;
|
||||
|
||||
public isLoading = false;
|
||||
public passphrase = '';
|
||||
|
||||
public readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
|
||||
|
||||
/**
|
||||
* Sets passphrase from child component.
|
||||
*/
|
||||
public navigateToCLIFlow(): void {
|
||||
this.$store.commit(APP_STATE_MUTATIONS.SET_ONB_AG_NAME_STEP_BACK_ROUTE, this.$route.path);
|
||||
this.analytics.pageVisit(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep.with(RouteConfig.AGName)).path);
|
||||
this.$router.push({ name: RouteConfig.AGName.name });
|
||||
}
|
||||
|
||||
/**
|
||||
* Lifecycle hook after initial render.
|
||||
* Sets local worker if new flow is used.
|
||||
*/
|
||||
public mounted(): void {
|
||||
if (!this.apiKey) {
|
||||
this.analytics.pageVisit(RouteConfig.Buckets.with(RouteConfig.BucketsManagement).path);
|
||||
this.$router.push(RouteConfig.Buckets.with(RouteConfig.BucketsManagement).path);
|
||||
}
|
||||
|
||||
this.setWorker();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets passphrase from child component.
|
||||
*/
|
||||
public setPassphrase(passphrase: string): void {
|
||||
this.passphrase = passphrase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets local worker with worker instantiated in store.
|
||||
*/
|
||||
public setWorker(): void {
|
||||
this.worker = this.$store.state.accessGrantsModule.accessGrantsWebWorker;
|
||||
this.worker.onerror = (error: ErrorEvent) => {
|
||||
this.$notify.error(error.message);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds on next button click logic.
|
||||
*/
|
||||
public async onNextClick(): Promise<void> {
|
||||
if (this.isLoading) return;
|
||||
|
||||
this.isLoading = true;
|
||||
|
||||
const SALT = 'storj-unique-salt';
|
||||
|
||||
const result: Buffer | Error = await this.pbkdf2Async(SALT);
|
||||
|
||||
if (result instanceof Error) {
|
||||
await this.$notify.error(result.message);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const keyToBeStored = result.toString('hex');
|
||||
|
||||
await LocalData.setUserIDPassSalt(this.$store.getters.user.id, keyToBeStored, SALT);
|
||||
await this.$store.dispatch(OBJECTS_ACTIONS.SET_PASSPHRASE, this.passphrase);
|
||||
|
||||
try {
|
||||
await this.setAccess();
|
||||
this.analytics.pageVisit(RouteConfig.UploadFile.path);
|
||||
await this.$router.push(RouteConfig.UploadFile.path);
|
||||
} catch (e) {
|
||||
await this.$notify.error(e.message);
|
||||
} finally {
|
||||
this.isLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds on back button click logic.
|
||||
*/
|
||||
public onBackClick(): void {
|
||||
this.analytics.pageVisit(RouteConfig.BucketsManagement.path);
|
||||
this.$router.push(RouteConfig.BucketsManagement.path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets access to S3 client.
|
||||
*/
|
||||
public async setAccess(): Promise<void> {
|
||||
const now = new Date();
|
||||
const inThreeDays = new Date(now.setDate(now.getDate() + 3));
|
||||
|
||||
await this.worker.postMessage({
|
||||
'type': 'SetPermission',
|
||||
'isDownload': true,
|
||||
'isUpload': true,
|
||||
'isList': true,
|
||||
'isDelete': true,
|
||||
'notAfter': inThreeDays.toISOString(),
|
||||
'buckets': this.bucket ? [this.bucket] : [],
|
||||
'apiKey': this.apiKey,
|
||||
});
|
||||
|
||||
const grantEvent: MessageEvent = await new Promise(resolve => this.worker.onmessage = resolve);
|
||||
if (grantEvent.data.error) {
|
||||
throw new Error(grantEvent.data.error);
|
||||
}
|
||||
|
||||
const satelliteNodeURL: string = MetaUtils.getMetaContent('satellite-nodeurl');
|
||||
const salt = await this.$store.dispatch(PROJECTS_ACTIONS.GET_SALT, this.$store.getters.selectedProject.id);
|
||||
|
||||
this.worker.postMessage({
|
||||
'type': 'GenerateAccess',
|
||||
'apiKey': grantEvent.data.value,
|
||||
'passphrase': this.passphrase,
|
||||
'salt': salt,
|
||||
'satelliteNodeURL': satelliteNodeURL,
|
||||
});
|
||||
|
||||
const accessGrantEvent: MessageEvent = await new Promise(resolve => this.worker.onmessage = resolve);
|
||||
if (accessGrantEvent.data.error) {
|
||||
throw new Error(accessGrantEvent.data.error);
|
||||
}
|
||||
|
||||
const accessGrant = accessGrantEvent.data.value;
|
||||
|
||||
const gatewayCredentials: EdgeCredentials = await this.$store.dispatch(ACCESS_GRANTS_ACTIONS.GET_GATEWAY_CREDENTIALS, { accessGrant });
|
||||
await this.$store.dispatch(OBJECTS_ACTIONS.SET_GATEWAY_CREDENTIALS, gatewayCredentials);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates passphrase fingerprint asynchronously.
|
||||
*/
|
||||
private pbkdf2Async(salt: string): Promise<Buffer | Error> {
|
||||
const ITERATIONS = 1;
|
||||
const KEY_LENGTH = 64;
|
||||
|
||||
return new Promise((response, reject) => {
|
||||
pbkdf2.pbkdf2(this.passphrase, salt, ITERATIONS, KEY_LENGTH, (error, key) => {
|
||||
error ? reject(error) : response(key);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns apiKey from store.
|
||||
*/
|
||||
private get apiKey(): string {
|
||||
return this.$store.state.objectsModule.apiKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns bucket name from store.
|
||||
*/
|
||||
private get bucket(): string {
|
||||
return this.$store.state.objectsModule.fileComponentBucketName;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.encrypt {
|
||||
font-family: 'font_regular', sans-serif;
|
||||
padding-bottom: 30px;
|
||||
|
||||
&__msg-container {
|
||||
margin: -20px auto 40px;
|
||||
max-width: 620px;
|
||||
background-color: var(--c-yellow-2);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
border-radius: 0 0 10px 10px;
|
||||
padding: 20px 0;
|
||||
|
||||
&__title {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 19px;
|
||||
text-align: center;
|
||||
color: #000;
|
||||
margin-bottom: 10px;
|
||||
|
||||
&__link {
|
||||
color: #000;
|
||||
text-decoration: underline !important;
|
||||
text-underline-position: under;
|
||||
|
||||
&:visited {
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__text {
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
color: #1b2533;
|
||||
max-width: 400px;
|
||||
|
||||
&__link {
|
||||
color: #1b2533;
|
||||
text-decoration: underline;
|
||||
text-underline-position: under;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__faq {
|
||||
max-width: 620px;
|
||||
|
||||
// display: flex; revert this when FAQ content will be confirmed
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin: 0 auto;
|
||||
|
||||
&__title {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 36px;
|
||||
line-height: 56px;
|
||||
letter-spacing: 1px;
|
||||
color: #14142b;
|
||||
margin: 75px 0 30px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -29,7 +29,6 @@ import CreditsHistory from '@/components/account/billing/coupons/CouponArea.vue'
|
||||
import SettingsArea from '@/components/account/SettingsArea.vue';
|
||||
import Page404 from '@/components/errors/Page404.vue';
|
||||
import BucketsView from '@/components/objects/BucketsView.vue';
|
||||
import EncryptData from '@/components/objects/EncryptData.vue';
|
||||
import ObjectsArea from '@/components/objects/ObjectsArea.vue';
|
||||
import UploadFile from '@/components/objects/UploadFile.vue';
|
||||
import OnboardingTourArea from '@/components/onboardingTour/OnboardingTourArea.vue';
|
||||
@ -129,7 +128,6 @@ export abstract class RouteConfig {
|
||||
public static SuccessScreen = new NavigationLink('success', 'Onboarding Success Screen');
|
||||
|
||||
// objects child paths.
|
||||
public static EncryptData = new NavigationLink('encrypt-data', 'Objects Encrypt Data');
|
||||
public static BucketsManagement = new NavigationLink('management', 'Buckets Management');
|
||||
public static BucketsDetails = new NavigationLink('details', 'Bucket Details');
|
||||
public static UploadFile = new NavigationLink('upload/', 'Objects Upload');
|
||||
@ -439,11 +437,6 @@ export const router = new Router({
|
||||
name: RouteConfig.Buckets.name,
|
||||
component: ObjectsArea,
|
||||
children: [
|
||||
{
|
||||
path: RouteConfig.EncryptData.path,
|
||||
name: RouteConfig.EncryptData.name,
|
||||
component: EncryptData,
|
||||
},
|
||||
{
|
||||
path: RouteConfig.BucketsManagement.path,
|
||||
name: RouteConfig.BucketsManagement.name,
|
||||
|
@ -61,7 +61,6 @@ class State {
|
||||
public couponCodeBillingUIEnabled = false,
|
||||
public couponCodeSignupUIEnabled = false,
|
||||
public isNewProjectDashboard = false,
|
||||
public isNewObjectsFlow = false,
|
||||
){}
|
||||
}
|
||||
|
||||
@ -215,9 +214,6 @@ export const appStateModule = {
|
||||
[APP_STATE_MUTATIONS.SET_ONB_CLEAN_API_KEY](state: State, apiKey: string): void {
|
||||
state.appState.onbCleanApiKey = apiKey;
|
||||
},
|
||||
[APP_STATE_MUTATIONS.SET_OBJECTS_FLOW_STATUS](state: State, isNewObjectsFlow: boolean): void {
|
||||
state.isNewObjectsFlow = isNewObjectsFlow;
|
||||
},
|
||||
[APP_STATE_MUTATIONS.SET_COUPON_CODE_BILLING_UI_STATUS](state: State, couponCodeBillingUIEnabled: boolean): void {
|
||||
state.couponCodeBillingUIEnabled = couponCodeBillingUIEnabled;
|
||||
},
|
||||
@ -369,9 +365,6 @@ export const appStateModule = {
|
||||
[APP_STATE_ACTIONS.SET_PROJECT_DASHBOARD_STATUS]: function ({ commit }: AppContext, isNewProjectDashboard: boolean): void {
|
||||
commit(APP_STATE_MUTATIONS.SET_PROJECT_DASHBOARD_STATUS, isNewProjectDashboard);
|
||||
},
|
||||
[APP_STATE_ACTIONS.SET_OBJECTS_FLOW_STATUS]: function ({ commit }: AppContext, isNewObjectsFlow: boolean): void {
|
||||
commit(APP_STATE_MUTATIONS.SET_OBJECTS_FLOW_STATUS, isNewObjectsFlow);
|
||||
},
|
||||
[APP_STATE_ACTIONS.SET_COUPON_CODE_BILLING_UI_STATUS]: function ({ commit }: AppContext, couponCodeBillingUIEnabled: boolean): void {
|
||||
commit(APP_STATE_MUTATIONS.SET_COUPON_CODE_BILLING_UI_STATUS, couponCodeBillingUIEnabled);
|
||||
},
|
||||
|
@ -56,6 +56,5 @@ export const APP_STATE_MUTATIONS = {
|
||||
SET_ONB_API_KEY_STEP_BACK_ROUTE: 'SET_ONB_API_KEY_STEP_BACK_ROUTE',
|
||||
SET_ONB_API_KEY: 'SET_ONB_API_KEY',
|
||||
SET_ONB_CLEAN_API_KEY: 'SET_ONB_CLEAN_API_KEY',
|
||||
SET_OBJECTS_FLOW_STATUS: 'SET_OBJECTS_FLOW_STATUS',
|
||||
SET_ONB_OS: 'SET_ONB_OS',
|
||||
};
|
||||
|
@ -31,7 +31,6 @@ export const APP_STATE_ACTIONS = {
|
||||
SET_COUPON_CODE_BILLING_UI_STATUS: 'SET_COUPON_CODE_BILLING_UI_STATUS',
|
||||
SET_COUPON_CODE_SIGNUP_UI_STATUS: 'SET_COUPON_CODE_SIGNUP_UI_STATUS',
|
||||
SET_PROJECT_DASHBOARD_STATUS: 'SET_PROJECT_DASHBOARD_STATUS',
|
||||
SET_OBJECTS_FLOW_STATUS: 'SET_OBJECTS_FLOW_STATUS',
|
||||
};
|
||||
|
||||
export const NOTIFICATION_ACTIONS = {
|
||||
|
@ -1,30 +0,0 @@
|
||||
// Copyright (C) 2019 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
import { createLocalVue, shallowMount } from '@vue/test-utils';
|
||||
import Vuex from 'vuex';
|
||||
|
||||
import { router } from '@/router';
|
||||
import { appStateModule } from '@/store/modules/appState';
|
||||
|
||||
import GeneratePassphrase from '@/components/common/GeneratePassphrase.vue';
|
||||
|
||||
const localVue = createLocalVue();
|
||||
localVue.use(Vuex);
|
||||
|
||||
const store = new Vuex.Store({ modules: {
|
||||
appStateModule,
|
||||
} });
|
||||
|
||||
describe('GeneratePassphrase.vue', () => {
|
||||
it('renders correctly with default props', () => {
|
||||
const wrapper = shallowMount<GeneratePassphrase>(GeneratePassphrase, {
|
||||
localVue,
|
||||
router,
|
||||
store,
|
||||
});
|
||||
|
||||
expect(wrapper.vm.passphrase).toBeTruthy();
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
@ -1,41 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`GeneratePassphrase.vue renders correctly with default props 1`] = `
|
||||
<div class="encrypt-container">
|
||||
<encrypticon-stub></encrypticon-stub>
|
||||
<h1 aria-roledescription="enc-title" class="encrypt-container__title">Encryption passphrase</h1>
|
||||
<p class="encrypt-container__info">
|
||||
The encryption passphrase is used to encrypt and access the data that you upload to Storj.
|
||||
</p>
|
||||
<div class="encrypt-container__functional">
|
||||
<div class="encrypt-container__functional__header">
|
||||
<p class="encrypt-container__functional__header__gen active">
|
||||
Generate a new passphrase
|
||||
</p>
|
||||
<div class="encrypt-container__functional__header__right">
|
||||
<p aria-roledescription="enter-passphrase-label" class="encrypt-container__functional__header__right__enter">
|
||||
Enter your own passphrase
|
||||
</p>
|
||||
<vinfo-stub title="" buttonlabel="" onbuttonclick="[Function]" class="encrypt-container__functional__header__right__info-button"></vinfo-stub>
|
||||
</div>
|
||||
</div>
|
||||
<div class="encrypt-container__functional__generate">
|
||||
<p class="encrypt-container__functional__generate__value"></p>
|
||||
<vbutton-stub label="Copy" width="66px" height="30px" fontsize="16px" borderradius="6px" icon="none" isbluewhite="true" isuppercase="true" onpress="[Function]" class="encrypt-container__functional__generate__button"></vbutton-stub>
|
||||
</div>
|
||||
<h2 aria-roledescription="warning-title" class="encrypt-container__functional__warning-title">
|
||||
Save your encryption passphrase
|
||||
</h2>
|
||||
<p class="encrypt-container__functional__warning-msg">
|
||||
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.
|
||||
</p>
|
||||
<p class="encrypt-container__functional__download">Download as a text file</p>
|
||||
<vcheckbox-stub label="I understand, and I have saved the passphrase." class="encrypt-container__functional__checkbox"></vcheckbox-stub>
|
||||
</div>
|
||||
<div class="encrypt-container__buttons">
|
||||
<!---->
|
||||
<vbutton-stub label="Next >" width="inherit" height="64px" fontsize="16px" borderradius="62px" icon="none" isdisabled="true" onpress="[Function]"></vbutton-stub>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
Loading…
Reference in New Issue
Block a user