@did-space/core 0.5.78 → 0.5.80

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.
Files changed (201) hide show
  1. package/dist/{constants → cjs/constants}/index.d.ts +0 -6
  2. package/dist/{constants → cjs/constants}/index.js +1 -9
  3. package/dist/{meta → cjs/meta}/template/app.d.ts +0 -8
  4. package/dist/cjs/meta/template/index.d.ts +2 -0
  5. package/dist/cjs/meta/template/index.js +18 -0
  6. package/dist/{model → cjs/model}/app-space-apply.d.ts +1 -1
  7. package/dist/cjs/model/audit-log.d.ts +527 -0
  8. package/dist/cjs/model/audit-log.js +72 -0
  9. package/dist/{model → cjs/model}/did-object-mapping.d.ts +1 -1
  10. package/dist/{model → cjs/model}/index.d.ts +1 -0
  11. package/dist/{model → cjs/model}/index.js +1 -0
  12. package/dist/{model → cjs/model}/object-collection.d.ts +1 -1
  13. package/dist/{model → cjs/model}/object.d.ts +1 -1
  14. package/dist/{model → cjs/model}/space.d.ts +1 -1
  15. package/dist/{model → cjs/model}/tree.d.ts +2 -2
  16. package/dist/cjs/model/tree.js +9 -0
  17. package/dist/cjs/protocols/driver.js +2 -0
  18. package/dist/cjs/protocols/space-config.js +2 -0
  19. package/dist/cjs/protocols/space-operator.js +2 -0
  20. package/dist/cjs/protocols/space-owner-operator.js +2 -0
  21. package/dist/cjs/protocols/space.js +2 -0
  22. package/dist/es/configuration/index.d.ts +1 -0
  23. package/dist/es/configuration/index.js +1 -0
  24. package/dist/es/configuration/space-config.d.ts +23 -0
  25. package/dist/es/configuration/space-config.js +5 -0
  26. package/dist/es/constants/index.d.ts +30 -0
  27. package/dist/es/constants/index.js +35 -0
  28. package/dist/es/drivers/base.d.ts +40 -0
  29. package/dist/es/drivers/base.js +113 -0
  30. package/dist/es/drivers/index.d.ts +1 -0
  31. package/dist/es/drivers/index.js +1 -0
  32. package/dist/es/index.d.ts +9 -0
  33. package/dist/es/index.js +9 -0
  34. package/dist/es/meta/data.d.ts +4 -0
  35. package/dist/es/meta/data.js +1 -0
  36. package/dist/es/meta/index.d.ts +3 -0
  37. package/dist/es/meta/index.js +3 -0
  38. package/dist/es/meta/object.d.ts +70 -0
  39. package/dist/es/meta/object.js +1 -0
  40. package/dist/es/meta/template/app.d.ts +27 -0
  41. package/dist/es/meta/template/app.js +1 -0
  42. package/dist/es/meta/template/index.d.ts +2 -0
  43. package/dist/es/meta/template/index.js +2 -0
  44. package/dist/es/meta/template/preview/base.d.ts +13 -0
  45. package/dist/es/meta/template/preview/base.js +12 -0
  46. package/dist/es/meta/template/preview/default.d.ts +75 -0
  47. package/dist/es/meta/template/preview/default.js +9 -0
  48. package/dist/es/meta/template/preview/index.d.ts +5 -0
  49. package/dist/es/meta/template/preview/index.js +5 -0
  50. package/dist/es/meta/template/preview/nft.d.ts +23 -0
  51. package/dist/es/meta/template/preview/nft.js +8 -0
  52. package/dist/es/meta/template/preview/passport.d.ts +35 -0
  53. package/dist/es/meta/template/preview/passport.js +19 -0
  54. package/dist/es/meta/template/preview/profile.d.ts +23 -0
  55. package/dist/es/meta/template/preview/profile.js +8 -0
  56. package/dist/es/model/app-space-apply.d.ts +75 -0
  57. package/dist/es/model/app-space-apply.js +1 -0
  58. package/dist/es/model/audit-log.d.ts +527 -0
  59. package/dist/es/model/audit-log.js +69 -0
  60. package/dist/es/model/did-object-mapping.d.ts +8 -0
  61. package/dist/es/model/did-object-mapping.js +1 -0
  62. package/dist/es/model/index.d.ts +5 -0
  63. package/dist/es/model/index.js +5 -0
  64. package/dist/es/model/object-collection.d.ts +42 -0
  65. package/dist/es/model/object-collection.js +1 -0
  66. package/dist/es/model/object.d.ts +59 -0
  67. package/dist/es/model/object.js +1 -0
  68. package/dist/es/model/space.d.ts +21 -0
  69. package/dist/es/model/space.js +1 -0
  70. package/dist/es/model/tree.d.ts +96 -0
  71. package/dist/es/model/tree.js +6 -0
  72. package/dist/es/protocols/base-space.d.ts +4 -0
  73. package/dist/es/protocols/base-space.js +1 -0
  74. package/dist/es/protocols/driver.d.ts +7 -0
  75. package/dist/es/protocols/driver.js +1 -0
  76. package/dist/es/protocols/index.d.ts +5 -0
  77. package/dist/es/protocols/index.js +5 -0
  78. package/dist/es/protocols/space-config.d.ts +17 -0
  79. package/dist/es/protocols/space-config.js +1 -0
  80. package/dist/es/protocols/space-operator.d.ts +63 -0
  81. package/dist/es/protocols/space-operator.js +1 -0
  82. package/dist/es/protocols/space-owner-operator.d.ts +43 -0
  83. package/dist/es/protocols/space-owner-operator.js +1 -0
  84. package/dist/es/protocols/space.d.ts +7 -0
  85. package/dist/es/protocols/space.js +1 -0
  86. package/dist/es/schemas/global-space.d.ts +2 -0
  87. package/dist/es/schemas/global-space.js +19 -0
  88. package/dist/es/schemas/index.d.ts +3 -0
  89. package/dist/es/schemas/index.js +3 -0
  90. package/dist/es/schemas/object-space.d.ts +6 -0
  91. package/dist/es/schemas/object-space.js +44 -0
  92. package/dist/es/schemas/space-configuration.d.ts +4 -0
  93. package/dist/es/schemas/space-configuration.js +22 -0
  94. package/dist/es/schemas/space-options.d.ts +1 -0
  95. package/dist/es/schemas/space-options.js +4 -0
  96. package/dist/es/space/global-space.d.ts +43 -0
  97. package/dist/es/space/global-space.js +169 -0
  98. package/dist/es/space/index.d.ts +3 -0
  99. package/dist/es/space/index.js +3 -0
  100. package/dist/es/space/object-space.d.ts +123 -0
  101. package/dist/es/space/object-space.js +684 -0
  102. package/dist/es/space/space.d.ts +47 -0
  103. package/dist/es/space/space.js +172 -0
  104. package/dist/es/utils/common.d.ts +5 -0
  105. package/dist/es/utils/common.js +38 -0
  106. package/dist/es/utils/hash.d.ts +31 -0
  107. package/dist/es/utils/hash.js +64 -0
  108. package/dist/es/utils/index.d.ts +4 -0
  109. package/dist/es/utils/index.js +4 -0
  110. package/dist/es/utils/logger.d.ts +2 -0
  111. package/dist/es/utils/logger.js +2 -0
  112. package/dist/es/utils/stream.d.ts +11 -0
  113. package/dist/es/utils/stream.js +18 -0
  114. package/package.json +98 -9
  115. package/dist/meta/template/app-backup.d.ts +0 -55
  116. package/dist/meta/template/app-backup.js +0 -4
  117. package/dist/meta/template/app-connected.d.ts +0 -49
  118. package/dist/meta/template/app-connected.js +0 -4
  119. package/dist/meta/template/app-disconnected.d.ts +0 -20
  120. package/dist/meta/template/app-disconnected.js +0 -4
  121. package/dist/meta/template/app-restore.d.ts +0 -40
  122. package/dist/meta/template/app-restore.js +0 -4
  123. package/dist/meta/template/index.d.ts +0 -6
  124. package/dist/meta/template/index.js +0 -22
  125. package/dist/model/app-space-apply.js +0 -7
  126. package/dist/model/did-object-mapping.js +0 -8
  127. package/dist/model/object-collection.js +0 -7
  128. package/dist/model/object.js +0 -7
  129. package/dist/model/space.js +0 -7
  130. package/dist/model/tree.js +0 -18
  131. /package/dist/{configuration → cjs/configuration}/index.d.ts +0 -0
  132. /package/dist/{configuration → cjs/configuration}/index.js +0 -0
  133. /package/dist/{configuration → cjs/configuration}/space-config.d.ts +0 -0
  134. /package/dist/{configuration → cjs/configuration}/space-config.js +0 -0
  135. /package/dist/{drivers → cjs/drivers}/base.d.ts +0 -0
  136. /package/dist/{drivers → cjs/drivers}/base.js +0 -0
  137. /package/dist/{drivers → cjs/drivers}/index.d.ts +0 -0
  138. /package/dist/{drivers → cjs/drivers}/index.js +0 -0
  139. /package/dist/{index.d.ts → cjs/index.d.ts} +0 -0
  140. /package/dist/{index.js → cjs/index.js} +0 -0
  141. /package/dist/{meta → cjs/meta}/data.d.ts +0 -0
  142. /package/dist/{meta → cjs/meta}/data.js +0 -0
  143. /package/dist/{meta → cjs/meta}/index.d.ts +0 -0
  144. /package/dist/{meta → cjs/meta}/index.js +0 -0
  145. /package/dist/{meta → cjs/meta}/object.d.ts +0 -0
  146. /package/dist/{meta → cjs/meta}/object.js +0 -0
  147. /package/dist/{meta → cjs/meta}/template/app.js +0 -0
  148. /package/dist/{meta → cjs/meta}/template/preview/base.d.ts +0 -0
  149. /package/dist/{meta → cjs/meta}/template/preview/base.js +0 -0
  150. /package/dist/{meta → cjs/meta}/template/preview/default.d.ts +0 -0
  151. /package/dist/{meta → cjs/meta}/template/preview/default.js +0 -0
  152. /package/dist/{meta → cjs/meta}/template/preview/index.d.ts +0 -0
  153. /package/dist/{meta → cjs/meta}/template/preview/index.js +0 -0
  154. /package/dist/{meta → cjs/meta}/template/preview/nft.d.ts +0 -0
  155. /package/dist/{meta → cjs/meta}/template/preview/nft.js +0 -0
  156. /package/dist/{meta → cjs/meta}/template/preview/passport.d.ts +0 -0
  157. /package/dist/{meta → cjs/meta}/template/preview/passport.js +0 -0
  158. /package/dist/{meta → cjs/meta}/template/preview/profile.d.ts +0 -0
  159. /package/dist/{meta → cjs/meta}/template/preview/profile.js +0 -0
  160. /package/dist/{protocols/base-space.js → cjs/model/app-space-apply.js} +0 -0
  161. /package/dist/{protocols/driver.js → cjs/model/did-object-mapping.js} +0 -0
  162. /package/dist/{protocols/space-config.js → cjs/model/object-collection.js} +0 -0
  163. /package/dist/{protocols/space-operator.js → cjs/model/object.js} +0 -0
  164. /package/dist/{protocols → cjs/model}/space.js +0 -0
  165. /package/dist/{protocols → cjs/protocols}/base-space.d.ts +0 -0
  166. /package/dist/{protocols/space-owner-operator.js → cjs/protocols/base-space.js} +0 -0
  167. /package/dist/{protocols → cjs/protocols}/driver.d.ts +0 -0
  168. /package/dist/{protocols → cjs/protocols}/index.d.ts +0 -0
  169. /package/dist/{protocols → cjs/protocols}/index.js +0 -0
  170. /package/dist/{protocols → cjs/protocols}/space-config.d.ts +0 -0
  171. /package/dist/{protocols → cjs/protocols}/space-operator.d.ts +0 -0
  172. /package/dist/{protocols → cjs/protocols}/space-owner-operator.d.ts +0 -0
  173. /package/dist/{protocols → cjs/protocols}/space.d.ts +0 -0
  174. /package/dist/{schemas → cjs/schemas}/global-space.d.ts +0 -0
  175. /package/dist/{schemas → cjs/schemas}/global-space.js +0 -0
  176. /package/dist/{schemas → cjs/schemas}/index.d.ts +0 -0
  177. /package/dist/{schemas → cjs/schemas}/index.js +0 -0
  178. /package/dist/{schemas → cjs/schemas}/object-space.d.ts +0 -0
  179. /package/dist/{schemas → cjs/schemas}/object-space.js +0 -0
  180. /package/dist/{schemas → cjs/schemas}/space-configuration.d.ts +0 -0
  181. /package/dist/{schemas → cjs/schemas}/space-configuration.js +0 -0
  182. /package/dist/{schemas → cjs/schemas}/space-options.d.ts +0 -0
  183. /package/dist/{schemas → cjs/schemas}/space-options.js +0 -0
  184. /package/dist/{space → cjs/space}/global-space.d.ts +0 -0
  185. /package/dist/{space → cjs/space}/global-space.js +0 -0
  186. /package/dist/{space → cjs/space}/index.d.ts +0 -0
  187. /package/dist/{space → cjs/space}/index.js +0 -0
  188. /package/dist/{space → cjs/space}/object-space.d.ts +0 -0
  189. /package/dist/{space → cjs/space}/object-space.js +0 -0
  190. /package/dist/{space → cjs/space}/space.d.ts +0 -0
  191. /package/dist/{space → cjs/space}/space.js +0 -0
  192. /package/dist/{utils → cjs/utils}/common.d.ts +0 -0
  193. /package/dist/{utils → cjs/utils}/common.js +0 -0
  194. /package/dist/{utils → cjs/utils}/hash.d.ts +0 -0
  195. /package/dist/{utils → cjs/utils}/hash.js +0 -0
  196. /package/dist/{utils → cjs/utils}/index.d.ts +0 -0
  197. /package/dist/{utils → cjs/utils}/index.js +0 -0
  198. /package/dist/{utils → cjs/utils}/logger.d.ts +0 -0
  199. /package/dist/{utils → cjs/utils}/logger.js +0 -0
  200. /package/dist/{utils → cjs/utils}/stream.d.ts +0 -0
  201. /package/dist/{utils → cjs/utils}/stream.js +0 -0
@@ -0,0 +1,684 @@
1
+ import Debug from 'debug';
2
+ import { Op, QueryTypes } from 'sequelize';
3
+ import jsYaml from 'js-yaml';
4
+ import { basename, join } from 'path';
5
+ import isEmpty from 'lodash/isEmpty';
6
+ import isUndefined from 'lodash/isUndefined';
7
+ import omit from 'lodash/omit';
8
+ import { isNumber } from 'lodash';
9
+ import EventEmitter from 'events';
10
+ import { Scopes } from '../configuration';
11
+ import { TreeModelType, } from '../model';
12
+ import { getHash, getHashPath, getSize, isDirectory, logger, isStream, streamToString } from '../utils';
13
+ import { DeleteAsOwnerOptionsSchema, OwnerOperatorKeySchema, ObjectSpaceOptionsSchema, WriteAsOwnerOptionsSchema, WriteOptionsSchema, } from '../schemas';
14
+ import { APPS } from '../constants';
15
+ import { GlobalSpace } from './global-space';
16
+ const debug = Debug('@did-space/core:ObjectSpace');
17
+ export class ObjectSpace extends EventEmitter {
18
+ options;
19
+ globalSpace;
20
+ driver;
21
+ static READONLY_OBJECT_KEYS = ['/config.yml'];
22
+ constructor(options) {
23
+ super();
24
+ const { error } = ObjectSpaceOptionsSchema.validate(options, {
25
+ allowUnknown: true,
26
+ stripUnknown: true,
27
+ });
28
+ if (error) {
29
+ throw error;
30
+ }
31
+ this.driver = options.driver;
32
+ this.options = options;
33
+ this.globalSpace = new GlobalSpace({
34
+ driver: options.driver,
35
+ treeRepository: options.treeRepository,
36
+ objectRepository: options.objectRepository,
37
+ objectCollectionRepository: options.objectCollectionRepository,
38
+ });
39
+ }
40
+ async createSpace(spaceConfig) {
41
+ debug('createSpace.before', JSON.stringify(spaceConfig));
42
+ await this.writeAsOwner('/', null);
43
+ await this.createConfig(spaceConfig);
44
+ debug('createSpace.after', JSON.stringify(spaceConfig));
45
+ }
46
+ async isSpaceCreated() {
47
+ const where = {
48
+ did: this.options.spaceDid,
49
+ };
50
+ debug('isSpaceCreated.before', JSON.stringify(where));
51
+ const created = await this.options.spaceRepository.count({
52
+ where,
53
+ });
54
+ debug('isSpaceCreated.after', JSON.stringify({ created }));
55
+ return Boolean(created);
56
+ }
57
+ /**
58
+ *
59
+ * @description
60
+ * @return {*} {Promise<void>}
61
+ * @memberof ObjectSpace
62
+ */
63
+ async destroySpace() {
64
+ await this.deleteAsOwner('/');
65
+ const { spaceDid } = this.options;
66
+ await this.options.appSpaceApplyRepository.destroy({
67
+ where: {
68
+ storageDid: spaceDid,
69
+ },
70
+ });
71
+ await this.options.didObjectMappingRepository.destroy({
72
+ where: {
73
+ spaceDid,
74
+ },
75
+ });
76
+ await this.options.spaceRepository.destroy({
77
+ where: {
78
+ did: spaceDid,
79
+ },
80
+ });
81
+ }
82
+ async getSpaceSize() {
83
+ const { size } = await this.getStatusAsOwner('/');
84
+ return size;
85
+ }
86
+ async createAppSpace(options) {
87
+ debug('createAppSpace.before', JSON.stringify({ options }));
88
+ await this.writeAsOwner(await this.getAppSpacePath(options), null);
89
+ debug('createAppSpace.after', JSON.stringify({ options }));
90
+ }
91
+ /**
92
+ * @description
93
+ * @param {AppSpaceOptions} options
94
+ * @return {Promise<void>} {Promise<void>}
95
+ * @memberof ObjectSpace
96
+ */
97
+ async destroyAppSpace(options) {
98
+ debug('destroyAppSpace.before', JSON.stringify({ options }));
99
+ const permissionOptions = {
100
+ fromAppDid: options.appDid,
101
+ toAppDid: options.appDid,
102
+ };
103
+ await this.setListable(permissionOptions, false);
104
+ await this.setReadable(permissionOptions, false);
105
+ await this.setWritable(permissionOptions, false);
106
+ await this.deleteAsOwner(await this.getAppSpacePath(options));
107
+ debug('destroyAppSpace.after', JSON.stringify({ options }));
108
+ }
109
+ getAppSpacePath(options) {
110
+ // @ts-expect-error
111
+ return join(`/${APPS}`, `${options.appDid}/`);
112
+ }
113
+ async write(options) {
114
+ debug('write.before', JSON.stringify(omit(options, 'data')));
115
+ const { error } = WriteOptionsSchema.validate(options, {
116
+ allowUnknown: true,
117
+ stripUnknown: true,
118
+ });
119
+ if (error) {
120
+ throw error;
121
+ }
122
+ const key = join(await this.getAppSpacePath(options), options.key);
123
+ // @ts-expect-error
124
+ await this.writeAsOwner(key, options.data, options);
125
+ debug('write.after', JSON.stringify({ key }));
126
+ }
127
+ async delete(options) {
128
+ debug('delete.before', JSON.stringify({ options }));
129
+ const key = join(await this.getAppSpacePath(options), options.key);
130
+ await this.deleteAsOwner(key);
131
+ debug('delete.after', JSON.stringify({ key }));
132
+ }
133
+ async read(options) {
134
+ debug('read.before', JSON.stringify({ options }));
135
+ const key = join(await this.getAppSpacePath(options), options.key);
136
+ debug('read.$key', key);
137
+ return this.readAsOwner(key);
138
+ }
139
+ async getHash(options) {
140
+ debug('getHash.before', JSON.stringify({ options }));
141
+ const key = join(await this.getAppSpacePath(options), options.key);
142
+ const where = {
143
+ spaceDid: this.options.spaceDid,
144
+ key,
145
+ };
146
+ debug('getHash.$key', key);
147
+ debug('getHash.$where', JSON.stringify({ where }));
148
+ if (!(await this.exists(options))) {
149
+ throw new Error(`Object(${key}) not exists`);
150
+ }
151
+ const tree = await this.options.treeRepository.findOne({
152
+ where,
153
+ attributes: ['objectId'],
154
+ raw: true,
155
+ });
156
+ if (!tree) {
157
+ throw new Error(`Object(${key}) not exists`);
158
+ }
159
+ return tree.objectId;
160
+ }
161
+ async exists(options) {
162
+ debug('exists.before', JSON.stringify({ options: omit(options, 'data') }));
163
+ const key = join(await this.getAppSpacePath(options), options.key);
164
+ return this.existsAsOwner(key);
165
+ }
166
+ async listsOneLevelAsOwner(options) {
167
+ const where = {
168
+ spaceDid: this.options.spaceDid,
169
+ key: options.key,
170
+ };
171
+ debug('listsOneLevel.before', JSON.stringify(options));
172
+ const parentTree = await this.options.treeRepository.findOne({
173
+ where,
174
+ attributes: ['id'],
175
+ raw: true,
176
+ });
177
+ if (!parentTree) {
178
+ return [];
179
+ }
180
+ debug('listsOneLevel.$parentTree', JSON.stringify(parentTree, null, 2));
181
+ const trees = await this.options.treeRepository.findAll({
182
+ where: {
183
+ spaceDid: this.options.spaceDid,
184
+ parentId: parentTree.id,
185
+ },
186
+ include: this.options.objectRepository,
187
+ });
188
+ return Promise.all(trees.map(async (x) => {
189
+ const absolutePath = join(x.spaceDid, x.key);
190
+ if (x.type === TreeModelType.FOLDER) {
191
+ const { size, lastModified } = await this.getStatusAsOwner(x.key);
192
+ return {
193
+ key: x.key,
194
+ name: basename(x.key),
195
+ isDir: true,
196
+ size,
197
+ lastModified: lastModified || new Date(x.updatedAt).getTime(),
198
+ editable: true,
199
+ mimeType: null,
200
+ absolutePath,
201
+ metadata: x.meta,
202
+ };
203
+ }
204
+ return {
205
+ key: x.key,
206
+ name: basename(x.key),
207
+ isDir: false,
208
+ size: x.Object.size,
209
+ lastModified: new Date(x.updatedAt).getTime(),
210
+ editable: true,
211
+ mimeType: x.Object.meta.mimeType,
212
+ hash: x.objectId,
213
+ absolutePath,
214
+ metadata: x.meta,
215
+ };
216
+ }));
217
+ }
218
+ async listsRecursiveAsOwner(options) {
219
+ debug('listsRecursiveAsOwner.before', JSON.stringify(options));
220
+ const where = {
221
+ spaceDid: this.options.spaceDid,
222
+ key: {
223
+ [Op.like]: `${options.key}%`,
224
+ },
225
+ type: TreeModelType.FILE,
226
+ };
227
+ const trees = await this.options.treeRepository.findAll({
228
+ where,
229
+ include: this.options.objectRepository,
230
+ });
231
+ return trees.map((x) => {
232
+ return {
233
+ key: x.key,
234
+ name: basename(x.key),
235
+ isDir: x.type === TreeModelType.FOLDER,
236
+ size: x.Object.size,
237
+ lastModified: new Date(x.updatedAt).getTime(),
238
+ editable: true,
239
+ mimeType: x.Object.meta.mimeType,
240
+ hash: x.objectId,
241
+ absolutePath: join(x.spaceDid, x.key),
242
+ metadata: x.meta,
243
+ };
244
+ });
245
+ }
246
+ /**
247
+ * @description 后续改名为 listsAsOwner 更合适
248
+ * @param {ListsOptions} options
249
+ * @return {*} {Promise<Object[]>}
250
+ * @memberof ObjectSpace
251
+ */
252
+ async lists(options) {
253
+ debug('lists.before', JSON.stringify(options));
254
+ if (!(await this.existsAsOwner(options.key))) {
255
+ return [];
256
+ }
257
+ // 以 非递归的方式 && 显示文件夹 的方式列出当前文件
258
+ if (!options.recursive) {
259
+ return this.listsOneLevelAsOwner(options);
260
+ }
261
+ if (options.recursive && options.ignoreDirectories) {
262
+ const objects = await this.listsRecursiveAsOwner(options);
263
+ return objects.map((x) => {
264
+ return {
265
+ ...x,
266
+ key: x.key.replace(options.key, '/'),
267
+ };
268
+ });
269
+ }
270
+ debug('lists.after', JSON.stringify(options));
271
+ throw new Error(`Filter methods not yet supported:\n ${JSON.stringify(options, null, 2)}`);
272
+ }
273
+ /**
274
+ * @FIXME: 接口的功能实际应该是 listAsOwner 实现的,属于技术债 @jianchao
275
+ * @description
276
+ * @param {ListOptions} options
277
+ * @return {*} {Promise<Object>}
278
+ * @memberof ObjectSpace
279
+ */
280
+ async list(options) {
281
+ debug('list.before', JSON.stringify(options));
282
+ const where = {
283
+ spaceDid: this.options.spaceDid,
284
+ key: options.key,
285
+ };
286
+ const tree = await this.options.treeRepository.findOne({
287
+ where,
288
+ include: this.options.objectRepository,
289
+ });
290
+ if (!tree) {
291
+ return null;
292
+ }
293
+ return {
294
+ key: tree.key,
295
+ name: basename(tree.key),
296
+ isDir: tree.type === TreeModelType.FOLDER,
297
+ size: tree.type === TreeModelType.FOLDER ? 0 : tree.Object.size,
298
+ lastModified: new Date(tree.updatedAt).getTime(),
299
+ editable: true,
300
+ mimeType: tree.type === TreeModelType.FOLDER ? null : tree.Object.meta.mimeType,
301
+ absolutePath: join(tree.spaceDid, tree.key),
302
+ metadata: tree.meta,
303
+ };
304
+ }
305
+ /**
306
+ * @refactor: @jianchao 后续让 write 的接口参数保持一致,即 key, data 都在 options 里面,目前改动收益比较小
307
+ * @description
308
+ * @param {string} key
309
+ * @param {Data} data
310
+ * @param {WriteAsOwnerOptions} options
311
+ * @return {*} {Promise<void>}
312
+ * @memberof ObjectSpace
313
+ */
314
+ async writeAsOwner(key, data, options = { hash: null, size: 0 }) {
315
+ debug('writeAsOwner.before', JSON.stringify({ key, options: omit(options, 'data') }));
316
+ const { error } = WriteAsOwnerOptionsSchema.validate({
317
+ ...options,
318
+ key,
319
+ data,
320
+ }, {
321
+ allowUnknown: true,
322
+ stripUnknown: true,
323
+ });
324
+ if (error) {
325
+ throw error;
326
+ }
327
+ if (!isDirectory(key) && !isStream(data)) {
328
+ options.hash = options.hash || (await getHash(data));
329
+ options.size = options.size || (await getSize(data));
330
+ // @note: size 可能是空的
331
+ if (!options.hash || !isNumber(options.size)) {
332
+ throw new Error('Hash and size cannot be empty');
333
+ }
334
+ }
335
+ try {
336
+ const exists = await this.existsAsOwner(key);
337
+ debug('writeAsOwner.$exists', exists);
338
+ if (exists) {
339
+ // 更新对象
340
+ await this._updateAsOwner(key, data, options);
341
+ }
342
+ else {
343
+ // 添加新对象
344
+ await this._createAsOwner(key, data, options);
345
+ }
346
+ }
347
+ catch (err) {
348
+ logger.error('writeAsOwner.$options', options);
349
+ logger.error('writeAsOwner.$error', err);
350
+ throw err;
351
+ }
352
+ finally {
353
+ this.emit('space.writeAsOwner.after', {
354
+ spaceDid: this.options.spaceDid,
355
+ key,
356
+ });
357
+ debug('writeAsOwner.after', JSON.stringify({ key, options: omit(options, 'data') }));
358
+ }
359
+ }
360
+ async _updateAsOwner(key, data, options) {
361
+ debug('updateAsOwner.before', { key, options: omit(options, 'data') });
362
+ await this.updateMetadata(key, data, options);
363
+ if (isUndefined(data)) {
364
+ return;
365
+ }
366
+ if (isDirectory(key)) {
367
+ return;
368
+ }
369
+ const where = {
370
+ spaceDid: this.options.spaceDid,
371
+ key,
372
+ };
373
+ const oldTree = await this.options.treeRepository.findOne({
374
+ where,
375
+ include: this.options.objectRepository,
376
+ });
377
+ if (!oldTree) {
378
+ return;
379
+ }
380
+ const listObject = await this.driver.list({
381
+ key: getHashPath(options.hash),
382
+ useGlobal: true,
383
+ });
384
+ // @note: 运行时的纠错机制,我们发现 size 有时候保存出错(修复后将不可能出现),必须 hash 和 size 都相同,才不需要修改底层存储
385
+ if (oldTree.objectId === options.hash &&
386
+ oldTree.Object.size === options.size &&
387
+ listObject?.size === options.size) {
388
+ // 内容不变,不需要再次存储对象,只需要更新对象的更新时间即可
389
+ await this.options.treeRepository.update({
390
+ updatedAt: new Date().toISOString(),
391
+ size: options.size,
392
+ }, {
393
+ where: {
394
+ id: oldTree.id,
395
+ },
396
+ });
397
+ return;
398
+ }
399
+ await this.globalSpace.add(key, data, options);
400
+ // 更新 tree 记录
401
+ await this.options.treeRepository.update({
402
+ objectId: options.hash,
403
+ size: options.size,
404
+ updatedAt: new Date().toISOString(),
405
+ }, {
406
+ where: {
407
+ id: oldTree.id,
408
+ },
409
+ });
410
+ // 标记清除
411
+ await this.globalSpace.mark(oldTree.objectId);
412
+ }
413
+ async _createAsOwner(key, data, options) {
414
+ debug('createAsOwner.before', JSON.stringify({ key, options: omit(options, 'data') }));
415
+ const isDir = key.endsWith('/');
416
+ if (isDir) {
417
+ // 添加 tree 记录
418
+ await this.options.treeRepository.deepCreate({
419
+ spaceDid: this.options.spaceDid,
420
+ objectId: null,
421
+ parentId: null,
422
+ key,
423
+ type: TreeModelType.FOLDER,
424
+ count: 0,
425
+ size: 0,
426
+ });
427
+ }
428
+ else {
429
+ // 存储新对象到全局存储区
430
+ await this.globalSpace.add(key, data, options);
431
+ // 添加 tree 记录
432
+ await this.options.treeRepository.deepCreate({
433
+ spaceDid: this.options.spaceDid,
434
+ objectId: options.hash,
435
+ parentId: null,
436
+ key,
437
+ type: TreeModelType.FILE,
438
+ count: 1,
439
+ size: options.size,
440
+ });
441
+ }
442
+ await this.updateMetadata(key, data, options);
443
+ }
444
+ async updateMetadata(key, _data, options) {
445
+ debug('updateMetadata.before', { key, options: omit(options, 'data') });
446
+ const where = {
447
+ spaceDid: this.options.spaceDid,
448
+ key,
449
+ type: key.endsWith('/') ? TreeModelType.FOLDER : TreeModelType.FILE,
450
+ };
451
+ debug('updateMetadata.before', JSON.stringify({ key, options: omit(options, 'data'), where }));
452
+ const oldTree = await this.options.treeRepository.findOne({
453
+ where,
454
+ attributes: ['id', 'meta'],
455
+ });
456
+ debug('updateMetadata.$oldTree', JSON.stringify(oldTree));
457
+ if (!oldTree) {
458
+ return;
459
+ }
460
+ if (!isEmpty(options.metadata)) {
461
+ await oldTree.update({
462
+ meta: {
463
+ ...oldTree.meta,
464
+ ...options.metadata,
465
+ },
466
+ updatedAt: new Date().toISOString(),
467
+ });
468
+ }
469
+ }
470
+ async deleteAsOwner(key) {
471
+ debug('deleteAsOwner.before', JSON.stringify({ key }));
472
+ const { error } = DeleteAsOwnerOptionsSchema.validate({
473
+ key,
474
+ });
475
+ if (error) {
476
+ throw error;
477
+ }
478
+ if (ObjectSpace.READONLY_OBJECT_KEYS.includes(key)) {
479
+ throw new Error(`Object ${key} cannot be deleted`);
480
+ }
481
+ const isDir = key.endsWith('/');
482
+ try {
483
+ if (isDir) {
484
+ // 删除文件夹
485
+ const whereForDeleteFolder = {
486
+ spaceDid: this.options.spaceDid,
487
+ // 删除以 key 开头的记录
488
+ key: {
489
+ [Op.like]: `${key}%`,
490
+ },
491
+ };
492
+ debug('deleteAsOwner.$whereForDeleteFolder', JSON.stringify(whereForDeleteFolder));
493
+ await this.options.objectCollectionRepository.sequelize.query(`INSERT OR REPLACE INTO ${this.options.objectCollectionRepository.tableName} (id, createdAt)
494
+ SELECT distinct objectId, strftime('%Y-%m-%d %H:%M:%f +00:00',DATETIME('now'))
495
+ FROM ${this.options.treeRepository.tableName} where spaceDid = '${this.options.spaceDid}' and type = ${TreeModelType.FILE} and key like '${key}%';`, {
496
+ type: QueryTypes.INSERT,
497
+ });
498
+ await this.options.treeRepository.destroy({
499
+ where: whereForDeleteFolder,
500
+ });
501
+ }
502
+ else {
503
+ const where = {
504
+ spaceDid: this.options.spaceDid,
505
+ key,
506
+ };
507
+ const tree = await this.options.treeRepository.findOne({
508
+ where,
509
+ attributes: ['objectId'],
510
+ raw: true,
511
+ });
512
+ if (!tree) {
513
+ return;
514
+ }
515
+ await this.options.treeRepository.destroy({
516
+ where,
517
+ });
518
+ await this.globalSpace.mark(tree.objectId);
519
+ }
520
+ }
521
+ catch (err) {
522
+ logger.error('deleteAsOwner.$key', key);
523
+ logger.error('deleteAsOwner.$error', err);
524
+ throw err;
525
+ }
526
+ finally {
527
+ this.emit('space.deleteAsOwner.after', {
528
+ spaceDid: this.options.spaceDid,
529
+ key,
530
+ });
531
+ }
532
+ }
533
+ async readAsOwner(key, options) {
534
+ debug('readAsOwner.before', JSON.stringify({ key, options }));
535
+ const where = {
536
+ spaceDid: this.options.spaceDid,
537
+ key,
538
+ };
539
+ debug('readAsOwner.$where', JSON.stringify(where));
540
+ const tree = await this.options.treeRepository.findOne({
541
+ where,
542
+ attributes: ['objectId'],
543
+ raw: true,
544
+ });
545
+ if (!tree) {
546
+ throw new Error(`Object(${key}) not exists`);
547
+ }
548
+ return this.driver.readAsOwner(getHashPath(tree.objectId), {
549
+ useGlobal: true,
550
+ });
551
+ }
552
+ async existsAsOwner(key) {
553
+ const where = {
554
+ spaceDid: this.options.spaceDid,
555
+ key,
556
+ };
557
+ debug('existsAsOwner.before', JSON.stringify({ where }));
558
+ const exists = Boolean(await this.options.treeRepository.count({
559
+ where,
560
+ }));
561
+ debug('existsAsOwner.after', JSON.stringify({ exists }));
562
+ return exists;
563
+ }
564
+ async getStatusAsOwner(key) {
565
+ const { error } = OwnerOperatorKeySchema.validate(key);
566
+ if (error) {
567
+ throw error;
568
+ }
569
+ const where = {
570
+ spaceDid: this.options.spaceDid,
571
+ key,
572
+ type: TreeModelType.FOLDER,
573
+ };
574
+ const tree = await this.options.treeRepository.findOne({
575
+ where,
576
+ attributes: ['updatedAt', 'size', 'count'],
577
+ raw: true,
578
+ });
579
+ if (!tree) {
580
+ return {
581
+ objects: 0,
582
+ size: 0,
583
+ lastModified: 0,
584
+ };
585
+ }
586
+ // @note: 我们采用了逐级冒泡的方式,从叶子节点自下而上的更新 folder 的更新时间,对象数,size
587
+ const objects = tree.count;
588
+ const lastModified = new Date(tree.updatedAt).getTime();
589
+ const { size } = tree;
590
+ return {
591
+ objects,
592
+ size,
593
+ lastModified,
594
+ };
595
+ }
596
+ async createConfig(spaceConfig) {
597
+ debug('createConfig.before', { spaceConfig });
598
+ const data = jsYaml.dump(spaceConfig);
599
+ const hash = await getHash(data);
600
+ const size = await getSize(data);
601
+ debug('createConfig.$data', data);
602
+ debug('createConfig.$hash', hash);
603
+ debug('createConfig.$size', size);
604
+ await this.writeAsOwner('/config.yml', data, {
605
+ hash,
606
+ size,
607
+ useGlobal: true,
608
+ });
609
+ }
610
+ destroyConfig() {
611
+ return this.deleteAsOwner('/config.yml');
612
+ }
613
+ async set(key, value) {
614
+ debug('set.before', JSON.stringify({ key, value }));
615
+ const configData = await this.readAsOwner('/config.yml');
616
+ const data = jsYaml.load(await streamToString(configData));
617
+ data[key] = value;
618
+ await this.createConfig(data);
619
+ }
620
+ async get(key, defaultValue = {}) {
621
+ debug('get.before', JSON.stringify({ key }));
622
+ const configData = await this.readAsOwner('/config.yml');
623
+ const data = jsYaml.load(await streamToString(configData));
624
+ return data[key] ?? defaultValue;
625
+ }
626
+ async getPermission({ fromAppDid, toAppDid }) {
627
+ const permissions = await this.get('permissions');
628
+ return permissions?.[fromAppDid]?.[toAppDid] || 0;
629
+ }
630
+ /**
631
+ *
632
+ * @see https://blog.csdn.net/a1173537204/article/details/89765932
633
+ * @private
634
+ * @param {PermissionOptions} { fromAppDid, toAppDid }
635
+ * @param {number} permission
636
+ * @param {boolean} status
637
+ * @return {*} {Promise<void>}
638
+ * @memberof S3SpaceConfig
639
+ */
640
+ async setPermission({ fromAppDid, toAppDid }, permission, status) {
641
+ const permissions = await this.get('permissions', {});
642
+ // 我想知道原来的权限是啥样的?
643
+ const oldPermission = permissions?.[fromAppDid]?.[toAppDid] || 0;
644
+ permissions[fromAppDid] = Object.assign(permissions?.[fromAppDid] ?? {}, {
645
+ ...permissions?.[fromAppDid],
646
+ // eslint-disable-next-line no-bitwise
647
+ [toAppDid]: status ? oldPermission | permission : oldPermission & ~permission,
648
+ });
649
+ await this.set('permissions', permissions);
650
+ }
651
+ /**
652
+ *
653
+ * @refactor 这部分的实现大同小异,之后可以抽象到上层实现,这样可以减少代码量 @jianchao
654
+ * @description
655
+ * @param {PermissionOptions} options
656
+ * @param {boolean} value
657
+ * @return {*} {Promise<void>}
658
+ * @memberof ObjectSpace
659
+ */
660
+ setListable(options, value) {
661
+ return this.setPermission(options, Scopes['list:object'], value);
662
+ }
663
+ setReadable(options, value) {
664
+ return this.setPermission(options, Scopes['read:object'], value);
665
+ }
666
+ setWritable(options, value) {
667
+ return this.setPermission(options, Scopes['write:object'], value);
668
+ }
669
+ async isListable(options) {
670
+ const permission = await this.getPermission(options);
671
+ return Boolean((permission & Scopes['list:object']) === Scopes['list:object']);
672
+ }
673
+ async isReadable(options) {
674
+ const permission = await this.getPermission(options);
675
+ return Boolean((permission & Scopes['read:object']) === Scopes['read:object']);
676
+ }
677
+ async isWritable(options) {
678
+ const permission = await this.getPermission(options);
679
+ return Boolean((permission & Scopes['write:object']) === Scopes['write:object']);
680
+ }
681
+ static editable(key) {
682
+ return !ObjectSpace.READONLY_OBJECT_KEYS.includes(key);
683
+ }
684
+ }