1
1
// unified.ts
2
+ import { Attachment } from '@hcengineering/attachment'
2
3
import card , { Card , MasterTag , Tag } from '@hcengineering/card'
3
4
import core , {
4
5
Association ,
5
6
Attribute ,
7
+ Blob as PlatformBlob ,
8
+ Class ,
6
9
Doc ,
7
10
Enum ,
8
11
generateId ,
@@ -11,15 +14,17 @@ import core, {
11
14
} from '@hcengineering/core'
12
15
import * as fs from 'fs'
13
16
import * as yaml from 'js-yaml'
17
+ import { contentType } from 'mime-types'
14
18
import * as path from 'path'
15
19
import { IntlString } from '../../../platform/types'
16
- import { Props , UnifiedDoc , UnifiedMixin } from '../types'
20
+ import { Props , UnifiedDoc , UnifiedFile , UnifiedMixin } from '../types'
17
21
import { MetadataStorage , RelationMetadata } from './metadata'
18
22
import { readMarkdownContent , readYamlHeader } from './parsing'
19
23
20
24
export interface UnifiedDocProcessResult {
21
25
docs : Map < string , Array < UnifiedDoc < Doc > > >
22
26
mixins : Map < string , Array < UnifiedMixin < Doc , Doc > > >
27
+ files : Map < string , UnifiedFile >
23
28
}
24
29
25
30
export class UnifiedDocProcessor {
@@ -28,7 +33,8 @@ export class UnifiedDocProcessor {
28
33
async importFromDirectory ( directoryPath : string ) : Promise < UnifiedDocProcessResult > {
29
34
const result : UnifiedDocProcessResult = {
30
35
docs : new Map ( ) ,
31
- mixins : new Map ( )
36
+ mixins : new Map ( ) ,
37
+ files : new Map ( )
32
38
}
33
39
// Первый проход - собираем метаданные
34
40
await this . processMetadata ( directoryPath , result )
@@ -137,68 +143,6 @@ export class UnifiedDocProcessor {
137
143
}
138
144
}
139
145
140
- // private async processDirectory (
141
- // currentPath: string,
142
- // result: UnifiedDocProcessResult,
143
- // parentMasterTagId?: Ref<MasterTag>,
144
- // parentMasterTagAttrs?: Map<string, UnifiedDoc<Attribute<MasterTag>>>
145
- // ): Promise<void> {
146
- // const entries = fs.readdirSync(currentPath, { withFileTypes: true })
147
-
148
- // // Сначала обрабатываем YAML файлы (потенциальные мастер-теги)
149
- // for (const entry of entries) {
150
- // if (!entry.isFile() || !entry.name.endsWith('.yaml')) continue // todo: filter entries by extension
151
-
152
- // const yamlPath = path.resolve(currentPath, entry.name)
153
- // const yamlConfig = yaml.load(fs.readFileSync(yamlPath, 'utf8')) as Record<string, any>
154
-
155
- // switch (yamlConfig?.class) {
156
- // case card.class.MasterTag: {
157
- // const masterTagId = this.metadataStorage.getIdByFullPath(yamlPath) as Ref<MasterTag>
158
- // const masterTag = await this.createMasterTag(yamlConfig, masterTagId, parentMasterTagId)
159
-
160
- // const masterTagAttrs = await this.createAttributes(yamlConfig, masterTagId)
161
- // this.metadataStorage.setAttributes(yamlPath, masterTagAttrs)
162
-
163
- // const docs = result.docs.get(yamlPath) ?? []
164
- // docs.push(
165
- // masterTag,
166
- // ...Array.from(masterTagAttrs.values())
167
- // )
168
- // result.docs.set(yamlPath, docs)
169
-
170
- // const masterTagDir = path.join(currentPath, path.basename(yamlPath, '.yaml'))
171
- // if (fs.existsSync(masterTagDir) && fs.statSync(masterTagDir).isDirectory()) {
172
- // await this.processDirectory(masterTagDir, result, masterTagId, masterTagAttrs)
173
- // }
174
- // break
175
- // }
176
- // case card.class.Tag: {
177
- // if (parentMasterTagId === undefined) {
178
- // throw new Error('Tag should be inside master tag folder: ' + currentPath)
179
- // }
180
-
181
- // await this.processTag(yamlPath, yamlConfig, result, parentMasterTagId)
182
- // break
183
- // }
184
- // case core.class.Association: {
185
- // const association = await this.createAssociation(yamlPath, yamlConfig)
186
- // result.docs.set(yamlPath, [association])
187
- // break
188
- // }
189
- // default:
190
- // throw new Error('Unsupported class: ' + yamlConfig?.class) // todo: handle default case just convert to UnifiedDoc
191
- // }
192
- // }
193
-
194
- // if (parentMasterTagId === undefined || parentMasterTagAttrs === undefined) {
195
- // await this.processSystemCards(currentPath, result)
196
- // } else {
197
- // // await this.processCardDirectory(result, currentPath, parentMasterTagId, parentMasterTagAttrs) // todo: handle parent master tag attrs
198
- // await this.processCardDirectory(result, currentPath, parentMasterTagId)
199
- // }
200
- // }
201
-
202
146
private async processSystemCards (
203
147
currentDir : string ,
204
148
result : UnifiedDocProcessResult ,
@@ -239,6 +183,9 @@ export class UnifiedDocProcessor {
239
183
const card = cardWithRelations [ 0 ] as UnifiedDoc < Card >
240
184
await this . applyTags ( card , cardProps , cardPath , result )
241
185
186
+ const attachments = cardProps . attachments ?? [ ]
187
+ await this . processAttachments ( attachments , cardPath , card , result )
188
+
242
189
// Проверяем наличие дочерних карточек
243
190
const cardDir = path . join ( path . dirname ( cardPath ) , path . basename ( cardPath , '.md' ) )
244
191
if ( fs . existsSync ( cardDir ) && fs . statSync ( cardDir ) . isDirectory ( ) ) {
@@ -563,6 +510,50 @@ export class UnifiedDocProcessor {
563
510
}
564
511
}
565
512
513
+ private async processAttachments (
514
+ attachments : string [ ] ,
515
+ cardPath : string ,
516
+ card : UnifiedDoc < Card > ,
517
+ result : UnifiedDocProcessResult
518
+ ) : Promise < void > {
519
+ for ( const attachment of attachments ) {
520
+ const attachmentPath = path . resolve ( path . dirname ( cardPath ) , attachment )
521
+ const attachmentName = path . basename ( attachmentPath )
522
+ const fileId = this . metadataStorage . getIdByFullPath ( attachmentPath ) as Ref < PlatformBlob >
523
+ const type = contentType ( attachmentPath )
524
+ const size = fs . statSync ( attachmentPath ) . size
525
+
526
+ const file : UnifiedFile = {
527
+ _id : fileId , // id for datastore
528
+ name : attachmentName ,
529
+ blobProvider : async ( ) => {
530
+ const data = fs . readFileSync ( attachmentPath )
531
+ const props = type !== false ? { type } : undefined
532
+ return new Blob ( [ data ] , props )
533
+ }
534
+ }
535
+ result . files . set ( attachmentPath , file )
536
+
537
+ const attachmentId = this . metadataStorage . getIdByFullPath ( attachmentPath ) as Ref < Attachment >
538
+ const attachmentDoc : UnifiedDoc < Attachment > = {
539
+ _class : 'attachment:class:Attachment' as Ref < Class < Attachment > > ,
540
+ props : {
541
+ _id : attachmentId , // id for attachment doc
542
+ space : core . space . Workspace ,
543
+ attachedTo : card . props . _id as Ref < Card > ,
544
+ attachedToClass : card . _class ,
545
+ file : fileId ,
546
+ name : attachmentName ,
547
+ collection : 'attachments' ,
548
+ lastModified : Date . now ( ) ,
549
+ type : type !== false ? type : 'application/octet-stream' ,
550
+ size
551
+ }
552
+ }
553
+ result . docs . set ( attachmentPath , [ attachmentDoc ] )
554
+ }
555
+ }
556
+
566
557
private async createAssociation (
567
558
yamlPath : string ,
568
559
yamlConfig : Record < string , any >
0 commit comments