@things-factory/operato-tools 8.0.40 → 9.0.0-9.0.0-beta.59.0

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@things-factory/operato-tools",
3
- "version": "8.0.40",
3
+ "version": "9.0.0-9.0.0-beta.59.0",
4
4
  "main": "dist-server/index.js",
5
5
  "browser": "client/index.js",
6
6
  "things-factory": true,
@@ -38,8 +38,8 @@
38
38
  "@things-factory/operato-license-checker": "^4.0.4"
39
39
  },
40
40
  "devDependencies": {
41
- "@things-factory/builder": "^8.0.37",
42
- "@things-factory/meta-ui": "^8.0.40"
41
+ "@things-factory/builder": "^9.0.0-9.0.0-beta.59.0",
42
+ "@things-factory/meta-ui": "^9.0.0-9.0.0-beta.59.0"
43
43
  },
44
- "gitHead": "6f97ca5dc8aab9acaeebbd3caace0c76bab2676d"
44
+ "gitHead": "cf6ee84b991f469a4e71198b0e6314b45e9e10b8"
45
45
  }
@@ -4,7 +4,7 @@
4
4
  <meta charset="utf-8" />
5
5
  <title>Operato Tools</title>
6
6
  <meta name="generator" content="Things Factory Starter Kit" />
7
- <meta name="viewport" content="width=device-width, initial-scale=1" />
7
+ <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
8
8
  <meta name="description" content="Heart of Logistics" />
9
9
 
10
10
  <base href="/" />
@@ -50,7 +50,6 @@
50
50
 
51
51
  <!-- Performance tip: hint to the browser to start the handshake for the fonts site -->
52
52
  <link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin />
53
- <link href="/node_modules/@material-design-icons/font/index.css" rel="stylesheet" />
54
53
  <link href="/node_modules/material-symbols/index.css" rel="stylesheet" />
55
54
  <link href="/node_modules/@fontsource/roboto/index.css" rel="stylesheet" />
56
55
  <link rel="stylesheet" href="/theme.css" />
@@ -4,7 +4,7 @@
4
4
  <meta charset="utf-8" />
5
5
  <title>Operato Tools</title>
6
6
  <meta name="generator" content="Things Factory Starter Kit" />
7
- <meta name="viewport" content="width=device-width, initial-scale=1" />
7
+ <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
8
8
  <meta name="description" content="Heart of Logistics" />
9
9
 
10
10
  <base href="/" />
@@ -50,7 +50,6 @@
50
50
 
51
51
  <!-- Performance tip: hint to the browser to start the handshake for the fonts site -->
52
52
  <link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin />
53
- <link href="/node_modules/@material-design-icons/font/index.css" rel="stylesheet" />
54
53
  <link href="/node_modules/material-symbols/index.css" rel="stylesheet" />
55
54
  <link href="/node_modules/@fontsource/roboto/index.css" rel="stylesheet" />
56
55
  <link rel="stylesheet" href="/theme.css" />
@@ -1,40 +0,0 @@
1
- module.exports = {
2
- SECRET: '0xD58F835B69D207A76CC5F84a70a1D0d4C79dAC95', // should be changed
3
- email: {
4
- host: 'smtp.office365.com', // your sender-email smtp host
5
- port: 587, // smtp server port
6
- secure: false, // true for 465, false for other ports
7
- auth: {
8
- user: 'your sender-email',
9
- pass: 'your sender-email password' // generated ethereal password
10
- },
11
- secureConnection: false,
12
- tls: {
13
- ciphers: 'SSLv3'
14
- }
15
- },
16
- logger: {
17
- file: {
18
- filename: 'logs/application-%DATE%.log',
19
- datePattern: 'YYYY-MM-DD-HH',
20
- zippedArchive: false,
21
- maxSize: '20m',
22
- maxFiles: '2d',
23
- level: 'info'
24
- },
25
- console: {
26
- level: 'silly'
27
- }
28
- },
29
- ormconfig: {
30
- name: 'default',
31
- type: 'postgres',
32
- host: 'postgres',
33
- port: 5432,
34
- database: 'postgres',
35
- username: 'postgres',
36
- password: 'abcd1234',
37
- synchronize: true,
38
- logging: true
39
- }
40
- }
@@ -1,42 +0,0 @@
1
- version: '3'
2
- services:
3
- nginx:
4
- image: hatiolab/operato-nginx:latest
5
- ports:
6
- - ${HostPort}:80
7
- depends_on:
8
- - app
9
- app:
10
- build: .
11
- container_name: operato-tools
12
- image: hatiolab/operato-tools:latest
13
- privileged: true
14
- volumes:
15
- - ./logs:/app/logs
16
- - ./config.production.js:/app/config.production.js
17
- ports:
18
- - 4000:3000
19
- depends_on:
20
- - postgres
21
- - mosquitto
22
- logging:
23
- driver: 'json-file'
24
- options:
25
- max-size: '100m'
26
- max-file: '3'
27
- postgres:
28
- image: postgres:13.2
29
- container_name: db-operato-tools
30
- environment:
31
- POSTGRES_PASSWORD: abcd1234
32
- POSTGRES_USER: postgres
33
- PGDATA: /var/lib/postgresql/data/pgdata
34
- volumes:
35
- - ./postgres_data:/var/lib/postgresql/data/pgdata
36
- ports:
37
- - '55432:5432'
38
- mosquitto:
39
- image: eclipse-mosquitto:latest
40
- ports:
41
- - 1883:1883
42
- - 9001:9001
@@ -1,54 +0,0 @@
1
- if [ -f "config.production.js" ] ; then
2
- echo "config.production.js exist"
3
- else
4
- echo "config.production.js create"
5
- curl -O https://raw.githubusercontent.com/things-factory/things-factory/master/packages/operato-tools/installer/config.production.js
6
- fi
7
-
8
- if [ -f "start.sh" ] ; then
9
- echo "start.sh exist"
10
- else
11
- echo "start.sh create"
12
- curl -O https://raw.githubusercontent.com/things-factory/things-factory/master/packages/operato-tools/installer/start.sh
13
- fi
14
-
15
- if [ -f "stop.sh" ] ; then
16
- echo "stop.sh exist"
17
- else
18
- echo "stop.sh create"
19
- curl -O https://raw.githubusercontent.com/things-factory/things-factory/master/packages/operato-tools/installer/stop.sh
20
- fi
21
-
22
- if [ -f "upgrade.sh" ] ; then
23
- echo "upgrade.sh exist"
24
- else
25
- echo "upgrade.sh create"
26
- curl -O https://raw.githubusercontent.com/things-factory/things-factory/master/packages/operato-tools/installer/upgrade.sh
27
- fi
28
-
29
- if [ -f "migrate.sh" ] ; then
30
- echo "migrate.sh exist"
31
- else
32
- echo "migrate.sh create"
33
- curl -O https://raw.githubusercontent.com/things-factory/things-factory/master/packages/operato-tools/installer/migrate.sh
34
- fi
35
-
36
- if [ -f "docker-compose.yml" ] ; then
37
- echo "docker-compose.yml exist"
38
- else
39
- echo "docker-compose.yml create"
40
- curl -O https://raw.githubusercontent.com/things-factory/things-factory/master/packages/operato-tools/installer/docker-compose.yml
41
- fi
42
-
43
- chmod u+x start.sh
44
- chmod u+x stop.sh
45
- chmod u+x upgrade.sh
46
- chmod u+x migrate.sh
47
-
48
- echo "HostPort=3000" > .env
49
-
50
- docker pull hatiolab/operato-tools:latest
51
-
52
- docker pull hatiolab/operato-nginx:latest
53
-
54
- docker-compose create
@@ -1 +0,0 @@
1
- docker exec -it operato-tools npx things-factory-migration --mode=production
@@ -1,18 +0,0 @@
1
- HOST_PORT=3000
2
-
3
- if [ $# -eq 0 ] ; then
4
- echo "Warning: default port 3000"
5
- else
6
- HOST_PORT=$1
7
- fi
8
-
9
-
10
- echo "HOST_PORT : ${HOST_PORT}"
11
-
12
- echo "HostPort="$HOST_PORT > .env
13
-
14
- if [[ "$OSTYPE" == "linux-gnu"* ]]; then
15
- xhost +"local:docker@"
16
- fi
17
-
18
- docker-compose up -d
package/installer/stop.sh DELETED
@@ -1 +0,0 @@
1
- docker-compose stop
@@ -1 +0,0 @@
1
- curl -fsSL https://raw.githubusercontent.com/things-factory/things-factory/master/packages/operato-tools/installer/install.sh | bash -s
package/server/index.ts DELETED
@@ -1 +0,0 @@
1
- export * from './service'
@@ -1,15 +0,0 @@
1
- /* EXPORT ENTITY TYPES */
2
-
3
- /* IMPORT ENTITIES AND RESOLVERS */
4
- import { resolvers as entityResolvers } from './tool-entity'
5
-
6
- export const entities = [
7
- /* ENTITIES */
8
- ]
9
-
10
- export const schema = {
11
- resolverClasses: [
12
- /* RESOLVER CLASSES */
13
- ...entityResolvers
14
- ]
15
- }
@@ -1,338 +0,0 @@
1
- import { Arg, Ctx, Mutation, Directive, Resolver } from 'type-graphql'
2
- import { Entity, EntityColumn } from '@things-factory/resource-base'
3
- import { Menu } from '@things-factory/menu-base'
4
- const { camelCase, startCase, snakeCase, kebabCase } = require('lodash')
5
- const { plural } = require('pluralize')
6
- import crypto from 'crypto'
7
-
8
- @Resolver()
9
- export class OperatoToolCreateMenu {
10
- @Directive('@transaction')
11
- @Mutation(returns => Boolean, { description: 'Operato Tool Create Menu' })
12
- async toolCreateMenu(@Arg('id') id: string, @Arg('parent_menu_id') parent_menu_id: string, @Ctx() context: any): Promise<Boolean> {
13
- const { domain, user, tx } = context.state
14
-
15
- // Entity 조회
16
- const entity: Entity = await tx.getRepository(Entity).findOne({
17
- where: {
18
- domain: { id: domain.id },
19
- id
20
- }
21
- })
22
-
23
- // Entity 컬럼 조회
24
- const entityColumns: EntityColumn[] = await tx.getRepository(EntityColumn).find({
25
- where: {
26
- domain: { id: domain.id },
27
- entity: { id: entity.id }
28
- }
29
- })
30
-
31
- let { name = '' } = entity || {}
32
- let serviceName: string = kebabCase(name)
33
-
34
- // 이름 치환 (타이틀 , grapql 관련 서비스 )
35
- let pageTemplate = modelTemplate
36
- .replace(/{{pascalCase name}}/g, startCase(camelCase(name)).replace(/ /g, ''))
37
- .replace(/{{camelCase name}}/g, camelCase(name))
38
- .replace(/{{snakeCase name}}/g, snakeCase(name))
39
- .replace(/{{pluralPascalCase name}}/g, startCase(camelCase(plural(name))).replace(/ /g, ''))
40
- .replace(/{{pluralCamelCase name}}/g, camelCase(plural(name)))
41
- .replace(/{{name}}/g, name)
42
-
43
- // 이력조회 버튼 추가
44
- let historyButton = ''
45
- if (entity.dataProp == 'JSON') {
46
- historyButton = `
47
- {
48
- "name": "history",
49
- "type": "basic",
50
- "label": "data_history",
51
- "icon": "history",
52
- "logic": "history_json"
53
- }
54
- `
55
- } else if (entity.dataProp == 'COPY') {
56
- historyButton = `
57
- {
58
- "name": "history",
59
- "type": "basic",
60
- "label": "data_history",
61
- "icon": "history",
62
- "logic": "history_copy"
63
- }
64
- `
65
- }
66
- pageTemplate = pageTemplate.replace(/{{HistoryButton}}/g, historyButton)
67
-
68
- // 그리드 정렬
69
- let sortColumns = this.createSortCols(entityColumns)
70
- pageTemplate = pageTemplate.replace(/{{SortColumns}}/g, sortColumns.join(','))
71
-
72
- // 검색 필드
73
- let searchColumns = this.createSearchCols(entityColumns)
74
- pageTemplate = pageTemplate.replace(/{{SearchColumns}}/g, searchColumns.join(','))
75
-
76
- // 그리드 컬럼
77
- let gridColumns = this.createGridColumns(entityColumns)
78
- pageTemplate = pageTemplate.replace(/{{GridColumns}}/g, gridColumns.join(','))
79
-
80
- // 암호화
81
- pageTemplate = this.enc(pageTemplate)
82
-
83
- // 메뉴 생성
84
- await this.createMenuData(tx, domain, user, parent_menu_id, serviceName, pageTemplate)
85
- return true
86
- }
87
-
88
- /**
89
- * 그리드 컬럼
90
- * @param entityColumns
91
- */
92
- createGridColumns(entityColumns: EntityColumn[]) {
93
- return entityColumns
94
- .filter(x => x.name != 'id' && x.gridRank > 0)
95
- .sort(function (a, b) {
96
- return a.gridRank - b.gridRank
97
- })
98
- .map(x => {
99
- let { gridEditor = 'string', name, term, gridWidth = 0, gridAlign = 'left', nullable = false } = x
100
- if (gridEditor == null) gridEditor = 'string'
101
- if (gridAlign == null) gridAlign = 'left'
102
-
103
- // 생성 자 수정 자 기본 값
104
- if (name == 'updater_id' || name == 'creator_id') {
105
- name = name == 'updater_id' ? 'updater' : 'creator'
106
- return `{"type": "object","name": "${name}","header": "${name}","width": 100,"editable":false, "align": "center","object_opt":{"queryName":"users"}}`
107
- }
108
- // 생성 시간 수정 시간 기본 값
109
- if (name == 'updated_at' || name == 'created_at') {
110
- return `{"type": "datetime","name": "${name == 'updated_at' ? 'updatedAt' : 'createdAt'}","header": "${name}","width": 160,"editable":false, "align": "center"}`
111
- }
112
-
113
- // 읽기 / 숨김 필드는 수정 불가
114
- let editable = gridEditor == 'readonly' || gridEditor == 'hidden' ? false : true
115
- // 히든 필드
116
- let hidden = gridEditor == 'hidden' ? true : false
117
- if (hidden == true || editable == false) {
118
- // 히든 필드는 문자열로 변환 숨김
119
- gridEditor = 'string'
120
- }
121
-
122
- let colTxt = `{"type": "${gridEditor}","name": "${camelCase(
123
- name
124
- )}","header": "${term}","hidden":${hidden} ,"editable": ${editable},"mandatory": ${!nullable},"sortable":true ,"align": "${gridAlign}","width":${gridWidth} ,"exportable": true`
125
-
126
- // 참조 타입이 공통 코드
127
- if (x.refType == 'code') {
128
- colTxt = colTxt + `,"select_opt":{"type":"code","name":"${x.refName}"}`
129
- }
130
-
131
- // 참조 타입이 시나리오
132
- if (x.refType == 'scenario') {
133
- colTxt = colTxt + `,"select_opt":{"type":"scenario","name":"${x.refName}"}`
134
- }
135
-
136
- // 참조 타입이 엔티티
137
- if (x.refType == 'entity') {
138
- colTxt = colTxt + `,"select_opt":{"type": "entity","args": {"queryName": "${camelCase(plural(x.refName))}","codeField": "id","dispField": "name"}}`
139
- }
140
- colTxt = colTxt + '}'
141
- return colTxt
142
- })
143
- }
144
-
145
- /**
146
- * 검색 컬럼
147
- * @param entityColumns
148
- */
149
- createSearchCols(entityColumns: EntityColumn[]) {
150
- return entityColumns
151
- .filter(x => x.searchRank > 0)
152
- .sort(function (a, b) {
153
- return a.searchRank - b.searchRank
154
- })
155
- .map(x => {
156
- let operator = x.searchOper
157
-
158
- if (operator == 'filter') {
159
- return `"${camelCase(x.name)}"`
160
- }
161
-
162
- if (!operator) operator = 'eq'
163
- return `{"name": "${camelCase(x.name)}","operator": "${operator}"}`
164
- })
165
- }
166
-
167
- /**
168
- * 그리드 정렬
169
- * @param entityColumns
170
- * @returns
171
- */
172
- createSortCols(entityColumns: EntityColumn[]) {
173
- return entityColumns
174
- .filter(x => x.sortRank > 0)
175
- .sort(function (a, b) {
176
- return a.sortRank - b.sortRank
177
- })
178
- .map(x => {
179
- return `{"name":"${camelCase(x.name)}","desc":${x.reverseSort}}`
180
- })
181
- }
182
-
183
- /**
184
- * 메뉴 데이터 생성
185
- * @param tx
186
- * @param domain
187
- * @param user
188
- * @param parent_menu_id
189
- * @param name
190
- * @param pageTemplate
191
- */
192
- async createMenuData(tx: any, domain: any, user: any, parent_menu_id: string, name: string, pageTemplate: string) {
193
- // 상위 메뉴
194
- let parentMenu = await tx.getRepository(Menu).findOne({
195
- where: {
196
- domain: { id: domain.id },
197
- id: parent_menu_id
198
- }
199
- })
200
-
201
- // 메뉴 랭크
202
- let menuRank = await tx.getRepository(Menu).query(`select coalesce(max(rank),0) as rank from menus where parent_id = '${parent_menu_id}' or id = '${parent_menu_id}'`)
203
- let rank = menuRank[0].rank + 10
204
-
205
- // 메뉴 저장
206
- let menu: Menu = new Menu()
207
- menu.name = name
208
- menu.menuType = 'SCREEN'
209
- menu.category = 'meta-grist-page'
210
- menu.rank = rank
211
- menu.hiddenFlag = false
212
- menu.routing = name
213
- menu.template = pageTemplate
214
- menu.resourceUrl = '@things-factory/meta-ui/client/pages/meta-grist-page'
215
- menu.parent = parentMenu
216
-
217
- await tx.getRepository(Menu).save({
218
- ...menu,
219
- domain,
220
- creator: user,
221
- updater: user
222
- })
223
- }
224
-
225
- /**
226
- * json 모델 암호화
227
- * @param template
228
- * @returns
229
- */
230
- enc(template: string) {
231
- let jsonStr = JSON.stringify(template)
232
- let templates = jsonStr.split(/(.{100})/).filter(O => O)
233
- let filters = ['aes-256-ecb', 'aes-256-cbc', 'aes-256-cfb', 'aes-256-cfb8', 'aes-256-cfb1', 'aes-256-ofb', 'aes-256-ctr', 'aes256']
234
- let salt = crypto.randomBytes(32).toString('hex')
235
- let hexKey = Buffer.from(salt, 'hex')
236
- let ivBase = salt.substring(0, 16)
237
- let iv = ['', ivBase, ivBase, ivBase, ivBase, ivBase, ivBase, ivBase]
238
- let filterIdx = 0
239
- let encResults = []
240
- encResults.push(salt)
241
-
242
- for (var i = 0; i < templates.length; i++, filterIdx++) {
243
- if (filterIdx == filters.length) filterIdx = 0
244
-
245
- let cipher = crypto.createCipheriv(filters[filterIdx], hexKey, iv[filterIdx])
246
- let encrypted = cipher.update(templates[i], 'utf8', 'hex')
247
- encrypted += cipher.final('hex')
248
- encResults.push(encrypted)
249
- }
250
-
251
- return encResults.join('h1z0q9i9x7q6')
252
- }
253
- }
254
-
255
- const modelTemplate = `
256
- {
257
- "menu": {
258
- "title": "{{name}}",
259
- "name": "",
260
- "desc": ""
261
- },
262
- "gql": {
263
- "query": {
264
- "list_func": "{{pluralCamelCase name}}",
265
- "find_one_func":"{{camelCase name}}"
266
- },
267
- "mutation":{
268
- "multiple":{
269
- "func":"updateMultiple{{pascalCase name}}",
270
- "type": "{{pascalCase name}}Patch"
271
- },
272
- "delete":{
273
- "func":"delete{{pluralPascalCase name}}"
274
- }
275
- }
276
- },
277
- "button": [
278
- {
279
- "name":"export"
280
- },
281
- {
282
- "name":"add"
283
- },
284
- {
285
- "name":"delete"
286
- },
287
- {
288
- "name":"save"
289
- }
290
- ],
291
- "grid_column": [
292
- {
293
- "type": "string",
294
- "name": "id",
295
- "header": "id",
296
- "hidden": true,
297
- "editable": false,
298
- "mandatory": false,
299
- "sortable": false,
300
- "align": "left",
301
- "width": 0,
302
- "exportable": false
303
- },
304
- {{GridColumns}}
305
- ],
306
- "grid": {
307
- "button":[
308
- {{HistoryButton}}
309
- ],
310
- "option": {
311
- "mobile_mode": "LIST",
312
- "desk_mode": "GRID",
313
- "use_row_checker": true,
314
- "pages": [
315
- 20,
316
- 50,
317
- 100,
318
- 300
319
- ],
320
- "view_mode": [
321
- "GRID",
322
- "LIST",
323
- "CARD"
324
- ],
325
- "sorters": [
326
- {{SortColumns}}
327
- ]
328
- },
329
- "row": {
330
- "multiple_select": true,
331
- "click": "select-row-toggle"
332
- }
333
- },
334
- "search": [
335
- {{SearchColumns}}
336
- ]
337
- }
338
- `