@intecoag/inteco-cli 0.5.1

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.
@@ -0,0 +1,216 @@
1
+ import prompts from "prompts"
2
+ import { rmSync } from "fs";
3
+ import ora from "ora";
4
+ import { DB } from "../utils/db/DB.js";
5
+ import { Config } from "../utils/config/config.js";
6
+ import chalk from "chalk";
7
+ import path from "path";
8
+ import { exec, execSync } from "child_process";
9
+
10
+
11
+ export async function dumpDB(cli) {
12
+ console.log()
13
+
14
+ const config = await Config.getConfig();
15
+
16
+ const databaseNames = await DB.getDatabaseNames();
17
+
18
+ let success = true;
19
+
20
+ const resultsDB = await prompts([
21
+ {
22
+ // DB-Auswahl von DB
23
+ type: 'autocomplete',
24
+ name: 'dbName',
25
+ message: 'DB-Name?',
26
+ choices: databaseNames.map(name => { return { title: name.name } })
27
+ },
28
+ {
29
+ type: 'toggle',
30
+ name: 'dataOnly',
31
+ message: 'Dump data only (no table recreation)?',
32
+ initial: false,
33
+ active: 'yes',
34
+ inactive: 'no'
35
+ }
36
+ , {
37
+ type: 'toggle',
38
+ name: 'selectIndividualTables',
39
+ message: 'Select individual tables?',
40
+ initial: false,
41
+ active: 'yes',
42
+ inactive: 'no'
43
+ }], {
44
+ onCancel: () => {
45
+ console.log()
46
+ console.log(chalk.red("Cancelled Import!"))
47
+ console.log()
48
+ success = false
49
+ }
50
+ })
51
+
52
+ if (success) {
53
+ let selectedTables = [];
54
+ if (resultsDB.selectIndividualTables) {
55
+
56
+ let tables = await DB.executeQueryOnDB("SHOW TABLES;", resultsDB.dbName);
57
+ const tableKey = Object.keys(tables[0])[0];
58
+ tables = tables.map(row => row[tableKey])
59
+ let continuePrompt = true;
60
+ while (continuePrompt) {
61
+ const resultsTable = await prompts([
62
+ {
63
+ // DB-Auswahl von DB
64
+ type: 'autocomplete',
65
+ name: 'table',
66
+ message: 'Table?',
67
+ choices: tables.map(name => { return { title: name } }),
68
+ default: ""
69
+ }, {
70
+ type: 'toggle',
71
+ name: 'continue',
72
+ message: 'Add another table?',
73
+ initial: false,
74
+ active: 'yes',
75
+ inactive: 'no'
76
+ }], {
77
+ onCancel: () => {
78
+ console.log()
79
+ console.log(chalk.red("Cancelled Import!"))
80
+ console.log()
81
+ success = false
82
+ }
83
+ })
84
+
85
+ selectedTables.push(resultsTable.table)
86
+ continuePrompt = resultsTable.continue
87
+ }
88
+
89
+ }
90
+
91
+ const results = await prompts([
92
+ {
93
+ // Ordnerauswahl von vorhandenen Ordner in configIndividual
94
+ type: 'text',
95
+ name: 'dumpName',
96
+ message: 'Dump-Name?',
97
+ initial: 'dump.sql'
98
+ }
99
+ ], {
100
+ onCancel: () => {
101
+ console.log()
102
+ console.log(chalk.red("Cancelled Import!"))
103
+ console.log()
104
+ success = false
105
+ }
106
+ })
107
+
108
+ if (success) {
109
+ console.log()
110
+ const spinner = ora('Dumping DB').start();
111
+
112
+ const dumpCommand = `mysqldump ${resultsDB.dataOnly?'--no-create-info':''} -u${config.dbUser} -p${config.dbPassword} -h${config.dbURL} ${resultsDB.dbName} ${selectedTables.join(" ")} > ${results.dumpName}`;
113
+
114
+ exec(dumpCommand, (error, stdout, stderr) => {
115
+ if (error) {
116
+ spinner.fail('Failed to dump DB');
117
+ console.error(error);
118
+ return;
119
+ }
120
+
121
+ spinner.succeed("Dumped DB to " + results.dumpName);
122
+ console.log();
123
+ });
124
+ }
125
+ }
126
+
127
+
128
+ }
129
+
130
+ export async function dumpDBMand(cli) {
131
+ console.log()
132
+
133
+ const config = await Config.getConfig();
134
+
135
+ const databaseNames = await DB.getDatabaseNames();
136
+
137
+ let success = true;
138
+
139
+ const results = await prompts([
140
+ {
141
+ // DB-Auswahl von DB
142
+ type: 'autocomplete',
143
+ name: 'dbName',
144
+ message: 'DB-Name?',
145
+ choices: databaseNames.map(name => { return { title: name.name } })
146
+ },
147
+ {
148
+ type: 'number',
149
+ name: 'mnr',
150
+ message: 'Mandant?',
151
+ initial: '1'
152
+ },
153
+ {
154
+ // Ordnerauswahl von vorhandenen Ordner in configIndividual
155
+ type: 'text',
156
+ name: 'dumpName',
157
+ message: 'Dump-Name?',
158
+ initial: 'dump.sql'
159
+ }
160
+ ], {
161
+ onCancel: () => {
162
+ console.log()
163
+ console.log(chalk.red("Cancelled Import!"))
164
+ console.log()
165
+ success = false
166
+ }
167
+ })
168
+
169
+ if (success) {
170
+ console.log()
171
+
172
+ const spinner = ora('Dumping DB').start();
173
+
174
+ // Load table names
175
+ let tables = await DB.executeQueryOnDB("SHOW TABLES;", results.dbName);
176
+ const tableKey = Object.keys(tables[0])[0];
177
+ tables = tables.map(row => row[tableKey])
178
+
179
+ if (tables == null || tables.length == 0) {
180
+ spinner.fail("Database has no tables: " + results.dbName)
181
+ } else {
182
+ // Remove previous dump
183
+ rmSync("." + path.sep + results.dumpName, { recursive: true, force: true })
184
+ for (const table of tables) {
185
+ // Check if Mand-Column exists
186
+ const tableSpinner = ora("Dumping data: " + table).start();
187
+ const tableCol = table + "_mnr"
188
+ const columnExists = await DB.executeQueryOnDB("SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = '" + results.dbName + "' AND table_name = '" + table + "' AND column_name = '" + tableCol + "';")
189
+
190
+ if (columnExists[0]['COUNT(*)'] != 0) {
191
+ // Check if Mand-Data is present
192
+ const dataExists = await DB.executeQueryOnDB("SELECT COUNT(*) FROM " + table + " WHERE " + tableCol + " = '" + results.mnr + "';", results.dbName);
193
+
194
+ if (dataExists[0]['COUNT(*)'] != 0) {
195
+ // Dump table
196
+ execSync("mysqldump -u" + config.dbUser + " -p" + config.dbPassword + " -h" + config.dbURL + " --no-create-info --where=\"" + tableCol + " = '" + results.mnr + "'\" " + results.dbName + " " + table + " >> " + results.dumpName);
197
+
198
+ tableSpinner.succeed("Data dumped: " + table);
199
+ } else {
200
+ tableSpinner.info("No data present, Skipping table: " + table)
201
+ }
202
+ } else {
203
+ tableSpinner.info("Column " + tableCol + " not found, Skipping table: " + table)
204
+ }
205
+ }
206
+ // Dump mand-table (special field name)
207
+ const tableSpinner = ora("Dumping mand (custom logic)")
208
+ execSync("mysqldump -u" + config.dbUser + " -p" + config.dbPassword + " -h" + config.dbURL + " --no-create-info --where=\"mand_mandant = '" + results.mnr + "'\" " + results.dbName + " mand >> " + results.dumpName);
209
+ tableSpinner.succeed("Data dumped: mand")
210
+ spinner.succeed("Dumped DB to " + results.dumpName);
211
+ }
212
+
213
+
214
+ console.log();
215
+ }
216
+ }
@@ -0,0 +1,154 @@
1
+ import fs from "fs";
2
+ import nReadlines from 'n-readlines';
3
+ import prompts from "prompts";
4
+ import path from "path";
5
+ import ora from "ora";
6
+ import Seven from 'node-7z'
7
+ import sevenBin from '7zip-bin'
8
+
9
+
10
+ export default async function dumpTableToCSV() {
11
+ console.log()
12
+
13
+ let success = true;
14
+
15
+ const results = await prompts([
16
+ {
17
+ // Tabelle die exportiert werden soll
18
+ type: 'text',
19
+ name: 'table',
20
+ message: 'Tabellen-Name?'
21
+ }
22
+ ], {
23
+ onCancel: () => {
24
+ console.log()
25
+ console.log(chalk.red("Cancelled Dump!"))
26
+ console.log()
27
+ success = false
28
+ }
29
+ })
30
+
31
+
32
+ if (success) {
33
+ console.log()
34
+
35
+ // Unpack archives
36
+ let archives = fs.readdirSync(process.cwd(), { withFileTypes: true }).filter(dirent => dirent.isFile() && (dirent.name.split(".")[dirent.name.split(".").length-1] == "gz") || (dirent.name.split(".")[dirent.name.split(".").length-1] == "7z")).map(dirent => { return dirent.name });
37
+ for (const archive of archives){
38
+ await extractDumpsFromArchive(archive)
39
+ }
40
+
41
+ console.log()
42
+
43
+ // Read Files and create csv
44
+ let files = fs.readdirSync(process.cwd(), { withFileTypes: true }).filter(dirent => dirent.isFile() && dirent.name.split(".")[dirent.name.split(".").length-1] == "sql").map(dirent => { return dirent.name });
45
+
46
+ await Promise.all(files.map(async (file) => {
47
+ await createCSVDump(file, results.table)
48
+ }));
49
+
50
+ console.log()
51
+ }
52
+ }
53
+
54
+ function getPromiseFromEvent(item, event) {
55
+ return new Promise((resolve) => {
56
+ const listener = (data) => {
57
+ resolve(data);
58
+ }
59
+ item.on(event, listener);
60
+ })
61
+ }
62
+
63
+ async function extractDumpsFromArchive(archive){
64
+ const spinnerZIP = ora('Unpacking Archive: '+archive).start();
65
+
66
+ const list = Seven.list(process.cwd() + path.sep + archive, {
67
+ $bin: sevenBin.path7za
68
+ })
69
+
70
+ const data = await getPromiseFromEvent(list, "data")
71
+
72
+ let file = data.file
73
+
74
+ if(file.split(".").length == 1){
75
+ const rename = Seven.rename(process.cwd() + path.sep + archive,[[file, file+".sql"]], {
76
+ $bin: sevenBin.path7za
77
+ })
78
+
79
+ await getPromiseFromEvent(rename, "end")
80
+ }
81
+
82
+ const unpack = Seven.extract(process.cwd() + path.sep + archive,"."+path.sep, {
83
+ $bin: sevenBin.path7za
84
+ })
85
+
86
+ await getPromiseFromEvent(unpack, "end")
87
+
88
+ spinnerZIP.succeed("Archive unpacked: "+archive)
89
+
90
+ }
91
+
92
+ async function createCSVDump(file, table) {
93
+ let count = 0;
94
+ const spinner = ora('Reading file (' + file +"): "+count).start();
95
+ const readFile = new nReadlines(process.cwd() + path.sep + file);
96
+
97
+ let line = "";
98
+ let data = "";
99
+ let readHeader = false;
100
+ let headerData = "";
101
+
102
+ while ((line = readFile.next())) {
103
+ line = line.toString('utf-8').trim()
104
+
105
+ if (line.includes("INSERT INTO `" + table + "` VALUES")) {
106
+ data = data + line;
107
+ }
108
+
109
+ if (line.includes("CREATE TABLE `" + table + "`")) {
110
+ readHeader = true;
111
+ }
112
+
113
+ if (readHeader) {
114
+ if (line.includes("PRIMARY KEY")) {
115
+ readHeader = false;
116
+ } else {
117
+ headerData = headerData.concat(line);
118
+ }
119
+
120
+ }
121
+
122
+ count++;
123
+ spinner.text = 'Reading file (' + file +"): "+count
124
+ spinner.render()
125
+ }
126
+ spinner.text = 'Writing file (' + file +")"
127
+ spinner.render()
128
+
129
+ let headers = []
130
+ let matchHeader = new RegExp(/`(.*?)`/g);
131
+ var found;
132
+ while (found = matchHeader.exec(headerData)) {
133
+ if (found[0] != "`" + table + "`") {
134
+ headers.push(found[0]);
135
+ }
136
+
137
+ };
138
+
139
+ let records = [];
140
+ var reBrackets = /\((.*?)\)/g;
141
+ var found;
142
+ while (found = reBrackets.exec(data)) {
143
+ records.push(found[1]);
144
+ };
145
+
146
+ let recordsString = records.join("\n").replaceAll(",", ";");
147
+ let headersString = headers.join(";")
148
+
149
+ if (!fs.existsSync(process.cwd() + path.sep + "csv")) {
150
+ fs.mkdirSync(process.cwd() + path.sep + "csv")
151
+ }
152
+ fs.writeFileSync(process.cwd() + path.sep + "csv" + path.sep + file.split(".")[0] + ".csv", headersString + "\n" + recordsString)
153
+ spinner.succeed("CSV created: "+file.split(".")[0] + ".csv")
154
+ }
@@ -0,0 +1,226 @@
1
+ import { DB } from "../utils/db/DB.js";
2
+ import prompts from "prompts";
3
+ import CliTable3 from "cli-table3";
4
+ import readline from "readline";
5
+ import chalk from "chalk";
6
+ import fuzzysort from "fuzzysort";
7
+
8
+ // Entry point
9
+ export default async function extdSearch() {
10
+ const responses = await configureDB();
11
+
12
+ if (!responses.dbName) return;
13
+
14
+ let currentSearchType = configs[0];
15
+ let lastQuery = '';
16
+
17
+ const baseData = await loadData(responses.dbName, responses.tables);
18
+ const rl = createReadline();
19
+
20
+ console.log(chalk.green("Interactive Search Started."));
21
+ rl.prompt();
22
+
23
+ rl.on('line', async (input) => {
24
+ const query = input.trim();
25
+
26
+ if (handleBuiltInCommands(query, rl)) return;
27
+
28
+ const matchedConfig = configs.find(c => query === `:${c.cmd}`);
29
+ if (matchedConfig) {
30
+ currentSearchType = matchedConfig;
31
+ } else {
32
+ lastQuery = query;
33
+ }
34
+
35
+ const results = await fuzzySearch(baseData, lastQuery, currentSearchType);
36
+ renderTable(results, currentSearchType);
37
+ rl.prompt();
38
+ });
39
+
40
+ rl.on('close', () => {
41
+ console.log(chalk.yellow("Search session ended."));
42
+ process.exit(0);
43
+ });
44
+ }
45
+
46
+ function createReadline() {
47
+ return readline.createInterface({
48
+ input: process.stdin,
49
+ output: process.stdout,
50
+ prompt: 'Search query (show help with :?)> '
51
+ });
52
+ }
53
+
54
+ function handleBuiltInCommands(query, rl) {
55
+ switch (query) {
56
+ case ':q':
57
+ rl.close();
58
+ return true;
59
+ case ':?':
60
+ printHelp();
61
+ rl.prompt();
62
+ return true;
63
+ default:
64
+ return false;
65
+ }
66
+ }
67
+
68
+ function printHelp() {
69
+ console.log("\n" + chalk.cyan("Available Commands:"));
70
+ console.log(chalk.yellow(":q") + " - Exit the application");
71
+ console.log(chalk.yellow(":?") + " - Show this help");
72
+ configs.forEach(config =>
73
+ console.log(`${chalk.yellow(`:${config.cmd}`)} - Switch to ${config.name}`)
74
+ );
75
+ console.log();
76
+ }
77
+
78
+ async function configureDB() {
79
+ console.log();
80
+ const databaseNames = await DB.getDatabaseNames();
81
+
82
+ return await prompts([
83
+ {
84
+ type: 'autocomplete',
85
+ name: 'dbName',
86
+ message: 'DB-Name?',
87
+ choices: databaseNames.map(db => ({ title: db.name }))
88
+ },
89
+ {
90
+ type: 'select',
91
+ name: 'tables',
92
+ message: 'Search-Type?',
93
+ choices: [
94
+ { title: 'EXTD/EXTI', value: 'EXTD/EXTI' },
95
+ { title: 'EXTI only', value: 'EXTI' },
96
+ { title: 'EXTD only', value: 'EXTD' }
97
+ ]
98
+ }
99
+ ], {
100
+ onCancel: () => {
101
+ console.log("\n" + chalk.red("Cancelled Search!\n"));
102
+ }
103
+ });
104
+ }
105
+
106
+ async function loadData(db, tableChoice) {
107
+ const extd = await getTableData('extd', db);
108
+ const exti = await getTableData('exti', db);
109
+
110
+ const cleaned = [
111
+ ...cleanData(extd, 'extd'),
112
+ ...cleanData(exti, 'exti')
113
+ ];
114
+
115
+ if (tableChoice === 'EXTD') return cleaned.filter(e => e.table === 'extd');
116
+ if (tableChoice === 'EXTI') return cleaned.filter(e => e.table === 'exti');
117
+ return cleaned;
118
+ }
119
+
120
+ function cleanData(entries, tableName) {
121
+ return entries.map(entry => {
122
+ const stripped = Object.fromEntries(Object.entries(entry).map(([k, v]) => {
123
+ return [k.replace(/^ext[di]_/, ''), v];
124
+ }));
125
+ return { ...stripped, table: tableName };
126
+ });
127
+ }
128
+
129
+ async function getTableData(table, db) {
130
+ const query = `SELECT * FROM ${table};`;
131
+ return await DB.executeQueryOnDB(query, db);
132
+ }
133
+
134
+ async function fuzzySearch(data, query, config) {
135
+ if (!query) return data.slice(0, 1000);
136
+ return fuzzysort.go(query, data, {
137
+ keys: config.searchKeys,
138
+ limit: 1000,
139
+ threshold: config.threshold
140
+ }).map(r => ({ ...r.obj, score: r.score }));
141
+ }
142
+
143
+ function renderTable(data, config) {
144
+ const table = new CliTable3({ head: config.tableHeader });
145
+ data.forEach(row => table.push(config.tableFormatter(row)));
146
+ console.log(table.toString());
147
+ }
148
+
149
+ // ---------- Configurations & Helpers ----------
150
+
151
+ const configs = [
152
+ {
153
+ name: "Overview",
154
+ cmd: "ow",
155
+ searchKeys: ['name', 'bez_d', 'bez_f', 'bez_i'],
156
+ threshold: 0.7,
157
+ tableHeader: ['Table', 'Name', 'Bezeichnung Deutsch', 'Bezeichnung Französisch', 'Bezeichnung Italieniesch'],
158
+ tableFormatter: p => [p.table, p.name, p.bez_d, p.bez_f, p.bez_i]
159
+ },
160
+ {
161
+ name: "Field-Information",
162
+ cmd: "fi",
163
+ searchKeys: [
164
+ 'name', 'bez_d', 'bez_f', 'bez_i',
165
+ 'b_dtext_1', 'b_dtext_2', 'b_dtext_3',
166
+ 'b_dtext_4', 'b_dtext_5', 'b_dtext_6',
167
+ 'b_dtext_7', 'b_dtext_8', 'b_dtext_9'
168
+ ],
169
+ threshold: 0.7,
170
+ tableHeader: ['Table', 'Name', 'Feldtyp', 'Flag', 'Testflag', 'Wert 1', 'Wert 2', 'Wert 3', 'Wert 4', 'Wert 5', 'Wert 6', 'Wert 7', 'Wert 8', 'Wert 9'],
171
+ tableFormatter: p => [
172
+ p.table, p.name,
173
+ formatFieldType(p.special),
174
+ formatFlag(p.flag),
175
+ formatTestFlag(p.testflag),
176
+ ...Array.from({ length: 9 }, (_, i) => formatWert(p[`b_value_${i + 1}`], p[`b_dtext_${i + 1}`]))
177
+ ]
178
+ },
179
+ {
180
+ name: "Disp-Fields",
181
+ cmd: "df",
182
+ searchKeys: [
183
+ 'name', 'bez_d', 'testfeld',
184
+ 'disp_feld_1', 'disp_feld_2', 'disp_feld_3',
185
+ 'disp_feld_4', 'disp_feld_5', 'disp_feld_6',
186
+ 'disp_feld_7', 'disp_feld_8', 'disp_feld_9'
187
+ ],
188
+ threshold: 0.7,
189
+ tableHeader: ['Table', 'Name', 'Test-Feld', 'Dispmask', 'Dispfeld 1', 'Dispfeld 2', 'Dispfeld 3', 'Dispfeld 4', 'Dispfeld 5', 'Dispfeld 6', 'Dispfeld 7', 'Dispfeld 8', 'Dispfeld 9'],
190
+ tableFormatter: p => [
191
+ p.table, p.name, p.testfeld, p.dispmask,
192
+ p.disp_feld_1, p.disp_feld_2, p.disp_feld_3,
193
+ p.disp_feld_4, p.disp_feld_5, p.disp_feld_6,
194
+ p.disp_feld_7, p.disp_feld_8, p.disp_feld_9
195
+ ]
196
+ }
197
+ ];
198
+
199
+ function formatWert(value, bez) {
200
+ return value || bez ? `'${value}'='${bez}'` : '';
201
+ }
202
+
203
+ function formatFieldType(type) {
204
+ return {
205
+ '0': 'Custom',
206
+ '1': 'Checkbox',
207
+ '2': 'Radiobutton',
208
+ '4': 'Text'
209
+ }[type] || '';
210
+ }
211
+
212
+ function formatFlag(flag) {
213
+ return {
214
+ '0': 'Optional',
215
+ '1': 'Zwingend',
216
+ '2': 'Aus',
217
+ '3': 'Dialog aus'
218
+ }[flag] || '';
219
+ }
220
+
221
+ function formatTestFlag(flag) {
222
+ return {
223
+ '0': 'Zwingend',
224
+ '1': 'Null/Space erlaubt'
225
+ }[flag] || '';
226
+ }
@@ -0,0 +1,61 @@
1
+ import prompts from "prompts";
2
+ import chalk from "chalk";
3
+ import graphql from "graphql"
4
+ import { writeFileSync, readFileSync, readdirSync, writeFile, unlinkSync, mkdirSync, renameSync, rmSync } from "fs";
5
+
6
+ export default async function qraphqlSchemaExport(){
7
+ console.log()
8
+
9
+ let success = true;
10
+
11
+ const responses = await prompts([{
12
+ type: 'text',
13
+ name: 'url',
14
+ message: 'URL?',
15
+ initial: "http://localhost:8080/graphql"
16
+ },
17
+ {
18
+ type: 'text',
19
+ name: 'token',
20
+ message: 'AUTH-Token?'
21
+ },
22
+ {
23
+ type: 'text',
24
+ name: 'file',
25
+ message: 'File-Name?',
26
+ initial: 'schema.graphqls'
27
+ }], {
28
+ onCancel: () => {
29
+ console.log()
30
+ console.log(chalk.red("Cancelled GraphQL-Schema-Export!"))
31
+ console.log()
32
+ success = false
33
+ }
34
+ })
35
+
36
+ if(success){
37
+ console.log()
38
+
39
+ try{
40
+ const {data, errors} = await fetch(responses.url, {
41
+ method: "POST",
42
+ headers: {
43
+ "Content-Type": "application/json",
44
+ "Accept": "application/json",
45
+ "Authorization": "Bearer "+responses.token
46
+ },
47
+ body: JSON.stringify({query: graphql.getIntrospectionQuery()})
48
+ }).then(res => res.json())
49
+
50
+ const schema = graphql.buildClientSchema(data)
51
+
52
+
53
+ writeFileSync(responses.file, graphql.printSchema(schema))
54
+ console.log(chalk.green("Schema loaded: "+responses.file))
55
+ console.log();
56
+ }catch(e){
57
+ console.log(chalk.red("Error loading schema: "+e))
58
+ console.log();
59
+ }
60
+ }
61
+ }