Skip to content

Commit

Permalink
feat: New API getObjectPreviewUrl (#270)
Browse files Browse the repository at this point in the history
  • Loading branch information
rrr523 authored Aug 28, 2023
1 parent 97f43f5 commit a3e6b47
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 6 deletions.
5 changes: 5 additions & 0 deletions .changeset/fast-drinks-move.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@bnb-chain/greenfield-js-sdk': patch
---

feat: New API getObjectPreviewUrl
2 changes: 1 addition & 1 deletion examples/nextjs/src/components/object/create/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export const CreateObject = () => {
bucketName: createObjectInfo.bucketName,
objectName: createObjectInfo.objectName,
creator: address,
visibility: 'VISIBILITY_TYPE_PUBLIC_READ',
visibility: 'VISIBILITY_TYPE_PRIVATE',
fileType: file.type,
redundancyType: 'REDUNDANCY_EC_TYPE',
contentLength,
Expand Down
39 changes: 39 additions & 0 deletions examples/nextjs/src/components/object/info/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,45 @@ export const ObjectInfo = () => {

<br />

<button
onClick={async () => {
if (!address) return;
const provider = await connector?.getProvider();
const offChainData = await getOffchainAuthKeys(address, provider);
if (!offChainData) {
alert('No offchain, please create offchain pairs first');
return;
}

const res = await client.object.getObjectPreviewUrl(
{
bucketName,
objectName,
queryMap: {
view: '0',
'X-Gnfd-User-Address': address,
'X-Gnfd-App-Domain': window.location.origin,
'X-Gnfd-Expiry-Timestamp': '2023-09-03T09%3A23%3A39Z',
},
// queryRaw:
// 'X-Gnfd-App-Domain=http%3A%2F%2Flocalhost%3A3000&X-Gnfd-Expiry-Timestamp=2023-09-03T09%3A23%3A39Z&X-Gnfd-User-Address=0x1C893441AB6c1A75E01887087ea508bE8e07AAae&view=0',
},
{
type: 'EDDSA',
address,
domain: window.location.origin,
seed: offChainData.seedString,
},
);

console.log(res);
}}
>
get object preview url
</button>

<br />

<div>
get objects list by bucket name
<br />
Expand Down
51 changes: 49 additions & 2 deletions packages/chain-sdk/src/api/objectt.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { encodePath } from '@/clients/spclient/auth';
import { encodePath, getMsgToSign, secpSign } from '@/clients/spclient/auth';
import { getGetObjectMetaInfo } from '@/clients/spclient/spApis/getObject';
import { parseGetObjectMetaResponse } from '@/clients/spclient/spApis/getObjectMeta';
import { parseListObjectsByBucketNameResponse } from '@/clients/spclient/spApis/listObjectsByBucket';
Expand All @@ -10,6 +10,7 @@ import { MsgCancelCreateObjectSDKTypeEIP712 } from '@/messages/greenfield/storag
import { MsgCreateObjectSDKTypeEIP712 } from '@/messages/greenfield/storage/MsgCreateObject';
import { MsgDeleteObjectSDKTypeEIP712 } from '@/messages/greenfield/storage/MsgDeleteObject';
import { MsgUpdateObjectInfoSDKTypeEIP712 } from '@/messages/greenfield/storage/MsgUpdateObjectInfo';
import { signSignatureByEddsa } from '@/offchainauth';
import { GetObjectMetaRequest, GetObjectMetaResponse } from '@/types/sp-xml/GetObjectMetaResponse';
import { ListObjectsByBucketNameResponse } from '@/types/sp-xml/ListObjectsByBucketNameResponse';
import {
Expand Down Expand Up @@ -37,8 +38,9 @@ import {
MsgUpdateObjectInfo,
} from '@bnb-chain/greenfield-cosmos-types/greenfield/storage/tx';
import { bytesFromBase64 } from '@bnb-chain/greenfield-cosmos-types/helpers';
import { hexlify } from '@ethersproject/bytes';
import { Headers } from 'cross-fetch';
import { bytesToUtf8, hexToBytes } from 'ethereum-cryptography/utils';
import { bytesToUtf8, hexToBytes, utf8ToBytes } from 'ethereum-cryptography/utils';
import { container, delay, inject, singleton } from 'tsyringe';
import {
GRNToString,
Expand All @@ -56,6 +58,7 @@ import {
Long,
TBaseGetCreateObject,
TBaseGetObject,
TBaseGetPrivewObject,
TBasePutObject,
TListObjects,
TxResponse,
Expand Down Expand Up @@ -97,6 +100,8 @@ export interface IObject {
*/
getObject(configParam: TBaseGetObject, authType: AuthType): Promise<IObjectResultType<Blob>>;

getObjectPreviewUrl(configParam: TBaseGetPrivewObject, authType: AuthType): Promise<string>;

/**
* download s3 object
*/
Expand Down Expand Up @@ -427,6 +432,48 @@ export class Objectt implements IObject {
}
}

public async getObjectPreviewUrl(configParam: TBaseGetPrivewObject, authType: AuthType) {
const { bucketName, objectName, queryMap } = configParam;
if (!isValidBucketName(bucketName)) {
throw new Error('Error bucket name');
}
if (!isValidObjectName(objectName)) {
throw new Error('Error object name');
}
const endpoint = await this.sp.getSPUrlByBucket(bucketName);

const path = '/' + encodePath(objectName);
const url = generateUrlByBucketName(endpoint, bucketName) + path;

const queryParams = new URLSearchParams();
for (const k in queryMap) {
queryParams.append(k, queryMap[k]);
}
queryParams.sort();

const queryRaw = queryParams.toString();

const canonicalRequest = [
METHOD_GET,
`/${encodePath(objectName)}`,
queryRaw,
new URL(url).host,
'\n',
].join('\n');

const unsignedMsg = getMsgToSign(utf8ToBytes(canonicalRequest));
let authorization = '';
if (authType.type === 'ECDSA') {
const sig = secpSign(unsignedMsg, authType.privateKey);
authorization = `GNFD1-ECDSA, Signature=${sig.slice(2)}`;
} else {
const sig = await signSignatureByEddsa(authType.seed, hexlify(unsignedMsg).slice(2));
authorization = `GNFD1-EDDSA,Signature=${sig}`;
}

return `${url}?Authorization=${encodeURIComponent(authorization)}&${queryRaw}`;
}

public async downloadFile(configParam: TBaseGetObject, authType: AuthType): Promise<void> {
try {
const { objectName } = configParam;
Expand Down
2 changes: 1 addition & 1 deletion packages/chain-sdk/src/clients/spclient/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ const SUPPORTED_HEADERS = [
];

// https://github.com/ethers-io/ethers.js/issues/823
const secpSign = (digestBz: Uint8Array, privateKey: string) => {
export const secpSign = (digestBz: Uint8Array, privateKey: string) => {
const signingKey = new SigningKey(privateKey);
const signature = signingKey.signDigest(digestBz);
let res = joinSignature(signature);
Expand Down
5 changes: 3 additions & 2 deletions packages/chain-sdk/src/clients/spclient/spApis/getObject.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { EMPTY_STRING_SHA256, METHOD_GET } from '@/constants';
import { ReqMeta } from '@/types';
import { generateUrlByBucketName } from '@/utils/s3';
import { encodePath } from '../auth';

// https://docs.bnbchain.org/greenfield-docs/docs/api/storgae-provider-rest/get_object
export const getGetObjectMetaInfo = async (
Expand All @@ -11,9 +12,9 @@ export const getGetObjectMetaInfo = async (
},
) => {
const { bucketName, objectName } = params;
const path = `/${objectName}`;
const path = `/${encodePath(objectName)}`;
const query = '';
const url = generateUrlByBucketName(endpoint, bucketName) + '/' + objectName;
const url = generateUrlByBucketName(endpoint, bucketName) + path;

const reqMeta: Partial<ReqMeta> = {
contentSHA256: EMPTY_STRING_SHA256,
Expand Down
7 changes: 7 additions & 0 deletions packages/chain-sdk/src/types/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,13 @@ export type TBaseGetObject = {
duration?: number;
};

export type TBaseGetPrivewObject = {
bucketName: string;
objectName: string;
duration?: number;
queryMap: Record<string, string>;
};

export type TListObjects = {
bucketName: string;
duration?: number;
Expand Down

0 comments on commit a3e6b47

Please sign in to comment.