document-drive 0.0.22 → 0.0.24
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/package.json +18 -11
- package/src/server/index.ts +146 -55
- package/src/server/types.ts +4 -4
- package/src/storage/browser.ts +68 -38
- package/src/storage/filesystem.ts +50 -19
- package/src/storage/index.ts +1 -0
- package/src/storage/memory.ts +73 -7
- package/src/storage/prisma.ts +243 -0
- package/src/storage/sequelize.ts +375 -0
- package/src/storage/types.ts +35 -6
- package/src/utils.ts +18 -1
package/src/storage/memory.ts
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import { DocumentDriveAction } from 'document-model-libs/document-drive';
|
|
2
|
+
import {
|
|
3
|
+
BaseAction,
|
|
4
|
+
Document,
|
|
5
|
+
DocumentHeader,
|
|
6
|
+
Operation
|
|
7
|
+
} from 'document-model/document';
|
|
8
|
+
import { mergeOperations } from '..';
|
|
9
|
+
import { DocumentDriveStorage, DocumentStorage, IDriveStorage } from './types';
|
|
4
10
|
|
|
5
11
|
export class MemoryStorage implements IDriveStorage {
|
|
6
|
-
private documents: Record<string, Record<string,
|
|
7
|
-
private drives: Record<string,
|
|
12
|
+
private documents: Record<string, Record<string, DocumentStorage>>;
|
|
13
|
+
private drives: Record<string, DocumentDriveStorage>;
|
|
8
14
|
|
|
9
15
|
constructor() {
|
|
10
16
|
this.documents = {};
|
|
@@ -32,6 +38,51 @@ export class MemoryStorage implements IDriveStorage {
|
|
|
32
38
|
this.documents[drive]![id] = document;
|
|
33
39
|
}
|
|
34
40
|
|
|
41
|
+
async createDocument(drive: string, id: string, document: DocumentStorage) {
|
|
42
|
+
this.documents[drive] = this.documents[drive] ?? {};
|
|
43
|
+
const {
|
|
44
|
+
operations,
|
|
45
|
+
initialState,
|
|
46
|
+
name,
|
|
47
|
+
revision,
|
|
48
|
+
documentType,
|
|
49
|
+
created,
|
|
50
|
+
lastModified
|
|
51
|
+
} = document;
|
|
52
|
+
this.documents[drive]![id] = {
|
|
53
|
+
operations,
|
|
54
|
+
initialState,
|
|
55
|
+
name,
|
|
56
|
+
revision,
|
|
57
|
+
documentType,
|
|
58
|
+
created,
|
|
59
|
+
lastModified
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async addDocumentOperations(
|
|
64
|
+
drive: string,
|
|
65
|
+
id: string,
|
|
66
|
+
operations: Operation[],
|
|
67
|
+
header: DocumentHeader
|
|
68
|
+
): Promise<void> {
|
|
69
|
+
const document = await this.getDocument(drive, id);
|
|
70
|
+
if (!document) {
|
|
71
|
+
throw new Error(`Document with id ${id} not found`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const mergedOperations = mergeOperations(
|
|
75
|
+
document.operations,
|
|
76
|
+
operations
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
this.documents[drive]![id] = {
|
|
80
|
+
...document,
|
|
81
|
+
...header,
|
|
82
|
+
operations: mergedOperations
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
35
86
|
async deleteDocument(drive: string, id: string) {
|
|
36
87
|
if (!this.documents[drive]) {
|
|
37
88
|
throw new Error(`Drive with id ${drive} not found`);
|
|
@@ -51,8 +102,23 @@ export class MemoryStorage implements IDriveStorage {
|
|
|
51
102
|
return drive;
|
|
52
103
|
}
|
|
53
104
|
|
|
54
|
-
async
|
|
55
|
-
this.drives[
|
|
105
|
+
async createDrive(id: string, drive: DocumentDriveStorage) {
|
|
106
|
+
this.drives[id] = drive;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async addDriveOperations(
|
|
110
|
+
id: string,
|
|
111
|
+
operations: Operation<DocumentDriveAction | BaseAction>[],
|
|
112
|
+
header: DocumentHeader
|
|
113
|
+
): Promise<void> {
|
|
114
|
+
const drive = await this.getDrive(id);
|
|
115
|
+
const mergedOperations = mergeOperations(drive.operations, operations);
|
|
116
|
+
|
|
117
|
+
this.drives[id] = {
|
|
118
|
+
...drive,
|
|
119
|
+
...header,
|
|
120
|
+
operations: mergedOperations
|
|
121
|
+
};
|
|
56
122
|
}
|
|
57
123
|
|
|
58
124
|
async deleteDrive(id: string) {
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { type Prisma } from '@prisma/client';
|
|
2
|
+
import {
|
|
3
|
+
DocumentDriveLocalState,
|
|
4
|
+
DocumentDriveState
|
|
5
|
+
} from 'document-model-libs/document-drive';
|
|
6
|
+
import {
|
|
7
|
+
DocumentHeader,
|
|
8
|
+
ExtendedState,
|
|
9
|
+
Operation,
|
|
10
|
+
OperationScope
|
|
11
|
+
} from 'document-model/document';
|
|
12
|
+
import { DocumentDriveStorage, DocumentStorage, IDriveStorage } from './types';
|
|
13
|
+
|
|
14
|
+
export class PrismaStorage implements IDriveStorage {
|
|
15
|
+
private db: Prisma.TransactionClient;
|
|
16
|
+
|
|
17
|
+
constructor(db: Prisma.TransactionClient) {
|
|
18
|
+
this.db = db;
|
|
19
|
+
}
|
|
20
|
+
async createDrive(id: string, drive: DocumentDriveStorage): Promise<void> {
|
|
21
|
+
await this.createDocument('drives', id, drive as DocumentStorage);
|
|
22
|
+
}
|
|
23
|
+
async addDriveOperations(
|
|
24
|
+
id: string,
|
|
25
|
+
operations: Operation[],
|
|
26
|
+
header: DocumentHeader
|
|
27
|
+
): Promise<void> {
|
|
28
|
+
await this.addDocumentOperations('drives', id, operations, header);
|
|
29
|
+
}
|
|
30
|
+
async createDocument(
|
|
31
|
+
drive: string,
|
|
32
|
+
id: string,
|
|
33
|
+
document: DocumentStorage
|
|
34
|
+
): Promise<void> {
|
|
35
|
+
await this.db.document.upsert({
|
|
36
|
+
where: {
|
|
37
|
+
id_driveId: {
|
|
38
|
+
id,
|
|
39
|
+
driveId: drive
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
update: {},
|
|
43
|
+
create: {
|
|
44
|
+
name: document.name,
|
|
45
|
+
documentType: document.documentType,
|
|
46
|
+
driveId: drive,
|
|
47
|
+
initialState: document.initialState as Prisma.InputJsonObject,
|
|
48
|
+
lastModified: document.lastModified,
|
|
49
|
+
revision: document.revision,
|
|
50
|
+
id
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
async addDocumentOperations(
|
|
55
|
+
drive: string,
|
|
56
|
+
id: string,
|
|
57
|
+
operations: Operation[],
|
|
58
|
+
header: DocumentHeader
|
|
59
|
+
): Promise<void> {
|
|
60
|
+
const document = await this.getDocument(drive, id);
|
|
61
|
+
if (!document) {
|
|
62
|
+
throw new Error(`Document with id ${id} not found`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
await Promise.all(
|
|
67
|
+
operations.map(async op => {
|
|
68
|
+
return this.db.operation.upsert({
|
|
69
|
+
where: {
|
|
70
|
+
driveId_documentId_scope_branch_index: {
|
|
71
|
+
driveId: drive,
|
|
72
|
+
documentId: id,
|
|
73
|
+
scope: op.scope,
|
|
74
|
+
branch: 'main',
|
|
75
|
+
index: op.index
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
create: {
|
|
79
|
+
driveId: drive,
|
|
80
|
+
documentId: id,
|
|
81
|
+
hash: op.hash,
|
|
82
|
+
index: op.index,
|
|
83
|
+
input: op.input as Prisma.InputJsonObject,
|
|
84
|
+
timestamp: op.timestamp,
|
|
85
|
+
type: op.type,
|
|
86
|
+
scope: op.scope,
|
|
87
|
+
branch: 'main'
|
|
88
|
+
},
|
|
89
|
+
update: {
|
|
90
|
+
driveId: drive,
|
|
91
|
+
documentId: id,
|
|
92
|
+
hash: op.hash,
|
|
93
|
+
index: op.index,
|
|
94
|
+
input: op.input as Prisma.InputJsonObject,
|
|
95
|
+
timestamp: op.timestamp,
|
|
96
|
+
type: op.type,
|
|
97
|
+
scope: op.scope,
|
|
98
|
+
branch: 'main'
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
})
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
await this.db.document.update({
|
|
105
|
+
where: {
|
|
106
|
+
id_driveId: {
|
|
107
|
+
id,
|
|
108
|
+
driveId: 'drives'
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
data: {
|
|
112
|
+
lastModified: header.lastModified,
|
|
113
|
+
revision: header.revision
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
} catch (e) {
|
|
117
|
+
console.log(e);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
await this.db.document.upsert({
|
|
121
|
+
where: {
|
|
122
|
+
id_driveId: {
|
|
123
|
+
id: 'drives',
|
|
124
|
+
driveId: id
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
create: {
|
|
128
|
+
id: 'drives',
|
|
129
|
+
driveId: id,
|
|
130
|
+
documentType: header.documentType,
|
|
131
|
+
initialState: document.initialState,
|
|
132
|
+
lastModified: header.lastModified,
|
|
133
|
+
revision: header.revision,
|
|
134
|
+
created: header.created
|
|
135
|
+
},
|
|
136
|
+
update: {
|
|
137
|
+
lastModified: header.lastModified,
|
|
138
|
+
revision: header.revision
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async getDocuments(drive: string) {
|
|
144
|
+
const docs = await this.db.document.findMany({
|
|
145
|
+
where: {
|
|
146
|
+
AND: {
|
|
147
|
+
driveId: drive,
|
|
148
|
+
NOT: {
|
|
149
|
+
id: 'drives'
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
return docs.map(doc => doc.id);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async getDocument(driveId: string, id: string) {
|
|
159
|
+
const result = await this.db.document.findFirst({
|
|
160
|
+
where: {
|
|
161
|
+
id: id,
|
|
162
|
+
driveId: driveId
|
|
163
|
+
},
|
|
164
|
+
include: {
|
|
165
|
+
operations: {
|
|
166
|
+
include: {
|
|
167
|
+
attachments: true
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
if (result === null) {
|
|
174
|
+
throw new Error(`Document with id ${id} not found`);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const dbDoc = result;
|
|
178
|
+
|
|
179
|
+
const doc = {
|
|
180
|
+
created: dbDoc.created.toISOString(),
|
|
181
|
+
name: dbDoc.name ? dbDoc.name : '',
|
|
182
|
+
documentType: dbDoc.documentType,
|
|
183
|
+
initialState: dbDoc.initialState as ExtendedState<
|
|
184
|
+
DocumentDriveState,
|
|
185
|
+
DocumentDriveLocalState
|
|
186
|
+
>,
|
|
187
|
+
lastModified: dbDoc.lastModified.toISOString(),
|
|
188
|
+
operations: {
|
|
189
|
+
global: dbDoc.operations
|
|
190
|
+
.filter(op => op.scope === 'global')
|
|
191
|
+
.map(op => ({
|
|
192
|
+
hash: op.hash,
|
|
193
|
+
index: op.index,
|
|
194
|
+
timestamp: new Date(op.timestamp).toISOString(),
|
|
195
|
+
input: op.input,
|
|
196
|
+
type: op.type,
|
|
197
|
+
scope: op.scope as OperationScope
|
|
198
|
+
// attachments: fileRegistry
|
|
199
|
+
})),
|
|
200
|
+
local: dbDoc.operations
|
|
201
|
+
.filter(op => op.scope === 'local')
|
|
202
|
+
.map(op => ({
|
|
203
|
+
hash: op.hash,
|
|
204
|
+
index: op.index,
|
|
205
|
+
timestamp: new Date(op.timestamp).toISOString(),
|
|
206
|
+
input: op.input,
|
|
207
|
+
type: op.type,
|
|
208
|
+
scope: op.scope as OperationScope
|
|
209
|
+
// attachments: fileRegistry
|
|
210
|
+
}))
|
|
211
|
+
},
|
|
212
|
+
revision: dbDoc.revision as Required<Record<OperationScope, number>>
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
return doc;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
async deleteDocument(drive: string, id: string) {
|
|
219
|
+
await this.db.document.deleteMany({
|
|
220
|
+
where: {
|
|
221
|
+
driveId: drive,
|
|
222
|
+
id: id
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async getDrives() {
|
|
228
|
+
return this.getDocuments('drives');
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
async getDrive(id: string) {
|
|
232
|
+
return this.getDocument('drives', id) as Promise<DocumentDriveStorage>;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
async deleteDrive(id: string) {
|
|
236
|
+
await this.deleteDocument('drives', id);
|
|
237
|
+
await this.db.document.deleteMany({
|
|
238
|
+
where: {
|
|
239
|
+
driveId: id
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
}
|
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DocumentDriveLocalState,
|
|
3
|
+
DocumentDriveState
|
|
4
|
+
} from 'document-model-libs/document-drive';
|
|
5
|
+
import {
|
|
6
|
+
AttachmentInput,
|
|
7
|
+
DocumentHeader,
|
|
8
|
+
ExtendedState,
|
|
9
|
+
Operation,
|
|
10
|
+
OperationScope
|
|
11
|
+
} from 'document-model/document';
|
|
12
|
+
import { DataTypes, Options, Sequelize } from 'sequelize';
|
|
13
|
+
import { DocumentDriveStorage, DocumentStorage, IDriveStorage } from './types';
|
|
14
|
+
|
|
15
|
+
export class SequelizeStorage implements IDriveStorage {
|
|
16
|
+
private db: Sequelize;
|
|
17
|
+
|
|
18
|
+
constructor(options: Options) {
|
|
19
|
+
this.db = new Sequelize(options);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public syncModels() {
|
|
23
|
+
const Document = this.db.define('document', {
|
|
24
|
+
id: {
|
|
25
|
+
type: DataTypes.STRING,
|
|
26
|
+
primaryKey: true
|
|
27
|
+
},
|
|
28
|
+
driveId: {
|
|
29
|
+
type: DataTypes.STRING,
|
|
30
|
+
primaryKey: true
|
|
31
|
+
},
|
|
32
|
+
name: DataTypes.STRING,
|
|
33
|
+
documentType: DataTypes.STRING,
|
|
34
|
+
initialState: DataTypes.JSON,
|
|
35
|
+
lastModified: DataTypes.DATE,
|
|
36
|
+
revision: DataTypes.JSON
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const Operation = this.db.define('operation', {
|
|
40
|
+
driveId: {
|
|
41
|
+
type: DataTypes.STRING,
|
|
42
|
+
primaryKey: true
|
|
43
|
+
},
|
|
44
|
+
documentId: {
|
|
45
|
+
type: DataTypes.STRING,
|
|
46
|
+
primaryKey: true
|
|
47
|
+
},
|
|
48
|
+
hash: DataTypes.STRING,
|
|
49
|
+
index: {
|
|
50
|
+
type: DataTypes.INTEGER,
|
|
51
|
+
primaryKey: true
|
|
52
|
+
},
|
|
53
|
+
input: DataTypes.JSON,
|
|
54
|
+
timestamp: DataTypes.DATE,
|
|
55
|
+
type: DataTypes.STRING,
|
|
56
|
+
scope: {
|
|
57
|
+
type: DataTypes.STRING,
|
|
58
|
+
primaryKey: true
|
|
59
|
+
},
|
|
60
|
+
branch: {
|
|
61
|
+
type: DataTypes.STRING,
|
|
62
|
+
primaryKey: true
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const Attachment = this.db.define('attachment', {
|
|
67
|
+
driveId: {
|
|
68
|
+
type: DataTypes.STRING,
|
|
69
|
+
primaryKey: true
|
|
70
|
+
},
|
|
71
|
+
documentId: {
|
|
72
|
+
type: DataTypes.STRING,
|
|
73
|
+
primaryKey: true
|
|
74
|
+
},
|
|
75
|
+
scope: {
|
|
76
|
+
type: DataTypes.STRING,
|
|
77
|
+
primaryKey: true
|
|
78
|
+
},
|
|
79
|
+
branch: {
|
|
80
|
+
type: DataTypes.STRING,
|
|
81
|
+
primaryKey: true
|
|
82
|
+
},
|
|
83
|
+
index: {
|
|
84
|
+
type: DataTypes.STRING,
|
|
85
|
+
primaryKey: true
|
|
86
|
+
},
|
|
87
|
+
hash: {
|
|
88
|
+
type: DataTypes.STRING,
|
|
89
|
+
primaryKey: true
|
|
90
|
+
},
|
|
91
|
+
mimeType: DataTypes.STRING,
|
|
92
|
+
fileName: DataTypes.STRING,
|
|
93
|
+
extension: DataTypes.STRING,
|
|
94
|
+
data: DataTypes.BLOB
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
Operation.hasMany(Attachment, {
|
|
98
|
+
onDelete: 'CASCADE'
|
|
99
|
+
});
|
|
100
|
+
Attachment.belongsTo(Operation);
|
|
101
|
+
Document.hasMany(Operation, {
|
|
102
|
+
onDelete: 'CASCADE'
|
|
103
|
+
});
|
|
104
|
+
Operation.belongsTo(Document);
|
|
105
|
+
|
|
106
|
+
return this.db.sync({ force: true });
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async createDrive(id: string, drive: DocumentDriveStorage): Promise<void> {
|
|
110
|
+
await this.createDocument('drives', id, drive as DocumentStorage);
|
|
111
|
+
}
|
|
112
|
+
async addDriveOperations(
|
|
113
|
+
id: string,
|
|
114
|
+
operations: Operation[],
|
|
115
|
+
header: DocumentHeader
|
|
116
|
+
): Promise<void> {
|
|
117
|
+
await this.addDocumentOperations('drives', id, operations, header);
|
|
118
|
+
}
|
|
119
|
+
async createDocument(
|
|
120
|
+
drive: string,
|
|
121
|
+
id: string,
|
|
122
|
+
document: DocumentStorage
|
|
123
|
+
): Promise<void> {
|
|
124
|
+
const Document = this.db.models.document;
|
|
125
|
+
|
|
126
|
+
if (!Document) {
|
|
127
|
+
throw new Error('Document model not found');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
await Document.create({
|
|
131
|
+
id: id,
|
|
132
|
+
driveId: drive,
|
|
133
|
+
name: document.name,
|
|
134
|
+
documentType: document.documentType,
|
|
135
|
+
initialState: document.initialState,
|
|
136
|
+
lastModified: document.lastModified,
|
|
137
|
+
revision: document.revision
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
async addDocumentOperations(
|
|
141
|
+
drive: string,
|
|
142
|
+
id: string,
|
|
143
|
+
operations: Operation[],
|
|
144
|
+
header: DocumentHeader
|
|
145
|
+
): Promise<void> {
|
|
146
|
+
const document = await this.getDocument(drive, id);
|
|
147
|
+
if (!document) {
|
|
148
|
+
throw new Error(`Document with id ${id} not found`);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const Operation = this.db.models.operation;
|
|
152
|
+
if (!Operation) {
|
|
153
|
+
throw new Error('Operation model not found');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
await Promise.all(
|
|
157
|
+
operations.map(async op => {
|
|
158
|
+
return Operation.create({
|
|
159
|
+
driveId: drive,
|
|
160
|
+
documentId: id,
|
|
161
|
+
hash: op.hash,
|
|
162
|
+
index: op.index,
|
|
163
|
+
input: op.input,
|
|
164
|
+
timestamp: op.timestamp,
|
|
165
|
+
type: op.type,
|
|
166
|
+
scope: op.scope,
|
|
167
|
+
branch: 'main'
|
|
168
|
+
}).then(async () => {
|
|
169
|
+
if (op.attachments) {
|
|
170
|
+
await this._addDocumentOperationAttachments(
|
|
171
|
+
drive,
|
|
172
|
+
id,
|
|
173
|
+
op,
|
|
174
|
+
op.attachments
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
})
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
const Document = this.db.models.document;
|
|
182
|
+
if (!Document) {
|
|
183
|
+
throw new Error('Document model not found');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
await Document.update(
|
|
187
|
+
{
|
|
188
|
+
lastModified: header.lastModified,
|
|
189
|
+
revision: header.revision
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
where: {
|
|
193
|
+
id: id,
|
|
194
|
+
driveId: drive
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
async _addDocumentOperationAttachments(
|
|
201
|
+
driveId: string,
|
|
202
|
+
documentId: string,
|
|
203
|
+
operation: Operation,
|
|
204
|
+
attachments: AttachmentInput[]
|
|
205
|
+
) {
|
|
206
|
+
const Attachment = this.db.models.attachment;
|
|
207
|
+
if (!Attachment) {
|
|
208
|
+
throw new Error('Attachment model not found');
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
await Promise.all(
|
|
212
|
+
attachments.map(async attachment => {
|
|
213
|
+
return Attachment.create({
|
|
214
|
+
driveId: driveId,
|
|
215
|
+
documentId: documentId,
|
|
216
|
+
scope: operation.scope,
|
|
217
|
+
branch: 'main',
|
|
218
|
+
index: operation.index,
|
|
219
|
+
mimeType: attachment.mimeType,
|
|
220
|
+
fileName: attachment.fileName,
|
|
221
|
+
extension: attachment.extension,
|
|
222
|
+
data: attachment.data,
|
|
223
|
+
hash: attachment.hash
|
|
224
|
+
});
|
|
225
|
+
})
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
async getDocuments(drive: string) {
|
|
230
|
+
const Document = this.db.models.document;
|
|
231
|
+
if (!Document) {
|
|
232
|
+
throw new Error('Document model not found');
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const result = await Document.findAll({
|
|
236
|
+
attributes: ['id'],
|
|
237
|
+
where: {
|
|
238
|
+
driveId: drive
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
const ids = result.map((e: { dataValues: { id: string } }) => {
|
|
243
|
+
const { id } = e.dataValues;
|
|
244
|
+
return id;
|
|
245
|
+
});
|
|
246
|
+
return ids;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
async getDocument(driveId: string, id: string) {
|
|
250
|
+
const Document = this.db.models.document;
|
|
251
|
+
if (!Document) {
|
|
252
|
+
throw new Error('Document model not found');
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const entry = await Document.findOne({
|
|
256
|
+
where: {
|
|
257
|
+
id: id,
|
|
258
|
+
driveId: driveId
|
|
259
|
+
},
|
|
260
|
+
include: [
|
|
261
|
+
{
|
|
262
|
+
model: this.db.models.operation,
|
|
263
|
+
as: 'operations'
|
|
264
|
+
}
|
|
265
|
+
]
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
if (entry === null) {
|
|
269
|
+
throw new Error(`Document with id ${id} not found`);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
273
|
+
const document: {
|
|
274
|
+
operations: [
|
|
275
|
+
{
|
|
276
|
+
hash: string;
|
|
277
|
+
index: number;
|
|
278
|
+
timestamp: Date;
|
|
279
|
+
input: JSON;
|
|
280
|
+
type: string;
|
|
281
|
+
scope: string;
|
|
282
|
+
}
|
|
283
|
+
];
|
|
284
|
+
revision: Required<Record<OperationScope, number>>;
|
|
285
|
+
createdAt: Date;
|
|
286
|
+
name: string;
|
|
287
|
+
updatedAt: Date;
|
|
288
|
+
documentType: string;
|
|
289
|
+
initialState: ExtendedState<
|
|
290
|
+
DocumentDriveState,
|
|
291
|
+
DocumentDriveLocalState
|
|
292
|
+
>;
|
|
293
|
+
} = entry.dataValues;
|
|
294
|
+
const Operation = this.db.models.operation;
|
|
295
|
+
if (!Operation) {
|
|
296
|
+
throw new Error('Operation model not found');
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const operations = document.operations.map(
|
|
300
|
+
(op: {
|
|
301
|
+
hash: string;
|
|
302
|
+
index: number;
|
|
303
|
+
timestamp: Date;
|
|
304
|
+
input: JSON;
|
|
305
|
+
type: string;
|
|
306
|
+
scope: string;
|
|
307
|
+
}) => ({
|
|
308
|
+
hash: op.hash,
|
|
309
|
+
index: op.index,
|
|
310
|
+
timestamp: new Date(op.timestamp).toISOString(),
|
|
311
|
+
input: op.input,
|
|
312
|
+
type: op.type,
|
|
313
|
+
scope: op.scope as OperationScope
|
|
314
|
+
// attachments: fileRegistry
|
|
315
|
+
})
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
const doc = {
|
|
319
|
+
created: document.createdAt.toISOString(),
|
|
320
|
+
name: document.name ? document.name : '',
|
|
321
|
+
documentType: document.documentType,
|
|
322
|
+
initialState: document.initialState,
|
|
323
|
+
lastModified: document.updatedAt.toISOString(),
|
|
324
|
+
operations: {
|
|
325
|
+
global: operations.filter(
|
|
326
|
+
(op: Operation) => op.scope === 'global'
|
|
327
|
+
),
|
|
328
|
+
local: operations.filter(
|
|
329
|
+
(op: Operation) => op.scope === 'local'
|
|
330
|
+
)
|
|
331
|
+
},
|
|
332
|
+
revision: document.revision
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
return doc;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
async deleteDocument(drive: string, id: string) {
|
|
339
|
+
const Document = this.db.models.document;
|
|
340
|
+
if (!Document) {
|
|
341
|
+
throw new Error('Document model not found');
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
await Document.destroy({
|
|
345
|
+
where: {
|
|
346
|
+
id: id,
|
|
347
|
+
driveId: drive
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
async getDrives() {
|
|
353
|
+
return this.getDocuments('drives');
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
async getDrive(id: string) {
|
|
357
|
+
const doc = await this.getDocument('drives', id);
|
|
358
|
+
return doc as DocumentDriveStorage;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
async deleteDrive(id: string) {
|
|
362
|
+
await this.deleteDocument('drives', id);
|
|
363
|
+
|
|
364
|
+
const Document = this.db.models.document;
|
|
365
|
+
if (!Document) {
|
|
366
|
+
throw new Error('Document model not found');
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
await Document.destroy({
|
|
370
|
+
where: {
|
|
371
|
+
driveId: id
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
}
|