From e57ccf6e41a6ef61259cc6b56cd267c57a2197de Mon Sep 17 00:00:00 2001 From: Juan Ferrer Toribio Date: Sat, 8 Apr 2023 15:08:37 +0200 Subject: [PATCH] First commit --- .gitignore | 3 + Dockerfile | 35 +++ Jenkinsfile | 54 ++++ LICENSE | 17 ++ README.md | 34 +++ config.local.yml | 36 +++ config.yml | 30 +++ docker-compose.yml | 23 ++ index.js | 17 ++ mylogger.js | 611 +++++++++++++++++++++++++++++++++++++++++++++ package-lock.json | 508 +++++++++++++++++++++++++++++++++++++ package.json | 9 + zongji.sql | 12 + zongji.undo.sql | 3 + 14 files changed, 1392 insertions(+) create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 Jenkinsfile create mode 100644 LICENSE create mode 100644 README.md create mode 100644 config.local.yml create mode 100644 config.yml create mode 100644 docker-compose.yml create mode 100644 index.js create mode 100644 mylogger.js create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 zongji.sql create mode 100644 zongji.undo.sql diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f14ce54 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules +zongji +config/*.local.yml \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ee09a4f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,35 @@ +FROM debian:bullseye-slim + +ARG DEBIAN_FRONTEND=noninteractive + +# NodeJs + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + curl \ + ca-certificates \ + gnupg2 \ + && url -fsSL https://deb.nodesource.com/setup_14.x | bash - \ + && apt-get install -y --no-install-recommends nodejs \ + && rm -rf /var/lib/apt/lists/* + +# Consumer + +WORKDIR /mycdc +COPY package.json package-lock.json ./ +RUN npm install --only=prod + +ARG BUILD_ID=unknown +ARG VERSION +ENV VERSION $VERSION +RUN echo $VERSION + +COPY \ + LICENSE \ + README.md \ + mylogger.js \ + index.js \ + config.yml \ + ./ + +CMD ["node", "mylogger.js"] diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..a0f43d3 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,54 @@ +#!/usr/bin/env groovy + +pipeline { + agent any + options { + disableConcurrentBuilds() + } + environment { + PROJECT_NAME = 'mycdc' + STACK_NAME = "${env.PROJECT_NAME}-${env.BRANCH_NAME}" + } + stages { + stage('Checkout') { + steps { + script { + def packageJson = readJSON file: 'package.json' + env.VERSION = packageJson.version + } + configFileProvider([ + configFile(fileId: "mycdc.groovy", + variable: 'GROOVY_FILE') + ]) { + load env.GROOVY_FILE + } + setEnv() + } + } + stage('Build') { + when {branch 'master'} + environment { + CREDENTIALS = credentials('docker-registry') + } + steps { + sh 'docker login --username $CREDS_USR --password $CREDS_PSW $REGISTRY' + sh 'docker-compose build --build-arg BUILD_ID=$BUILD_ID --parallel' + sh 'docker-compose push' + } + } + stage('Deploy') { + when {branch 'master'} + environment { + DOCKER_HOST = "${env.SWARM_HOST}" + } + steps { + sh "docker stack deploy --with-registry-auth --compose-file docker-compose.yml ${env.STACK_NAME}" + } + } + } + post { + unsuccessful { + sendEmail() + } + } +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..1853d69 --- /dev/null +++ b/LICENSE @@ -0,0 +1,17 @@ +Copyright (C) 2018 - Verdnatura Levante S.L. + +This package is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +On Debian systems, the complete text of the GNU General Public +License can be found in "/usr/share/common-licenses/GPL-3". \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..daed738 --- /dev/null +++ b/README.md @@ -0,0 +1,34 @@ +# Asynchronous DB calculations reading the binary log + +## Enviroment setup + +Because a bug with MariaDB wich it's fix is pending to be merged into main +project branch, a *zongji* fork must be cloned into project root directory. +More info at https://github.com/nevill/zongji/issues/143 +```text +git clone https://github.com/juan-ferrer-toribio/zongji.git +cd zongji +git checkout fix-143 +``` + +Apply *zongji.sql* script into DB. + +Copy *config.json* to *config.local.json* and place your local configuration +there. + +Install dependencies. +```text +npm install +``` + +## Run application + +Launch app. +```text +node index.js +``` + +## Built With + +* [Zongji](https://github.com/nevill/zongji) +* [MySQL2](https://github.com/sidorares/node-mysql2#readme) diff --git a/config.local.yml b/config.local.yml new file mode 100644 index 0000000..a805586 --- /dev/null +++ b/config.local.yml @@ -0,0 +1,36 @@ +debug: true +testMode: false +db: + host: localhost + port: 3306 + user: root + password: root + database: vn +showField: + - name + description +logs: + ticket: + logTable: clientLog + mainTable: ticket + tables: + - sale + - ticketTracking + client: + logTable: clientLog + mainTable: client + tables: + - client + - address + item: + logTable: itemLog + mainTable: item + tables: + - name: item + exclude: + - image + - supplyResponseFk + - itemTag + route: + logTable: routeLog + mainTable: route diff --git a/config.yml b/config.yml new file mode 100644 index 0000000..7c632a9 --- /dev/null +++ b/config.yml @@ -0,0 +1,30 @@ +code: mylogger +debug: false +testMode: false +pingInterval: 60 +flushInterval: 10 +db: + host: localhost + port: 3306 + user: zongji + password: password + database: util +showFields: + - name + - description +castTypes: + tinyint: boolean +logs: + item: + logTable: vn.itemLog + mainTable: item + tables: + - name: item + exclude: + - image + - supplyResponseFk + types: + id: number + isPrinted: boolean + - name: itemTag + relation: itemFk diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..81e9e48 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,23 @@ +version: '3.7' +services: + main: + image: registry.verdnatura.es/mylogger + build: + context: . + dockerfile: Dockerfile + args: + - VERSION=${VERSION:?} + configs: + - source: config + target: /mylogger/config.local.yml + deploy: + resources: + limits: + memory: 1G + placement: + constraints: + - node.role == worker +configs: + config: + external: true + name: mylogger_config diff --git a/index.js b/index.js new file mode 100644 index 0000000..1d3ec22 --- /dev/null +++ b/index.js @@ -0,0 +1,17 @@ +const MyLogger = require('./mylogger'); + +async function main() { + const mylogger = new MyLogger() + await mylogger.start(); + + process.on('SIGINT', async function() { + console.log('Got SIGINT.'); + try { + await mylogger.stop(); + } catch (err) { + console.error(err); + } + process.exit(); + }); +} +main(); diff --git a/mylogger.js b/mylogger.js new file mode 100644 index 0000000..bded49a --- /dev/null +++ b/mylogger.js @@ -0,0 +1,611 @@ +require('require-yaml'); +require('colors'); +const fs = require('fs'); +const path = require('path'); +const ZongJi = require('./zongji'); +const mysql = require('mysql2/promise'); + +const allEvents = [ + 'writerows', + 'updaterows', + 'deleterows' +]; + +const actions = { + writerows: 'insert', + updaterows: 'update', + deleterows: 'delete' +}; + +module.exports = class MyLogger { + constructor() { + this.running = false; + this.filename = null; + this.position = null; + this.schemaMap = new Map(); + this.logMap = new Map(); + } + + async start() { + const defaultConfig = require('./config.yml'); + const conf = this.conf = Object.assign({}, defaultConfig); + const localPath = path.join(__dirname, 'config.local.yml'); + if (fs.existsSync(localPath)) { + const localConfig = require(localPath); + Object.assign(conf, localConfig); + } + + const defaultSchema = conf.db.database; + function parseTable(tableString) { + let name, schema; + const split = tableString.split('.'); + if (split.length == 1) { + name = split[0]; + schema = defaultSchema; + } else { + [name, schema] = split; + } + return {name, schema}; + } + + const schemaMap = this.schemaMap; + function addTable(tableConf, logInfo) { + if (typeof tableConf == 'string') + tableConf = {name: tableConf}; + const table = parseTable(tableConf.name); + + let tableMap = schemaMap.get(table.schema); + if (!tableMap) { + tableMap = new Map(); + schemaMap.set(table.schema, tableMap); + } + + let tableInfo = tableMap.get(table.name); + if (!tableInfo) { + tableInfo = { + conf: tableConf, + log: logInfo + }; + tableMap.set(table.name, tableInfo); + } + + Object.assign(tableInfo, { + conf: tableConf, + exclude: new Set(tableConf.exclude), + castTypes: new Map(), + columns: new Map(), + showField: tableConf.showField, + relation: tableConf.relation + }); + + if (tableConf.types) + for (const col in tableConf.types) + tableInfo.castTypes.set(col, tableConf.types[col]); + + return tableInfo; + } + + for (const logName in conf.logs) { + const logConf = conf.logs[logName]; + const logInfo = { + conf: logConf, + table: parseTable(logConf.logTable), + mainTable: parseTable(logConf.mainTable) + }; + this.logMap.set(logName, logInfo); + + const mainTable = addTable(logInfo.mainTable, logInfo); + mainTable.isMain = true; + + if (logConf.tables) + for (const tableConf of logConf.tables){ + const table = addTable(tableConf, logInfo); + if (table !== mainTable) { + Object.assign(table, { + main: mainTable, + isMain: false + }); + } + } + } + + const includeSchema = {}; + for (const [schemaName, tableMap] of this.schemaMap) + includeSchema[schemaName] = Array.from(tableMap.keys()); + + this.opts = { + includeEvents: [ + 'rotate', + 'tablemap', + 'writerows', + 'updaterows', + 'deleterows' + ], + includeSchema + }; + + if (conf.testMode) + console.log('Test mode enabled, just logging queries to console.'); + + console.log('Starting process.'); + await this.init(); + console.log('Process started.'); + } + + async stop() { + console.log('Stopping process.'); + await this.end(); + console.log('Process stopped.'); + } + + async init() { + const {conf} = this; + this.debug('MyLogger', 'Initializing.'); + this.onErrorListener = err => this.onError(err); + + // DB connection + + const db = this.db = await mysql.createConnection(conf.db); + db.on('error', this.onErrorListener); + + for (const logInfo of this.logMap.values()) { + const table = logInfo.table; + const sqlTable = `${db.escapeId(table.schema)}.${db.escapeId(table.name)}` + logInfo.addStmt = await db.prepare( + `INSERT INTO ${sqlTable} + SET originFk = ?, + userFk = ?, + action = ?, + creationDate = ?, + changedModel = ?, + oldInstance = ?, + newInstance = ?, + changedModelId = ?, + changedModelValue = ?` + ); + logInfo.fetchStmt = await db.prepare( + `SELECT id FROM ${sqlTable} + WHERE changedModel = ? + AND changedModelId = ? + AND action = 'delete'` + ); + logInfo.updateStmt = await db.prepare( + `UPDATE ${sqlTable} + SET originFk = ?, + creationDate = ?, + oldInstance = ?, + changedModelValue = ? + WHERE id = ?` + ); + } + + for (const [schema, tableMap] of this.schemaMap) + for (const [table, tableInfo] of tableMap) { + + // Fetch columns & types + + const [dbCols] = await db.query( + `SELECT COLUMN_NAME \`col\`, DATA_TYPE \`type\`, COLUMN_DEFAULT \`def\` + FROM information_schema.\`COLUMNS\` + WHERE TABLE_NAME = ? AND TABLE_SCHEMA = ?`, + [table, schema] + ); + + for (const {col, type, def} of dbCols) { + if (!tableInfo.exclude.has(col)) + tableInfo.columns.set(col, {type, def}); + + const castType = conf.castTypes[type]; + if (castType && !tableInfo.castTypes.has(col)) + tableInfo.castTypes.set(col, castType); + } + + // Fetch primary key + + const [dbPks] = await db.query( + `SELECT COLUMN_NAME idName + FROM information_schema.KEY_COLUMN_USAGE + WHERE CONSTRAINT_NAME = 'PRIMARY' + AND TABLE_NAME = ? + AND TABLE_SCHEMA = ?`, + [table, schema] + ); + + if (!dbPks.length) + throw new Error(`Primary not found for table: ${schema}.${table}`); + if (dbPks.length > 1) + throw new Error(`Only one column primary is supported: ${schema}.${table}`); + + for (const {idName} of dbPks) + tableInfo.idName = idName; + + // Get show field + + if (!tableInfo.showField) { + for (const showField of conf.showFields) { + if (tableInfo.columns.has(showField)) { + tableInfo.showField = showField; + break; + } + } + } + } + + for (const [schema, tableMap] of this.schemaMap) + for (const [table, tableInfo] of tableMap) { + + // Fetch relation + + if (!tableInfo.relation && !tableInfo.isMain) { + const mainTable = tableInfo.log.mainTable; + const mainTableInfo = this.schemaMap + .get(mainTable.schema) + .get(mainTable.name); + + const [relations] = await db.query( + `SELECT COLUMN_NAME relation + FROM information_schema.KEY_COLUMN_USAGE + WHERE TABLE_NAME = ? + AND TABLE_SCHEMA = ? + AND REFERENCED_TABLE_NAME = ? + AND REFERENCED_TABLE_SCHEMA = ? + AND REFERENCED_COLUMN_NAME = ?`, + [ + table, + schema, + mainTable.name, + mainTable.schema, + mainTableInfo.idName + ] + ); + + console.debug( + table, + schema, + mainTable.name, + mainTable.schema, + mainTableInfo.idName + ); + + if (!relations.length) + throw new Error(`No relation to main table found for table: ${schema}.${table}`); + if (relations.length > 1) + throw new Error(`Found more multiple relations to main table: ${schema}.${table}`); + + for (const {relation} of relations) + tableInfo.relation = relation; + } + } + + // Zongji + + const zongji = new ZongJi(conf.db); + this.zongji = zongji; + + this.onBinlogListener = evt => this.onBinlog(evt); + zongji.on('binlog', this.onBinlogListener); + + const [res] = await db.query( + 'SELECT `logName`, `position` FROM `util`.`binlogQueue` WHERE code = ?', + [conf.code] + ); + if (res.length) { + const [row] = res; + this.filename = row.logName; + this.position = row.position; + Object.assign(this.opts, { + filename: this.filename, + position: this.position + }); + } else + this.opts.startAtEnd = true; + + this.debug('Zongji', 'Starting.'); + await new Promise((resolve, reject) => { + const onReady = () => { + zongji.off('error', onError); + resolve(); + }; + const onError = err => { + this.zongji = null; + zongji.off('ready', onReady); + zongji.off('binlog', this.onBinlogListener); + reject(err); + } + + zongji.once('ready', onReady); + zongji.once('error', onError); + zongji.start(this.opts); + }); + this.debug('Zongji', 'Started.'); + + this.zongji.on('error', this.onErrorListener); + + this.flushInterval = setInterval( + () => this.flushQueue(), conf.flushInterval * 1000); + this.pingInterval = setInterval( + () => this.connectionPing(), conf.pingInterval * 1000); + + // Summary + + this.running = true; + this.debug('MyLogger', 'Initialized.'); + } + + async end(silent) { + const zongji = this.zongji; + if (!zongji) return; + + this.debug('MyLogger', 'Ending.'); + + // Zongji + + clearInterval(this.flushInterval); + clearInterval(this.pingInterval); + zongji.off('binlog', this.onBinlogListener); + zongji.off('error', this.onErrorListener); + this.zongji = null; + this.running = false; + + this.debug('Zongji', 'Stopping.'); + // FIXME: Cannot call Zongji.stop(), it doesn't wait to end connection + zongji.connection.destroy(() => { + console.log('zongji.connection.destroy'); + }); + await new Promise(resolve => { + zongji.ctrlConnection.query('KILL ?', [zongji.connection.threadId], + err => { + if (err && err.code !== 'ER_NO_SUCH_THREAD' && !silent) + console.error(err); + resolve(); + }); + }); + zongji.ctrlConnection.destroy(() => { + console.log('zongji.ctrlConnection.destroy'); + }); + zongji.emit('stopped'); + this.debug('Zongji', 'Stopped.'); + + // DB connection + + this.db.off('error', this.onErrorListener); + // FIXME: mysql2/promise bug, db.end() ends process + this.db.on('error', () => {}); + try { + await this.db.end(); + } catch (err) { + if (!silent) + console.error(err); + } + + // Summary + + this.debug('MyLogger', 'Ended.'); + } + + async tryRestart() { + try { + await this.init(); + console.log('Process restarted.'); + } catch(err) { + setTimeout(() => this.tryRestart(), 30); + } + } + + async onError(err) { + console.log(`Error: ${err.code}: ${err.message}`); + try { + await this.end(true); + } catch(e) {} + + switch (err.code) { + case 'PROTOCOL_CONNECTION_LOST': + case 'ECONNRESET': + console.log('Trying to restart process.'); + await this.tryRestart(); + break; + default: + process.exit(); + } + } + + onBinlog(evt) { + //evt.dump(); + const eventName = evt.getEventName(); + let position = evt.nextPosition; + + switch (eventName) { + case 'rotate': + this.filename = evt.binlogName; + position = evt.position; + console.log(`[${eventName}] filename: ${this.filename}`, `position: ${this.position}, nextPosition: ${evt.nextPosition}`); + break; + case 'writerows': + case 'deleterows': + case 'updaterows': + this.onRowEvent(evt, eventName); + break; + } + + this.position = position; + this.flushed = false; + } + + async onRowEvent(evt, eventName) { + const table = evt.tableMap[evt.tableId]; + const tableName = table.tableName; + const tableMap = this.schemaMap.get(table.parentSchema); + if (!tableMap) return; + + const tableInfo = tableMap.get(tableName); + if (!tableInfo) return; + + const action = actions[eventName]; + const columns = tableInfo.columns; + let changes = []; + + function castValue(col, value) { + switch(tableInfo.castTypes.get(col)) { + case 'boolean': + return !!value; + default: + return value; + + } + } + + if (action == 'update') { + for (const row of evt.rows) { + let nColsChanged = 0; + const before = row.before; + const after = row.after; + const oldI = {}; + const newI = {}; + + for (const col in before) { + if (columns.has(col) + && after[col] !== undefined + && !equals(after[col], before[col])) { + if (before[col] !== null) + oldI[col] = castValue(col, before[col]); + newI[col] = castValue(col, after[col]); + nColsChanged++; + } + } + if (nColsChanged) + changes.push({row: after, oldI, newI}); + } + } else { + const cols = columns.keys(); + + for (const row of evt.rows) { + const instance = {}; + + for (const col of cols) { + if (row[col] !== null) + instance[col] = castValue(col, row[col]); + } + + changes.push({row, instance}); + } + } + + if (!changes.length) return; + + if (this.debug) { + console.debug('Log:'.blue, + `${tableName}(${changes}) [${eventName}]`); + } + + const logInfo = tableInfo.log; + const isDelete = action == 'delete'; + + for (const change of changes) { + let newI, oldI; + const row = change.row; + + switch(action) { + case 'update': + newI = change.newI; + oldI = change.oldI; + break; + case 'insert': + newI = change.instance; + break; + case 'delete': + oldI = change.instance; + break; + } + + const modelId = row[tableInfo.idName]; + const modelValue = tableInfo.showField + ? row[tableInfo.showField] || null + : null; + const created = new Date(evt.timestamp); + const oldInstance = oldI ? JSON.stringify(oldI) : null; + const originFk = !tableInfo.isMain + ? row[tableInfo.relation] + : modelId; + + let deleteRow; + + if (isDelete) { + [[deleteRow]] = await logInfo.fetchStmt.execute([ + tableName, modelId + ]); + if (deleteRow) + await logInfo.updateStmt.execute([ + originFk, + created, + oldInstance, + modelValue, + deleteRow.id + ]); + } + if (!isDelete || !deleteRow) { + await logInfo.addStmt.execute([ + originFk, + row.editorFk || null, + action, + created, + tableName, + oldInstance, + newI ? JSON.stringify(newI) : null, + modelId, + modelValue + ]); + } + } + } + + async flushQueue() { + if (this.flushed) return; + const position = this.nextPosition; + + if (position) { + const filename = this.nextFilename; + this.debug('Flush', `filename: ${filename}, position: ${position}`); + + const replaceQuery = + 'REPLACE INTO `util`.`binlogQueue` SET `code` = ?, `logName` = ?, `position` = ?'; + if (!this.conf.testMode) + await this.db.query(replaceQuery, [this.conf.code, filename, position]); + + this.flushed = true; + } + + this.nextFilename = this.filename; + this.nextPosition = this.position; + } + + async connectionPing() { + this.debug('Ping', 'Sending ping to database.'); + + // FIXME: Should Zongji.connection be pinged? + await new Promise((resolve, reject) => { + this.zongji.ctrlConnection.ping(err => { + if (err) return reject(err); + resolve(); + }); + }) + await this.db.ping(); + } + + debug(namespace, message) { + if (this.conf.debug) + console.debug(`${namespace}:`.blue, message.yellow); + } +} + +function equals(a, b) { + if (a === b) + return true; + const type = typeof a; + if (a == null || b == null || type !== typeof b) + return false; + if (type === 'object' && a.constructor === b.constructor) { + if (a instanceof Date) + return a.getTime() === b.getTime(); + } + return false; +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..6d58e66 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,508 @@ +{ + "name": "mycdc", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "dependencies": { + "amqplib": "^0.10.3", + "colors": "^1.4.0", + "mysql2": "^2.3.3", + "require-yaml": "^0.0.1", + "zongji": "file:../zongji" + } + }, + "../zongji": {}, + "node_modules/@acuminous/bitsyntax": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@acuminous/bitsyntax/-/bitsyntax-0.1.2.tgz", + "integrity": "sha512-29lUK80d1muEQqiUsSo+3A0yP6CdspgC95EnKBMi22Xlwt79i/En4Vr67+cXhU+cZjbti3TgGGC5wy1stIywVQ==", + "dependencies": { + "buffer-more-ints": "~1.0.0", + "debug": "^4.3.4", + "safe-buffer": "~5.1.2" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/amqplib": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/amqplib/-/amqplib-0.10.3.tgz", + "integrity": "sha512-UHmuSa7n8vVW/a5HGh2nFPqAEr8+cD4dEZ6u9GjP91nHfr1a54RyAKyra7Sb5NH7NBKOUlyQSMXIp0qAixKexw==", + "dependencies": { + "@acuminous/bitsyntax": "^0.1.2", + "buffer-more-ints": "~1.0.0", + "readable-stream": "1.x >=1.1.9", + "url-parse": "~1.5.10" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/buffer-more-ints": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-more-ints/-/buffer-more-ints-1.0.0.tgz", + "integrity": "sha512-EMetuGFz5SLsT0QTnXzINh4Ksr+oo4i+UGTXEshiGCQWnsgSs7ZhJ8fzlwQ+OzEMs0MpDAMr1hxnblp5a4vcHg==" + }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "dependencies": { + "is-property": "^1.0.2" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==" + }, + "node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/mysql2": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-2.3.3.tgz", + "integrity": "sha512-wxJUev6LgMSgACDkb/InIFxDprRa6T95+VEoR+xPvtngtccNH2dGjEB/fVZ8yg1gWv1510c9CvXuJHi5zUm0ZA==", + "dependencies": { + "denque": "^2.0.1", + "generate-function": "^2.3.1", + "iconv-lite": "^0.6.3", + "long": "^4.0.0", + "lru-cache": "^6.0.0", + "named-placeholders": "^1.1.2", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.2" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/named-placeholders": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.2.tgz", + "integrity": "sha512-wiFWqxoLL3PGVReSZpjLVxyJ1bRqe+KKJVbr4hGs1KWfTZTQyezHFBbuKj9hsizHyGV2ne7EMjHdxEGAybD5SA==", + "dependencies": { + "lru-cache": "^4.1.3" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/named-placeholders/node_modules/lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dependencies": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "node_modules/named-placeholders/node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==" + }, + "node_modules/pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==" + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "node_modules/readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/require-yaml": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/require-yaml/-/require-yaml-0.0.1.tgz", + "integrity": "sha512-M6eVEgLPRbeOhgSCnOTtdrOOEQzbXRchg24Xa13c39dMuraFKdI9emUo97Rih0YEFzSICmSKg8w4RQp+rd9pOQ==", + "dependencies": { + "js-yaml": "" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==" + }, + "node_modules/sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/zongji": { + "resolved": "../zongji", + "link": true + } + }, + "dependencies": { + "@acuminous/bitsyntax": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@acuminous/bitsyntax/-/bitsyntax-0.1.2.tgz", + "integrity": "sha512-29lUK80d1muEQqiUsSo+3A0yP6CdspgC95EnKBMi22Xlwt79i/En4Vr67+cXhU+cZjbti3TgGGC5wy1stIywVQ==", + "requires": { + "buffer-more-ints": "~1.0.0", + "debug": "^4.3.4", + "safe-buffer": "~5.1.2" + } + }, + "amqplib": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/amqplib/-/amqplib-0.10.3.tgz", + "integrity": "sha512-UHmuSa7n8vVW/a5HGh2nFPqAEr8+cD4dEZ6u9GjP91nHfr1a54RyAKyra7Sb5NH7NBKOUlyQSMXIp0qAixKexw==", + "requires": { + "@acuminous/bitsyntax": "^0.1.2", + "buffer-more-ints": "~1.0.0", + "readable-stream": "1.x >=1.1.9", + "url-parse": "~1.5.10" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "buffer-more-ints": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-more-ints/-/buffer-more-ints-1.0.0.tgz", + "integrity": "sha512-EMetuGFz5SLsT0QTnXzINh4Ksr+oo4i+UGTXEshiGCQWnsgSs7ZhJ8fzlwQ+OzEMs0MpDAMr1hxnblp5a4vcHg==" + }, + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==" + }, + "generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "requires": { + "is-property": "^1.0.2" + } + }, + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==" + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "requires": { + "argparse": "^2.0.1" + } + }, + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "mysql2": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-2.3.3.tgz", + "integrity": "sha512-wxJUev6LgMSgACDkb/InIFxDprRa6T95+VEoR+xPvtngtccNH2dGjEB/fVZ8yg1gWv1510c9CvXuJHi5zUm0ZA==", + "requires": { + "denque": "^2.0.1", + "generate-function": "^2.3.1", + "iconv-lite": "^0.6.3", + "long": "^4.0.0", + "lru-cache": "^6.0.0", + "named-placeholders": "^1.1.2", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.2" + } + }, + "named-placeholders": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.2.tgz", + "integrity": "sha512-wiFWqxoLL3PGVReSZpjLVxyJ1bRqe+KKJVbr4hGs1KWfTZTQyezHFBbuKj9hsizHyGV2ne7EMjHdxEGAybD5SA==", + "requires": { + "lru-cache": "^4.1.3" + }, + "dependencies": { + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==" + } + } + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==" + }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "require-yaml": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/require-yaml/-/require-yaml-0.0.1.tgz", + "integrity": "sha512-M6eVEgLPRbeOhgSCnOTtdrOOEQzbXRchg24Xa13c39dMuraFKdI9emUo97Rih0YEFzSICmSKg8w4RQp+rd9pOQ==", + "requires": { + "js-yaml": "" + } + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==" + }, + "sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==" + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" + }, + "url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "zongji": { + "version": "file:../zongji" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..4cca3de --- /dev/null +++ b/package.json @@ -0,0 +1,9 @@ +{ + "dependencies": { + "amqplib": "^0.10.3", + "colors": "^1.4.0", + "mysql2": "^2.3.3", + "require-yaml": "^0.0.1", + "zongji": "file:../zongji" + } +} diff --git a/zongji.sql b/zongji.sql new file mode 100644 index 0000000..bd3fc83 --- /dev/null +++ b/zongji.sql @@ -0,0 +1,12 @@ + +CREATE TABLE `util`.`binlogQueue`( + `code` VARCHAR(255) NOT NULL, + `logName` VARCHAR(255) NOT NULL, + `position` BIGINT UNSIGNED NOT NULL, + PRIMARY KEY (`code`) +) ENGINE = InnoDB; + +CREATE USER 'zongji'@'%' IDENTIFIED BY 'password'; +GRANT REPLICATION SLAVE, REPLICATION CLIENT, SELECT ON *.* TO 'zongji'@'%'; + +GRANT INSERT, DELETE ON `util`.* TO 'zongji'@'%'; diff --git a/zongji.undo.sql b/zongji.undo.sql new file mode 100644 index 0000000..62e9607 --- /dev/null +++ b/zongji.undo.sql @@ -0,0 +1,3 @@ + +DROP TABLE IF EXISTS `util`.`binlogQueue`; +DROP USER 'zongji'@'%';