@things-factory/board-service 8.0.5 → 9.0.0-beta.12

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 (66) hide show
  1. package/dist-server/controllers/headless-pdf-to-image.js +7 -2
  2. package/dist-server/controllers/headless-pdf-to-image.js.map +1 -1
  3. package/dist-server/tsconfig.tsbuildinfo +1 -1
  4. package/package.json +7 -7
  5. package/views/internal-board-full-feature-view.html +1 -2
  6. package/views/internal-board-player-view.html +0 -1
  7. package/views/internal-board-service-view.html +0 -1
  8. package/server/constants/error-code.ts +0 -2
  9. package/server/controllers/analyzer/analyze-integration.ts +0 -142
  10. package/server/controllers/fonts.ts +0 -83
  11. package/server/controllers/headless-model.ts +0 -53
  12. package/server/controllers/headless-pdf-to-image.ts +0 -103
  13. package/server/controllers/headless-playlist.ts +0 -71
  14. package/server/controllers/headless-pool-for-board.ts +0 -71
  15. package/server/controllers/headless-pool-for-label.ts +0 -141
  16. package/server/controllers/index.ts +0 -11
  17. package/server/controllers/label-command.ts +0 -62
  18. package/server/controllers/pdf.ts +0 -132
  19. package/server/controllers/screenshot.ts +0 -127
  20. package/server/controllers/thumbnail.ts +0 -18
  21. package/server/errors/index.ts +0 -1
  22. package/server/errors/license-error.ts +0 -21
  23. package/server/index.ts +0 -36
  24. package/server/migrations/1556862253000-SeedGroup.ts +0 -51
  25. package/server/migrations/index.ts +0 -9
  26. package/server/routers/internal-board-view-router.ts +0 -33
  27. package/server/routers/standalone-board-service-router.ts +0 -326
  28. package/server/routes.ts +0 -25
  29. package/server/service/analysis/analysis-query.ts +0 -13
  30. package/server/service/analysis/index.ts +0 -3
  31. package/server/service/board/board-history.ts +0 -137
  32. package/server/service/board/board-mutation.ts +0 -446
  33. package/server/service/board/board-query.ts +0 -180
  34. package/server/service/board/board-subscription.ts +0 -43
  35. package/server/service/board/board-type.ts +0 -58
  36. package/server/service/board/board.ts +0 -125
  37. package/server/service/board/event-subscriber.ts +0 -68
  38. package/server/service/board/index.ts +0 -10
  39. package/server/service/board-favorite/board-favorite-query.ts +0 -53
  40. package/server/service/board-favorite/board-favorite-type.ts +0 -18
  41. package/server/service/board-favorite/index.ts +0 -4
  42. package/server/service/board-template/board-template-mutation.ts +0 -161
  43. package/server/service/board-template/board-template-query.ts +0 -121
  44. package/server/service/board-template/board-template-type.ts +0 -53
  45. package/server/service/board-template/board-template.ts +0 -114
  46. package/server/service/board-template/index.ts +0 -7
  47. package/server/service/group/group-mutation.ts +0 -82
  48. package/server/service/group/group-query.ts +0 -58
  49. package/server/service/group/group-type.ts +0 -30
  50. package/server/service/group/group.ts +0 -69
  51. package/server/service/group/index.ts +0 -6
  52. package/server/service/index.ts +0 -56
  53. package/server/service/permission/domain-permission-subscriber.ts +0 -27
  54. package/server/service/permission/index.ts +0 -3
  55. package/server/service/play-group/event-subscriber.ts +0 -58
  56. package/server/service/play-group/index.ts +0 -9
  57. package/server/service/play-group/play-group-mutation.ts +0 -148
  58. package/server/service/play-group/play-group-query.ts +0 -92
  59. package/server/service/play-group/play-group-subscription.ts +0 -43
  60. package/server/service/play-group/play-group-type.ts +0 -30
  61. package/server/service/play-group/play-group.ts +0 -74
  62. package/server/service/theme/index.ts +0 -7
  63. package/server/service/theme/theme-mutation.ts +0 -128
  64. package/server/service/theme/theme-query.ts +0 -48
  65. package/server/service/theme/theme-type.ts +0 -55
  66. package/server/service/theme/theme.ts +0 -97
@@ -1,446 +0,0 @@
1
- import { Arg, Ctx, Mutation, Resolver, Directive } from 'type-graphql'
2
- import { EntityManager, In } from 'typeorm'
3
- import type { FileUpload } from 'graphql-upload/GraphQLUpload.js'
4
- import GraphQLUpload from 'graphql-upload/GraphQLUpload.js'
5
- import { Domain, getDataSource, getRedirectSubdomainPath, getRepository } from '@things-factory/shell'
6
-
7
- import { thumbnail } from '../../controllers/thumbnail'
8
- import { Group } from '../group/group'
9
- import { Board } from './board'
10
- import { BoardHistory } from './board-history'
11
- import { BoardPatch, NewBoard } from './board-type'
12
-
13
- async function parseJSONFile(uploadedFile: FileUpload): Promise<any> {
14
- var { createReadStream } = await uploadedFile
15
-
16
- return new Promise((resolve, reject) => {
17
- const chunks: Uint8Array[] = []
18
-
19
- createReadStream()
20
- .on('data', (chunk: Uint8Array) => {
21
- chunks.push(chunk)
22
- })
23
- .on('end', () => {
24
- try {
25
- const fileContents = Buffer.concat(chunks).toString('utf-8')
26
- const jsonData = JSON.parse(fileContents)
27
- resolve(jsonData)
28
- } catch (error) {
29
- reject(error)
30
- }
31
- })
32
- .on('error', (error: Error) => {
33
- reject(error)
34
- })
35
- })
36
- }
37
-
38
- @Resolver(Board)
39
- export class BoardMutation {
40
- @Directive('@transaction')
41
- @Directive('@privilege(category: "board", privilege: "mutation", domainOwnerGranted: true)')
42
- @Mutation(returns => Board, { description: 'To create new Board' })
43
- async createBoard(@Arg('board') board: NewBoard, @Ctx() context: ResolverContext): Promise<Board> {
44
- const { domain, user, notify, tx } = context.state
45
- const repository = tx.getRepository(Board)
46
- const groupRepository = tx.getRepository(Group)
47
-
48
- const oldBoard: Board = await repository.findOneBy({
49
- name: board.name,
50
- domain: { id: domain.id }
51
- })
52
-
53
- if (oldBoard) {
54
- throw new Error(context.t('error.board name is already taken', { name: board.name }))
55
- }
56
-
57
- const newBoard: Board = {
58
- ...board
59
- }
60
-
61
- newBoard.thumbnail ||=
62
- 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==' /* empty thumbnail */
63
-
64
- if (board.groupId) {
65
- newBoard.group = await groupRepository.findOneBy({
66
- id: board.groupId
67
- })
68
- }
69
-
70
- const created = await repository.save({
71
- domain,
72
- ...newBoard,
73
- state: 'draft',
74
- creator: user,
75
- updater: user
76
- })
77
-
78
- notify &&
79
- notify({
80
- mode: 'in-app',
81
- title: `Board '${created.name}' created`,
82
- body: `Board '${created.name}' created by ${user.name}\n${created.description}`,
83
- url: getRedirectSubdomainPath(context, domain.subdomain, `/board-viewer/${created.id}`)
84
- })
85
-
86
- return created
87
- }
88
-
89
- @Directive('@transaction')
90
- @Directive('@privilege(category: "board", privilege: "mutation", domainOwnerGranted: true)')
91
- @Mutation(returns => Board, { description: 'To clone a Board from existing Board' })
92
- async cloneBoard(
93
- @Arg('id') id: string,
94
- @Arg('patch') patch: BoardPatch,
95
- @Arg('targetSubdomain') targetSubdomain: string,
96
- @Arg('targetGroupId', { nullable: true }) targetGroupId: string,
97
- @Ctx() context: ResolverContext
98
- ): Promise<Board> {
99
- const { domain, user, notify, tx } = context.state
100
- const { t } = context
101
- const repository = tx.getRepository(Board)
102
-
103
- const board = await repository.findOneBy({ domain: { id: In([domain.id, domain.parentId].filter(Boolean)) }, id })
104
-
105
- if (!patch.name || (patch.name == board.name && targetSubdomain == domain.subdomain)) {
106
- throw t('error.name must be unique from the original board', { name: patch.name })
107
- }
108
-
109
- if (targetSubdomain != domain.subdomain) {
110
- if ((await repository.count({ where: { domain: { subdomain: targetSubdomain }, name: patch.name } })) > 0) {
111
- throw t('error.name must be unique from the original board', { name: patch.name })
112
- }
113
- }
114
-
115
- var targetDomain = domain
116
- if (targetDomain && domain.subdomain != targetSubdomain) {
117
- targetDomain = await tx.getRepository(Domain).findOneBy({ subdomain: targetSubdomain })
118
- if (!targetDomain) {
119
- throw `given subdomain(${targetSubdomain}) not found`
120
- }
121
- }
122
-
123
- var targetGroup = null
124
- if (targetGroupId) {
125
- targetGroup = await tx.getRepository(Group).findOneBy({ domain: { id: targetDomain.id }, id: targetGroupId })
126
- if (!targetGroup) {
127
- throw `given group(${targetGroupId}) in domain(${targetSubdomain}) not found`
128
- }
129
- }
130
-
131
- const { id: excluded, ...clone } = board
132
-
133
- const cloned = await repository.save({
134
- domain: targetDomain,
135
- ...clone,
136
- ...patch,
137
- group: targetGroup,
138
- version: 0,
139
- state: 'draft',
140
- updater: user,
141
- creator: user
142
- })
143
-
144
- notify &&
145
- notify({
146
- mode: 'in-app',
147
- title: `Board '${cloned.name}' cloned`,
148
- body: `Board '${cloned.name}' cloned by ${user.name}\n${cloned.description}`,
149
- image: getRedirectSubdomainPath(context, targetSubdomain, `/thumbnail/${cloned.id}`),
150
- url: getRedirectSubdomainPath(context, targetSubdomain, `/board-viewer/${cloned.id}`)
151
- })
152
-
153
- return cloned
154
- }
155
-
156
- @Directive('@transaction')
157
- @Directive('@privilege(category: "board", privilege: "mutation", domainOwnerGranted: true)')
158
- @Mutation(returns => Board, { description: 'To modify Board information' })
159
- async updateBoard(
160
- @Arg('id') id: string,
161
- @Arg('patch') patch: BoardPatch,
162
- @Ctx() context: ResolverContext
163
- ): Promise<Board> {
164
- const { domain, user, notify, tx } = context.state
165
- const repository = tx.getRepository(Board)
166
-
167
- const board = await repository.findOne({
168
- where: { domain: { id: domain.id }, id },
169
- relations: ['creator']
170
- })
171
-
172
- if (patch.model) {
173
- const thumbnailPromise = thumbnail({
174
- model: patch.model,
175
- context
176
- })
177
-
178
- try {
179
- const thumbnailBase64 = await Promise.race([
180
- thumbnailPromise,
181
- new Promise((_, reject) => setTimeout(() => reject(new Error('5 seconds timeout')), 5000))
182
- ])
183
-
184
- patch.thumbnail = 'data:image/png;base64,' + thumbnailBase64.toString('base64')
185
- } catch (e) {
186
- console.warn(`Failed to get thumbnail for '${board.name}' in first 5 seconds`)
187
- // 5초 안에 썸네일이 생성되지 않았으므로 모델만 저장합니다.
188
- patch.thumbnail =
189
- 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==' /* empty thumbnail */
190
-
191
- Promise.race([
192
- thumbnailPromise,
193
- new Promise((_, reject) => setTimeout(() => reject(new Error('Thumbnail extended timeout')), 5000))
194
- ])
195
- .then(async thumbnailBase64 => {
196
- /* use new resource manager */
197
- await getDataSource().transaction(async (tx: EntityManager) => {
198
- await tx.getRepository(Board).save({
199
- id: updated.id,
200
- thumbnail: 'data:image/png;base64,' + thumbnailBase64.toString('base64')
201
- })
202
- })
203
- })
204
- .catch(error => {
205
- console.error(`Failed to save thumbnail for '${board.name}' even after extended time:`, error)
206
- })
207
- }
208
- }
209
-
210
- const { groupId, ...patched } = patch
211
-
212
- if (groupId !== undefined) {
213
- const groupRepository = tx.getRepository(Group)
214
- board.group = groupId
215
- ? (await groupRepository.findOneBy({
216
- domain: { id: domain.id },
217
- id: groupId
218
- })) || null
219
- : null
220
- }
221
-
222
- const updated = await repository.save({
223
- domain,
224
- ...board,
225
- ...patched,
226
- state: 'draft',
227
- updater: user
228
- })
229
-
230
- notify &&
231
- notify({
232
- mode: 'in-app',
233
- title: `Board '${updated.name}' updated`,
234
- body: `Board '${updated.name}' updated by ${user.name}\n${updated.description}`,
235
- image: getRedirectSubdomainPath(context, domain.subdomain, `/thumbnail/${updated.id}`),
236
- url: getRedirectSubdomainPath(context, domain.subdomain, `/board-viewer/${updated.id}`)
237
- })
238
-
239
- return updated
240
- }
241
-
242
- @Directive('@transaction')
243
- @Directive('@privilege(category: "board", privilege: "mutation", domainOwnerGranted: true)')
244
- @Mutation(returns => Board, { description: 'To release a Board' })
245
- async releaseBoard(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<Board> {
246
- const { domain, user, notify, tx } = context.state
247
- const repository = tx.getRepository(Board)
248
-
249
- const board = await repository.findOne({
250
- where: { domain: { id: domain.id }, id },
251
- relations: ['creator']
252
- })
253
-
254
- if (!board) {
255
- throw `Board given id(${id}) is not found`
256
- }
257
-
258
- if (board.state == 'released') {
259
- throw `Board given id(${id}) is already released`
260
- }
261
-
262
- const updated = await repository.save({
263
- domain,
264
- ...board,
265
- version: (board.version || 0) + 1,
266
- state: 'released',
267
- updater: user
268
- })
269
-
270
- notify &&
271
- notify({
272
- mode: 'in-app',
273
- title: `Board '${updated.name}' released`,
274
- body: `Board '${updated.name}' released by ${user.name}\n${updated.description}`,
275
- image: getRedirectSubdomainPath(context, domain.subdomain, `/thumbnail/${updated.id}`),
276
- url: getRedirectSubdomainPath(context, domain.subdomain, `/board-viewer/${updated.id}`)
277
- })
278
-
279
- return updated
280
- }
281
-
282
- @Directive('@transaction')
283
- @Directive('@privilege(category: "board", privilege: "mutation", domainOwnerGranted: true)')
284
- @Mutation(returns => Board, { description: 'To revert Board version' })
285
- async revertBoardVersion(
286
- @Arg('id') id: string,
287
- @Arg('version') version: number,
288
- @Ctx() context: ResolverContext
289
- ): Promise<Board> {
290
- const { domain, user, notify, tx } = context.state
291
- const repository = tx.getRepository(Board)
292
-
293
- const board = await repository.findOne({
294
- where: { domain: { id: domain.id }, id },
295
- relations: ['creator']
296
- })
297
-
298
- if (!board) {
299
- throw `Board with id(${id}) is not found`
300
- }
301
-
302
- const historyRepository = tx.getRepository(BoardHistory)
303
-
304
- const boardHistory = await historyRepository.findOne({
305
- where: { domain: { id: domain.id }, originalId: id, version },
306
- order: { version: 'DESC' }
307
- })
308
-
309
- if (!boardHistory) {
310
- throw `Board with id:version(${id}:${version}) is not found`
311
- }
312
-
313
- const updated = await repository.save({
314
- domain,
315
- ...board,
316
- model: boardHistory.model,
317
- thumbnail: boardHistory.thumbnail,
318
- state: 'draft',
319
- updater: user
320
- })
321
-
322
- notify &&
323
- notify({
324
- mode: 'in-app',
325
- title: `Board '${updated.name}' updated`,
326
- body: `Board '${updated.name}' updated by ${user.name}\n${updated.description}`,
327
- image: getRedirectSubdomainPath(context, domain.subdomain, `/thumbnail/${updated.id}`),
328
- url: getRedirectSubdomainPath(context, domain.subdomain, `/board-viewer/${updated.id}`)
329
- })
330
-
331
- return updated
332
- }
333
-
334
- @Directive('@transaction')
335
- @Directive('@privilege(category: "board", privilege: "mutation", domainOwnerGranted: true)')
336
- @Mutation(returns => Boolean, { description: 'To delete Board' })
337
- async deleteBoard(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<boolean> {
338
- const { domain, user, notify, tx } = context.state
339
- const repository = tx.getRepository(Board)
340
- const board = await repository.findOneBy({ domain: { id: domain.id }, id })
341
-
342
- const deleted = await repository.softDelete(id)
343
-
344
- notify &&
345
- notify({
346
- mode: 'in-app',
347
- title: `Board '${board.name}' deleted`,
348
- body: `Board '${board.name}' deleted by ${user.name}\n${board.description}`
349
- })
350
-
351
- return true
352
- }
353
-
354
- @Directive('@transaction')
355
- @Directive('@privilege(category: "board", privilege: "mutation", domainOwnerGranted: true)')
356
- @Mutation(returns => [Board], { description: 'To import some Boards' })
357
- async importBoards(
358
- @Arg('groupId') groupId: string,
359
- @Arg('files', () => [GraphQLUpload]) files: FileUpload[],
360
- @Arg('overwrite') overwrite: boolean,
361
- @Ctx() context: ResolverContext
362
- ): Promise<Board[]> {
363
- const { domain, user, notify, tx } = context.state
364
- const groupRepository = tx.getRepository(Group)
365
- const boardRepository = tx.getRepository(Board)
366
- const group = await groupRepository.findOneBy({ domain: { id: domain.id }, id: groupId })
367
-
368
- if (!group) {
369
- throw `Group with id(${groupId}) is not found`
370
- }
371
-
372
- const boards = []
373
-
374
- for (const file of files) {
375
- const { id, name, description, model, thumbnail } = await parseJSONFile(file)
376
-
377
- var sameNameBoard = await boardRepository.findOneBy({ domain: { id: domain.id }, name })
378
- var sameIdBoard = await boardRepository.findOneBy({ id })
379
-
380
- if (overwrite) {
381
- var board = {} as any
382
-
383
- if (sameIdBoard) {
384
- if (overwrite && sameIdBoard.domainId != domain.id) {
385
- throw `Board with id(${id}) is already taken in another domain`
386
- }
387
-
388
- board = {
389
- ...sameIdBoard,
390
- name
391
- }
392
-
393
- if (sameNameBoard && sameIdBoard.id != sameNameBoard.id) {
394
- /* 이름 충돌 회피 */
395
- board.name = `${board.name}(${Date.now()})`
396
- }
397
- } else {
398
- board = {
399
- id,
400
- name: sameNameBoard ? `${name}(${Date.now()})` : name,
401
- version: 0,
402
- creator: user
403
- }
404
- }
405
- } else {
406
- board = {
407
- name,
408
- version: 0,
409
- creator: user
410
- }
411
-
412
- /* ID가 없으면, 사용해도 됨 */
413
- if (!sameIdBoard) {
414
- board.id = id
415
- }
416
-
417
- /* 이름 충돌 회피 */
418
- if (sameNameBoard) {
419
- board.name = `${board.name}(${Date.now()})`
420
- }
421
- }
422
-
423
- boards.push(
424
- await boardRepository.save({
425
- ...board,
426
- domain,
427
- description,
428
- model: typeof model != 'string' ? JSON.stringify(model) : model,
429
- thumbnail,
430
- group,
431
- state: 'draft',
432
- updater: user
433
- })
434
- )
435
- }
436
-
437
- notify &&
438
- notify({
439
- mode: 'in-app',
440
- title: `${boards.length} Board(s) are imported`,
441
- body: `${boards.length} Board(s) are imported into group ${group.name} by ${user.name}`
442
- })
443
-
444
- return boards
445
- }
446
- }
@@ -1,180 +0,0 @@
1
- import { In } from 'typeorm'
2
- import { Arg, Args, Ctx, FieldResolver, Query, Resolver, Root, Directive } from 'type-graphql'
3
-
4
- import { User } from '@things-factory/auth-base'
5
- import { Domain, getQueryBuilderFromListParams, getRepository, ListParam } from '@things-factory/shell'
6
- import { checkTarget, checkDomain, getPermission } from '@things-factory/operato-license-checker'
7
-
8
- import { Group } from '../group/group'
9
- import { PlayGroup } from '../play-group/play-group'
10
- import { Board } from './board'
11
- import { BoardList } from './board-type'
12
- import { BoardHistory } from './board-history'
13
-
14
- import { LicenseError } from '../../errors/license-error'
15
- @Resolver(Board)
16
- export class BoardQuery {
17
- @Directive('@privilege(category: "board", privilege: "query", domainOwnerGranted: true)')
18
- @Query(returns => Board, { description: 'To fetch a board' })
19
- async board(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<Board> {
20
- const { domain } = context.state
21
-
22
- var board = await getRepository(Board).findOne({
23
- where: { domain: { id: In([domain.id, domain.parentId].filter(Boolean)) }, id }
24
- })
25
-
26
- if (domain) {
27
- // 1. check domainSecret is over limit quantity or not
28
- var count = await getRepository(Domain)
29
- .manager.createQueryBuilder()
30
- .select('id')
31
- .from(Domain, 'domain')
32
- .getCount()
33
-
34
- if (!checkDomain(count)) {
35
- throw new LicenseError({
36
- errorCode: context.t(LicenseError.ERROR_CODES.OVER_LIMIT)
37
- })
38
- }
39
- }
40
-
41
- if (board) {
42
- // 1. check boardSecret is over limit quantity or not
43
- var count = await getRepository(Board)
44
- .manager.createQueryBuilder()
45
- .select('id')
46
- .from(Board, 'board')
47
- .where('"domain_id" = :domainId', { domainId: domain.id })
48
- .getCount()
49
-
50
- if (!checkTarget(count)) {
51
- throw new LicenseError({
52
- errorCode: context.t(LicenseError.ERROR_CODES.OVER_LIMIT)
53
- })
54
- }
55
- }
56
-
57
- return board
58
- }
59
-
60
- @Query(returns => Board, { nullable: true, description: 'To fetch a Board Model by name' })
61
- @Directive('@privilege(category: "board", privilege: "query", domainOwnerGranted: true)')
62
- async boardByName(@Arg('name') name: string, @Ctx() context: ResolverContext): Promise<Board> {
63
- const { domain } = context.state
64
-
65
- return await getRepository(Board).findOne({
66
- where: { domain: { id: In([domain.id, domain.parentId].filter(Boolean)) }, name }
67
- })
68
- }
69
-
70
- @Query(returns => BoardList, { description: 'To fetch Boards created by me' })
71
- async boardsCreatedByMe(
72
- @Args(type => ListParam) params: ListParam,
73
- @Ctx() context: ResolverContext
74
- ): Promise<BoardList> {
75
- const { domain, user } = context.state
76
-
77
- const queryBuilder = getQueryBuilderFromListParams({
78
- repository: getRepository(Board),
79
- params,
80
- domain,
81
- alias: 'board',
82
- searchables: ['name', 'description']
83
- }).andWhere('board.creator = :user', { user: user.id })
84
-
85
- const [items, total] = await queryBuilder.getManyAndCount()
86
-
87
- return { items, total }
88
- }
89
-
90
- @Query(returns => [BoardHistory], { description: 'To fetch a Board Versions' })
91
- @Directive('@privilege(category: "board", privilege: "query", domainOwnerGranted: true)')
92
- async boardVersions(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<BoardHistory[]> {
93
- const { domain } = context.state
94
-
95
- return await getRepository(BoardHistory).find({
96
- where: { domain: { id: In([domain.id, domain.parentId].filter(Boolean)) }, originalId: id },
97
- relations: ['updater'],
98
- order: { version: 'DESC' },
99
- take: 10
100
- })
101
- }
102
-
103
- @Query(returns => BoardHistory, { description: 'To fetch the latest Board published' })
104
- @Directive('@privilege(category: "board", privilege: "query", domainOwnerGranted: true)')
105
- async boardPublished(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<BoardHistory> {
106
- const { domain } = context.state
107
-
108
- return await getRepository(BoardHistory).findOne({
109
- where: { domain: { id: In([domain.id, domain.parentId].filter(Boolean)) }, originalId: id },
110
- order: { version: 'DESC' }
111
- })
112
- }
113
-
114
- @Query(returns => BoardList, { description: 'To fetch multiple Boards' })
115
- @Directive('@privilege(category: "board", privilege: "query", domainOwnerGranted: true)')
116
- async boards(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<BoardList> {
117
- const { domain } = context.state
118
-
119
- const queryBuilder = getQueryBuilderFromListParams({
120
- repository: getRepository(Board),
121
- params,
122
- domain,
123
- searchables: ['name', 'description']
124
- })
125
-
126
- const [items, total] = await queryBuilder.getManyAndCount()
127
-
128
- return { items, total }
129
- }
130
-
131
- @FieldResolver(type => Group)
132
- async group(@Root() board: Board) {
133
- return (
134
- board.groupId &&
135
- (await getRepository(Group).findOneBy({
136
- id: board.groupId
137
- }))
138
- )
139
- }
140
-
141
- @FieldResolver(type => [PlayGroup])
142
- async playGroups(@Root() board: Board) {
143
- return (
144
- await getRepository(Board).findOne({
145
- where: { id: board.id },
146
- relations: ['playGroups']
147
- })
148
- )?.playGroups
149
- }
150
-
151
- @FieldResolver(type => Domain)
152
- async domain(@Root() board: Board) {
153
- return (
154
- board.domainId &&
155
- (await getRepository(Domain).findOneBy({
156
- id: board.domainId
157
- }))
158
- )
159
- }
160
-
161
- @FieldResolver(type => User)
162
- async updater(@Root() board: Board): Promise<User> {
163
- return (
164
- board.updaterId &&
165
- (await getRepository(User).findOneBy({
166
- id: board.updaterId
167
- }))
168
- )
169
- }
170
-
171
- @FieldResolver(type => User)
172
- async creator(@Root() board: Board): Promise<User> {
173
- return (
174
- board.creatorId &&
175
- (await getRepository(User).findOneBy({
176
- id: board.creatorId
177
- }))
178
- )
179
- }
180
- }
@@ -1,43 +0,0 @@
1
- import { Resolver, Subscription, Root, Arg } from 'type-graphql'
2
- import { filter, pipe } from 'graphql-yoga'
3
- import { pubsub } from '@things-factory/shell'
4
- import { Board } from './board'
5
-
6
- @Resolver(Board)
7
- export class BoardSubscription {
8
- @Subscription({
9
- subscribe: ({ args, context, info }) => {
10
- const { domain, user } = context.state
11
- const { id } = args
12
- const subdomain = domain?.subdomain
13
-
14
- if (!domain) {
15
- throw new Error('domain required')
16
- }
17
-
18
- if (!user.domains?.find(d => d.subdomain === subdomain) && !process.superUserGranted(domain, user)) {
19
- throw new Error(`domain(${subdomain}) is not working for user(${user.email}).`)
20
- }
21
-
22
- return pipe(
23
- pubsub.subscribe('board'),
24
- filter((payload: { board: Board }) => {
25
- const { domainId, id: boardId } = payload.board
26
-
27
- if (domainId !== domain.id) {
28
- return false
29
- }
30
-
31
- if (id !== boardId) {
32
- return false
33
- }
34
-
35
- return true
36
- })
37
- )
38
- }
39
- })
40
- board(@Root() payload: { board: Board }, @Arg('id') id: string): Board {
41
- return payload.board
42
- }
43
- }