commit e57ccf6e41a6ef61259cc6b56cd267c57a2197de Author: Juan Ferrer Toribio Date: Sat Apr 8 15:08:37 2023 +0200 First commit 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'@'%';