@things-factory/board-service 8.0.0 → 9.0.0-beta.3

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 (64) 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 -1
  6. package/server/constants/error-code.ts +0 -2
  7. package/server/controllers/analyzer/analyze-integration.ts +0 -142
  8. package/server/controllers/fonts.ts +0 -83
  9. package/server/controllers/headless-model.ts +0 -53
  10. package/server/controllers/headless-pdf-to-image.ts +0 -103
  11. package/server/controllers/headless-playlist.ts +0 -71
  12. package/server/controllers/headless-pool-for-board.ts +0 -71
  13. package/server/controllers/headless-pool-for-label.ts +0 -141
  14. package/server/controllers/index.ts +0 -11
  15. package/server/controllers/label-command.ts +0 -62
  16. package/server/controllers/pdf.ts +0 -132
  17. package/server/controllers/screenshot.ts +0 -127
  18. package/server/controllers/thumbnail.ts +0 -18
  19. package/server/errors/index.ts +0 -1
  20. package/server/errors/license-error.ts +0 -21
  21. package/server/index.ts +0 -36
  22. package/server/migrations/1556862253000-SeedGroup.ts +0 -51
  23. package/server/migrations/index.ts +0 -9
  24. package/server/routers/internal-board-view-router.ts +0 -33
  25. package/server/routers/standalone-board-service-router.ts +0 -326
  26. package/server/routes.ts +0 -25
  27. package/server/service/analysis/analysis-query.ts +0 -13
  28. package/server/service/analysis/index.ts +0 -3
  29. package/server/service/board/board-history.ts +0 -137
  30. package/server/service/board/board-mutation.ts +0 -446
  31. package/server/service/board/board-query.ts +0 -180
  32. package/server/service/board/board-subscription.ts +0 -43
  33. package/server/service/board/board-type.ts +0 -58
  34. package/server/service/board/board.ts +0 -125
  35. package/server/service/board/event-subscriber.ts +0 -68
  36. package/server/service/board/index.ts +0 -10
  37. package/server/service/board-favorite/board-favorite-query.ts +0 -53
  38. package/server/service/board-favorite/board-favorite-type.ts +0 -18
  39. package/server/service/board-favorite/index.ts +0 -4
  40. package/server/service/board-template/board-template-mutation.ts +0 -161
  41. package/server/service/board-template/board-template-query.ts +0 -121
  42. package/server/service/board-template/board-template-type.ts +0 -53
  43. package/server/service/board-template/board-template.ts +0 -114
  44. package/server/service/board-template/index.ts +0 -7
  45. package/server/service/group/group-mutation.ts +0 -82
  46. package/server/service/group/group-query.ts +0 -58
  47. package/server/service/group/group-type.ts +0 -30
  48. package/server/service/group/group.ts +0 -69
  49. package/server/service/group/index.ts +0 -6
  50. package/server/service/index.ts +0 -56
  51. package/server/service/permission/domain-permission-subscriber.ts +0 -27
  52. package/server/service/permission/index.ts +0 -3
  53. package/server/service/play-group/event-subscriber.ts +0 -58
  54. package/server/service/play-group/index.ts +0 -9
  55. package/server/service/play-group/play-group-mutation.ts +0 -148
  56. package/server/service/play-group/play-group-query.ts +0 -92
  57. package/server/service/play-group/play-group-subscription.ts +0 -43
  58. package/server/service/play-group/play-group-type.ts +0 -30
  59. package/server/service/play-group/play-group.ts +0 -74
  60. package/server/service/theme/index.ts +0 -7
  61. package/server/service/theme/theme-mutation.ts +0 -128
  62. package/server/service/theme/theme-query.ts +0 -48
  63. package/server/service/theme/theme-type.ts +0 -55
  64. package/server/service/theme/theme.ts +0 -97
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@things-factory/board-service",
3
- "version": "8.0.0",
3
+ "version": "9.0.0-beta.3",
4
4
  "main": "dist-server/index.js",
5
5
  "things-factory": true,
6
6
  "author": "",
@@ -23,15 +23,15 @@
23
23
  "migration:create": "node ../../node_modules/typeorm/cli.js migration:create ./server/migrations/migration"
24
24
  },
25
25
  "dependencies": {
26
- "@things-factory/auth-base": "^8.0.0",
27
- "@things-factory/env": "^8.0.0",
28
- "@things-factory/fav-base": "^8.0.0",
29
- "@things-factory/font-base": "^8.0.0",
30
- "@things-factory/integration-base": "^8.0.0",
26
+ "@things-factory/auth-base": "^9.0.0-beta.3",
27
+ "@things-factory/env": "^8.0.0-beta.4",
28
+ "@things-factory/fav-base": "^9.0.0-beta.3",
29
+ "@things-factory/font-base": "^9.0.0-beta.3",
30
+ "@things-factory/integration-base": "^9.0.0-beta.3",
31
31
  "@things-factory/operato-license-checker": "^4.0.4",
32
32
  "content-disposition": "^0.5.3",
33
33
  "generic-pool": "^3.8.2",
34
34
  "puppeteer": "^23.0.0"
35
35
  },
36
- "gitHead": "07ef27d272dd9a067a9648ac7013748510556a18"
36
+ "gitHead": "1d7e0dd4c88f3c3f3bd311c00e4b1d1542d53634"
37
37
  }
@@ -69,7 +69,7 @@
69
69
  const startSubscribingForAutoRefresh = window.startSubscribingForAutoRefresh
70
70
 
71
71
  const board = {
72
- id: '---',
72
+ id: (new URL(window.location.href)).pathname.split('/').at(-1),
73
73
  model
74
74
  }
75
75
 
@@ -1,2 +0,0 @@
1
- export const VERIFICATION_ERROR = 'error.license token not valid'
2
- export const OVER_LIMIT = 'error.count over license limit'
@@ -1,142 +0,0 @@
1
- import uniq from 'lodash/uniq'
2
-
3
- import { Domain, getRepository } from '@things-factory/shell'
4
- import { analyzeIntegration } from '@things-factory/integration-base'
5
- import { Board } from '../../service/board/board'
6
-
7
- function findObjectsWithCondition(container, condition: (component: any) => boolean) {
8
- let result = []
9
-
10
- if (condition(container)) {
11
- result.push(container)
12
- }
13
-
14
- if (container.components) {
15
- for (const component of container.components) {
16
- result = result.concat(findObjectsWithCondition(component, condition))
17
- }
18
- }
19
-
20
- return result
21
- }
22
-
23
- function getRelationsInBoard(board: Board) {
24
- try {
25
- const model = JSON.parse(board.model) || {}
26
-
27
- const scenarioNames = findObjectsWithCondition(model, component => component.scenarioName).map(
28
- component => component.scenarioName
29
- )
30
-
31
- const tags = findObjectsWithCondition(model, component => component.tag).map(component => component.tag)
32
-
33
- return {
34
- scenarioNames: uniq(scenarioNames.filter(Boolean)),
35
- tags: uniq(tags.filter(Boolean))
36
- }
37
- } catch (err) {
38
- console.error('analyze-integration::getRelationsInBoard error', err)
39
- return {
40
- scenarioNames: [],
41
- tags: []
42
- }
43
- }
44
- }
45
-
46
- export async function analyzeBoardIntegration(domain: Domain) {
47
- const model = await analyzeIntegration(domain)
48
-
49
- var id = 0
50
-
51
- const boards = await getRepository(Board).find({
52
- where: { domain: { id: domain.id } }
53
- })
54
-
55
- model.nodes = model.nodes.concat(
56
- boards.map(board => {
57
- return {
58
- id: board.id,
59
- labels: ['Board'],
60
- text: board.name,
61
- icon: 'dashboard',
62
- properties: {
63
- name: board.name,
64
- description: board.description
65
- }
66
- }
67
- })
68
- )
69
-
70
- boards.forEach(board => {
71
- const { scenarioNames, tags } = getRelationsInBoard(board)
72
-
73
- var scenarioNodes = scenarioNames.map(name =>
74
- model.nodes.find(node => node.labels.includes('Scenario') && node.properties?.name == name)
75
- )
76
- var tagNodes = tags.map(tag => model.nodes.find(node => node.labels.includes('Tag') && node.properties?.tag == tag))
77
-
78
- const missingScenarioNodes = scenarioNodes
79
- .filter(node => !node)
80
- .map((_, i) => scenarioNames[i])
81
- .map(missingScenario => {
82
- return {
83
- id: `missing-scenario-${missingScenario}`,
84
- labels: ['Scenario'],
85
- text: missingScenario,
86
- icon: 'settings',
87
- properties: {
88
- name: missingScenario,
89
- missing: true
90
- }
91
- }
92
- })
93
-
94
- model.nodes = model.nodes.concat(missingScenarioNodes)
95
-
96
- const missingTagNodes = tagNodes
97
- .filter(node => !node)
98
- .map((_, i) => tags[i])
99
- .map(missingTag => {
100
- return {
101
- id: `missing-tag-${missingTag}`,
102
- labels: ['Tag'],
103
- text: missingTag,
104
- icon: 'label',
105
- properties: {
106
- name: missingTag,
107
- missing: true
108
- }
109
- }
110
- })
111
-
112
- model.nodes = model.nodes.concat(missingTagNodes)
113
-
114
- tagNodes.filter(node => !node).map((_, i) => tags[i])
115
-
116
- var relationships = [...scenarioNodes.filter(Boolean), ...missingScenarioNodes].map(node => {
117
- return {
118
- id: `bsce-${++id}`,
119
- type: 'call',
120
- startNode: board.id,
121
- endNode: node.id,
122
- properties: {}
123
- }
124
- })
125
-
126
- model.relationships = model.relationships.concat(relationships)
127
-
128
- relationships = [...tagNodes.filter(Boolean), ...missingTagNodes].map(node => {
129
- return {
130
- id: `btag-${++id}`,
131
- type: 'subscribe',
132
- startNode: board.id,
133
- endNode: node.id,
134
- properties: {}
135
- }
136
- })
137
-
138
- model.relationships = model.relationships.concat(relationships)
139
- })
140
-
141
- return model
142
- }
@@ -1,83 +0,0 @@
1
- import { SelectQueryBuilder } from 'typeorm'
2
-
3
- import { Font } from '@things-factory/font-base'
4
- import { Attachment } from '@things-factory/attachment-base'
5
- import { getRepository, Domain } from '@things-factory/shell'
6
-
7
- export const fonts = async (domain?: Domain) => {
8
- const qb: SelectQueryBuilder<Font> = await getRepository(Font).createQueryBuilder('FONT')
9
-
10
- var fonts = domain
11
- ? await qb
12
- .select('FONT.name', 'name')
13
- .distinct(true)
14
- .addSelect('FONT.provider', 'provider')
15
- .addSelect('FONT.id', 'id')
16
- .addSelect('FONT.uri', 'uri')
17
- .where({ domain: { id: domain.id }, active: true })
18
- .getRawMany()
19
- : await qb
20
- .select('FONT.name', 'name')
21
- .distinct(true)
22
- .addSelect('FONT.provider', 'provider')
23
- .addSelect('FONT.id', 'id')
24
- .addSelect('FONT.uri', 'uri')
25
- .where({ active: true })
26
- .getRawMany()
27
-
28
- var googleFonts = fonts.filter(({ provider }) => provider == 'google')
29
- var customFonts = fonts.filter(({ provider }) => provider == 'custom')
30
-
31
- var customFontCSS: string = ''
32
-
33
- for (const font of customFonts) {
34
- var files: Attachment[] = domain
35
- ? await getRepository(Attachment).findBy({
36
- domain: { id: domain.id },
37
- refBy: font.id
38
- })
39
- : await getRepository(Attachment).findBy({
40
- refBy: font.id
41
- })
42
-
43
- if (files && files.length > 0) {
44
- customFontCSS += files
45
- .map(file => {
46
- const { name: filename, fullpath } = file
47
- const bold = filename.toUpperCase().indexOf('BOLD') !== -1
48
-
49
- return `@font-face {
50
- font-family: '${font.name}';
51
- src: local('${font.name}'), url(${fullpath});
52
- font-weight: ${bold ? 'bold' : 'normal'};
53
- }
54
- `
55
- })
56
- .join('\n')
57
- } else {
58
- customFontCSS += `@font-face {
59
- font-family: '${font.name}';
60
- src: local('${font.name}')${font.uri ? `, url(${font.uri})` : ''};
61
- }
62
- `
63
- }
64
- }
65
-
66
- return googleFonts.length > 0 || customFonts.length > 0
67
- ? [
68
- {
69
- ...(googleFonts.length > 0 && {
70
- google: {
71
- families: googleFonts.map(({ name }) => name)
72
- }
73
- }),
74
- ...(customFonts.length > 0 && {
75
- custom: {
76
- families: customFonts.map(({ name }) => name)
77
- }
78
- })
79
- },
80
- customFonts.length > 0 ? customFontCSS : null
81
- ]
82
- : []
83
- }
@@ -1,53 +0,0 @@
1
- import { In } from 'typeorm'
2
- import { getContextPath, getRepository } from '@things-factory/shell'
3
-
4
- import { Board } from '../service/board/board'
5
- import { BoardHistory } from '../service/board/board-history'
6
-
7
- export const headlessModel = async (target, draft: boolean = false) => {
8
- var { domain, id, model, name } = target || {}
9
-
10
- if (model) {
11
- if (typeof model == 'string') {
12
- model = JSON.parse(model)
13
- } else if (typeof model !== 'object') {
14
- throw 'model should be a string or object'
15
- }
16
- } else {
17
- if (id) {
18
- var board = await getRepository(Board).findOne({
19
- where: { domain: { id: In([domain.id, domain.parentId].filter(Boolean)) }, id }
20
- })
21
- } else if (name) {
22
- var board = await getRepository(Board).findOne({
23
- where: { domain: { id: In([domain.id, domain.parentId].filter(Boolean)) }, name }
24
- })
25
- } else {
26
- throw 'parameter model or id mandatory'
27
- }
28
-
29
- if (!draft && board) {
30
- const latestReleased =
31
- board.state == 'released'
32
- ? board
33
- : await getRepository(BoardHistory)
34
- .createQueryBuilder('history')
35
- .where('history.originalId = :originalId', { originalId: board.id })
36
- .orderBy('history.version', 'DESC')
37
- .limit(1)
38
- .getOne()
39
-
40
- model = (latestReleased || board).model
41
- } else {
42
- model = board.model
43
- }
44
-
45
- model = JSON.parse(model)
46
- }
47
-
48
- return {
49
- base: getContextPath(domain?.subdomain) + (domain ? '/' : ''),
50
- model,
51
- board
52
- }
53
- }
@@ -1,103 +0,0 @@
1
- const puppeteer = require('puppeteer')
2
- const { Readable } = require('stream')
3
- const ejs = require('ejs')
4
-
5
- export const pdfToImage = async ({ pdfPath, fileName, extension = 'png', quality = 2, defaultViewport = null }) => {
6
- const browser = await puppeteer.launch({
7
- headless: true,
8
- args: [
9
- '--disable-web-security',
10
- '--disable-features=IsolateOrigins',
11
- '--disable-site-isolation-trials',
12
- '--no-sandbox'
13
- ],
14
- defaultViewport
15
- })
16
-
17
- try {
18
- const protocol = 'http'
19
- const host = 'localhost'
20
- const port = process.env.PORT
21
- const pdfUrl = `${protocol}://${host}:${port}${pdfPath}`
22
-
23
- const page = await browser.newPage()
24
- const html = await ejs.render(getPdfHtmlTemplate(), { data: { pdfUrl, quality } })
25
-
26
- // 페이지 로딩시 까지 기다리고 스크린샷
27
- await page.setContent(html, { waitUntil: 'networkidle0' })
28
- await page.waitForNetworkIdle()
29
- await page.$('#page')
30
- const screenshot = await page.screenshot({
31
- type: extension,
32
- omitBackground: true
33
- })
34
-
35
- // graphql fileupload형태로 return을 위해 stream 생성
36
- const stream = new Readable()
37
- stream.push(screenshot)
38
- stream.push(null)
39
-
40
- await browser.close()
41
-
42
- // file upload 형태로 return
43
- return {
44
- filename: `${fileName}.${extension}`,
45
- mimetype: `image/${extension}`,
46
- encoding: '7bit',
47
- createReadStream: () => stream
48
- }
49
- } catch (e) {
50
- await browser.close()
51
- console.log('Error creating thumbnail', e)
52
- throw new Error('Error creating thumbnail')
53
- }
54
- }
55
-
56
- function getPdfHtmlTemplate() {
57
- return `
58
- <html lang="en">
59
- <head>
60
- <meta charset="UTF-8" />
61
- <meta http-equiv="X-UA-Compatible" content="IE=edge" />
62
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
63
-
64
- <style nonce="<%= nonce %>">
65
- body {
66
- width: 100vw;
67
- height: 100vh;
68
- margin: 0;
69
- }
70
- #page {
71
- display: flex;
72
- width: 100%;
73
- height: 100%;
74
- }
75
- </style>
76
-
77
- </head>
78
- <body>
79
- <canvas id="page"></canvas>
80
- <script src="https://unpkg.com/pdfjs-dist@2.0.489/build/pdf.min.js"></script>
81
- <script nonce="<%= nonce %>">
82
- ;(async () => {
83
- const pdf = await pdfjsLib.getDocument('<%= data.pdfUrl %>')
84
- const page = await pdf.getPage(1)
85
- const viewport = page.getViewport('<%= data.quality %>')
86
- const canvas = document.getElementById('page')
87
- const context = canvas.getContext('2d')
88
-
89
- canvas.height = viewport.height
90
- canvas.width = viewport.width
91
-
92
- const renderContext = {
93
- canvasContext: context,
94
- viewport: viewport
95
- }
96
-
97
- page.render(renderContext)
98
- })()
99
- </script>
100
- </body>
101
- </html>
102
- `
103
- }
@@ -1,71 +0,0 @@
1
- import { getContextPath, getRepository } from '@things-factory/shell'
2
-
3
- import { PlayGroup } from '../service/play-group/play-group'
4
- import { BoardHistory } from '../service/board/board-history'
5
-
6
- export const headlessPlaylist = async (target, draft: boolean = false) => {
7
- var { domain, id, name } = target || {}
8
-
9
- if (id || name) {
10
- const repository = getRepository(PlayGroup)
11
-
12
- if (id) {
13
- var playGroup = await repository.findOne({
14
- where: { domain: { id: domain.id }, id },
15
- relations: ['boards']
16
- })
17
- } else if (name) {
18
- var playGroup = await repository.findOne({
19
- where: { domain: { id: domain.id }, name },
20
- relations: ['boards']
21
- })
22
- }
23
-
24
- if (!draft && playGroup) {
25
- const { boards } = playGroup
26
- for (let board of boards) {
27
- const latestReleased =
28
- board.state == 'released'
29
- ? board
30
- : await getRepository(BoardHistory)
31
- .createQueryBuilder('history')
32
- .where('history.originalId = :originalId', { originalId: board.id })
33
- .orderBy('history.version', 'DESC')
34
- .limit(1)
35
- .getOne()
36
-
37
- if (latestReleased) {
38
- board.model = latestReleased.model
39
- }
40
- }
41
- }
42
-
43
- playGroup.boards = playGroup.boards.sort((a, b) => {
44
- // 배열 A에 포함된 아이디의 순서를 가져옵니다.
45
- const indexOfOrder = (playGroup.order || []).indexOf(a.id)
46
- const indexOfBoards = (playGroup.order || []).indexOf(b.id)
47
-
48
- // 두 아이디의 순서를 비교하여 정렬합니다.
49
- if (indexOfOrder === -1 && indexOfBoards === -1) {
50
- // 두 아이디 모두 배열 A에 없는 경우, 그대로 유지합니다.
51
- return 0
52
- } else if (indexOfOrder === -1) {
53
- // 아이디 A만 배열 A에 없는 경우, 아이디 B를 먼저 정렬합니다.
54
- return 1
55
- } else if (indexOfBoards === -1) {
56
- // 아이디 B만 배열 A에 없는 경우, 아이디 A를 먼저 정렬합니다.
57
- return -1
58
- } else {
59
- // 두 아이디 모두 배열 A에 있는 경우, 배열 A의 아이디 순서대로 정렬합니다.
60
- return indexOfOrder - indexOfBoards
61
- }
62
- })
63
- } else {
64
- throw 'parameter id or name mandatory'
65
- }
66
-
67
- return {
68
- base: getContextPath(domain?.subdomain) + (domain ? '/' : ''),
69
- playGroup
70
- }
71
- }
@@ -1,71 +0,0 @@
1
- import * as genericPool from 'generic-pool'
2
-
3
- import { config, logger } from '@things-factory/env'
4
-
5
- try {
6
- var puppeteer = require('puppeteer')
7
- } catch (err) {
8
- logger.error(err)
9
- }
10
-
11
- var headlessPool
12
-
13
- export function getHeadlessPool() {
14
- if (!headlessPool) {
15
- headlessPool = genericPool.createPool(
16
- {
17
- create() {
18
- console.log('headless instance in headless-pool-for-board about to create')
19
- return initializeChromium()
20
- },
21
- validate(browser) {
22
- return Promise.race([
23
- new Promise(res => setTimeout(() => res(false), 1500)),
24
- browser
25
- //@ts-ignore
26
- .version()
27
- .then(_ => true)
28
- .catch(_ => false)
29
- ])
30
- },
31
- destroy(browser) {
32
- //@ts-ignore
33
- return browser.close()
34
- }
35
- },
36
- {
37
- min: 2,
38
- max: 10,
39
- testOnBorrow: true,
40
- acquireTimeoutMillis: 15000
41
- }
42
- )
43
- }
44
-
45
- return headlessPool
46
- }
47
-
48
- const CHROMIUM_PATH = config.get('CHROMIUM_PATH')
49
-
50
- async function initializeChromium() {
51
- try {
52
- if (!puppeteer) {
53
- return
54
- }
55
-
56
- var launchSetting = {
57
- args: ['--hide-scrollbars', '--mute-audio', '--no-sandbox', '--use-gl=egl'],
58
- headless: 'shell'
59
- }
60
-
61
- if (CHROMIUM_PATH) {
62
- launchSetting['executablePath'] = CHROMIUM_PATH
63
- }
64
-
65
- const browser = await puppeteer.launch(launchSetting)
66
-
67
- return browser
68
- } catch (err) {
69
- logger.error(err)
70
- }
71
- }