@blocklet/did-space-js 1.1.0 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15,6 +15,7 @@ export declare class SpaceClient {
15
15
  */
16
16
  putNftObject(command: PutNftObjectCommand): Promise<PutNftObjectCommandOutput>;
17
17
  send<Input extends CommandInput, Output extends CommandOutput>(command: CommandProtocol<Input, Output>): Promise<Output>;
18
+ private getOptionsUrl;
18
19
  /**
19
20
  *
20
21
  *
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.SpaceClient = void 0;
7
+ const axios_1 = __importDefault(require("axios"));
7
8
  const is_url_1 = __importDefault(require("is-url"));
8
9
  const ufo_1 = require("ufo");
9
10
  const debug_1 = __importDefault(require("debug"));
@@ -15,7 +16,9 @@ class SpaceClient {
15
16
  options;
16
17
  constructor(options) {
17
18
  this.options = options;
18
- this.options.endpoint = this.normalizeEndpoint(this.options.endpoint);
19
+ this.options.endpoint = this.options.endpoint
20
+ ? this.normalizeEndpoint(this.options.endpoint)
21
+ : this.options.endpoint;
19
22
  }
20
23
  /**
21
24
  *
@@ -39,6 +42,7 @@ class SpaceClient {
39
42
  }
40
43
  async send(command) {
41
44
  try {
45
+ this.options.url = await this.getOptionsUrl();
42
46
  command.context = {
43
47
  ...command.context,
44
48
  ...this.options,
@@ -58,6 +62,18 @@ class SpaceClient {
58
62
  };
59
63
  }
60
64
  }
65
+ async getOptionsUrl() {
66
+ const url = this.options.url || 'https://www.didspaces.com';
67
+ const u = new URL(url);
68
+ const { data: blockletMeta } = await axios_1.default.get((0, ufo_1.joinURL)(u.origin, '/__blocklet__.js?type=json'), {
69
+ timeout: 5000,
70
+ });
71
+ const mountPoint = blockletMeta.componentMountPoints.find((x) => x.did === 'z8iZnaYxnkMD5AKRjTKiCb8pQr1ut8UantAcf')?.mountPoint;
72
+ if (!mountPoint) {
73
+ throw new Error(`MountPoint(${mountPoint}) not found in ${url}`);
74
+ }
75
+ return (0, ufo_1.joinURL)(u.origin, mountPoint);
76
+ }
61
77
  /**
62
78
  *
63
79
  *
@@ -20,7 +20,8 @@ class PostObjectsSyncCommand extends base_1.BaseCommand {
20
20
  return Promise.resolve(null);
21
21
  }
22
22
  async resolveMiddleware() {
23
- const url = (0, ufo_1.joinURL)(this.context.endpoint, 'audit-log');
23
+ const endpoint = this.getEndpoint();
24
+ const url = (0, ufo_1.joinURL)(endpoint, 'audit-log');
24
25
  const data = {
25
26
  actionType: constants_1.AuditLogAction.APP_OBJECTS_SYNC,
26
27
  data: {
@@ -20,7 +20,8 @@ class PutObjectsSyncCommand extends base_1.BaseCommand {
20
20
  return Promise.resolve(null);
21
21
  }
22
22
  async resolveMiddleware() {
23
- const url = (0, ufo_1.joinURL)(this.context.endpoint, 'audit-log');
23
+ const endpoint = this.getEndpoint();
24
+ const url = (0, ufo_1.joinURL)(endpoint, 'audit-log');
24
25
  const { id, status, reason, ...rest } = this.input;
25
26
  const body = {
26
27
  id,
@@ -24,7 +24,8 @@ class BackupBlockletCommand extends base_1.BaseCommand {
24
24
  let appBackup = null;
25
25
  try {
26
26
  // @note: 备份还原的时候我们希望使用 service url 来完成,不希望走 CDN
27
- this.context.endpoint = await (0, space_1.getSpaceServiceEndpoint)(this.context.endpoint);
27
+ const endpoint = this.getEndpoint();
28
+ this.context.endpoint = await (0, space_1.getSpaceServiceEndpoint)(endpoint);
28
29
  appBackup = (await new post_app_backup_1.PostAppBackupCommand({
29
30
  userDid: this.input.userDid,
30
31
  referrer: this.input.referrer,
@@ -21,7 +21,8 @@ class PostAppBackupCommand extends base_1.BaseCommand {
21
21
  return Promise.resolve(null);
22
22
  }
23
23
  async resolveMiddleware() {
24
- const url = (0, ufo_1.joinURL)(this.context.endpoint, 'backup');
24
+ const endpoint = this.getEndpoint();
25
+ const url = (0, ufo_1.joinURL)(endpoint, 'backup');
25
26
  const data = this.input;
26
27
  const method = 'POST';
27
28
  const { headers } = await (0, security_1.signRequest)({
@@ -21,7 +21,8 @@ class PutAppBackupCommand extends base_1.BaseCommand {
21
21
  return Promise.resolve(null);
22
22
  }
23
23
  async resolveMiddleware() {
24
- const url = (0, ufo_1.joinURL)(this.context.endpoint, 'backup');
24
+ const endpoint = this.getEndpoint();
25
+ const url = (0, ufo_1.joinURL)(endpoint, 'backup');
25
26
  const data = this.input;
26
27
  const method = 'PUT';
27
28
  const { headers } = await (0, security_1.signRequest)({
@@ -15,7 +15,14 @@ export declare abstract class BaseCommand<BaseCommandInput extends CommandInput,
15
15
  readonly context: SpaceClientOptions;
16
16
  constructor(input: BaseCommandInput, context?: SpaceClientOptions);
17
17
  abstract getAxiosRequestConfig(): Promise<AxiosRequestConfig | null>;
18
+ isAccessKeyVisit(): boolean;
18
19
  getKey(key: string, options?: GetKeyOptions): string;
20
+ protected assertEndpointRequired(): void;
21
+ protected getEndpoint({ strict }?: {
22
+ strict?: true | false;
23
+ }): string;
24
+ protected getRequestUrl(key: string, type?: string): string;
25
+ getRequestHeaders(extraHeaders?: Record<string, string>): Record<string, string>;
19
26
  request(): Promise<BaseCommandOutput>;
20
27
  resolveMiddleware(res?: AxiosResponse<BaseCommandOutput['data'], any>): Promise<BaseCommandOutput>;
21
28
  abort(): void;
@@ -9,6 +9,8 @@ const ufo_1 = require("ufo");
9
9
  const did_1 = require("@arcblock/did");
10
10
  const constants_1 = require("@did-space/constants");
11
11
  const stream_1 = require("stream");
12
+ const node_path_1 = require("node:path");
13
+ const wallet_1 = require("@ocap/wallet");
12
14
  const libs_1 = require("../libs");
13
15
  class BaseCommand extends stream_1.EventEmitter {
14
16
  input = {};
@@ -22,12 +24,78 @@ class BaseCommand extends stream_1.EventEmitter {
22
24
  }
23
25
  this.context.abortController = this.context.abortController ?? this.input.abortController ?? new AbortController();
24
26
  }
27
+ isAccessKeyVisit() {
28
+ return Boolean(this.context.auth && ('secretKey' in this.context.auth || 'authorization' in this.context.auth));
29
+ }
25
30
  getKey(key, options = { encode: true }) {
26
- const path = (0, isEmpty_1.default)(this.context.componentDid)
31
+ const path = (0, isEmpty_1.default)(this.context.componentDid) || this.isAccessKeyVisit()
27
32
  ? key
28
- : (0, ufo_1.joinURL)(constants_1.APP.COMPONENT.FOLDER_PREFIX, this.context.componentDid, key);
33
+ : (0, node_path_1.join)(constants_1.APP.COMPONENT.FOLDER_PREFIX, this.context.componentDid, key);
29
34
  return options.encode ? (0, libs_1.encode)(path) : path;
30
35
  }
36
+ assertEndpointRequired() {
37
+ const endpoint = this.context.endpoint || this.context?.auth?.endpoint;
38
+ if (!endpoint) {
39
+ if (this.context.auth || this.context.url) {
40
+ throw new Error('This interface requires endpoint authentication and cannot be accessed through auth and url parameters');
41
+ }
42
+ throw new Error('Endpoint is required for this operation');
43
+ }
44
+ }
45
+ getEndpoint({ strict } = {
46
+ strict: true,
47
+ }) {
48
+ const endpoint = this.context.endpoint || this.context?.auth?.endpoint;
49
+ if (endpoint) {
50
+ return endpoint;
51
+ }
52
+ if (strict) {
53
+ if (this.context.auth || this.context.url) {
54
+ throw new Error('This interface requires endpoint authentication and cannot be accessed through auth and url parameters');
55
+ }
56
+ throw new Error('Endpoint is required for this operation');
57
+ }
58
+ if (this.context.auth && this.context.url) {
59
+ return (0, ufo_1.joinURL)(this.context.url, '/api/space/default/app/default');
60
+ }
61
+ throw new Error('Please provide either an "endpoint" or both "auth" and "url" parameters to initialize the space client');
62
+ }
63
+ getRequestUrl(key, type = 'object') {
64
+ // 优先使用 context.endpoint
65
+ if (this.context.endpoint) {
66
+ return (0, ufo_1.joinURL)(this.context.endpoint, type, this.getKey(key));
67
+ }
68
+ // 检查 auth 中是否有 endpoint(类型安全检查)
69
+ if (this.context.auth && 'endpoint' in this.context.auth && this.context.auth.endpoint) {
70
+ return (0, ufo_1.joinURL)(this.context.auth.endpoint, type, this.getKey(key));
71
+ }
72
+ // 使用 auth + url 组合
73
+ if (this.context.auth && this.context.url) {
74
+ if ('secretKey' in this.context.auth) {
75
+ this.context.wallet = (0, wallet_1.fromSecretKey)(this.context.auth.secretKey);
76
+ }
77
+ return (0, ufo_1.joinURL)(this.context.url, '/api/space/default/app/default', type, this.getKey(key));
78
+ }
79
+ throw new Error('Please provide either an "endpoint" or both "auth" and "url" parameters to initialize the space client');
80
+ }
81
+ getRequestHeaders(extraHeaders = {}) {
82
+ const headers = {
83
+ [constants_1.HEADERS.X_SIGNATURE_VERSION]: '2',
84
+ };
85
+ if (this.context.auth && 'authorization' in this.context.auth) {
86
+ headers[constants_1.HEADERS.X_AUTHORIZATION] = this.context.auth.authorization;
87
+ // 此处为了兼容标准的认证方式
88
+ headers.authorization = this.context.auth.authorization;
89
+ }
90
+ if (this.context.auth && 'secretKey' in this.context.auth) {
91
+ const accessWallet = (0, wallet_1.fromSecretKey)(this.context.auth.secretKey);
92
+ headers[constants_1.HEADERS.X_ACCESS_KEY] = accessWallet.address;
93
+ }
94
+ return {
95
+ ...headers,
96
+ ...extraHeaders,
97
+ };
98
+ }
31
99
  async request() {
32
100
  const axiosRequestConfig = await this.getAxiosRequestConfig();
33
101
  if (!axiosRequestConfig) {
@@ -24,7 +24,8 @@ class IncrementalBackupBlockletCommand extends base_1.BaseCommand {
24
24
  let incrementalSyncPushInstance = null;
25
25
  try {
26
26
  // @note: 备份还原的时候我们希望使用 service url 来完成,不希望走 CDN
27
- this.context.endpoint = await (0, space_1.getSpaceServiceEndpoint)(this.context.endpoint);
27
+ const endpoint = this.getEndpoint();
28
+ this.context.endpoint = await (0, space_1.getSpaceServiceEndpoint)(endpoint);
28
29
  appBackup = (await new backup_1.PostAppBackupCommand({
29
30
  userDid: this.input.userDid,
30
31
  referrer: this.input.referrer,
@@ -25,7 +25,7 @@ class PutNftObjectCommand extends base_1.BaseCommand {
25
25
  (0, fs_extra_1.ensureDirSync)((0, path_1.dirname)(this.tempCacheObjectKey));
26
26
  }
27
27
  getUrl() {
28
- const { endpoint } = this.context;
28
+ const endpoint = this.getEndpoint();
29
29
  const didObjectUrl = (0, ufo_1.joinURL)(endpoint, 'did-objects', (0, libs_1.encode)(this.input.display.key));
30
30
  const didObjectURL = new URL(didObjectUrl);
31
31
  didObjectURL.searchParams.set('did', this.input.did);
@@ -100,6 +100,9 @@ class PutNftObjectCommand extends base_1.BaseCommand {
100
100
  const method = 'PUT';
101
101
  const { streamForUpload, streamForHash } = await this.getStreams();
102
102
  const didDocument = await this.getDidDocument(streamForHash);
103
+ const requestHeaders = this.getRequestHeaders({
104
+ [constants_1.HEADERS.X_HASH]: this.tempCacheObjectHash,
105
+ });
103
106
  const formData = new form_data_1.default();
104
107
  formData.append('data', streamForUpload);
105
108
  formData.append('didDocument', JSON.stringify(didDocument));
@@ -107,10 +110,7 @@ class PutNftObjectCommand extends base_1.BaseCommand {
107
110
  url,
108
111
  method,
109
112
  data: formData,
110
- headers: {
111
- [constants_1.HEADERS.X_HASH]: this.tempCacheObjectHash,
112
- [constants_1.HEADERS.X_SIGNATURE_VERSION]: '2',
113
- },
113
+ headers: requestHeaders,
114
114
  wallet: this.context.wallet,
115
115
  delegation: this.context.delegation,
116
116
  });
@@ -1,23 +1,21 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DeleteObjectCommand = void 0;
4
- const ufo_1 = require("ufo");
5
- const constants_1 = require("@did-space/constants");
6
4
  const base_1 = require("../base");
7
5
  const security_1 = require("../../security");
8
6
  class DeleteObjectCommand extends base_1.BaseCommand {
9
7
  async getAxiosRequestConfig() {
10
- const url = (0, ufo_1.joinURL)(this.context.endpoint, 'object', this.getKey(this.input.key));
8
+ const url = this.getRequestUrl(this.input.key);
9
+ const requestHeaders = await this.getRequestHeaders();
11
10
  const method = 'DELETE';
12
11
  const { headers } = await (0, security_1.signRequest)({
13
12
  url,
14
13
  method,
15
14
  data: {},
16
- headers: {
17
- [constants_1.HEADERS.X_SIGNATURE_VERSION]: '2',
18
- },
15
+ headers: requestHeaders,
19
16
  wallet: this.context.wallet,
20
17
  delegation: this.context.delegation,
18
+ throwError: false,
21
19
  });
22
20
  const axiosRequestConfig = {
23
21
  url,
@@ -1,23 +1,21 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.GetObjectCommand = void 0;
4
- const ufo_1 = require("ufo");
5
- const constants_1 = require("@did-space/constants");
6
4
  const base_1 = require("../base");
7
5
  const security_1 = require("../../security");
8
6
  class GetObjectCommand extends base_1.BaseCommand {
9
7
  async getAxiosRequestConfig() {
10
- const url = (0, ufo_1.joinURL)(this.context.endpoint, 'object', this.getKey(this.input.key));
8
+ const url = this.getRequestUrl(this.input.key);
9
+ const requestHeaders = this.getRequestHeaders();
11
10
  const method = 'GET';
12
11
  const { headers } = await (0, security_1.signRequest)({
13
12
  url,
14
13
  method,
15
14
  data: {},
16
- headers: {
17
- [constants_1.HEADERS.X_SIGNATURE_VERSION]: '2',
18
- },
15
+ headers: requestHeaders,
19
16
  wallet: this.context.wallet,
20
17
  delegation: this.context.delegation,
18
+ throwError: false,
21
19
  });
22
20
  const config = {
23
21
  url,
@@ -1,23 +1,21 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ListObjectCommand = void 0;
4
- const constants_1 = require("@did-space/constants");
5
- const ufo_1 = require("ufo");
6
4
  const base_1 = require("../base");
7
5
  const security_1 = require("../../security");
8
6
  class ListObjectCommand extends base_1.BaseCommand {
9
7
  async getAxiosRequestConfig() {
10
- const url = (0, ufo_1.joinURL)(this.context.endpoint, 'list', this.getKey(this.input.key));
8
+ const url = this.getRequestUrl(this.input.key, 'list/');
9
+ const requestHeaders = await this.getRequestHeaders();
11
10
  const method = 'GET';
12
11
  const { headers } = await (0, security_1.signRequest)({
13
12
  url,
14
13
  method,
15
14
  data: {},
16
- headers: {
17
- [constants_1.HEADERS.X_SIGNATURE_VERSION]: '2',
18
- },
15
+ headers: requestHeaders,
19
16
  wallet: this.context.wallet,
20
17
  delegation: this.context.delegation,
18
+ throwError: false,
21
19
  });
22
20
  const config = {
23
21
  url,
@@ -4,12 +4,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.ListObjectsCommand = void 0;
7
- const constants_1 = require("@did-space/constants");
8
7
  const ufo_1 = require("ufo");
9
8
  const debug_1 = __importDefault(require("debug"));
10
9
  const pick_1 = __importDefault(require("lodash/pick"));
11
- const querystring_1 = __importDefault(require("querystring"));
12
- const isEmpty_1 = __importDefault(require("lodash/isEmpty"));
13
10
  const base_1 = require("../base");
14
11
  const security_1 = require("../../security");
15
12
  const { name, version } = require('../../../package.json');
@@ -18,19 +15,18 @@ class ListObjectsCommand extends base_1.BaseCommand {
18
15
  async getAxiosRequestConfig() {
19
16
  // TODO: 此处应该有 joi 校验
20
17
  const options = (0, pick_1.default)(this.input, ['recursive', 'ignoreDirectories']);
21
- const queryParams = (0, isEmpty_1.default)(options) ? '' : `?${querystring_1.default.stringify(options)}`;
22
- const url = (0, ufo_1.joinURL)(this.context.endpoint, 'lists', this.getKey(this.input.key), queryParams);
18
+ const url = (0, ufo_1.withQuery)(await this.getRequestUrl(this.input.key, 'lists/'), options);
19
+ const requestHeaders = this.getRequestHeaders();
23
20
  const method = 'GET';
24
21
  debug('getAxiosRequestConfig.$url', url);
25
22
  const { headers } = await (0, security_1.signRequest)({
26
23
  url,
27
24
  method,
28
25
  data: {},
29
- headers: {
30
- [constants_1.HEADERS.X_SIGNATURE_VERSION]: '2',
31
- },
26
+ headers: requestHeaders,
32
27
  wallet: this.context.wallet,
33
28
  delegation: this.context.delegation,
29
+ throwError: false,
34
30
  });
35
31
  const config = {
36
32
  url,
@@ -4,10 +4,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.PutObjectCommand = void 0;
7
- const constants_1 = require("@did-space/constants");
8
7
  const form_data_1 = __importDefault(require("form-data"));
9
8
  const isEmpty_1 = __importDefault(require("lodash/isEmpty"));
10
- const ufo_1 = require("ufo");
11
9
  const libs_1 = require("../../libs");
12
10
  const security_1 = require("../../security");
13
11
  const base_1 = require("../base");
@@ -16,7 +14,8 @@ class PutObjectCommand extends base_1.BaseCommand {
16
14
  return Promise.resolve(null);
17
15
  }
18
16
  async resolveMiddleware() {
19
- const url = (0, ufo_1.joinURL)(this.context.endpoint, 'object', this.getKey(this.input.key));
17
+ const url = this.getRequestUrl(this.input.key);
18
+ const requestHeaders = this.getRequestHeaders();
20
19
  const method = 'PUT';
21
20
  const body = new form_data_1.default();
22
21
  if (!(0, isEmpty_1.default)(this.input.data)) {
@@ -32,11 +31,10 @@ class PutObjectCommand extends base_1.BaseCommand {
32
31
  url,
33
32
  method,
34
33
  data: body,
35
- headers: {
36
- [constants_1.HEADERS.X_SIGNATURE_VERSION]: '2',
37
- },
34
+ headers: requestHeaders,
38
35
  wallet: this.context.wallet,
39
36
  delegation: this.context.delegation,
37
+ throwError: false,
40
38
  });
41
39
  const optionsOfTextResponseBody = {
42
40
  method,
@@ -2,8 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DeletePreviewObjectCommand = void 0;
4
4
  const path_1 = require("path");
5
- const ufo_1 = require("ufo");
6
- const constants_1 = require("@did-space/constants");
7
5
  const meta_1 = require("../../meta");
8
6
  const base_1 = require("../base");
9
7
  const security_1 = require("../../security");
@@ -13,16 +11,16 @@ class DeletePreviewObjectCommand extends base_1.BaseCommand {
13
11
  return Promise.resolve(null);
14
12
  }
15
13
  async resolveMiddleware() {
14
+ this.assertEndpointRequired();
16
15
  Object.assign(this.input, await meta_1.GetPreviewObjectCommandInputSchema.parseAsync(this.input));
17
- const url = (0, ufo_1.joinURL)(this.context.endpoint, 'object', this.getKey((0, path_1.join)(this.input.key, '.meta/preview.yml')));
16
+ const url = this.getRequestUrl((0, path_1.join)(this.input.key, '.meta/preview.yml'));
17
+ const requestHeaders = this.getRequestHeaders();
18
18
  const method = 'DELETE';
19
19
  const { headers } = await (0, security_1.signRequest)({
20
20
  url,
21
21
  method,
22
22
  data: {},
23
- headers: {
24
- [constants_1.HEADERS.X_SIGNATURE_VERSION]: '2',
25
- },
23
+ headers: requestHeaders,
26
24
  wallet: this.context.wallet,
27
25
  delegation: this.context.delegation,
28
26
  });
@@ -6,8 +6,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.GetPreviewObjectCommand = void 0;
7
7
  const js_yaml_1 = __importDefault(require("js-yaml"));
8
8
  const path_1 = require("path");
9
- const ufo_1 = require("ufo");
10
- const constants_1 = require("@did-space/constants");
11
9
  const meta_1 = require("../../meta");
12
10
  const base_1 = require("../base");
13
11
  const security_1 = require("../../security");
@@ -17,16 +15,16 @@ class GetPreviewObjectCommand extends base_1.BaseCommand {
17
15
  return Promise.resolve(null);
18
16
  }
19
17
  async resolveMiddleware() {
18
+ this.assertEndpointRequired();
20
19
  Object.assign(this.input, await meta_1.GetPreviewObjectCommandInputSchema.parseAsync(this.input));
21
- const url = (0, ufo_1.joinURL)(this.context.endpoint, 'object', this.getKey((0, path_1.join)(this.input.key, '.meta/preview.yml')));
20
+ const url = this.getRequestUrl((0, path_1.join)(this.input.key, '.meta/preview.yml'));
21
+ const requestHeaders = this.getRequestHeaders();
22
22
  const method = 'GET';
23
23
  const { headers } = await (0, security_1.signRequest)({
24
24
  url,
25
25
  method,
26
26
  data: {},
27
- headers: {
28
- [constants_1.HEADERS.X_SIGNATURE_VERSION]: '2',
29
- },
27
+ headers: requestHeaders,
30
28
  wallet: this.context.wallet,
31
29
  delegation: this.context.delegation,
32
30
  });
@@ -4,10 +4,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.PutPreviewObjectCommand = void 0;
7
- const constants_1 = require("@did-space/constants");
8
7
  const form_data_1 = __importDefault(require("form-data"));
9
8
  const isEmpty_1 = __importDefault(require("lodash/isEmpty"));
10
- const ufo_1 = require("ufo");
11
9
  const js_yaml_1 = __importDefault(require("js-yaml"));
12
10
  const path_1 = require("path");
13
11
  const p_all_1 = __importDefault(require("p-all"));
@@ -30,8 +28,10 @@ class PutPreviewObjectCommand extends base_1.BaseCommand {
30
28
  }
31
29
  }
32
30
  async resolveMiddleware() {
31
+ this.assertEndpointRequired();
33
32
  await this.normalize();
34
- const url = (0, ufo_1.joinURL)(this.context.endpoint, 'object', this.getKey((0, path_1.join)(this.input.key, '.meta/preview.yml')));
33
+ const url = this.getRequestUrl((0, path_1.join)(this.input.key, '.meta/preview.yml'));
34
+ const requestHeaders = this.getRequestHeaders();
35
35
  const method = 'PUT';
36
36
  const data = new form_data_1.default();
37
37
  // @see: https://stackoverflow.com/a/43914175
@@ -45,9 +45,7 @@ class PutPreviewObjectCommand extends base_1.BaseCommand {
45
45
  url,
46
46
  method,
47
47
  data,
48
- headers: {
49
- [constants_1.HEADERS.X_SIGNATURE_VERSION]: '2',
50
- },
48
+ headers: requestHeaders,
51
49
  wallet: this.context.wallet,
52
50
  delegation: this.context.delegation,
53
51
  });
@@ -15,7 +15,8 @@ class PostAppRestoreCommand extends base_1.BaseCommand {
15
15
  if (error) {
16
16
  throw error;
17
17
  }
18
- const url = (0, ufo_1.joinURL)(this.context.endpoint, 'restore');
18
+ const endpoint = this.getEndpoint();
19
+ const url = (0, ufo_1.joinURL)(endpoint, 'restore');
19
20
  const data = this.input;
20
21
  const method = 'POST';
21
22
  const { headers } = await (0, security_1.signRequest)({
@@ -19,7 +19,8 @@ class PutAppRestoreCommand extends base_1.BaseCommand {
19
19
  return Promise.resolve(null);
20
20
  }
21
21
  async resolveMiddleware() {
22
- const url = (0, ufo_1.joinURL)(this.context.endpoint, 'restore');
22
+ const endpoint = this.getEndpoint();
23
+ const url = (0, ufo_1.joinURL)(endpoint, 'restore');
23
24
  const data = this.input;
24
25
  const method = 'PUT';
25
26
  const { headers } = await (0, security_1.signRequest)({
@@ -23,8 +23,9 @@ class RestoreBlockletCommand extends base_1.BaseCommand {
23
23
  async resolveMiddleware() {
24
24
  let appRestore = null;
25
25
  try {
26
+ const endpoint = this.getEndpoint();
26
27
  // @note: 备份还原的时候我们希望使用 service url 来完成,不希望走 CDN
27
- this.context.endpoint = await (0, space_1.getSpaceServiceEndpoint)(this.context.endpoint);
28
+ this.context.endpoint = await (0, space_1.getSpaceServiceEndpoint)(endpoint);
28
29
  appRestore = (await new post_app_restore_1.PostAppRestoreCommand({
29
30
  userDid: this.input.userDid,
30
31
  referrer: this.input.referrer,
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.HeadSpaceCommand = void 0;
4
- const constants_1 = require("@did-space/constants");
5
4
  const ufo_1 = require("ufo");
6
5
  const libs_1 = require("../../libs");
7
6
  const security_1 = require("../../security");
@@ -12,25 +11,33 @@ class HeadSpaceCommand extends base_1.BaseCommand {
12
11
  return Promise.resolve(null);
13
12
  }
14
13
  getUrl() {
15
- const array = this.context.endpoint.split('/');
16
- const lastIndexOfApp = array.lastIndexOf('app');
17
- const url = array.filter((_, i) => i < lastIndexOfApp).join('/');
18
- return (0, ufo_1.withQuery)(url, {
19
- ...this.input,
20
- });
14
+ if (this.context.endpoint) {
15
+ const array = this.context.endpoint.split('/');
16
+ const lastIndexOfApp = array.lastIndexOf('app');
17
+ const url = array.filter((_, i) => i < lastIndexOfApp).join('/');
18
+ return (0, ufo_1.withQuery)(url, {
19
+ ...this.input,
20
+ });
21
+ }
22
+ if (this.context.auth && this.context.url) {
23
+ return (0, ufo_1.withQuery)((0, ufo_1.joinURL)(this.context.url, '/api/space/default'), {
24
+ ...this.input,
25
+ });
26
+ }
27
+ throw new Error('Invalid configuration: Please provide either "endpoint" or both "auth" and "url" parameters to initialize the space client');
21
28
  }
22
29
  async resolveMiddleware() {
23
30
  const url = this.getUrl();
31
+ const requestHeaders = this.getRequestHeaders();
24
32
  const method = 'HEAD';
25
33
  const { headers } = await (0, security_1.signRequest)({
26
34
  url,
27
35
  method,
28
36
  data: {},
29
- headers: {
30
- [constants_1.HEADERS.X_SIGNATURE_VERSION]: '2',
31
- },
37
+ headers: requestHeaders,
32
38
  wallet: this.context.wallet,
33
39
  delegation: this.context.delegation,
40
+ throwError: false,
34
41
  });
35
42
  const optionsOfTextResponseBody = {
36
43
  method,
@@ -46,7 +46,8 @@ class SyncFolderPushCommand extends sync_folder_base_1.SyncFolderBaseCommand {
46
46
  async request() {
47
47
  try {
48
48
  // @note: 备份还原的时候我们希望使用 service url 来完成,不希望走 CDN
49
- this.context.endpoint = await (0, libs_1.getSpaceServiceEndpoint)(this.context.endpoint);
49
+ const endpoint = this.getEndpoint();
50
+ this.context.endpoint = await (0, libs_1.getSpaceServiceEndpoint)(endpoint);
50
51
  if (!this.objectsSync) {
51
52
  this.objectsSync = await this.createObjectsSyncAuditLog();
52
53
  }
@@ -37,6 +37,35 @@ export interface CommandOutput<D = any> {
37
37
  data: D;
38
38
  response?: Response;
39
39
  }
40
+ export interface SpaceClientOptionsEndpointAuth {
41
+ /**
42
+ *
43
+ * @description spaces endpoint
44
+ * @example https://cedcaa27-znkomkclejcfbjaxw9knzzebmzmqrxjnn9bb.did.abtnet.io/api/space/z3T6EHN7sLhH5cty1PshDeSipeG7JNxEVaRFS/app/zNKabhhwdvVmXjtRTtSHk2YQz4pdDPStV289/
45
+ * @type {string}
46
+ * @memberof SpaceClientOptionsEndpointAuth
47
+ */
48
+ endpoint: string;
49
+ }
50
+ export interface SpaceClientOptionsAuthorizationAuth {
51
+ /**
52
+ *
53
+ * @description authorization token
54
+ * @type {string}
55
+ * @memberof SpaceClientOptionsAuthorizationAuth
56
+ */
57
+ authorization: string;
58
+ }
59
+ export interface SpaceClientOptionsAccessKeyAuth {
60
+ /**
61
+ *
62
+ * @description secret key
63
+ * @type {string}
64
+ * @memberof SpaceClientOptionsAccessKeyAuth
65
+ */
66
+ secretKey: string;
67
+ }
68
+ export type SpaceClientOptionsAuth = SpaceClientOptionsEndpointAuth | SpaceClientOptionsAuthorizationAuth | SpaceClientOptionsAccessKeyAuth;
40
69
  export interface SpaceClientOptions {
41
70
  /**
42
71
  *
@@ -46,13 +75,29 @@ export interface SpaceClientOptions {
46
75
  */
47
76
  wallet: WalletObject;
48
77
  /**
49
- *
78
+ * @recommend use `auth` field instead
50
79
  * @description spaces endpoint
51
80
  * @example https://cedcaa27-znkomkclejcfbjaxw9knzzebmzmqrxjnn9bb.did.abtnet.io/api/space/z3T6EHN7sLhH5cty1PshDeSipeG7JNxEVaRFS/app/zNKabhhwdvVmXjtRTtSHk2YQz4pdDPStV289/
52
81
  * @type {string}
53
82
  * @memberof SpaceClientOptions
54
83
  */
55
- endpoint: string;
84
+ endpoint?: string;
85
+ /**
86
+ *
87
+ * @description spaces 的访问地址,需要具体到 mount point
88
+ * @example https://www.didspaces.com/app
89
+ * @default https://www.didspaces.com/app
90
+ * @type {string}
91
+ * @memberof SpaceClientOptions
92
+ */
93
+ url?: string;
94
+ /**
95
+ *
96
+ * @description 认证信息
97
+ * @type {SpaceClientOptionsAuth}
98
+ * @memberof SpaceClientOptions
99
+ */
100
+ auth?: SpaceClientOptionsAuth;
56
101
  /**
57
102
  * A jwt token that certifies the requester is authorized to access the space
58
103
  * @description delegation token
@@ -1,14 +1,15 @@
1
1
  import { WalletObject } from '@ocap/wallet';
2
2
  type Method = import('got/dist/source').Method;
3
3
  type Headers = {
4
- 'x-app-did': string;
5
- 'x-app-pk': string;
6
- 'x-app-token': string;
7
- 'x-app-delegation': string;
4
+ 'x-app-did'?: string;
5
+ 'x-app-pk'?: string;
6
+ 'x-app-token'?: string;
7
+ 'x-app-delegation'?: string;
8
8
  [key: string]: any;
9
9
  };
10
+ export declare function isAccessKeyVisit(headers: Headers): boolean;
10
11
  declare function verifyDelegation(delegation: string, delegatee: WalletObject): Promise<string>;
11
- declare function signRequest({ url, method, data, headers, wallet, delegation, }: {
12
+ declare function signRequest({ url, method, data, headers, wallet, delegation, throwError, }: {
12
13
  url: string;
13
14
  method: Method;
14
15
  data: any;
@@ -17,6 +18,7 @@ declare function signRequest({ url, method, data, headers, wallet, delegation, }
17
18
  };
18
19
  wallet: WalletObject;
19
20
  delegation?: string;
21
+ throwError?: true | false;
20
22
  }): Promise<{
21
23
  url: string;
22
24
  method: Method;
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.verifyDelegation = exports.verifyRequest = exports.signRequest = void 0;
6
+ exports.verifyDelegation = exports.verifyRequest = exports.signRequest = exports.isAccessKeyVisit = void 0;
7
7
  const jwt_1 = require("@arcblock/jwt");
8
8
  const isEmpty_1 = __importDefault(require("lodash/isEmpty"));
9
9
  const isObject_1 = __importDefault(require("lodash/isObject"));
@@ -15,7 +15,12 @@ const mcrypto_1 = require("@ocap/mcrypto");
15
15
  const wallet_1 = require("@ocap/wallet");
16
16
  const is_url_1 = __importDefault(require("is-url"));
17
17
  const debug_1 = __importDefault(require("debug"));
18
+ const constants_1 = require("@did-space/constants");
18
19
  const debug = (0, debug_1.default)('request-signature');
20
+ function isAccessKeyVisit(headers) {
21
+ return Boolean(headers[constants_1.HEADERS.X_AUTHORIZATION] || headers[constants_1.HEADERS.X_ACCESS_KEY]);
22
+ }
23
+ exports.isAccessKeyVisit = isAccessKeyVisit;
19
24
  // verifyDelegation to get actual appDid
20
25
  async function verifyDelegation(delegation, delegatee) {
21
26
  const { from, to, userPk, permissions } = (0, jwt_1.decode)(delegation);
@@ -52,18 +57,32 @@ function getSignUrl({ url }) {
52
57
  }
53
58
  return signUrl;
54
59
  }
55
- async function signRequest({ url, method, data, headers, wallet, delegation, }) {
60
+ async function signRequest({ url, method, data, headers, wallet, delegation, throwError = true, }) {
61
+ let error = null;
56
62
  if ((0, isEmpty_1.default)(url)) {
57
- throw new Error(`url(${url}) cannot be empty`);
63
+ error = new Error(`url(${url}) cannot be empty`);
58
64
  }
59
65
  if (!(0, isObject_1.default)(data)) {
60
- throw new Error(`data(${data}) is not an object`);
66
+ error = new Error(`data(${data}) is not an object`);
67
+ }
68
+ if (!wallet) {
69
+ error = new Error('wallet is required');
70
+ }
71
+ if (error) {
72
+ if (throwError) {
73
+ throw error;
74
+ }
75
+ return {
76
+ url,
77
+ method,
78
+ data,
79
+ headers,
80
+ };
61
81
  }
62
82
  if (delegation) {
63
83
  await verifyDelegation(delegation, wallet);
64
84
  }
65
85
  const signUrl = getSignUrl({ url });
66
- // @ts-expect-error
67
86
  const signData = (0, isString_1.default)(data?.data) ? data : {};
68
87
  const token = (0, jwt_1.sign)(wallet.address, wallet.secretKey, {
69
88
  digest: mcrypto_1.Hasher.SHA3.hash256((0, json_stable_stringify_1.default)({
@@ -74,12 +93,13 @@ async function signRequest({ url, method, data, headers, wallet, delegation, })
74
93
  // 请求在一个小时内都是不过期的
75
94
  exp: String(Math.floor(Date.now() / 1000) + 60 * 60),
76
95
  });
96
+ const appDid = wallet.address;
77
97
  return {
78
98
  url,
79
99
  method,
80
100
  data,
81
101
  headers: Object.assign(headers ?? {}, {
82
- 'x-app-did': wallet.address,
102
+ 'x-app-did': appDid,
83
103
  'x-app-pk': wallet.publicKey,
84
104
  'x-app-token': token,
85
105
  'x-app-delegation': delegation || '',
@@ -101,7 +121,7 @@ async function verifyRequest({ url, method, data, headers, }) {
101
121
  if ((0, isUndefined_1.default)(appToken)) {
102
122
  throw new Error('x-app-token must be present in request headers');
103
123
  }
104
- if (!(0, did_1.isValid)(appDid)) {
124
+ if (!(0, did_1.isValid)(appDid) && !isAccessKeyVisit(headers)) {
105
125
  throw new Error(`appDid(${appDid}) must be a valid did`);
106
126
  }
107
127
  if (!(0, did_1.isFromPublicKey)(appDid, appPk)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocklet/did-space-js",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -32,9 +32,9 @@
32
32
  "@arcblock/did": "^1.20.15",
33
33
  "@arcblock/jwt": "^1.20.15",
34
34
  "@arcblock/validator": "^1.20.15",
35
- "@blocklet/env": "^1.16.44",
36
- "@did-space/constants": "^1.1.0",
37
- "@did-space/core": "^1.1.0",
35
+ "@blocklet/env": "^1.16.45",
36
+ "@did-space/constants": "^1.1.2",
37
+ "@did-space/core": "^1.1.2",
38
38
  "@ocap/mcrypto": "^1.20.15",
39
39
  "@ocap/util": "^1.20.15",
40
40
  "@ocap/wallet": "^1.20.15",
@@ -78,5 +78,5 @@
78
78
  "ts-jest": "^28.0.8",
79
79
  "typescript": "^4.9.5"
80
80
  },
81
- "gitHead": "5b93f14e6011e4d81663549078127e4b927c5f12"
81
+ "gitHead": "922e4725da8965f66033b82f9ba28d1d691f9cc5"
82
82
  }