@cyklang/cli 0.1.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.
Files changed (51) hide show
  1. package/README.md +1 -0
  2. package/aws-ecs.env +38 -0
  3. package/build/Cmd.d.ts +7 -0
  4. package/build/Cmd.js +35 -0
  5. package/build/DBClient.d.ts +10 -0
  6. package/build/DBClient.js +205 -0
  7. package/build/InitCommand.d.ts +9 -0
  8. package/build/InitCommand.js +122 -0
  9. package/build/ModuleCommand.d.ts +5 -0
  10. package/build/ModuleCommand.js +202 -0
  11. package/build/NodeCrypto.d.ts +5 -0
  12. package/build/NodeCrypto.js +49 -0
  13. package/build/QueryCommand.d.ts +5 -0
  14. package/build/QueryCommand.js +62 -0
  15. package/build/RunCommand.d.ts +5 -0
  16. package/build/RunCommand.js +112 -0
  17. package/build/TableCommand.d.ts +5 -0
  18. package/build/TableCommand.js +140 -0
  19. package/build/TestCommand copy.d.ts +5 -0
  20. package/build/TestCommand copy.js +111 -0
  21. package/build/TestCommand.d.ts +5 -0
  22. package/build/TestCommand.js +111 -0
  23. package/build/UserCommand.d.ts +5 -0
  24. package/build/UserCommand.js +202 -0
  25. package/build/console.d.ts +13 -0
  26. package/build/console.js +109 -0
  27. package/build/cykquery.d.ts +2 -0
  28. package/build/cykquery.js +145 -0
  29. package/build/cyktable.d.ts +2 -0
  30. package/build/cyktable.js +133 -0
  31. package/build/index.d.ts +2 -0
  32. package/build/index.js +32 -0
  33. package/build/module.d.ts +2 -0
  34. package/build/module.js +290 -0
  35. package/docker-compose.ecs.yml +44 -0
  36. package/docker-compose.yml +43 -0
  37. package/docker.env +38 -0
  38. package/package.json +38 -0
  39. package/src/Cmd.ts +33 -0
  40. package/src/DBClient.ts +220 -0
  41. package/src/InitCommand.ts +107 -0
  42. package/src/ModuleCommand.ts +192 -0
  43. package/src/NodeCrypto.ts +60 -0
  44. package/src/QueryCommand.ts +62 -0
  45. package/src/RunCommand.ts +98 -0
  46. package/src/TableCommand.ts +127 -0
  47. package/src/TestCommand.ts +98 -0
  48. package/src/UserCommand.ts +203 -0
  49. package/src/console.ts +97 -0
  50. package/src/index.ts +30 -0
  51. package/tsconfig.json +12 -0
@@ -0,0 +1,220 @@
1
+ import { BasicType, DBColumn, DBExecuteRequest, DBManager, DBTable, ObjectData, parseXML, PrimitiveData } from "@cyklang/core";
2
+ import loglevel from "loglevel";
3
+ const logger = loglevel.getLogger('DBClient.ts')
4
+ logger.setLevel('debug')
5
+ // DBManager wrapper class
6
+
7
+ export class DBClient {
8
+ dbManager: DBManager
9
+ constructor(dbManager: DBManager) {
10
+ this.dbManager = dbManager
11
+ }
12
+
13
+ async selectFromTable(title: string, tableName: string, options:
14
+ { fields: string, sort?: string | undefined, where?: string | undefined }) {
15
+
16
+ try {
17
+
18
+ const objectDataType = this.dbManager.scope.structure.objectDataType;
19
+ const stringDataType = this.dbManager.scope.structure.stringDataType
20
+ const request = new DBExecuteRequest()
21
+ request.selectFromTable = tableName
22
+
23
+ request.parameters.addVariable('fields', stringDataType).data = new PrimitiveData(stringDataType, options.fields)
24
+ request.parameters.addVariable('where', stringDataType).data = new PrimitiveData(stringDataType, options.where)
25
+ if (options.sort !== undefined) {
26
+ const colnames = options.fields.split(',')
27
+ const sortcols = options.sort.split(',')
28
+ let order_by = ''
29
+ for (let ind = 0; ind < sortcols.length; ind++ ) {
30
+ const coli = sortcols[ind]
31
+ const colname = colnames[Number(coli)]
32
+ if (order_by !== '') order_by += ','
33
+ order_by += colname
34
+ }
35
+ logger.debug('order_by '+order_by)
36
+ request.parameters.addVariable('order_by', stringDataType).data = new PrimitiveData(stringDataType, order_by)
37
+ }
38
+
39
+ // if (options !== undefined) {
40
+ // Object.entries(options).forEach(([key, value]) => {
41
+ // const data = new PrimitiveData(stringDataType, value)
42
+ // request.parameters.addVariable(key, stringDataType).data = data
43
+ // })
44
+ // }
45
+
46
+ const xmlResult = await this.dbManager.dbExecute(request)
47
+ if (xmlResult === undefined) throw 'xmlResult undefined'
48
+ const tagXmlResult = parseXML('xmlResult', xmlResult)
49
+
50
+ const objXmlResult = (await objectDataType.parseData(tagXmlResult, this.dbManager.scope)) as ObjectData
51
+ const objectMeta = (objXmlResult.variables.getData('meta')) as ObjectData
52
+ if (objectMeta === undefined) throw 'objectMeta not found'
53
+ const xmlMeta = objectMeta.variables.getString(tableName)
54
+ if (xmlMeta === undefined) throw 'meta does not have ' + tableName + ' description'
55
+
56
+ const tagDBTable = parseXML('dbTable', xmlMeta)
57
+ const dbTable = new DBTable(tagDBTable)
58
+
59
+ const fields = options.fields.split(',')
60
+ const list = new List()
61
+
62
+ for (let ind = 0; ind < fields.length; ind++) {
63
+ const field = fields[ind]
64
+ const dbColumn = dbTable.columns.filter((dbColumn) => dbColumn.name === field)[0]
65
+ if (dbColumn === undefined) throw 'field ' + field + ' not found'
66
+ list.addDBColumn(dbColumn)
67
+ }
68
+
69
+ const objDataset = objXmlResult.variables.getData(tableName) as ObjectData
70
+ for (let ind = 0; ind < objDataset.variables.length(); ind++) {
71
+ const record = (objDataset.variables.at(ind)?.data as ObjectData)
72
+ list.addObjectData(record)
73
+ }
74
+ logger.info(list.renderList(title))
75
+
76
+ }
77
+ catch (err) {
78
+ logger.error(err)
79
+ }
80
+ }
81
+ }
82
+
83
+ class List {
84
+ columns: Column[] = []
85
+ rows: Row[] = []
86
+ lines: string[] = []
87
+ currentLine: string = ''
88
+
89
+ addDBColumn(dbColumn: DBColumn) {
90
+ const col = new Column(dbColumn.name)
91
+ col.label = dbColumn.label || dbColumn.name
92
+ col.dbColumn = dbColumn
93
+ this.columns.push(col)
94
+ }
95
+
96
+ addObjectData(objectData: ObjectData) {
97
+ const cells: BasicType[] = []
98
+ this.columns.forEach((column) => {
99
+ let cell: BasicType = undefined
100
+ const data = objectData.variables.getData(column.name)
101
+ if (data !== undefined && data !== null) {
102
+ cell = (data as PrimitiveData).value
103
+ }
104
+ cells.push(cell)
105
+ })
106
+ this.rows.push(new Row(cells))
107
+ }
108
+
109
+ renderList(title: string): string {
110
+ // calculate column.width
111
+ let total_width = 0
112
+ for (let indi = 0; indi < this.columns.length; indi++) {
113
+ const column = this.columns[indi]
114
+ column.width = column.label.length
115
+ this.rows.forEach((row) => {
116
+ const cell = row.cells[indi]
117
+ const renderedCell = this.renderCell(cell)
118
+ if (renderedCell.length > column.width) {
119
+ column.width = renderedCell.length
120
+ }
121
+ })
122
+ if (total_width > 0) total_width += 3
123
+ total_width += column.width
124
+ }
125
+
126
+ // title
127
+ this.lines.push(this.renderCell(title, total_width, 'center'))
128
+
129
+ // header
130
+ this.lines.push(this.renderHeaderRow())
131
+ this.lines.push(this.renderSeparator())
132
+
133
+ // rows
134
+ for (let indj = 0; indj < this.rows.length; indj++ ) {
135
+ const row = this.rows[indj]
136
+ this.lines.push(this.renderRow(row))
137
+ }
138
+ return this.lines.join('\n')
139
+ }
140
+
141
+ renderHeaderRow(): string {
142
+ let result = ''
143
+ for (let indi = 0; indi < this.columns.length; indi++ ) {
144
+ const column = this.columns[indi]
145
+ if (result !== '') result += ' | '
146
+ result += this.renderCell(column.label, column.width, 'center')
147
+ }
148
+ return result
149
+ }
150
+
151
+ renderRow(row: Row): string {
152
+ let result = ''
153
+ for (let indi = 0; indi < this.columns.length; indi++ ) {
154
+ const column = this.columns[indi]
155
+ let justify: 'left' | 'right' | 'center' | undefined = 'left'
156
+ if (column.dbColumn?.dataTypeName === 'number' || column?.type === 'number') {
157
+ justify = 'right'
158
+ }
159
+ if (result !== '') result += ' | '
160
+ result += this.renderCell(row.cells[indi], column.width, justify)
161
+ }
162
+ return result
163
+ }
164
+
165
+ renderSeparator(): string {
166
+ let result = ''
167
+ for ( let indi = 0; indi < this.columns.length; indi++ ) {
168
+ const column = this.columns[indi]
169
+ if (result !== '') result += '-+-'
170
+ result += '-'.repeat(column.width)
171
+ }
172
+ return result
173
+ }
174
+
175
+ renderCell(value: BasicType, width?: number | undefined, justify?: 'left' | 'right' | 'center' | undefined): string {
176
+ let result: string
177
+ if (value === undefined) {
178
+ result = ''
179
+ }
180
+ else if (value === null) {
181
+ result = 'null'
182
+ }
183
+ else {
184
+ result = value.toString()
185
+ }
186
+ if (width !== undefined && result.length < width) {
187
+ if (justify === undefined || justify === 'left') {
188
+ result += ' '.repeat(width - result.length)
189
+ }
190
+ else if (justify === 'right') {
191
+ result = ' '.repeat(width - result.length) + result
192
+ }
193
+ else {
194
+ // justify === center
195
+ const before = Math.floor((width - result.length) / 2)
196
+ result = ' '.repeat(before) + result + ' '.repeat(width - result.length - before)
197
+ }
198
+ }
199
+ return result
200
+ }
201
+ }
202
+
203
+ class Column {
204
+ name: string;
205
+ label: string = '';
206
+ dbColumn: DBColumn | undefined;
207
+ type?: string | undefined;
208
+ computed?: string | undefined;
209
+ width: number = 0
210
+ constructor(name: string) {
211
+ this.name = name
212
+ }
213
+ }
214
+
215
+ class Row {
216
+ cells: BasicType[]
217
+ constructor(cells: BasicType[]) {
218
+ this.cells = cells
219
+ }
220
+ }
@@ -0,0 +1,107 @@
1
+ import { Command } from "commander";
2
+ import * as fs from 'fs'
3
+ import { getLogger } from "loglevel";
4
+ const logger = getLogger('InitCommand.ts')
5
+
6
+ logger.setLevel('debug')
7
+
8
+ export class InitCommand extends Command {
9
+ constructor(name: string) {
10
+ super(name)
11
+ this.description('initialize a folder with files necessary for a cyklang project')
12
+ .action(async (options: any) => {
13
+ await this.commandInit(options)
14
+ })
15
+ }
16
+ async commandInit(options: any) {
17
+ try {
18
+ await this.writeEnvFile()
19
+ await this.writeDockerComposeFile()
20
+ }
21
+ catch (err) {
22
+ logger.error(err)
23
+ }
24
+ }
25
+
26
+ // developer.mozilla.org Math.random()
27
+ // On renvoie un entier aléatoire entre une valeur min (incluse)
28
+ // et une valeur max (exclue).
29
+ // Attention : si on utilisait Math.round(), on aurait une distribution
30
+ // non uniforme !
31
+
32
+ getRandomInt(min: number, max: number) {
33
+ min = Math.ceil(min);
34
+ max = Math.floor(max);
35
+ return Math.floor(Math.random() * (max - min)) + min;
36
+ }
37
+
38
+ generateToken(): string {
39
+ let result = ''
40
+ for (let ind = 0; ind < 32; ind++ ) {
41
+ result += this.getRandomInt(0,15).toString(16)
42
+ }
43
+ return result
44
+ }
45
+
46
+ async writeEnvFile() {
47
+ const envFilename = '.env'
48
+ if (fs.existsSync(envFilename) === true) {
49
+ logger.info(envFilename + ' file already exists. Remove it before launching init command')
50
+ return
51
+ }
52
+ Math.random()
53
+ const content = `# PostgreSql connection
54
+ # used by psql command line utility
55
+ PGHOST="localhost"
56
+ PGPORT="4321"
57
+ PGUSER="postgres"
58
+ PGPASSWORD="postgres"
59
+
60
+ # at postgresql database initialization
61
+
62
+ # POSTGRES_USER="postgres"
63
+ POSTGRES_PASSWORD="postgres"
64
+
65
+ # admin user
66
+ # created at database initialization
67
+ ADMIN_NAME=cyk
68
+ ADMIN_PASSWORD=cyk
69
+ ADMIN_EMAIL=my-email@my-domain
70
+
71
+ # JWT token generation
72
+ # e.g. https://www.allkeysgenerator.com
73
+ # 128 bits Hex
74
+
75
+ SECRET_TOKEN="${this.generateToken()}"
76
+ SECRET_RTOKEN="${this.generateToken()}"
77
+
78
+ # Login Page Config
79
+ LOGIN_IMAGE="https://media.smartbox.com/pim/1000001967971201770883.jpg?thumbor=800x0/filters:quality(90)"
80
+ LOGIN_TITLE="CYK Framework"
81
+ LOGIN_SUBTITLE="Une application pilote du framework CYK"
82
+ LOGIN_APPLI="accounting"
83
+
84
+ # credential used by cyk CLI
85
+
86
+ USER_NAME=cyk
87
+ USER_PASSWORD=cyk
88
+ CYK_PORT="3000"
89
+ DBREMOTE_URL="http://localhost:3000"
90
+ `
91
+ fs.writeFileSync(envFilename, content)
92
+ logger.debug('created ' + envFilename)
93
+ }
94
+
95
+ async writeDockerComposeFile() {
96
+ const dockerComposeFilename = 'docker-compose.yml'
97
+ if (fs.existsSync(dockerComposeFilename) === true) {
98
+ logger.info(dockerComposeFilename + ' already exists. Remove it before launching init command')
99
+ return
100
+ }
101
+
102
+ const modelFilename = __dirname + '/../' + dockerComposeFilename
103
+ const content = fs.readFileSync(modelFilename)
104
+ fs.writeFileSync(dockerComposeFilename, content)
105
+ logger.debug('created ' + dockerComposeFilename)
106
+ }
107
+ }
@@ -0,0 +1,192 @@
1
+ #!/usr/bin/env ts-node
2
+ import { DBModule, parseXML } from "@cyklang/core"
3
+ import * as fs from "fs"
4
+ import loglevel from 'loglevel'
5
+ import path from "path"
6
+ import { Command } from 'commander'
7
+ import { Cmd } from "./Cmd"
8
+ import { DBClient } from "./DBClient"
9
+ const logger = loglevel.getLogger("ModuleCommand.ts")
10
+ logger.setLevel("debug")
11
+
12
+ export class ModuleCommand extends Command {
13
+ constructor(name: string) {
14
+ super(name)
15
+ this.description('manage modules')
16
+ // upload
17
+ this.addCommand(new ModuleUpload('upload', `Upload local module file(s) to the database.
18
+ Module name is the base name of the file. If a module with the same name already exists, it will be updated.
19
+ Otherwise, a new module is inserted in the database. To rename a module, use option --id to indicate the module you want to rename`)
20
+ )
21
+ this.addCommand(new ModuleUpload('u', 'shortcut for (u)pload'))
22
+ // download
23
+ this.addCommand(new ModuleDownload('download', 'download module files to the current directory'))
24
+ this.addCommand(new ModuleDownload('d', 'shortcut for (d)ownload'))
25
+ // list
26
+ this.addCommand(new ModuleList('list', 'List modules'))
27
+ this.addCommand(new ModuleList('l', 'shortcut for (l)ist'))
28
+ // rename
29
+
30
+ }
31
+ }
32
+
33
+ function getModuleDBName(filePath: string): string {
34
+ let result
35
+ if (filePath.endsWith('.xml') === false) throw filePath + ' does not have .xml extension'
36
+ result = path.basename(filePath, '.xml')
37
+ const identifierRegex = /^[_a-zA-Z][_a-zA-Z0-9]*$/
38
+ if (result.match(identifierRegex) === null) throw 'filename ' + result + ' does not match identifier syntax'
39
+ return result
40
+ }
41
+ interface UploadOptions {
42
+ id: string | undefined
43
+ env: string | undefined
44
+ }
45
+ class ModuleUpload extends Cmd {
46
+ constructor(name: string, description: string) {
47
+ super(name)
48
+ this.description(description)
49
+ .option('-i --id <id>', 'module ID to download')
50
+ .argument('[files...]', 'local module file(s) to upload to the server')
51
+ .action(async (files: any, options: any) => {
52
+ await this.commandUpload(files, options)
53
+ })
54
+ }
55
+ async commandUpload(files: string[], options: UploadOptions) {
56
+ try {
57
+ await this.prologue(options)
58
+ if (this.dbManager === undefined) throw 'dbManager undefined'
59
+
60
+ if (files.length === 0) throw 'file(s) argument is missing'
61
+ if (options.id !== undefined) {
62
+ if (files.length > 1) throw 'with --id option you can only specify 1 local file'
63
+ const dbModule = await this.dbManager.getModule(options.id)
64
+ dbModule.source = fs.readFileSync(files[0]).toString()
65
+ dbModule.dbname = getModuleDBName(files[0])
66
+ const tag = parseXML(dbModule.dbname, dbModule.source)
67
+ dbModule.access = tag.attributes.ACCESS
68
+ dbModule.description = tag.attributes.DESCRIPTION
69
+ await this.dbManager.dbModuleUpdate(dbModule)
70
+ }
71
+ else {
72
+ for (let ind = 0; ind < files.length; ind++) {
73
+ const file = files[ind]
74
+ const dbname = getModuleDBName(file)
75
+ const source = fs.readFileSync(file).toString()
76
+ const tag = parseXML(dbname, source)
77
+ const dbModuleExist = await this.dbManager.dbModuleExist(dbname)
78
+ if (dbModuleExist === undefined) {
79
+ // insert
80
+ const dbModule = new DBModule()
81
+ dbModule.source = source
82
+ dbModule.dbname = dbname
83
+ dbModule.access = tag.attributes.ACCESS
84
+ dbModule.description = tag.attributes.DESCRIPTION
85
+ logger.debug('module insert ' + dbname + ' access ' + dbModule.access + ' description ' + dbModule.description)
86
+ const id = await this.dbManager.dbModuleInsert(dbModule)
87
+ logger.info('module ' + dbname + ' added with ID ' + id)
88
+ }
89
+ else {
90
+ // update
91
+ dbModuleExist.access = tag.attributes.ACCESS
92
+ dbModuleExist.description = tag.attributes.DESCRIPTION
93
+ logger.debug('module update ' + dbname + ' access ' + dbModuleExist.access + ' description ' + dbModuleExist.description)
94
+ await this.dbManager.dbModuleUpdate(dbModuleExist)
95
+ logger.info('module ' + dbname + ' updated')
96
+ }
97
+ }
98
+ }
99
+ }
100
+ catch (err) {
101
+ logger.error(err)
102
+ }
103
+ }
104
+ }
105
+
106
+ interface DownloadOptions {
107
+ id: string | undefined
108
+ env: string | undefined
109
+ all: string | undefined
110
+ }
111
+ class ModuleDownload extends Cmd {
112
+ constructor(name: string, description: string) {
113
+ super(name)
114
+ this.description(description)
115
+ .option('-i --id <id>', 'module ID')
116
+ .option('-a --all', 'download all the modules')
117
+ .argument('[files...]', 'module(s) name(s) are derived as the basename(s) of the file(s) path')
118
+ .action(async (files: string[], options: DownloadOptions) => {
119
+ await this.commandDownload(files, options)
120
+ })
121
+ }
122
+ async commandDownload(files: string[], options: DownloadOptions) {
123
+ try {
124
+ await this.prologue(options)
125
+ if (this.dbManager === undefined) throw 'dbManager undefined'
126
+
127
+ if (options.id !== undefined) {
128
+ const dbModule = await this.dbManager.getModule(options.id)
129
+ await this.downloadModule(dbModule.dbname || '', dbModule)
130
+ }
131
+ else if (options.all !== undefined) {
132
+ const modules = await this.dbManager.getModules(undefined)
133
+ for (let ind = 0; ind < modules.length; ind++) {
134
+ const dbModule = modules[ind]
135
+ await this.downloadModule(dbModule.dbname || '', dbModule)
136
+ }
137
+ }
138
+ else {
139
+ for (let ind = 0; ind < files.length; ind++) {
140
+ await this.downloadModule(getModuleDBName(files[ind]))
141
+ }
142
+ }
143
+
144
+ }
145
+ catch (err) {
146
+ logger.error(err)
147
+ }
148
+ }
149
+
150
+ async downloadModule(dbname: string, dbModule?: DBModule) {
151
+ logger.debug('downloadModule ' + dbname)
152
+ if (dbModule === undefined) {
153
+ dbModule = await this.dbManager?.dbModuleExist(dbname)
154
+ if (dbModule === undefined) throw 'module not found : ' + dbname
155
+ }
156
+ const filePath = dbname + '.xml'
157
+ if (fs.existsSync(filePath) === true) {
158
+ logger.info('file ' + filePath + ' already exists and will not be overwritten ')
159
+ }
160
+ else {
161
+ fs.writeFileSync(filePath, dbModule.source || '')
162
+ logger.info('module ' + dbname + ' has been downloaded and file ' + filePath + ' has been created')
163
+ }
164
+ }
165
+ }
166
+
167
+ class ModuleList extends Cmd {
168
+ constructor(name: string, description: string) {
169
+ super(name)
170
+ this.description(description)
171
+ .option('-s --sort <columns>', 'sort list by column numbers (begins by 0) separated by comma')
172
+ .action(async (options: any) => {
173
+ await this.commandList(options)
174
+ })
175
+ }
176
+
177
+ async commandList(options: any) {
178
+ try {
179
+ await this.prologue(options)
180
+ if (this.dbManager === undefined) throw 'dbManager undefined'
181
+
182
+ const dbClient = new DBClient(this.dbManager)
183
+ dbClient.selectFromTable('List of Modules', 'cyk_module',
184
+ { fields: 'module_id,module_dbname,module_access,module_description', sort: options.sort || '1' })
185
+
186
+ }
187
+ catch (err) {
188
+ logger.error(err)
189
+ }
190
+ }
191
+ }
192
+
@@ -0,0 +1,60 @@
1
+ import { createCipheriv, createDecipheriv, randomBytes } from 'crypto'
2
+ import { getLogger } from 'loglevel'
3
+ import { CryptoEngine } from '@cyklang/core'
4
+ import { cipherKey } from '@cyklang/core'
5
+ import pako from 'pako'
6
+ import { TextDecoder } from 'util'
7
+ const logger = getLogger('NodeCrypto.ts')
8
+ logger.setLevel('debug')
9
+
10
+ const algorithm = 'aes-128-cbc'
11
+ const iv_length = 16
12
+
13
+ export class NodeCrypto implements CryptoEngine {
14
+
15
+ // last_iv: Buffer | undefined
16
+
17
+ async encrypt(plain: string): Promise<string> {
18
+
19
+ const deflatedData = pako.deflate(plain)
20
+ // logger.debug('deflatedData : ', deflatedData[0], deflatedData[1])
21
+ const iv: Buffer = randomBytes(iv_length)
22
+ // this.last_iv = iv
23
+
24
+ // logger.debug("encrypt iv " + iv.toString('hex'))
25
+
26
+ const cipher = createCipheriv(algorithm, cipherKey, iv)
27
+ let result = cipher.update(deflatedData, undefined, "hex")
28
+ result += cipher.final("hex")
29
+ result += iv.toString('hex')
30
+
31
+ return result
32
+ }
33
+
34
+ async decrypt(encoded: string): Promise<string> {
35
+
36
+ const iv_hex = encoded.substring(encoded.length - 2 * iv_length)
37
+ const encryptedData = encoded.substring(0, encoded.length - 2 * iv_length)
38
+
39
+ // logger.debug("decrypt iv " + iv_hex)
40
+ const iv = Buffer.from(iv_hex, 'hex')
41
+
42
+ // if (this.last_iv === undefined) throw 'last_iv undefined'
43
+ // if (Buffer.compare(iv, this.last_iv) !== 0) throw 'iv and last_iv are different'
44
+
45
+ const decipher = createDecipheriv(algorithm, cipherKey, iv)
46
+ let deflatedData1 = decipher.update(encryptedData, 'hex')
47
+
48
+ const deflatedData2 = decipher.final()
49
+ const deflatedData = Buffer.concat([deflatedData1, deflatedData2])
50
+
51
+ let inflatedData = deflatedData
52
+ if (deflatedData[0] === 0x78 && deflatedData[1] === 0x9c) {
53
+ inflatedData = Buffer.from(pako.inflate(deflatedData))
54
+ }
55
+
56
+ const result = (new TextDecoder()).decode(inflatedData)
57
+ return result
58
+ }
59
+ }
60
+
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env ts-node
2
+ import { Script, Structure, DBRemote, DBManager, DBQueryInstruction, parseXML, Tag, Scope } from "@cyklang/core"
3
+ import * as fs from "fs"
4
+ import loglevel from 'loglevel'
5
+ import { Cmd } from './Cmd'
6
+ import { Command } from 'commander'
7
+ const logger = loglevel.getLogger("QueryCommand.ts")
8
+ logger.setLevel("debug")
9
+
10
+ export class QueryCommand extends Command {
11
+ constructor(name: string) {
12
+ super(name)
13
+ }
14
+ }
15
+
16
+ function parseXmlFile(filepath: string, basename: string, xmlSource: string): Tag {
17
+ const tag = parseXML(filepath, xmlSource)
18
+ if (tag.name !== 'DB.QUERY') {
19
+ throw Error('tag ' + tag.name + ', should be <db.query>')
20
+ }
21
+ const name = tag.attributes.NAME
22
+ if (name === undefined) {
23
+ throw Error('name attribute is missing')
24
+ }
25
+ if (basename !== name + '.xml') {
26
+ throw Error('filename ' + filepath + ' and name ' + name + ' do not correspond')
27
+ }
28
+ return tag
29
+ }
30
+
31
+ async function executeXml(structure: Structure, tag: Tag, xmlSource: string) {
32
+ try {
33
+ logger.debug('executeXml ' + tag.attributes.NAME)
34
+ // structure.setInstructionType("print", new PrintInstructionType(logfilename))
35
+ const dbQueryInstructionType = structure.scope.getInstructionType('db.query')
36
+ if (dbQueryInstructionType === undefined) {
37
+ throw Error('dbQueryInstructionType not found')
38
+ }
39
+
40
+ const scopeParse = new Scope(structure, structure.scope, undefined)
41
+ const dbQueryInst = <DBQueryInstruction>await dbQueryInstructionType.parseInstruction(tag, scopeParse)
42
+ dbQueryInst.source = xmlSource
43
+ const scopeExecute = new Scope(structure, structure.scope, undefined)
44
+ await dbQueryInst.execute(scopeExecute)
45
+ }
46
+ catch (err) {
47
+ logger.error(err)
48
+ }
49
+ }
50
+
51
+ async function testQuery(structure: Structure, queryName: string) {
52
+ const xmlScript = `
53
+ <block>
54
+ <object name='result'/>
55
+ <db.execute query='${queryName}' result='result' />
56
+ <print>string(result)</print>
57
+ </block>
58
+ `
59
+ const script = new Script(structure.scope, 'test', xmlScript)
60
+ await script.parseInstructions()
61
+ await script.execute()
62
+ }