@things-factory/operato-tools 8.0.0-beta.0 → 8.0.0-beta.2

Sign up to get free protection for your applications and to get access to all the features.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@things-factory/operato-tools",
3
- "version": "8.0.0-beta.0",
3
+ "version": "8.0.0-beta.2",
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.0-beta.0",
42
- "@things-factory/meta-ui": "^8.0.0-beta.0"
41
+ "@things-factory/builder": "^8.0.0-beta.2",
42
+ "@things-factory/meta-ui": "^8.0.0-beta.2"
43
43
  },
44
- "gitHead": "add6fb8224b2cb19cbea47bed6a5ecb0424c9a28"
44
+ "gitHead": "f03431a09435511b2595515658f9cb8f78ba4ebb"
45
45
  }
@@ -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 npm run 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
- `