@did-space/s3-driver 0.2.5
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.
- package/README.md +1 -0
- package/dist/config/index.d.ts +31 -0
- package/dist/config/index.js +152 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +19 -0
- package/dist/operator/index.d.ts +74 -0
- package/dist/operator/index.js +435 -0
- package/dist/s3/index.d.ts +1 -0
- package/dist/s3/index.js +17 -0
- package/dist/s3/options.d.ts +6 -0
- package/dist/s3/options.js +2 -0
- package/dist/s3/s3.d.ts +14 -0
- package/dist/s3/s3.js +49 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +17 -0
- package/dist/utils/stream-to-string.d.ts +1 -0
- package/dist/utils/stream-to-string.js +10 -0
- package/package.json +65 -0
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# DID Spaces Core
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { SpaceConfigProtocol, SpaceConfig, PermissionOptions } from '@did-space/core';
|
|
2
|
+
import { S3Client } from '@aws-sdk/client-s3';
|
|
3
|
+
import { S3DriverOptions } from '../s3/options';
|
|
4
|
+
export declare class S3SpaceConfig implements SpaceConfigProtocol {
|
|
5
|
+
readonly key: string;
|
|
6
|
+
readonly s3Client: S3Client;
|
|
7
|
+
readonly options: S3DriverOptions;
|
|
8
|
+
constructor(options: S3DriverOptions);
|
|
9
|
+
createConfig(spaceConfig: SpaceConfig): Promise<void>;
|
|
10
|
+
destroyConfig(): Promise<void>;
|
|
11
|
+
set<T = any>(key: string, value: T): Promise<void>;
|
|
12
|
+
get<T = any>(key: string, defaultValue?: T): Promise<T>;
|
|
13
|
+
private getPermission;
|
|
14
|
+
/**
|
|
15
|
+
*
|
|
16
|
+
* @see https://blog.csdn.net/a1173537204/article/details/89765932
|
|
17
|
+
* @private
|
|
18
|
+
* @param {PermissionOptions} { fromAppDid, toAppDid }
|
|
19
|
+
* @param {number} permission
|
|
20
|
+
* @param {boolean} status
|
|
21
|
+
* @return {*} {Promise<void>}
|
|
22
|
+
* @memberof S3SpaceConfig
|
|
23
|
+
*/
|
|
24
|
+
private setPermission;
|
|
25
|
+
setListable(options: PermissionOptions, status: boolean): Promise<void>;
|
|
26
|
+
setReadable(options: PermissionOptions, status: boolean): Promise<void>;
|
|
27
|
+
setWritable(options: PermissionOptions, status: boolean): Promise<void>;
|
|
28
|
+
isListable(options: PermissionOptions): Promise<boolean>;
|
|
29
|
+
isReadable(options: PermissionOptions): Promise<boolean>;
|
|
30
|
+
isWritable(options: PermissionOptions): Promise<boolean>;
|
|
31
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.S3SpaceConfig = void 0;
|
|
16
|
+
/* eslint-disable no-bitwise */
|
|
17
|
+
const js_yaml_1 = __importDefault(require("js-yaml"));
|
|
18
|
+
const core_1 = require("@did-space/core");
|
|
19
|
+
const client_s3_1 = require("@aws-sdk/client-s3");
|
|
20
|
+
const path_1 = __importDefault(require("path"));
|
|
21
|
+
const utils_1 = require("../utils");
|
|
22
|
+
class S3SpaceConfig {
|
|
23
|
+
constructor(options) {
|
|
24
|
+
this.options = options;
|
|
25
|
+
this.key = path_1.default.join(this.options.root, 'config.yml');
|
|
26
|
+
this.s3Client = new client_s3_1.S3Client(this.options.s3ClientConfig);
|
|
27
|
+
}
|
|
28
|
+
createConfig(spaceConfig) {
|
|
29
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
30
|
+
const { error } = core_1.SpaceConfigSchema.validate(spaceConfig);
|
|
31
|
+
if (error) {
|
|
32
|
+
core_1.logger.error('S3SpaceConfig.createConfig', error);
|
|
33
|
+
throw error;
|
|
34
|
+
}
|
|
35
|
+
let exists = false;
|
|
36
|
+
try {
|
|
37
|
+
const data = yield this.s3Client.send(new client_s3_1.HeadObjectCommand({
|
|
38
|
+
Bucket: this.options.bucket,
|
|
39
|
+
Key: this.key,
|
|
40
|
+
}));
|
|
41
|
+
exists = data.$metadata.httpStatusCode === 200;
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
core_1.logger.error('S3SpaceConfig.createConfig', err);
|
|
45
|
+
}
|
|
46
|
+
if (exists) {
|
|
47
|
+
throw new Error('The configuration has been initialized and cannot be modified');
|
|
48
|
+
}
|
|
49
|
+
yield this.s3Client.send(new client_s3_1.PutObjectCommand({
|
|
50
|
+
Bucket: this.options.bucket,
|
|
51
|
+
Key: this.key,
|
|
52
|
+
Body: js_yaml_1.default.dump(spaceConfig),
|
|
53
|
+
}));
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
// @see: https://stackoverflow.com/questions/20207063/how-can-i-delete-folder-on-s3-with-node-js
|
|
57
|
+
// FIXME: @yejianchao 目前只能删除空文件夹
|
|
58
|
+
destroyConfig() {
|
|
59
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
60
|
+
return undefined;
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
set(key, value) {
|
|
64
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
65
|
+
const output = yield this.s3Client.send(new client_s3_1.GetObjectCommand({
|
|
66
|
+
Bucket: this.options.bucket,
|
|
67
|
+
Key: this.key,
|
|
68
|
+
}));
|
|
69
|
+
const data = js_yaml_1.default.load(yield (0, utils_1.streamToString)(output.Body));
|
|
70
|
+
data[key] = value;
|
|
71
|
+
yield this.s3Client.send(new client_s3_1.PutObjectCommand({
|
|
72
|
+
Bucket: this.options.bucket,
|
|
73
|
+
Key: this.key,
|
|
74
|
+
Body: js_yaml_1.default.dump(data),
|
|
75
|
+
}));
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
get(key, defaultValue = undefined) {
|
|
79
|
+
var _a;
|
|
80
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
81
|
+
const output = yield this.s3Client.send(new client_s3_1.GetObjectCommand({
|
|
82
|
+
Bucket: this.options.bucket,
|
|
83
|
+
Key: this.key,
|
|
84
|
+
}));
|
|
85
|
+
const data = js_yaml_1.default.load(yield (0, utils_1.streamToString)(output.Body));
|
|
86
|
+
return (_a = data[key]) !== null && _a !== void 0 ? _a : defaultValue;
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
getPermission({ fromAppDid, toAppDid }) {
|
|
90
|
+
var _a;
|
|
91
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
92
|
+
const permissions = yield this.get('permissions');
|
|
93
|
+
return ((_a = permissions === null || permissions === void 0 ? void 0 : permissions[fromAppDid]) === null || _a === void 0 ? void 0 : _a[toAppDid]) || 0;
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
*
|
|
98
|
+
* @see https://blog.csdn.net/a1173537204/article/details/89765932
|
|
99
|
+
* @private
|
|
100
|
+
* @param {PermissionOptions} { fromAppDid, toAppDid }
|
|
101
|
+
* @param {number} permission
|
|
102
|
+
* @param {boolean} status
|
|
103
|
+
* @return {*} {Promise<void>}
|
|
104
|
+
* @memberof S3SpaceConfig
|
|
105
|
+
*/
|
|
106
|
+
setPermission({ fromAppDid, toAppDid }, permission, status) {
|
|
107
|
+
var _a, _b;
|
|
108
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
109
|
+
const permissions = yield this.get('permissions', {});
|
|
110
|
+
// 我想知道原来的权限是啥样的?
|
|
111
|
+
const oldPermission = ((_a = permissions === null || permissions === void 0 ? void 0 : permissions[fromAppDid]) === null || _a === void 0 ? void 0 : _a[toAppDid]) || 0;
|
|
112
|
+
permissions[fromAppDid] = Object.assign((_b = permissions === null || permissions === void 0 ? void 0 : permissions[fromAppDid]) !== null && _b !== void 0 ? _b : {}, Object.assign(Object.assign({}, permissions === null || permissions === void 0 ? void 0 : permissions[fromAppDid]), {
|
|
113
|
+
// eslint-disable-next-line no-bitwise
|
|
114
|
+
[toAppDid]: status ? oldPermission | permission : oldPermission & ~permission }));
|
|
115
|
+
yield this.set('permissions', permissions);
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
setListable(options, status) {
|
|
119
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
120
|
+
return this.setPermission(options, core_1.Scopes['list:object'], status);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
setReadable(options, status) {
|
|
124
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
125
|
+
return this.setPermission(options, core_1.Scopes['read:object'], status);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
setWritable(options, status) {
|
|
129
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
130
|
+
return this.setPermission(options, core_1.Scopes['write:object'], status);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
isListable(options) {
|
|
134
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
135
|
+
const permission = yield this.getPermission(options);
|
|
136
|
+
return Boolean((permission & core_1.Scopes['list:object']) === core_1.Scopes['list:object']);
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
isReadable(options) {
|
|
140
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
141
|
+
const permission = yield this.getPermission(options);
|
|
142
|
+
return Boolean((permission & core_1.Scopes['read:object']) === core_1.Scopes['read:object']);
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
isWritable(options) {
|
|
146
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
147
|
+
const permission = yield this.getPermission(options);
|
|
148
|
+
return Boolean((permission & core_1.Scopes['write:object']) === core_1.Scopes['write:object']);
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
exports.S3SpaceConfig = S3SpaceConfig;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./s3"), exports);
|
|
18
|
+
__exportStar(require("./config"), exports);
|
|
19
|
+
__exportStar(require("./operator"), exports);
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { ReadOptions, WriteOptions, SpaceOperatorProtocol, SpaceConfig, AppSpaceOptions, DeleteOptions, ListOptions, Object, Data, ListsOptions, GetHashOptions } from '@did-space/core';
|
|
3
|
+
import { S3Client } from '@aws-sdk/client-s3';
|
|
4
|
+
import { Stream } from 'stream';
|
|
5
|
+
import { S3DriverOptions } from '../s3/options';
|
|
6
|
+
type PathStatus = {
|
|
7
|
+
objects: number;
|
|
8
|
+
lastModified: number;
|
|
9
|
+
size: number;
|
|
10
|
+
};
|
|
11
|
+
export declare class S3SpaceOperator implements SpaceOperatorProtocol {
|
|
12
|
+
readonly options: S3DriverOptions;
|
|
13
|
+
readonly s3Client: S3Client;
|
|
14
|
+
static readonly APPS = "apps";
|
|
15
|
+
constructor(options: S3DriverOptions);
|
|
16
|
+
createSpace(_storageConfig: SpaceConfig): Promise<void>;
|
|
17
|
+
isSpaceCreated(): Promise<boolean>;
|
|
18
|
+
destroySpace(): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* FIXME: @yejianchao 对于 s3 而言,获得 folder 的 size 只能遍历所有文件获得,更可恶的是 ListObjectsCommand 每次只能返回 1000 个对象,此处后续需要改进
|
|
21
|
+
* @returns {Promise<number>}
|
|
22
|
+
*/
|
|
23
|
+
getSpaceSize(): Promise<number>;
|
|
24
|
+
getPathStatus(path: string): Promise<PathStatus>;
|
|
25
|
+
createAppSpace(options: AppSpaceOptions): Promise<void>;
|
|
26
|
+
destroyAppSpace(options: AppSpaceOptions): Promise<void>;
|
|
27
|
+
getAppSpacePath(options: AppSpaceOptions): Promise<string>;
|
|
28
|
+
private getObjectKey;
|
|
29
|
+
/**
|
|
30
|
+
*
|
|
31
|
+
* @FIXME lib-storage 暂时不支持计算部件的 MD5 https://github.com/aws/aws-sdk-js-v3/issues/2673#issuecomment-1057408451
|
|
32
|
+
* @param {WriteOptions} options
|
|
33
|
+
* @return {*} {Promise<void>}
|
|
34
|
+
* @memberof S3SpaceOperator
|
|
35
|
+
*/
|
|
36
|
+
write(options: WriteOptions): Promise<void>;
|
|
37
|
+
delete(options: DeleteOptions): Promise<void>;
|
|
38
|
+
read(options: ReadOptions): Promise<Stream>;
|
|
39
|
+
getHash(options: GetHashOptions): Promise<string>;
|
|
40
|
+
exists(options: ReadOptions): Promise<boolean>;
|
|
41
|
+
/**
|
|
42
|
+
*
|
|
43
|
+
* @FIXME: @yejianchao 现在的解决足够满足要求了,希望少侠可以找到更优雅的解法
|
|
44
|
+
* @see How can I get only one level of objects in a S3 bucket? https://stackoverflow.com/a/5497707
|
|
45
|
+
* @description 仅列出当前文件下的文件和文件夹,不包含嵌套的文件和文件夹
|
|
46
|
+
* @private
|
|
47
|
+
* @param {ListsOptions} options
|
|
48
|
+
* @return {*} {Promise<Object[]>}
|
|
49
|
+
* @memberof S3SpaceOperator
|
|
50
|
+
*/
|
|
51
|
+
private listsOneLevel;
|
|
52
|
+
/**
|
|
53
|
+
*
|
|
54
|
+
* @see 使用paginateListObjectsV2快速检索所有对象 https://stackoverflow.com/a/34912646
|
|
55
|
+
* @see s3开发者指南 https://docs.aws.amazon.com/zh_cn/sdk-for-javascript/v3/developer-guide/javascript_s3_code_examples.html
|
|
56
|
+
* @param {ListOptions} options
|
|
57
|
+
* @return {*} {Promise<Object[]>}
|
|
58
|
+
* @memberof S3SpaceOperator
|
|
59
|
+
*/
|
|
60
|
+
lists(options: ListsOptions): Promise<Object[]>;
|
|
61
|
+
list(options: ListOptions): Promise<Object>;
|
|
62
|
+
writeAsOwner(key: string, data: Data): Promise<void>;
|
|
63
|
+
/**
|
|
64
|
+
*
|
|
65
|
+
* FIXME: @yejianchao 需要支持一下删除一个文件夹(非空)的操作
|
|
66
|
+
* @param {string} key
|
|
67
|
+
* @return {*} {Promise<void>}
|
|
68
|
+
* @memberof S3SpaceOperator
|
|
69
|
+
*/
|
|
70
|
+
deleteAsOwner(key: string): Promise<void>;
|
|
71
|
+
readAsOwner(key: string): Promise<Stream>;
|
|
72
|
+
existsAsOwner(key: string): Promise<boolean>;
|
|
73
|
+
}
|
|
74
|
+
export {};
|
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __asyncValues = (this && this.__asyncValues) || function (o) {
|
|
12
|
+
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
13
|
+
var m = o[Symbol.asyncIterator], i;
|
|
14
|
+
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
|
|
15
|
+
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
|
|
16
|
+
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
|
17
|
+
};
|
|
18
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
19
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
20
|
+
};
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
exports.S3SpaceOperator = void 0;
|
|
23
|
+
const core_1 = require("@did-space/core");
|
|
24
|
+
const url_join_1 = __importDefault(require("url-join"));
|
|
25
|
+
const client_s3_1 = require("@aws-sdk/client-s3");
|
|
26
|
+
const path_1 = require("path");
|
|
27
|
+
const lib_storage_1 = require("@aws-sdk/lib-storage");
|
|
28
|
+
const mime_types_1 = __importDefault(require("mime-types"));
|
|
29
|
+
const lodash_1 = require("lodash");
|
|
30
|
+
class S3SpaceOperator {
|
|
31
|
+
constructor(options) {
|
|
32
|
+
this.options = options;
|
|
33
|
+
this.s3Client = new client_s3_1.S3Client(this.options.s3ClientConfig);
|
|
34
|
+
}
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
36
|
+
createSpace(_storageConfig) {
|
|
37
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
38
|
+
yield this.s3Client.send(new client_s3_1.PutObjectCommand({
|
|
39
|
+
Bucket: this.options.bucket,
|
|
40
|
+
Key: `${this.options.root}/`,
|
|
41
|
+
}));
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
isSpaceCreated() {
|
|
45
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
46
|
+
return this.existsAsOwner('/');
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
destroySpace() {
|
|
50
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
51
|
+
// @see: https://stackoverflow.com/questions/20207063/how-can-i-delete-folder-on-s3-with-node-js
|
|
52
|
+
// FIXME: @yejianchao 目前只能删除空文件夹
|
|
53
|
+
throw new Error('Method not implemented.');
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* FIXME: @yejianchao 对于 s3 而言,获得 folder 的 size 只能遍历所有文件获得,更可恶的是 ListObjectsCommand 每次只能返回 1000 个对象,此处后续需要改进
|
|
58
|
+
* @returns {Promise<number>}
|
|
59
|
+
*/
|
|
60
|
+
getSpaceSize() {
|
|
61
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
62
|
+
const prefix = (0, url_join_1.default)(this.options.root, '/');
|
|
63
|
+
const { size } = yield this.getPathStatus(prefix);
|
|
64
|
+
return size;
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
getPathStatus(path) {
|
|
68
|
+
var _a, e_1, _b, _c;
|
|
69
|
+
var _d;
|
|
70
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
71
|
+
const pathStatus = {
|
|
72
|
+
size: 0,
|
|
73
|
+
objects: 0,
|
|
74
|
+
lastModified: 0,
|
|
75
|
+
};
|
|
76
|
+
try {
|
|
77
|
+
for (var _e = true, _f = __asyncValues((0, client_s3_1.paginateListObjectsV2)({ client: this.s3Client }, {
|
|
78
|
+
Bucket: this.options.bucket,
|
|
79
|
+
Prefix: path,
|
|
80
|
+
})), _g; _g = yield _f.next(), _a = _g.done, !_a;) {
|
|
81
|
+
_c = _g.value;
|
|
82
|
+
_e = false;
|
|
83
|
+
try {
|
|
84
|
+
const response = _c;
|
|
85
|
+
const contents = (_d = response === null || response === void 0 ? void 0 : response.Contents) !== null && _d !== void 0 ? _d : [];
|
|
86
|
+
contents.forEach((content) => {
|
|
87
|
+
var _a;
|
|
88
|
+
pathStatus.size += (_a = content === null || content === void 0 ? void 0 : content.Size) !== null && _a !== void 0 ? _a : 0;
|
|
89
|
+
pathStatus.lastModified = Math.max(new Date(content.LastModified).getTime(), pathStatus.lastModified);
|
|
90
|
+
});
|
|
91
|
+
pathStatus.objects += contents.length;
|
|
92
|
+
}
|
|
93
|
+
finally {
|
|
94
|
+
_e = true;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
99
|
+
finally {
|
|
100
|
+
try {
|
|
101
|
+
if (!_e && !_a && (_b = _f.return)) yield _b.call(_f);
|
|
102
|
+
}
|
|
103
|
+
finally { if (e_1) throw e_1.error; }
|
|
104
|
+
}
|
|
105
|
+
return pathStatus;
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
createAppSpace(options) {
|
|
109
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
110
|
+
yield this.s3Client.send(new client_s3_1.PutObjectCommand({
|
|
111
|
+
Bucket: this.options.bucket,
|
|
112
|
+
Key: `${yield this.getAppSpacePath(options)}/`,
|
|
113
|
+
}));
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
117
|
+
destroyAppSpace(options) {
|
|
118
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
119
|
+
// @see: https://stackoverflow.com/questions/20207063/how-can-i-delete-folder-on-s3-with-node-js
|
|
120
|
+
// FIXME: @yejianchao 目前只能删除空文件夹
|
|
121
|
+
throw new Error('Method not implemented.');
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
getAppSpacePath(options) {
|
|
125
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
126
|
+
return (0, path_1.join)(this.options.root, S3SpaceOperator.APPS, options.appDid);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
getObjectKey(options) {
|
|
130
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
131
|
+
return (0, path_1.join)(yield this.getAppSpacePath(options), options.key);
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
*
|
|
136
|
+
* @FIXME lib-storage 暂时不支持计算部件的 MD5 https://github.com/aws/aws-sdk-js-v3/issues/2673#issuecomment-1057408451
|
|
137
|
+
* @param {WriteOptions} options
|
|
138
|
+
* @return {*} {Promise<void>}
|
|
139
|
+
* @memberof S3SpaceOperator
|
|
140
|
+
*/
|
|
141
|
+
write(options) {
|
|
142
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
143
|
+
const upload = new lib_storage_1.Upload({
|
|
144
|
+
client: this.s3Client,
|
|
145
|
+
params: {
|
|
146
|
+
Bucket: this.options.bucket,
|
|
147
|
+
Key: yield this.getObjectKey(options),
|
|
148
|
+
Body: options.data,
|
|
149
|
+
// @FIXME lib-storage 暂时不支持计算部件的 MD5 https://github.com/aws/aws-sdk-js-v3/issues/2673#issuecomment-1057408451
|
|
150
|
+
// ContentMD5: 'wSunmxovn3F4x1+NV+/d1A==',
|
|
151
|
+
Metadata: {
|
|
152
|
+
'x-hash': options.hash,
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
yield upload.done();
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
delete(options) {
|
|
160
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
161
|
+
yield this.s3Client.send(new client_s3_1.DeleteObjectCommand({
|
|
162
|
+
Bucket: this.options.bucket,
|
|
163
|
+
Key: yield this.getObjectKey(options),
|
|
164
|
+
}));
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
read(options) {
|
|
168
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
169
|
+
const output = yield this.s3Client.send(new client_s3_1.GetObjectCommand({
|
|
170
|
+
Bucket: this.options.bucket,
|
|
171
|
+
Key: yield this.getObjectKey(options),
|
|
172
|
+
}));
|
|
173
|
+
return output.Body;
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
getHash(options) {
|
|
177
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
178
|
+
const output = yield this.s3Client.send(new client_s3_1.HeadObjectCommand({
|
|
179
|
+
Bucket: this.options.bucket,
|
|
180
|
+
Key: yield this.getObjectKey(options),
|
|
181
|
+
}));
|
|
182
|
+
return output.Metadata['x-hash'];
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
exists(options) {
|
|
186
|
+
var _a;
|
|
187
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
188
|
+
const $key = yield this.getObjectKey(options);
|
|
189
|
+
const isDir = $key.endsWith('/');
|
|
190
|
+
try {
|
|
191
|
+
// 特别地,如果这是一个 folder 的话,那么需要使用 ListObjectsCommand 做特殊判断
|
|
192
|
+
if (isDir) {
|
|
193
|
+
const output = yield this.s3Client.send(new client_s3_1.ListObjectsCommand({
|
|
194
|
+
Bucket: this.options.bucket,
|
|
195
|
+
Prefix: $key,
|
|
196
|
+
MaxKeys: 1,
|
|
197
|
+
}));
|
|
198
|
+
return !!((_a = output === null || output === void 0 ? void 0 : output.Contents) === null || _a === void 0 ? void 0 : _a.length);
|
|
199
|
+
}
|
|
200
|
+
const data = yield this.s3Client.send(new client_s3_1.HeadObjectCommand({
|
|
201
|
+
Bucket: this.options.bucket,
|
|
202
|
+
Key: $key,
|
|
203
|
+
}));
|
|
204
|
+
return data.$metadata.httpStatusCode === 200;
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
*
|
|
213
|
+
* @FIXME: @yejianchao 现在的解决足够满足要求了,希望少侠可以找到更优雅的解法
|
|
214
|
+
* @see How can I get only one level of objects in a S3 bucket? https://stackoverflow.com/a/5497707
|
|
215
|
+
* @description 仅列出当前文件下的文件和文件夹,不包含嵌套的文件和文件夹
|
|
216
|
+
* @private
|
|
217
|
+
* @param {ListsOptions} options
|
|
218
|
+
* @return {*} {Promise<Object[]>}
|
|
219
|
+
* @memberof S3SpaceOperator
|
|
220
|
+
*/
|
|
221
|
+
listsOneLevel(options) {
|
|
222
|
+
var _a, e_2, _b, _c;
|
|
223
|
+
var _d, _e;
|
|
224
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
225
|
+
const prefix = (0, url_join_1.default)(this.options.root, options.key);
|
|
226
|
+
const objects = [];
|
|
227
|
+
const folders = [];
|
|
228
|
+
try {
|
|
229
|
+
for (var _f = true, _g = __asyncValues((0, client_s3_1.paginateListObjectsV2)({ client: this.s3Client }, {
|
|
230
|
+
Bucket: this.options.bucket,
|
|
231
|
+
Prefix: prefix,
|
|
232
|
+
Delimiter: '/',
|
|
233
|
+
})), _h; _h = yield _g.next(), _a = _h.done, !_a;) {
|
|
234
|
+
_c = _h.value;
|
|
235
|
+
_f = false;
|
|
236
|
+
try {
|
|
237
|
+
const response = _c;
|
|
238
|
+
(_d = response === null || response === void 0 ? void 0 : response.Contents) === null || _d === void 0 ? void 0 : _d.forEach((content) => {
|
|
239
|
+
const name = content.Key.replace(prefix, '');
|
|
240
|
+
// @note: 有时候 name的值可能是空字符 ‘’
|
|
241
|
+
if ((0, lodash_1.isEmpty)(name)) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
const key = (0, url_join_1.default)(options.key, name);
|
|
245
|
+
const isDir = content.Key.endsWith('/');
|
|
246
|
+
objects.push({
|
|
247
|
+
key,
|
|
248
|
+
name,
|
|
249
|
+
isDir,
|
|
250
|
+
size: content.Size,
|
|
251
|
+
lastModified: new Date(content.LastModified).getTime(),
|
|
252
|
+
editable: core_1.Space.editable(key),
|
|
253
|
+
mimeType: mime_types_1.default.lookup(name) || 'unknown',
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
folders.push(...((_e = response === null || response === void 0 ? void 0 : response.CommonPrefixes) !== null && _e !== void 0 ? _e : []).map((c) => c.Prefix));
|
|
257
|
+
}
|
|
258
|
+
finally {
|
|
259
|
+
_f = true;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
264
|
+
finally {
|
|
265
|
+
try {
|
|
266
|
+
if (!_f && !_a && (_b = _g.return)) yield _b.call(_g);
|
|
267
|
+
}
|
|
268
|
+
finally { if (e_2) throw e_2.error; }
|
|
269
|
+
}
|
|
270
|
+
yield Promise.all(folders.map((folderKey) => __awaiter(this, void 0, void 0, function* () {
|
|
271
|
+
const keys = folderKey.split('/');
|
|
272
|
+
const name = `${keys[keys.length - 2]}/`;
|
|
273
|
+
const key = folderKey.replace(this.options.root, '');
|
|
274
|
+
const pathStatus = yield this.getPathStatus(folderKey);
|
|
275
|
+
const { size, lastModified } = pathStatus;
|
|
276
|
+
objects.push({
|
|
277
|
+
key,
|
|
278
|
+
name,
|
|
279
|
+
isDir: true,
|
|
280
|
+
size,
|
|
281
|
+
lastModified,
|
|
282
|
+
editable: false,
|
|
283
|
+
mimeType: mime_types_1.default.lookup(name) || 'unknown',
|
|
284
|
+
});
|
|
285
|
+
})));
|
|
286
|
+
return objects;
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
*
|
|
291
|
+
* @see 使用paginateListObjectsV2快速检索所有对象 https://stackoverflow.com/a/34912646
|
|
292
|
+
* @see s3开发者指南 https://docs.aws.amazon.com/zh_cn/sdk-for-javascript/v3/developer-guide/javascript_s3_code_examples.html
|
|
293
|
+
* @param {ListOptions} options
|
|
294
|
+
* @return {*} {Promise<Object[]>}
|
|
295
|
+
* @memberof S3SpaceOperator
|
|
296
|
+
*/
|
|
297
|
+
lists(options) {
|
|
298
|
+
var _a, e_3, _b, _c;
|
|
299
|
+
var _d;
|
|
300
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
301
|
+
const prefix = (0, url_join_1.default)(this.options.root, options.key);
|
|
302
|
+
// 以 非递归的方式 && 显示文件夹 的方式列出当前文件
|
|
303
|
+
if (!options.recursive) {
|
|
304
|
+
return this.listsOneLevel(options);
|
|
305
|
+
}
|
|
306
|
+
if (options.recursive && options.ignoreDirectories) {
|
|
307
|
+
const objects = [];
|
|
308
|
+
try {
|
|
309
|
+
for (var _e = true, _f = __asyncValues((0, client_s3_1.paginateListObjectsV2)({ client: this.s3Client }, {
|
|
310
|
+
Bucket: this.options.bucket,
|
|
311
|
+
Prefix: prefix,
|
|
312
|
+
})), _g; _g = yield _f.next(), _a = _g.done, !_a;) {
|
|
313
|
+
_c = _g.value;
|
|
314
|
+
_e = false;
|
|
315
|
+
try {
|
|
316
|
+
const response = _c;
|
|
317
|
+
const contents = (_d = response === null || response === void 0 ? void 0 : response.Contents) !== null && _d !== void 0 ? _d : [];
|
|
318
|
+
contents.forEach((content) => {
|
|
319
|
+
const key = content.Key.replace(prefix, '/');
|
|
320
|
+
const name = (0, path_1.basename)(key);
|
|
321
|
+
objects.push({
|
|
322
|
+
key,
|
|
323
|
+
name,
|
|
324
|
+
isDir: name.endsWith('/'),
|
|
325
|
+
size: content.Size,
|
|
326
|
+
lastModified: new Date(content.LastModified).getTime(),
|
|
327
|
+
editable: core_1.Space.editable(key),
|
|
328
|
+
mimeType: mime_types_1.default.lookup(name) || 'unknown',
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
finally {
|
|
333
|
+
_e = true;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
catch (e_3_1) { e_3 = { error: e_3_1 }; }
|
|
338
|
+
finally {
|
|
339
|
+
try {
|
|
340
|
+
if (!_e && !_a && (_b = _f.return)) yield _b.call(_f);
|
|
341
|
+
}
|
|
342
|
+
finally { if (e_3) throw e_3.error; }
|
|
343
|
+
}
|
|
344
|
+
return objects;
|
|
345
|
+
}
|
|
346
|
+
throw new Error(`Filter methods not yet supported:\n ${JSON.stringify(options, null, 2)}`);
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
// FIXME: 最新的研究发现,似乎通过 GetObjectCommand 也能满足我的要求
|
|
350
|
+
list(options) {
|
|
351
|
+
var _a;
|
|
352
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
353
|
+
const key = (0, url_join_1.default)(this.options.root, options.key);
|
|
354
|
+
const response = yield this.s3Client.send(new client_s3_1.ListObjectsCommand({
|
|
355
|
+
Bucket: this.options.bucket,
|
|
356
|
+
Prefix: key,
|
|
357
|
+
}));
|
|
358
|
+
const [content] = (_a = response === null || response === void 0 ? void 0 : response.Contents) !== null && _a !== void 0 ? _a : [];
|
|
359
|
+
if (!content) {
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
362
|
+
return {
|
|
363
|
+
key: options.key,
|
|
364
|
+
name: (0, path_1.basename)(options.key),
|
|
365
|
+
isDir: content.Key.endsWith('/'),
|
|
366
|
+
size: content.Size,
|
|
367
|
+
lastModified: new Date(content.LastModified).getTime(),
|
|
368
|
+
editable: core_1.Space.editable(options.key),
|
|
369
|
+
mimeType: mime_types_1.default.lookup(options.key) || 'unknown',
|
|
370
|
+
};
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
writeAsOwner(key, data) {
|
|
374
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
375
|
+
yield this.s3Client.send(new client_s3_1.PutObjectCommand({
|
|
376
|
+
Bucket: this.options.bucket,
|
|
377
|
+
Key: (0, path_1.join)(this.options.root, key),
|
|
378
|
+
Body: data,
|
|
379
|
+
}));
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
*
|
|
384
|
+
* FIXME: @yejianchao 需要支持一下删除一个文件夹(非空)的操作
|
|
385
|
+
* @param {string} key
|
|
386
|
+
* @return {*} {Promise<void>}
|
|
387
|
+
* @memberof S3SpaceOperator
|
|
388
|
+
*/
|
|
389
|
+
deleteAsOwner(key) {
|
|
390
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
391
|
+
yield this.s3Client.send(new client_s3_1.DeleteObjectCommand({
|
|
392
|
+
Bucket: this.options.bucket,
|
|
393
|
+
Key: (0, path_1.join)(this.options.root, key),
|
|
394
|
+
}));
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
readAsOwner(key) {
|
|
398
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
399
|
+
const output = yield this.s3Client.send(new client_s3_1.GetObjectCommand({
|
|
400
|
+
Bucket: this.options.bucket,
|
|
401
|
+
Key: (0, path_1.join)(this.options.root, key),
|
|
402
|
+
ResponseContentType: 'application/octet-stream',
|
|
403
|
+
}));
|
|
404
|
+
return output.Body;
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
existsAsOwner(key) {
|
|
408
|
+
var _a;
|
|
409
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
410
|
+
const $key = (0, path_1.join)(this.options.root, key);
|
|
411
|
+
const isDir = $key.endsWith('/');
|
|
412
|
+
try {
|
|
413
|
+
// 特别地,如果这是一个 folder 的话,那么需要使用 ListObjectsCommand 做特殊判断
|
|
414
|
+
if (isDir) {
|
|
415
|
+
const output = yield this.s3Client.send(new client_s3_1.ListObjectsCommand({
|
|
416
|
+
Bucket: this.options.bucket,
|
|
417
|
+
Prefix: $key,
|
|
418
|
+
MaxKeys: 1,
|
|
419
|
+
}));
|
|
420
|
+
return !!((_a = output === null || output === void 0 ? void 0 : output.Contents) === null || _a === void 0 ? void 0 : _a.length);
|
|
421
|
+
}
|
|
422
|
+
const data = yield this.s3Client.send(new client_s3_1.HeadObjectCommand({
|
|
423
|
+
Bucket: this.options.bucket,
|
|
424
|
+
Key: $key,
|
|
425
|
+
}));
|
|
426
|
+
return data.$metadata.httpStatusCode === 200;
|
|
427
|
+
}
|
|
428
|
+
catch (error) {
|
|
429
|
+
return false;
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
exports.S3SpaceOperator = S3SpaceOperator;
|
|
435
|
+
S3SpaceOperator.APPS = 'apps';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './s3';
|
package/dist/s3/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./s3"), exports);
|
package/dist/s3/s3.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { S3ClientConfig } from '@aws-sdk/client-s3';
|
|
2
|
+
import { BaseDriver, SpaceConfigProtocol, SpaceOperatorProtocol } from '@did-space/core';
|
|
3
|
+
import { S3DriverOptions } from './options';
|
|
4
|
+
export declare class S3Driver extends BaseDriver {
|
|
5
|
+
private _spaceConfig;
|
|
6
|
+
private _spaceOperator;
|
|
7
|
+
constructor(options: S3DriverOptions);
|
|
8
|
+
get spaceOperator(): SpaceOperatorProtocol;
|
|
9
|
+
get spaceConfig(): SpaceConfigProtocol;
|
|
10
|
+
static existsBucket({ bucket, s3ClientConfig, }: {
|
|
11
|
+
bucket: string;
|
|
12
|
+
s3ClientConfig: S3ClientConfig;
|
|
13
|
+
}): Promise<boolean>;
|
|
14
|
+
}
|
package/dist/s3/s3.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.S3Driver = void 0;
|
|
13
|
+
const client_s3_1 = require("@aws-sdk/client-s3");
|
|
14
|
+
const core_1 = require("@did-space/core");
|
|
15
|
+
const config_1 = require("../config");
|
|
16
|
+
const operator_1 = require("../operator");
|
|
17
|
+
class S3Driver extends core_1.BaseDriver {
|
|
18
|
+
constructor(options) {
|
|
19
|
+
super(options);
|
|
20
|
+
this._spaceConfig = new config_1.S3SpaceConfig(options);
|
|
21
|
+
this._spaceOperator = new operator_1.S3SpaceOperator(options);
|
|
22
|
+
}
|
|
23
|
+
get spaceOperator() {
|
|
24
|
+
return this._spaceOperator;
|
|
25
|
+
}
|
|
26
|
+
get spaceConfig() {
|
|
27
|
+
return this._spaceConfig;
|
|
28
|
+
}
|
|
29
|
+
static existsBucket({ bucket, s3ClientConfig, }) {
|
|
30
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
31
|
+
try {
|
|
32
|
+
const s3Client = new client_s3_1.S3Client(s3ClientConfig);
|
|
33
|
+
const data = yield s3Client.send(new client_s3_1.HeadBucketCommand({
|
|
34
|
+
Bucket: bucket,
|
|
35
|
+
}));
|
|
36
|
+
return data.$metadata.httpStatusCode === 200;
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
core_1.logger.error('S3Driver.existsBucket', {
|
|
40
|
+
bucket,
|
|
41
|
+
region: s3ClientConfig.region,
|
|
42
|
+
});
|
|
43
|
+
core_1.logger.error('S3Driver.existsBucket', error);
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.S3Driver = S3Driver;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './stream-to-string';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./stream-to-string"), exports);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const streamToString: (stream: any) => Promise<string>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.streamToString = void 0;
|
|
4
|
+
const streamToString = (stream) => new Promise((resolve, reject) => {
|
|
5
|
+
const chunks = [];
|
|
6
|
+
stream.on('data', (chunk) => chunks.push(chunk));
|
|
7
|
+
stream.on('error', reject);
|
|
8
|
+
stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
|
|
9
|
+
});
|
|
10
|
+
exports.streamToString = streamToString;
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@did-space/s3-driver",
|
|
3
|
+
"version": "0.2.5",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"description": "Decentralized file space managed by DID",
|
|
8
|
+
"author": "linchen1987 <linchen.1987@foxmail.com>",
|
|
9
|
+
"homepage": "",
|
|
10
|
+
"license": "ISC",
|
|
11
|
+
"main": "dist/index.js",
|
|
12
|
+
"typings": "dist/index.d.ts",
|
|
13
|
+
"files": [
|
|
14
|
+
"dist"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"lint": "eslint src tests",
|
|
18
|
+
"lint:fix": "npm run lint -- --fix",
|
|
19
|
+
"test": "jest --forceExit --detectOpenHandles --passWithNoTests",
|
|
20
|
+
"coverage": "yarn test -- --coverage",
|
|
21
|
+
"prebuild": "rm -fr dist",
|
|
22
|
+
"build": "tsc",
|
|
23
|
+
"build:watch": "yarn build --watch",
|
|
24
|
+
"pre-commit": "lint-staged",
|
|
25
|
+
"verify": "npm run lint && npm run test && npm run build"
|
|
26
|
+
},
|
|
27
|
+
"lint-staged": {
|
|
28
|
+
"**/*.{js,ts}": [
|
|
29
|
+
"npm run lint",
|
|
30
|
+
"npm run test",
|
|
31
|
+
"git add ."
|
|
32
|
+
]
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@arcblock/did": "^1.18.57",
|
|
36
|
+
"@arcblock/jwt": "^1.18.57",
|
|
37
|
+
"@aws-sdk/client-s3": "^3.241.0",
|
|
38
|
+
"@aws-sdk/lib-storage": "^3.241.0",
|
|
39
|
+
"@did-space/core": "0.2.5",
|
|
40
|
+
"@ocap/mcrypto": "^1.18.57",
|
|
41
|
+
"@ocap/wallet": "^1.18.57",
|
|
42
|
+
"js-yaml": "^4.1.0",
|
|
43
|
+
"json-stable-stringify": "^1.0.1",
|
|
44
|
+
"lodash": "^4.17.21",
|
|
45
|
+
"mime-types": "^2.1.35",
|
|
46
|
+
"url-join": "^4.0.1"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@arcblock/eslint-config-ts": "^0.2.3",
|
|
50
|
+
"@types/fs-extra": "^9.0.13",
|
|
51
|
+
"@types/jest": "^28.1.5",
|
|
52
|
+
"@types/js-yaml": "^4.0.5",
|
|
53
|
+
"@types/json-stable-stringify": "^1.0.34",
|
|
54
|
+
"@types/lodash": "^4.14.182",
|
|
55
|
+
"@types/mime-types": "^2.1.1",
|
|
56
|
+
"@types/node": "15.12.2",
|
|
57
|
+
"@types/url-join": "^4.0.1",
|
|
58
|
+
"@typescript-eslint/eslint-plugin": "^5.30.6",
|
|
59
|
+
"eslint": "^8.19.0",
|
|
60
|
+
"lint-staged": "^13.0.3",
|
|
61
|
+
"ts-jest": "^28.0.6",
|
|
62
|
+
"typescript": "^4.9.5"
|
|
63
|
+
},
|
|
64
|
+
"gitHead": "4d49e73817f610375982509ba8f250ba34e16efb"
|
|
65
|
+
}
|