web/satellite: import file browser component
WHAT: import and instantiate file browser component WHY: to operate over folders and objects Change-Id: Ib6fb4fdc2668d2f274df3d1b23f8cc0bb6a361ea
This commit is contained in:
parent
444b1f4757
commit
6ae2351389
@ -88,6 +88,7 @@ type Config struct {
|
||||
BetaSatelliteSupportURL string `help:"url link for for beta satellite support" default:""`
|
||||
DocumentationURL string `help:"url link to documentation" devDefault:"https://documentation.storj.io/" releaseDefault:"https://documentation.tardigrade.io/"`
|
||||
CouponCodeUIEnabled bool `help:"indicates if user is allowed to add coupon codes to account" default:"false"`
|
||||
FileBrowserFlowDisabled bool `help:"indicates if file browser flow is disabled" default:"true"`
|
||||
|
||||
RateLimit web.IPRateLimiterConfig
|
||||
|
||||
@ -311,6 +312,7 @@ func (server *Server) appHandler(w http.ResponseWriter, r *http.Request) {
|
||||
BetaSatelliteSupportURL string
|
||||
DocumentationURL string
|
||||
CouponCodeUIEnabled bool
|
||||
FileBrowserFlowDisabled bool
|
||||
}
|
||||
|
||||
data.ExternalAddress = server.config.ExternalAddress
|
||||
@ -330,6 +332,7 @@ func (server *Server) appHandler(w http.ResponseWriter, r *http.Request) {
|
||||
data.BetaSatelliteSupportURL = server.config.BetaSatelliteSupportURL
|
||||
data.DocumentationURL = server.config.DocumentationURL
|
||||
data.CouponCodeUIEnabled = server.config.CouponCodeUIEnabled
|
||||
data.FileBrowserFlowDisabled = server.config.FileBrowserFlowDisabled
|
||||
|
||||
if server.templates.index == nil {
|
||||
server.log.Error("index template is not set")
|
||||
|
3
scripts/testdata/satellite-config.yaml.lock
vendored
3
scripts/testdata/satellite-config.yaml.lock
vendored
@ -103,6 +103,9 @@ compensation.withheld-percents: 75,75,75,50,50,50,25,25,25,0,0,0,0,0,0
|
||||
# external endpoint of the satellite if hosted
|
||||
# console.external-address: ""
|
||||
|
||||
# indicates if file browser flow is disabled
|
||||
# console.file-browser-flow-disabled: true
|
||||
|
||||
# allow domains to embed the satellite in a frame, space separated
|
||||
# console.frame-ancestors: tardigrade.io
|
||||
|
||||
|
@ -20,6 +20,7 @@
|
||||
<meta name="beta-satellite-support-url" content="{{ .BetaSatelliteSupportURL }}">
|
||||
<meta name="documentation-url" content="{{ .DocumentationURL }}">
|
||||
<meta name="coupon-code-ui-enabled" content="{{ .CouponCodeUIEnabled }}">
|
||||
<meta name="file-browser-flow-disabled" content="{{ .FileBrowserFlowDisabled }}">
|
||||
<title>{{ .SatelliteName }}</title>
|
||||
<link rel="shortcut icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAL8SURBVHgBxVcxTFNRFL01JvylrRsmNWETWWSybg5I3NTQBCdRFyfrDuoKMgMyOQgdTcDAJmrUrWWCgQIbhJKy0TJQpsc73P/S9/tfP+8/GnqSn1fo77/3nnPufe8nhAR1ETeoy7hJjpj93/ycu0+UuUVuEA5Y2xaifirEVpX/nvknnBFbgpMGUfmIKOkRLRT578oxXy6IJcFCialH0EyaaPoZBy7tEQ3NEY1IKd4/iidHwqYLijLA559cuY6dT0RjBU5AAYm9fiivLFnBKMGBTyeqQ4BXhXDwdqjUiKZkskOzREsbzeeBNRMCEiDgr12uYl1WNbnW/oc2iUys8jrQyyxhHRkM3hdgAMFBHQyGG/GDqyDlsSeS/npQC99jlEBpOnyX2XCF8sGhZLbeMLMZkCDbJ1nYYTfDeMP9fMH5y5vmIKYE8RxUjBXPedDH1Zu6I9QFSzLQxErz4Xn5oNwg+2NSmuv3Lkvz4QlTi8rupDlBmA6tqQLrnYNCvoxSNAOtUEaakwzMv+ALidTP2OlKKiSK75Cs6hy9NYFkjzmG1SBCIuUq0Za8pgydge8R9E+e10qNrGE1ikH5435mo11bQgr4B9LEgVUC0Npm1o+vcuvBxB1NYFsaaeC2XUuW/Xs7msC9Xqa+MMa9jQr1KtXAQoKYHakeskbIhDrVasdTbbVY4s8ZYld/9PWuyeTSHksFBjBFcZ+aH/j/yZk5gcAcgImgIX6MNsKKhKBta1sB2A3HV5pD6iJQIzw/MICwoohc1F6ALBH03XemFYPl+VdzcBNUh6j5gZZEcP341opAAnX/AXl/A0FlrrshgMRR+YUvPPN8CHgAxlqWVYuEdH7V/ZilA6cosFDa53EcmUDKC+7X+IwxHEVhO0DK6aeXH88uHcWQA8xE7Yg69M6xgdWZUEFtNNDyx1s2KnyDIxu22zdZTjgWhANm/vL6clGIsnw3+Fbk94RreS8AMGrBxvwoT0lMPnSNC2JJoAPdgnMBJLjKq5lzAp1C19+OzwFiYzAU5f7eeQAAAABJRU5ErkJggg==" type="image/x-icon">
|
||||
<link rel="dns-prefetch" href="https://js.stripe.com">
|
||||
|
4100
web/satellite/package-lock.json
generated
4100
web/satellite/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -10,7 +10,8 @@
|
||||
"dev": "vue-cli-service build --mode development"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "3.8.1",
|
||||
"aws-sdk": "2.853.0",
|
||||
"browser": "git+https://github.com/storj/browser#7e16121e9070e671e3c68557048ced234c61943b",
|
||||
"apollo-cache-inmemory": "1.6.6",
|
||||
"apollo-client": "2.6.10",
|
||||
"apollo-link": "1.2.14",
|
||||
|
@ -157,16 +157,17 @@ export class AccessGrantsApiGql extends BaseGql implements AccessGrantsApi {
|
||||
*
|
||||
* @param accessGrant - generated access grant
|
||||
* @param optionalURL - optional requestURL
|
||||
* @param isPublic - optional status
|
||||
* @throws Error
|
||||
*/
|
||||
public async getGatewayCredentials(accessGrant: string, optionalURL?: string): Promise<GatewayCredentials> {
|
||||
public async getGatewayCredentials(accessGrant: string, optionalURL?: string, isPublic?: boolean): Promise<GatewayCredentials> {
|
||||
const requestURL: string = optionalURL || MetaUtils.getMetaContent('gateway-credentials-request-url');
|
||||
if (!requestURL) throw new Error('Cannot get gateway credentials: request URL is not provided');
|
||||
|
||||
const path = `${requestURL}/v1/access`;
|
||||
const body = {
|
||||
access_grant: accessGrant,
|
||||
public: false,
|
||||
public: isPublic || false,
|
||||
};
|
||||
const response = await this.client.post(path, JSON.stringify(body));
|
||||
if (!response.ok) {
|
||||
|
@ -173,6 +173,7 @@ export default class ConfirmDeletePopup extends Vue {
|
||||
&__item {
|
||||
padding: 25px;
|
||||
width: calc(100% - 50px);
|
||||
max-width: calc(100% - 50px);
|
||||
background: rgba(245, 246, 250, 0.6);
|
||||
|
||||
&__name {
|
||||
@ -182,6 +183,7 @@ export default class ConfirmDeletePopup extends Vue {
|
||||
font-size: 14px;
|
||||
line-height: 19px;
|
||||
color: #1b2533;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import { AuthHttpApi } from '@/api/auth';
|
||||
import { RouteConfig } from '@/router';
|
||||
import { ACCESS_GRANTS_ACTIONS } from '@/store/modules/accessGrants';
|
||||
import { BUCKET_ACTIONS } from '@/store/modules/buckets';
|
||||
import { OBJECTS_ACTIONS } from '@/store/modules/objects';
|
||||
import { PROJECTS_ACTIONS } from '@/store/modules/projects';
|
||||
import { USER_ACTIONS } from '@/store/modules/users';
|
||||
import {
|
||||
@ -63,6 +64,7 @@ export default class AccountDropdown extends Vue {
|
||||
await this.$store.dispatch(ACCESS_GRANTS_ACTIONS.STOP_ACCESS_GRANTS_WEB_WORKER);
|
||||
await this.$store.dispatch(NOTIFICATION_ACTIONS.CLEAR);
|
||||
await this.$store.dispatch(BUCKET_ACTIONS.CLEAR);
|
||||
await this.$store.dispatch(OBJECTS_ACTIONS.CLEAR);
|
||||
await this.$store.dispatch(APP_STATE_ACTIONS.CLOSE_POPUPS);
|
||||
|
||||
LocalData.removeUserId();
|
||||
|
@ -31,6 +31,7 @@ import TeamIcon from '@/../static/images/navigation/team.svg';
|
||||
|
||||
import { RouteConfig } from '@/router';
|
||||
import { NavigationLink } from '@/types/navigation';
|
||||
import { MetaUtils } from '@/utils/meta';
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
@ -42,16 +43,30 @@ import { NavigationLink } from '@/types/navigation';
|
||||
},
|
||||
})
|
||||
export default class NavigationArea extends Vue {
|
||||
public navigation: NavigationLink[] = [];
|
||||
|
||||
/**
|
||||
* Array of navigation links with icons.
|
||||
* Lifecycle hook before initial render.
|
||||
* Sets navigation side bar list.
|
||||
*/
|
||||
public readonly navigation: NavigationLink[] = [
|
||||
RouteConfig.ProjectDashboard.withIcon(DashboardIcon),
|
||||
// TODO: enable when the flow will be finished
|
||||
// RouteConfig.Objects.withIcon(ObjectsIcon),
|
||||
RouteConfig.AccessGrants.withIcon(AccessGrantsIcon),
|
||||
RouteConfig.Users.withIcon(TeamIcon),
|
||||
];
|
||||
public async beforeMount(): Promise<void> {
|
||||
if (await JSON.parse(MetaUtils.getMetaContent('file-browser-flow-disabled'))) {
|
||||
this.navigation = [
|
||||
RouteConfig.ProjectDashboard.withIcon(DashboardIcon),
|
||||
RouteConfig.AccessGrants.withIcon(AccessGrantsIcon),
|
||||
RouteConfig.Users.withIcon(TeamIcon),
|
||||
];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.navigation = [
|
||||
RouteConfig.ProjectDashboard.withIcon(DashboardIcon),
|
||||
RouteConfig.Objects.withIcon(ObjectsIcon),
|
||||
RouteConfig.AccessGrants.withIcon(AccessGrantsIcon),
|
||||
RouteConfig.Users.withIcon(TeamIcon),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if navigation side bar is hidden.
|
||||
|
@ -21,6 +21,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Bucket } from 'aws-sdk/clients/s3';
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||
|
||||
import ObjectsPopup from '@/components/objects/ObjectsPopup.vue';
|
||||
@ -29,8 +30,6 @@ import BucketIcon from '@/../static/images/objects/bucketItem.svg';
|
||||
import DeleteIcon from '@/../static/images/objects/delete.svg';
|
||||
import DotsIcon from '@/../static/images/objects/dots.svg';
|
||||
|
||||
import { Bucket } from '@aws-sdk/client-s3';
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
BucketIcon,
|
||||
|
@ -11,14 +11,14 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="buckets-view__loader" v-if="isLoading"/>
|
||||
<p class="buckets-view__no-buckets" v-if="!(isLoading || buckets.length)">No Buckets</p>
|
||||
<div class="buckets-view__list" v-if="!isLoading && buckets.length">
|
||||
<p class="buckets-view__no-buckets" v-if="!(isLoading || bucketsList.length)">No Buckets</p>
|
||||
<div class="buckets-view__list" v-if="!isLoading && bucketsList.length">
|
||||
<div class="buckets-view__list__sorting-header">
|
||||
<p class="buckets-view__list__sorting-header__name">Name</p>
|
||||
<p class="buckets-view__list__sorting-header__date">Date Added</p>
|
||||
<p class="buckets-view__list__sorting-header__empty"/>
|
||||
</div>
|
||||
<div class="buckets-view__list__item" v-for="(bucket, key) in buckets" :key="key" @click.stop="openBucket">
|
||||
<div class="buckets-view__list__item" v-for="(bucket, key) in bucketsList" :key="key" @click.stop="openBucket(bucket.Name)">
|
||||
<BucketItem
|
||||
:item-data="bucket"
|
||||
:show-delete-bucket-popup="showDeleteBucketPopup"
|
||||
@ -55,6 +55,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Bucket } from 'aws-sdk/clients/s3';
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
|
||||
import BucketItem from '@/components/objects/BucketItem.vue';
|
||||
@ -67,7 +68,6 @@ import { ACCESS_GRANTS_ACTIONS } from '@/store/modules/accessGrants';
|
||||
import { OBJECTS_ACTIONS } from '@/store/modules/objects';
|
||||
import { AccessGrant, GatewayCredentials } from '@/types/accessGrants';
|
||||
import { MetaUtils } from '@/utils/meta';
|
||||
import { Bucket } from '@aws-sdk/client-s3';
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
@ -102,85 +102,88 @@ export default class BucketsView extends Vue {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.removeTemporaryAccessGrant();
|
||||
|
||||
try {
|
||||
const cleanAPIKey: AccessGrant = await this.$store.dispatch(ACCESS_GRANTS_ACTIONS.CREATE, this.FILE_BROWSER_AG_NAME);
|
||||
if (!this.accessGrantFromStore) {
|
||||
await this.setWorker();
|
||||
// This is done just in case old temporary access grant still exists.
|
||||
await this.removeTemporaryAccessGrant();
|
||||
await this.setAccess();
|
||||
await this.fetchBuckets();
|
||||
}
|
||||
|
||||
this.worker = this.$store.state.accessGrantsModule.accessGrantsWebWorker;
|
||||
this.worker.onmessage = (event: MessageEvent) => {
|
||||
const data = event.data;
|
||||
if (data.error) {
|
||||
throw new Error(data.error);
|
||||
}
|
||||
this.isLoading = false;
|
||||
|
||||
this.grantWithPermissions = data.value;
|
||||
};
|
||||
|
||||
const now = new Date();
|
||||
const inADay = new Date(now.setDate(now.getDate() + 1));
|
||||
|
||||
await this.worker.postMessage({
|
||||
'type': 'SetPermission',
|
||||
'isDownload': true,
|
||||
'isUpload': true,
|
||||
'isList': true,
|
||||
'isDelete': true,
|
||||
'buckets': [],
|
||||
'apiKey': cleanAPIKey.secret,
|
||||
'notBefore': now.toISOString(),
|
||||
'notAfter': inADay.toISOString(),
|
||||
});
|
||||
|
||||
// Timeout is used to give some time for web worker to return value.
|
||||
setTimeout(() => {
|
||||
this.worker.onmessage = (event: MessageEvent) => {
|
||||
const data = event.data;
|
||||
if (data.error) {
|
||||
throw new Error(data.error);
|
||||
}
|
||||
|
||||
this.accessGrant = data.value;
|
||||
};
|
||||
|
||||
const satelliteNodeURL: string = MetaUtils.getMetaContent('satellite-nodeurl');
|
||||
this.worker.postMessage({
|
||||
'type': 'GenerateAccess',
|
||||
'apiKey': this.grantWithPermissions,
|
||||
'passphrase': this.$route.params.passphrase,
|
||||
'projectID': this.$store.getters.selectedProject.id,
|
||||
'satelliteNodeURL': satelliteNodeURL,
|
||||
});
|
||||
|
||||
// Timeout is used to give some time for web worker to return value.
|
||||
setTimeout(async () => {
|
||||
await this.$store.dispatch(OBJECTS_ACTIONS.SET_ACCESS_GRANT, this.accessGrant);
|
||||
|
||||
// TODO: use this value until all the satellites will have this URL set.
|
||||
const gatewayURL = 'https://auth.tardigradeshare.io';
|
||||
const gatewayCredentials: GatewayCredentials = await this.$store.dispatch(ACCESS_GRANTS_ACTIONS.GET_GATEWAY_CREDENTIALS, {accessGrant: this.accessGrant, optionalURL: gatewayURL});
|
||||
await this.$store.dispatch(OBJECTS_ACTIONS.SET_GATEWAY_CREDENTIALS, gatewayCredentials);
|
||||
await this.$store.dispatch(OBJECTS_ACTIONS.SET_S3_CLIENT);
|
||||
await this.$store.dispatch(OBJECTS_ACTIONS.FETCH_BUCKETS);
|
||||
|
||||
this.isLoading = false;
|
||||
|
||||
if (!this.buckets.length) this.showCreateBucketPopup();
|
||||
}, 1000);
|
||||
}, 1000);
|
||||
if (!this.bucketsList.length) this.showCreateBucketPopup();
|
||||
} catch (error) {
|
||||
await this.$notify.error(`Failed to setup Objects view. ${error.message}`);
|
||||
|
||||
return;
|
||||
await this.$notify.error(`Failed to setup Buckets view. ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lifecycle hook before component destroying.
|
||||
* Remove temporary created access grant.
|
||||
* Sets access to S3 client.
|
||||
*/
|
||||
public async beforeDestroy(): Promise<void> {
|
||||
await this.removeTemporaryAccessGrant();
|
||||
public async setAccess(): Promise<void> {
|
||||
const cleanAPIKey: AccessGrant = await this.$store.dispatch(ACCESS_GRANTS_ACTIONS.CREATE, this.FILE_BROWSER_AG_NAME);
|
||||
await this.$store.dispatch(OBJECTS_ACTIONS.SET_API_KEY, cleanAPIKey.secret);
|
||||
|
||||
const now = new Date();
|
||||
const inADay = new Date(now.setDate(now.getDate() + 1));
|
||||
|
||||
await this.worker.postMessage({
|
||||
'type': 'SetPermission',
|
||||
'isDownload': true,
|
||||
'isUpload': true,
|
||||
'isList': true,
|
||||
'isDelete': true,
|
||||
'buckets': [],
|
||||
'apiKey': cleanAPIKey.secret,
|
||||
'notBefore': new Date().toISOString(),
|
||||
'notAfter': inADay.toISOString(),
|
||||
});
|
||||
|
||||
const grantEvent: MessageEvent = await new Promise(resolve => this.worker.onmessage = resolve);
|
||||
this.grantWithPermissions = grantEvent.data.value;
|
||||
if (grantEvent.data.error) {
|
||||
throw new Error(grantEvent.data.error);
|
||||
}
|
||||
|
||||
const satelliteNodeURL: string = MetaUtils.getMetaContent('satellite-nodeurl');
|
||||
this.worker.postMessage({
|
||||
'type': 'GenerateAccess',
|
||||
'apiKey': this.grantWithPermissions,
|
||||
'passphrase': this.passphrase,
|
||||
'projectID': this.$store.getters.selectedProject.id,
|
||||
'satelliteNodeURL': satelliteNodeURL,
|
||||
});
|
||||
|
||||
const accessGrantEvent: MessageEvent = await new Promise(resolve => this.worker.onmessage = resolve);
|
||||
this.accessGrant = accessGrantEvent.data.value;
|
||||
if (accessGrantEvent.data.error) {
|
||||
throw new Error(accessGrantEvent.data.error);
|
||||
}
|
||||
|
||||
await this.$store.dispatch(OBJECTS_ACTIONS.SET_ACCESS_GRANT, this.accessGrant);
|
||||
|
||||
const gatewayCredentials: GatewayCredentials = await this.$store.dispatch(ACCESS_GRANTS_ACTIONS.GET_GATEWAY_CREDENTIALS, {accessGrant: this.accessGrant});
|
||||
await this.$store.dispatch(OBJECTS_ACTIONS.SET_GATEWAY_CREDENTIALS, gatewayCredentials);
|
||||
await this.$store.dispatch(OBJECTS_ACTIONS.SET_S3_CLIENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches bucket using S3 client.
|
||||
*/
|
||||
public async fetchBuckets(): Promise<void> {
|
||||
await this.$store.dispatch(OBJECTS_ACTIONS.FETCH_BUCKETS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@ -235,6 +238,12 @@ export default class BucketsView extends Vue {
|
||||
* Opens utils dropdown.
|
||||
*/
|
||||
public openDropdown(key: number): void {
|
||||
if (this.activeDropdown === key) {
|
||||
this.activeDropdown = -1;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.activeDropdown = key;
|
||||
}
|
||||
|
||||
@ -294,15 +303,33 @@ export default class BucketsView extends Vue {
|
||||
}
|
||||
}
|
||||
|
||||
public openBucket(): void {
|
||||
/**
|
||||
* Holds on bucket click. Proceeds to file browser.
|
||||
*/
|
||||
public openBucket(bucketName: string): void {
|
||||
this.$store.dispatch(OBJECTS_ACTIONS.SET_FILE_COMPONENT_BUCKET_NAME, bucketName);
|
||||
this.$router.push(RouteConfig.Objects.with(RouteConfig.UploadFile).path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns fetched buckets from store.
|
||||
*/
|
||||
public get buckets(): Bucket[] {
|
||||
return this.$store.state.objectsModule.buckets;
|
||||
public get bucketsList(): Bucket[] {
|
||||
return this.$store.state.objectsModule.bucketsList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns passphrase from store.
|
||||
*/
|
||||
private get passphrase(): string {
|
||||
return this.$store.state.objectsModule.passphrase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns access grant from store.
|
||||
*/
|
||||
private get accessGrantFromStore(): string {
|
||||
return this.$store.state.objectsModule.accessGrant;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -22,7 +22,7 @@ import GeneratePassphrase from '@/components/common/GeneratePassphrase.vue';
|
||||
|
||||
import { RouteConfig } from '@/router';
|
||||
import { OBJECTS_ACTIONS } from '@/store/modules/objects';
|
||||
import { LocalData } from '@/utils/localData';
|
||||
import { LocalData, UserIDPassSalt } from '@/utils/localData';
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
@ -31,9 +31,21 @@ import { LocalData } from '@/utils/localData';
|
||||
})
|
||||
export default class CreatePassphrase extends Vue {
|
||||
private isLoading: boolean = false;
|
||||
private keyToBeStored: string = '';
|
||||
|
||||
public passphrase: string = '';
|
||||
|
||||
/**
|
||||
* Lifecycle hook after initial render.
|
||||
* Chooses correct route.
|
||||
*/
|
||||
public mounted(): void {
|
||||
const idPassSalt: UserIDPassSalt | null = LocalData.getUserIDPassSalt();
|
||||
if (idPassSalt && idPassSalt.userId === this.$store.getters.user.id) {
|
||||
this.$router.push({name: RouteConfig.EnterPassphrase.name});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets passphrase from child component.
|
||||
*/
|
||||
@ -44,22 +56,43 @@ export default class CreatePassphrase extends Vue {
|
||||
/**
|
||||
* Holds on next button click logic.
|
||||
*/
|
||||
public onNextClick(): void {
|
||||
public async onNextClick(): Promise<void> {
|
||||
if (this.isLoading) return;
|
||||
|
||||
this.isLoading = true;
|
||||
|
||||
const SALT = 'storj-unique-salt';
|
||||
pbkdf2.pbkdf2(this.passphrase, SALT, 1, 64, (error, key) => {
|
||||
if (error) return this.$notify.error(error.message);
|
||||
|
||||
LocalData.setUserIDPassSalt(this.$store.getters.user.id, key.toString('hex'), SALT);
|
||||
});
|
||||
const result: Buffer | Error = await this.pbkdf2Async(SALT);
|
||||
|
||||
if (result instanceof Error) {
|
||||
await this.$notify.error(result.message);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.keyToBeStored = await result.toString('hex');
|
||||
|
||||
await LocalData.setUserIDPassSalt(this.$store.getters.user.id, this.keyToBeStored, SALT);
|
||||
await this.$store.dispatch(OBJECTS_ACTIONS.SET_PASSPHRASE, this.passphrase);
|
||||
|
||||
this.isLoading = false;
|
||||
|
||||
this.$store.dispatch(OBJECTS_ACTIONS.SET_PASSPHRASE, this.passphrase);
|
||||
this.$router.push({name: RouteConfig.BucketsManagement.name});
|
||||
await this.$router.push({name: RouteConfig.EnterPassphrase.name});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -13,14 +13,6 @@
|
||||
</div>
|
||||
<p class="enter-pass__container__warning__message">
|
||||
Entering your encryption passphrase here will share encryption data with your browser.
|
||||
<a
|
||||
class="enter-pass__container__warning__message__link"
|
||||
:href="docsLink"
|
||||
target="_blank"
|
||||
rel="noopener norefferer"
|
||||
>
|
||||
Learn More
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
<label class="enter-pass__container__textarea" for="enter-pass-textarea">
|
||||
@ -38,8 +30,9 @@
|
||||
<div class="enter-pass__container__error" v-if="isError">
|
||||
<h2 class="enter-pass__container__error__title">Encryption Passphrase Does not Match</h2>
|
||||
<p class="enter-pass__container__error__message">
|
||||
This passphrase hasn’t yet been used in the browser. Please ensure this is the encryption passphrase
|
||||
used in libulink or the Uplink CLI.
|
||||
A previous fingerprint of a passphrase-based-key-derivation-function created in this browser doesn't
|
||||
match the passphrase you just entered. Entering a passphrase not previously created will result in
|
||||
the creation of a new passphrase.
|
||||
</p>
|
||||
<label class="enter-pass__container__error__check-area" :class="{ error: isCheckboxError }" for="error-checkbox">
|
||||
<input
|
||||
@ -84,11 +77,24 @@ import { MetaUtils } from '@/utils/meta';
|
||||
},
|
||||
})
|
||||
export default class EnterPassphrase extends Vue {
|
||||
private hashFromInput: string = '';
|
||||
|
||||
public passphrase: string = '';
|
||||
public isError: boolean = false;
|
||||
public isCheckboxChecked: boolean = false;
|
||||
public isCheckboxError: boolean = false;
|
||||
|
||||
/**
|
||||
* Lifecycle hook after initial render.
|
||||
* Chooses correct route.
|
||||
*/
|
||||
public mounted(): void {
|
||||
const idPassSalt: UserIDPassSalt | null = LocalData.getUserIDPassSalt();
|
||||
if (!idPassSalt) {
|
||||
this.$router.push({name: RouteConfig.CreatePassphrase.name});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns docs link from config.
|
||||
*/
|
||||
@ -99,38 +105,43 @@ export default class EnterPassphrase extends Vue {
|
||||
/**
|
||||
* Holds on access data button click logic.
|
||||
*/
|
||||
public onAccessDataClick(): void {
|
||||
public async onAccessDataClick(): Promise<void> {
|
||||
if (!this.passphrase) return;
|
||||
|
||||
const hashFromStorage: UserIDPassSalt | null = LocalData.getUserIDPassSalt();
|
||||
if (!hashFromStorage) return;
|
||||
|
||||
pbkdf2.pbkdf2(this.passphrase, hashFromStorage.salt, 1, 64, (error, key) => {
|
||||
if (error) return this.$notify.error(error.message);
|
||||
const result: Buffer | Error = await this.pbkdf2Async(hashFromStorage.salt);
|
||||
|
||||
const hashFromInput: string = key.toString('hex');
|
||||
const areHashesEqual = () => {
|
||||
return hashFromStorage.passwordHash === hashFromInput;
|
||||
};
|
||||
if (result instanceof Error) {
|
||||
await this.$notify.error(result.message);
|
||||
|
||||
switch (true) {
|
||||
case areHashesEqual() ||
|
||||
!areHashesEqual() && this.isError && this.isCheckboxChecked:
|
||||
this.$store.dispatch(OBJECTS_ACTIONS.SET_PASSPHRASE, this.passphrase);
|
||||
this.$router.push({name: RouteConfig.BucketsManagement.name});
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
case !areHashesEqual() && this.isError && !this.isCheckboxChecked:
|
||||
this.isCheckboxError = true;
|
||||
this.hashFromInput = await result.toString('hex');
|
||||
|
||||
return;
|
||||
case !areHashesEqual():
|
||||
this.isError = true;
|
||||
const areHashesEqual = () => {
|
||||
return hashFromStorage.passwordHash === this.hashFromInput;
|
||||
};
|
||||
|
||||
return;
|
||||
default:
|
||||
}
|
||||
});
|
||||
switch (true) {
|
||||
case areHashesEqual() ||
|
||||
!areHashesEqual() && this.isError && this.isCheckboxChecked:
|
||||
await this.$store.dispatch(OBJECTS_ACTIONS.SET_PASSPHRASE, this.passphrase);
|
||||
await this.$router.push({name: RouteConfig.BucketsManagement.name});
|
||||
|
||||
return;
|
||||
case !areHashesEqual() && this.isError && !this.isCheckboxChecked:
|
||||
this.isCheckboxError = true;
|
||||
|
||||
return;
|
||||
case !areHashesEqual():
|
||||
this.isError = true;
|
||||
|
||||
return;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -141,6 +152,20 @@ export default class EnterPassphrase extends Vue {
|
||||
this.isCheckboxChecked = false;
|
||||
this.isError = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -212,12 +237,6 @@ export default class EnterPassphrase extends Vue {
|
||||
line-height: 19px;
|
||||
color: #1b2533;
|
||||
margin: 10px 0 0 0;
|
||||
|
||||
&__link {
|
||||
font-family: 'font_medium', sans-serif;
|
||||
color: #0068dc;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,39 +11,17 @@
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
|
||||
import { RouteConfig } from '@/router';
|
||||
import { LocalData, UserIDPassSalt } from '@/utils/localData';
|
||||
import { MetaUtils } from '@/utils/meta';
|
||||
|
||||
@Component
|
||||
export default class ObjectsArea extends Vue {
|
||||
/**
|
||||
* Lifecycle hook after initial render.
|
||||
* Chooses correct route.
|
||||
* Redirects if flow is disabled.
|
||||
*/
|
||||
public async mounted(): Promise<void> {
|
||||
const DUPLICATE_NAV_ERROR: string = 'NavigationDuplicated';
|
||||
const idPassSalt: UserIDPassSalt | null = LocalData.getUserIDPassSalt();
|
||||
if (idPassSalt && idPassSalt.userId === this.$store.getters.user.id) {
|
||||
try {
|
||||
await this.$router.push(RouteConfig.Objects.with(RouteConfig.EnterPassphrase).path);
|
||||
|
||||
return;
|
||||
} catch (error) {
|
||||
if (error.name === DUPLICATE_NAV_ERROR) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.$notify.error(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await this.$router.push(RouteConfig.Objects.with(RouteConfig.CreatePassphrase).path);
|
||||
} catch (error) {
|
||||
if (error.name === DUPLICATE_NAV_ERROR) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.$notify.error(error.message);
|
||||
if (await JSON.parse(MetaUtils.getMetaContent('file-browser-flow-disabled'))) {
|
||||
await this.$router.push(RouteConfig.ProjectDashboard.path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,199 @@
|
||||
// Copyright (C) 2021 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<div class="file-browser">
|
||||
<FileBrowser></FileBrowser>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { FileBrowser } from 'browser';
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
|
||||
@Component
|
||||
export default class UploadFile extends Vue {}
|
||||
import { RouteConfig } from '@/router';
|
||||
import { ACCESS_GRANTS_ACTIONS } from '@/store/modules/accessGrants';
|
||||
import { AccessGrant, GatewayCredentials } from '@/types/accessGrants';
|
||||
import { MetaUtils } from '@/utils/meta';
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
FileBrowser,
|
||||
},
|
||||
})
|
||||
export default class UploadFile extends Vue {
|
||||
private worker: Worker;
|
||||
|
||||
/**
|
||||
* Lifecycle hook before initial render.
|
||||
* Checks if bucket is chosen.
|
||||
*/
|
||||
public beforeMount(): void {
|
||||
if (!this.bucket) {
|
||||
this.$router.push(RouteConfig.Objects.with(RouteConfig.EnterPassphrase).path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lifecycle hook after initial render.
|
||||
* Checks if bucket is chosen.
|
||||
* Sets local worker.
|
||||
*/
|
||||
public mounted(): void {
|
||||
if (!this.bucket) {
|
||||
this.$router.push(RouteConfig.Objects.with(RouteConfig.EnterPassphrase).path);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.setWorker();
|
||||
}
|
||||
|
||||
/**
|
||||
* Lifecycle hook after vue instance was created.
|
||||
* Initiates file browser.
|
||||
*/
|
||||
public created(): void {
|
||||
if (!this.bucket) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.$store.commit('files/init', {
|
||||
endpoint: this.$store.state.objectsModule.gatewayCredentials.endpoint,
|
||||
accessKey: this.$store.state.objectsModule.gatewayCredentials.accessKeyId,
|
||||
secretKey: this.$store.state.objectsModule.gatewayCredentials.secretKey,
|
||||
bucket: this.bucket,
|
||||
browserRoot: RouteConfig.Objects.with(RouteConfig.UploadFile).path,
|
||||
getObjectMapUrl: async (path: string) => await this.generateObjectMapUrl(path),
|
||||
getSharedLink: async (path: string) => await this.generateShareLinkUrl(path),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a URL for an object map.
|
||||
*/
|
||||
public async generateObjectMapUrl(path: string): Promise<string> {
|
||||
path = `${this.bucket}/${path}`;
|
||||
const now = new Date();
|
||||
const inADay = new Date(now.setDate(now.getDate() + 1));
|
||||
|
||||
try {
|
||||
const key: string = await this.accessKey(this.apiKey, inADay, path);
|
||||
|
||||
return `https://link.tardigradeshare.io/s/${key}/${path}?map=1`;
|
||||
} catch (error) {
|
||||
await this.$notify.error(error.message);
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a URL for a link sharing service.
|
||||
*/
|
||||
public async generateShareLinkUrl(path: string): Promise<string> {
|
||||
path = `${this.bucket}/${path}`;
|
||||
const now = new Date();
|
||||
const notAfter = new Date(now.setFullYear(now.getFullYear() + 100));
|
||||
const LINK_SHARING_AG_NAME = `${path}_shared-object_${now.toISOString()}`;
|
||||
const cleanAPIKey: AccessGrant = await this.$store.dispatch(ACCESS_GRANTS_ACTIONS.CREATE, LINK_SHARING_AG_NAME);
|
||||
|
||||
try {
|
||||
const key: string = await this.accessKey(cleanAPIKey.secret, notAfter, path);
|
||||
|
||||
return `https://link.tardigradeshare.io/${key}/${path}`;
|
||||
} catch (error) {
|
||||
await this.$notify.error(error.message);
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates public access key.
|
||||
*/
|
||||
private async accessKey(cleanApiKey: string, notAfter: Date, path: string): Promise<string> {
|
||||
const satelliteNodeURL = MetaUtils.getMetaContent('satellite-nodeurl');
|
||||
|
||||
this.worker.postMessage({
|
||||
'type': 'GenerateAccess',
|
||||
'apiKey': cleanApiKey,
|
||||
'passphrase': this.passphrase,
|
||||
'projectID': this.$store.getters.selectedProject.id,
|
||||
'satelliteNodeURL': satelliteNodeURL,
|
||||
});
|
||||
|
||||
const grantEvent: MessageEvent = await new Promise(resolve => this.worker.onmessage = resolve);
|
||||
const grantData = grantEvent.data;
|
||||
if (grantData.error) {
|
||||
await this.$notify.error(grantData.error);
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
this.worker.postMessage({
|
||||
'type': 'RestrictGrant',
|
||||
'isDownload': true,
|
||||
'isUpload': true,
|
||||
'isList': true,
|
||||
'isDelete': true,
|
||||
'paths': [path],
|
||||
'grant': grantData.value,
|
||||
'notBefore': new Date().toISOString(),
|
||||
'notAfter': notAfter.toISOString(),
|
||||
});
|
||||
|
||||
const event: MessageEvent = await new Promise(resolve => this.worker.onmessage = resolve);
|
||||
const data = event.data;
|
||||
if (data.error) {
|
||||
await this.$notify.error(data.error);
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
const gatewayCredentials: GatewayCredentials = await this.$store.dispatch(ACCESS_GRANTS_ACTIONS.GET_GATEWAY_CREDENTIALS, {accessGrant: data.value, isPublic: true});
|
||||
|
||||
return gatewayCredentials.accessKeyId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns passphrase from store.
|
||||
*/
|
||||
private get passphrase(): string {
|
||||
return this.$store.state.objectsModule.passphrase;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>
|
||||
@import '../../../node_modules/browser/dist/browser.css';
|
||||
|
||||
.file-browser {
|
||||
font-family: 'font_regular', sans-serif;
|
||||
padding-bottom: 200px;
|
||||
}
|
||||
</style>
|
||||
|
@ -388,6 +388,12 @@ router.beforeEach((to, from, next) => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (navigateToDefaultSubTab(to.matched, RouteConfig.Objects)) {
|
||||
next(RouteConfig.Objects.with(RouteConfig.CreatePassphrase).path);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (to.name === 'default') {
|
||||
next(RouteConfig.ProjectDashboard.path);
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
// Copyright (C) 2019 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
import { files } from 'browser';
|
||||
import Vue from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
|
||||
@ -62,6 +63,7 @@ export const store = new Vuex.Store<ModulesState>({
|
||||
projectsModule: makeProjectsModule(projectsApi),
|
||||
bucketUsageModule: makeBucketsModule(bucketsApi),
|
||||
objectsModule: makeObjectsModule(),
|
||||
files,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -215,7 +215,7 @@ export function makeAccessGrantsModule(api: AccessGrantsApi): StoreModule<Access
|
||||
await api.deleteByNameAndProjectID(name, rootGetters.selectedProject.id);
|
||||
},
|
||||
getGatewayCredentials: async function({commit}: any, payload): Promise<GatewayCredentials> {
|
||||
const credentials: GatewayCredentials = await api.getGatewayCredentials(payload.accessGrant, payload.optionalURL);
|
||||
const credentials: GatewayCredentials = await api.getGatewayCredentials(payload.accessGrant, payload.optionalURL, payload.isPublic);
|
||||
|
||||
commit(SET_GATEWAY_CREDENTIALS, credentials);
|
||||
|
||||
|
@ -1,23 +1,19 @@
|
||||
// Copyright (C) 2021 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
import S3, { Bucket } from 'aws-sdk/clients/s3';
|
||||
|
||||
import { StoreModule } from '@/store';
|
||||
import { GatewayCredentials } from '@/types/accessGrants';
|
||||
import {
|
||||
Bucket,
|
||||
CreateBucketCommand,
|
||||
DeleteBucketCommand,
|
||||
ListBucketsCommand,
|
||||
ListBucketsOutput,
|
||||
S3Client,
|
||||
} from '@aws-sdk/client-s3';
|
||||
|
||||
export const OBJECTS_ACTIONS = {
|
||||
CLEAR: 'clearObjects',
|
||||
SET_GATEWAY_CREDENTIALS: 'setGatewayCredentials',
|
||||
SET_API_KEY: 'setApiKey',
|
||||
SET_ACCESS_GRANT: 'setAccessGrant',
|
||||
SET_S3_CLIENT: 'setS3Client',
|
||||
SET_PASSPHRASE: 'setPassphrase',
|
||||
SET_FILE_COMPONENT_BUCKET_NAME: 'setFileComponentBucketName',
|
||||
FETCH_BUCKETS: 'fetchBuckets',
|
||||
CREATE_BUCKET: 'createBucket',
|
||||
DELETE_BUCKET: 'deleteBucket',
|
||||
@ -25,28 +21,34 @@ export const OBJECTS_ACTIONS = {
|
||||
|
||||
export const OBJECTS_MUTATIONS = {
|
||||
SET_GATEWAY_CREDENTIALS: 'setGatewayCredentials',
|
||||
SET_API_KEY: 'setApiKey',
|
||||
SET_ACCESS_GRANT: 'setAccessGrant',
|
||||
CLEAR: 'clearObjects',
|
||||
SET_S3_CLIENT: 'setS3Client',
|
||||
SET_BUCKETS: 'setBuckets',
|
||||
SET_FILE_COMPONENT_BUCKET_NAME: 'setFileComponentBucketName',
|
||||
SET_PASSPHRASE: 'setPassphrase',
|
||||
};
|
||||
|
||||
const {
|
||||
CLEAR,
|
||||
SET_API_KEY,
|
||||
SET_ACCESS_GRANT,
|
||||
SET_GATEWAY_CREDENTIALS,
|
||||
SET_S3_CLIENT,
|
||||
SET_BUCKETS,
|
||||
SET_PASSPHRASE,
|
||||
SET_FILE_COMPONENT_BUCKET_NAME,
|
||||
} = OBJECTS_MUTATIONS;
|
||||
|
||||
export class ObjectsState {
|
||||
public apiKey: string = '';
|
||||
public accessGrant: string = '';
|
||||
public gatewayCredentials: GatewayCredentials = new GatewayCredentials();
|
||||
public s3Client: S3Client = new S3Client({});
|
||||
public buckets: Bucket[] = [];
|
||||
public s3Client: S3 = new S3({});
|
||||
public bucketsList: Bucket[] = [];
|
||||
public passphrase: string = '';
|
||||
public fileComponentBucketName: string = '';
|
||||
}
|
||||
|
||||
/**
|
||||
@ -56,6 +58,9 @@ export function makeObjectsModule(): StoreModule<ObjectsState> {
|
||||
return {
|
||||
state: new ObjectsState(),
|
||||
mutations: {
|
||||
[SET_API_KEY](state: ObjectsState, apiKey: string) {
|
||||
state.apiKey = apiKey;
|
||||
},
|
||||
[SET_ACCESS_GRANT](state: ObjectsState, accessGrant: string) {
|
||||
state.accessGrant = accessGrant;
|
||||
},
|
||||
@ -63,36 +68,37 @@ export function makeObjectsModule(): StoreModule<ObjectsState> {
|
||||
state.gatewayCredentials = credentials;
|
||||
},
|
||||
[SET_S3_CLIENT](state: ObjectsState) {
|
||||
// TODO: use this for local testing. Remove after final implementation.
|
||||
// state.gatewayCredentials.accessKeyId = 'jwitszrc76z4amjcrinv4zjpnlia';
|
||||
// state.gatewayCredentials.secretKey = 'jyjufay7ddmwj6tlboyuj23yy4lqigfqa2ie25y526qmjj65khxzw';
|
||||
// state.gatewayCredentials.endpoint = 'https://gateway.tardigradeshare.io';
|
||||
|
||||
const s3Config = {
|
||||
credentials: {
|
||||
accessKeyId: state.gatewayCredentials.accessKeyId,
|
||||
secretAccessKey: state.gatewayCredentials.secretKey,
|
||||
},
|
||||
accessKeyId: state.gatewayCredentials.accessKeyId,
|
||||
secretAccessKey: state.gatewayCredentials.secretKey,
|
||||
endpoint: state.gatewayCredentials.endpoint,
|
||||
s3ForcePathStyle: true,
|
||||
signatureVersion: 'v4',
|
||||
region: 'REGION',
|
||||
};
|
||||
|
||||
state.s3Client = new S3Client(s3Config);
|
||||
state.s3Client = new S3(s3Config);
|
||||
},
|
||||
[SET_BUCKETS](state: ObjectsState, buckets: Bucket[]) {
|
||||
state.buckets = buckets;
|
||||
state.bucketsList = buckets;
|
||||
},
|
||||
[SET_PASSPHRASE](state: ObjectsState, passphrase: string) {
|
||||
state.passphrase = passphrase;
|
||||
},
|
||||
[SET_FILE_COMPONENT_BUCKET_NAME](state: ObjectsState, bucketName: string) {
|
||||
state.fileComponentBucketName = bucketName;
|
||||
},
|
||||
[CLEAR](state: ObjectsState) {
|
||||
state.apiKey = '';
|
||||
state.passphrase = '';
|
||||
state.accessGrant = '';
|
||||
state.gatewayCredentials = new GatewayCredentials();
|
||||
state.s3Client = new S3({});
|
||||
state.bucketsList = [];
|
||||
state.fileComponentBucketName = '';
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
setApiKey: function({commit}: any, apiKey: string): void {
|
||||
commit(SET_API_KEY, apiKey);
|
||||
},
|
||||
setAccessGrant: function({commit}: any, accessGrant: string): void {
|
||||
commit(SET_ACCESS_GRANT, accessGrant);
|
||||
},
|
||||
@ -105,25 +111,23 @@ export function makeObjectsModule(): StoreModule<ObjectsState> {
|
||||
setPassphrase: function({commit}: any, passphrase: string): void {
|
||||
commit(SET_PASSPHRASE, passphrase);
|
||||
},
|
||||
setFileComponentBucketName: function({commit}: any, bucketName: string): void {
|
||||
commit(SET_FILE_COMPONENT_BUCKET_NAME, bucketName);
|
||||
},
|
||||
fetchBuckets: async function(ctx): Promise<void> {
|
||||
const bucketsOutput: ListBucketsOutput = await ctx.state.s3Client.send(new ListBucketsCommand({
|
||||
credentials: {
|
||||
accessKeyId: ctx.state.gatewayCredentials.accessKeyId,
|
||||
secretAccessKey: ctx.state.gatewayCredentials.secretKey,
|
||||
},
|
||||
}));
|
||||
const result = await ctx.state.s3Client.listBuckets().promise();
|
||||
|
||||
ctx.commit(SET_BUCKETS, bucketsOutput.Buckets);
|
||||
ctx.commit(SET_BUCKETS, result.Buckets);
|
||||
},
|
||||
createBucket: async function(ctx, name: string): Promise<void> {
|
||||
await ctx.state.s3Client.send(new CreateBucketCommand({
|
||||
await ctx.state.s3Client.createBucket({
|
||||
Bucket: name,
|
||||
}));
|
||||
}).promise();
|
||||
},
|
||||
deleteBucket: async function(ctx, name: string): Promise<void> {
|
||||
await ctx.state.s3Client.send(new DeleteBucketCommand({
|
||||
await ctx.state.s3Client.deleteBucket({
|
||||
Bucket: name,
|
||||
}));
|
||||
}).promise();
|
||||
},
|
||||
clearObjects: function ({commit}: any): void {
|
||||
commit(CLEAR);
|
||||
|
@ -47,7 +47,7 @@ export interface AccessGrantsApi {
|
||||
* @returns GatewayCredentials
|
||||
* @throws Error
|
||||
*/
|
||||
getGatewayCredentials(accessGrant: string, optionalURL?: string): Promise<GatewayCredentials>;
|
||||
getGatewayCredentials(accessGrant: string, optionalURL?: string, isPublic?: boolean): Promise<GatewayCredentials>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -53,7 +53,7 @@ self.onmessage = function (event) {
|
||||
permission.NotBefore = notBefore;
|
||||
permission.NotAfter = notAfter;
|
||||
|
||||
if (data.type == "SetPermission") {
|
||||
if (data.type === "SetPermission") {
|
||||
const buckets = data.buckets;
|
||||
apiKey = data.apiKey;
|
||||
result = self.setAPIKeyPermission(apiKey, buckets, permission);
|
||||
|
Loading…
Reference in New Issue
Block a user