First commit
gitea/mylogger/pipeline/head There was a failure building this commit
Details
gitea/mylogger/pipeline/head There was a failure building this commit
Details
This commit is contained in:
commit
e57ccf6e41
|
@ -0,0 +1,3 @@
|
|||
node_modules
|
||||
zongji
|
||||
config/*.local.yml
|
|
@ -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"]
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
On Debian systems, the complete text of the GNU General Public
|
||||
License can be found in "/usr/share/common-licenses/GPL-3".
|
|
@ -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)
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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();
|
|
@ -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;
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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'@'%';
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
DROP TABLE IF EXISTS `util`.`binlogQueue`;
|
||||
DROP USER 'zongji'@'%';
|
Loading…
Reference in New Issue