Initial commit
This commit is contained in:
38
library/LibBullAdapter.js
Normal file
38
library/LibBullAdapter.js
Normal file
@ -0,0 +1,38 @@
|
||||
const LibSchedulerReverseGeocode = require('./LibSchedulerReverseGeocode');
|
||||
const LibQueueBlastOrder = require('./LibQueueBlastOrder');
|
||||
const LibSchedulerDrvUpLoc = require('./LibSchedulerDrvUpLoc');
|
||||
const LibSchedulerDrvUpPhoto = require('./LibSchedulerDrvUpPhoto');
|
||||
const LibSchedulerDrvUpLocIdle = require('./LibSchedulerDrvUpLocIdle');
|
||||
const LibSchedulerDrvBlastNotif = require('./LibSchedulerDrvBlastNotif');
|
||||
const LibSchedulerGpsTrackerWakeUp = require('./LibSchedulerGpsTrackerWakeUp');
|
||||
const { createBullBoard } = require('@bull-board/api');
|
||||
const { BullMQAdapter } = require('@bull-board/api/bullMQAdapter');
|
||||
const { ExpressAdapter } = require('@bull-board/express');
|
||||
|
||||
const serverAdapter = new ExpressAdapter();
|
||||
|
||||
const { addQueue, removeQueue, setQueues, replaceQueues } = createBullBoard({
|
||||
queues: [
|
||||
new BullMQAdapter(LibSchedulerReverseGeocode.queue),
|
||||
new BullMQAdapter(LibQueueBlastOrder.queue),
|
||||
new BullMQAdapter(LibSchedulerDrvUpLoc.queue),
|
||||
new BullMQAdapter(LibSchedulerDrvUpPhoto.queue),
|
||||
new BullMQAdapter(LibSchedulerDrvUpLocIdle.queue),
|
||||
new BullMQAdapter(LibSchedulerDrvBlastNotif.queue),
|
||||
new BullMQAdapter(LibSchedulerGpsTrackerWakeUp.queue),
|
||||
],
|
||||
serverAdapter: serverAdapter,
|
||||
})
|
||||
|
||||
// will work on old added queue or new added queue
|
||||
LibSchedulerReverseGeocode.setWorker();
|
||||
LibQueueBlastOrder.setWorker();
|
||||
LibSchedulerDrvUpLoc.setWorker();
|
||||
LibSchedulerDrvUpPhoto.setWorker();
|
||||
LibSchedulerDrvUpLocIdle.setWorker();
|
||||
LibSchedulerDrvBlastNotif.setWorker();
|
||||
LibSchedulerGpsTrackerWakeUp.setWorker();
|
||||
|
||||
serverAdapter.setBasePath(process.env.PATH_URL + '/bull/monitor');
|
||||
|
||||
module.exports = serverAdapter;
|
||||
300
library/LibCurl.js
Normal file
300
library/LibCurl.js
Normal file
@ -0,0 +1,300 @@
|
||||
const axios = require('axios').default;
|
||||
const url = require('url');
|
||||
const request = require('../config/request');
|
||||
const RegionModels = require('../models/RegionModels');
|
||||
|
||||
const DEFAULT_COUNTRY_ID = 1;
|
||||
|
||||
class LibCurl {
|
||||
|
||||
static reverseGeo(lat, lng) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
let respReverseGeo = null;
|
||||
try {
|
||||
let params = new url.URLSearchParams({
|
||||
lat,
|
||||
lon: lng,
|
||||
format: 'geojson',
|
||||
});
|
||||
const axInstance = axios.create();
|
||||
axios.defaults.timeout = 3000;
|
||||
axios.defaults.crossDomain = true;
|
||||
// respReverseGeo = await axios({
|
||||
// url: request.osm_reverse_geo.urlFull,
|
||||
// method: request.osm_reverse_geo.method,
|
||||
// params: params,
|
||||
// responseType: 'json',
|
||||
// });
|
||||
respReverseGeo = await axInstance.get(request.osm_reverse_geo.urlFull + '?' + params.toString(), {
|
||||
timeout: 3000,
|
||||
});
|
||||
let respData = respReverseGeo.data || {};
|
||||
if (respReverseGeo.status == 200) {
|
||||
if (respData.features.length < 1) {
|
||||
resolve({type: 'no_available', respData});
|
||||
} else {
|
||||
let respAddr = respData.features[0].properties.address;
|
||||
let addrData = {
|
||||
lat,
|
||||
lng,
|
||||
country_id: DEFAULT_COUNTRY_ID,
|
||||
country_code: respAddr.country_code,
|
||||
country_text: (respAddr.country) ? respAddr.country.toUpperCase() : respAddr.country || null,
|
||||
postcode: respAddr.postcode,
|
||||
fulladdress: encodeURIComponent(respData.features[0].properties.display_name),
|
||||
log_reverse_geo: (respData.features[0].properties) ? JSON.stringify(respData.features[0].properties) : null,
|
||||
};
|
||||
// if (respAddr.state || respAddr.city) {
|
||||
addrData.state_id = null;
|
||||
addrData.state_text = respAddr.region || respAddr.state || respAddr.state_district || respAddr.county || respAddr.city || '';
|
||||
addrData.state_text = (addrData.state_text) ? addrData.state_text.toUpperCase() : addrData.state_text || null;
|
||||
addrData.state_text = addrData.state_text || null;
|
||||
// }
|
||||
// if (respAddr.city_district || respAddr.city) {
|
||||
addrData.city_id = null;
|
||||
addrData.city_text = respAddr.city_district || respAddr.city || '';
|
||||
addrData.city_text = (addrData.city_text) ? addrData.city_text.toUpperCase() : addrData.city_text || null;
|
||||
addrData.city_text = addrData.city_text || null;
|
||||
// }
|
||||
// if (respAddr.suburb || respAddr.subdistrict) {
|
||||
addrData.district_id = null;
|
||||
addrData.district_text = respAddr.suburb || respAddr.subdistrict || respAddr.subdivision || '';
|
||||
addrData.district_text = (addrData.district_text) ? addrData.district_text.toUpperCase() : addrData.district_text || null;
|
||||
addrData.district_text = addrData.district_text || null;
|
||||
// }
|
||||
// if (respAddr.village || respAddr.neighbourhood) {
|
||||
addrData.village_id = null;
|
||||
addrData.village_text = respAddr.village || respAddr.neighbourhood || '';
|
||||
addrData.village_text = (addrData.village_text) ? addrData.village_text.toUpperCase() : addrData.village_text || null;
|
||||
addrData.village_text = addrData.village_text || null;
|
||||
// }
|
||||
// if (respAddr.amenity || respAddr.road || respAddr.city_block) {
|
||||
addrData.streets = '';
|
||||
addrData.streets += (respAddr.amenity) ? respAddr.amenity + ', ' : '';
|
||||
addrData.streets += (respAddr.road) ? respAddr.road + ', ' : '';
|
||||
addrData.streets += (respAddr.house_number) ? respAddr.house_number + ', ' : '';
|
||||
addrData.streets += (respAddr.house_name) ? respAddr.house_name + ', ' : '';
|
||||
addrData.streets += (respAddr.city_block) ? respAddr.city_block + ', ' : '';
|
||||
addrData.streets += addrData.streets || null;
|
||||
if (addrData.streets) {
|
||||
addrData.streets = encodeURIComponent(addrData.streets.slice(0, -2));
|
||||
}
|
||||
// }
|
||||
|
||||
let byAll = await RegionModels.whereLike({
|
||||
nmProvinsiKel: addrData.state_text,
|
||||
nmKotamadyaKel: addrData.city_text,
|
||||
nmKecamatanKel: addrData.district_text,
|
||||
nmKelurahan: addrData.village_text,
|
||||
});
|
||||
if (byAll.length > 1) {
|
||||
addrData.state_id = byAll[0].kodeProv;
|
||||
addrData.city_id = byAll[0].kodeKab;
|
||||
addrData.district_id = byAll[0].kodeKec;
|
||||
addrData.village_id = byAll[0].kodeKel;
|
||||
}
|
||||
|
||||
let byKel = await RegionModels.whereLike({
|
||||
nmKelurahan: addrData.village_text,
|
||||
});
|
||||
if (byAll.length < 1 && byKel.length > 0) {
|
||||
addrData.state_id = byKel[0].kodeProv;
|
||||
addrData.city_id = byKel[0].kodeKab;
|
||||
addrData.district_id = byKel[0].kodeKec;
|
||||
addrData.village_id = byKel[0].kodeKel;
|
||||
}
|
||||
|
||||
if (addrData.state_id === null) {
|
||||
let byPr = await RegionModels.whereLike({ nmProvinsiKel: addrData.state_text });
|
||||
if (byPr.length > 0) { addrData.state_id = byPr[0].kodeProv; }
|
||||
}
|
||||
|
||||
if (addrData.state_id !== null && addrData.city_id === null) {
|
||||
let byKt = await RegionModels.whereLike({ nmKotamadyaKel: addrData.city_text });
|
||||
if (byKt.length > 0) { addrData.city_id = byKt[0].kodeKab; }
|
||||
}
|
||||
|
||||
if (addrData.state_id !== null && addrData.city_id !== null && addrData.district_id === null) {
|
||||
let byKc = await RegionModels.whereLike({ nmKecamatanKel: addrData.district_text });
|
||||
if (byKc.length > 0) { addrData.district_id = byKc[0].kodeKec; }
|
||||
}
|
||||
|
||||
if (addrData.state_id !== null && addrData.city_id !== null && addrData.district_id !== null && addrData.village_id === null) {
|
||||
let byKl = await RegionModels.whereLike({ nmKelurahan: addrData.village_text });
|
||||
if (byKl.length > 0) { addrData.village_id = byKl[0].kodeKel; }
|
||||
}
|
||||
|
||||
resolve({type: 'sc', respData, addrData});
|
||||
}
|
||||
} else {
|
||||
resolve({type: 'fail', respData});
|
||||
}
|
||||
} catch (e) {
|
||||
let respData = {}
|
||||
if (respReverseGeo) {
|
||||
if (respReverseGeo.status != 200) {
|
||||
respData.data = respReverseGeo.data;
|
||||
} else {
|
||||
respData.msg = e.message;
|
||||
}
|
||||
} else if (typeof e.response != 'undefined') {
|
||||
respData.data = e.response;
|
||||
} else {
|
||||
respData.msg = e.message;
|
||||
}
|
||||
resolve({type: 'error', respData});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// https://github.com/sooluh/kodepos
|
||||
// https://kodepos.vercel.app/search?q=16418&json=true
|
||||
static getRegionId(postcode) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
let respRegion = null;
|
||||
try {
|
||||
let params = new url.URLSearchParams({
|
||||
q: postcode,
|
||||
json: 'true',
|
||||
});
|
||||
const axInstance = axios.create();
|
||||
axios.defaults.timeout = 3000;
|
||||
axios.defaults.crossDomain = true;
|
||||
// respRegion = await axios({
|
||||
// url: request.kodepos_region.urlFull,
|
||||
// method: request.kodepos_region.method,
|
||||
// params: params,
|
||||
// responseType: 'json',
|
||||
// });
|
||||
respRegion = await axInstance.get(request.kodepos_region.urlFull + '?' + params.toString(), {
|
||||
timeout: 3000,
|
||||
});
|
||||
let respData = respRegion.data || {};
|
||||
if (respData.status !== true || respRegion.status == 200) {
|
||||
if (typeof respData.data === undefied || respData.data.length < 1) {
|
||||
resolve({type: 'no_available', respData});
|
||||
} else {
|
||||
let respAddr = respData.data[0];
|
||||
let addrData = {
|
||||
state_id: null,
|
||||
state_text: respAddr.province || null,
|
||||
city_id: null,
|
||||
city_text: respAddr.city || null,
|
||||
district_id: null,
|
||||
district_text: respAddr.subdistrict || null,
|
||||
village_id: null,
|
||||
village_text: respAddr.urban || null,
|
||||
};
|
||||
|
||||
let byAll = await RegionModels.whereLike({
|
||||
nmProvinsiKel: addrData.state_text,
|
||||
nmKotamadyaKel: addrData.city_text,
|
||||
nmKecamatanKel: addrData.district_text,
|
||||
nmKelurahan: addrData.village_text,
|
||||
});
|
||||
if (byAll.length > 1) {
|
||||
addrData.state_id = byAll[0].kodeProv;
|
||||
addrData.city_id = byAll[0].kodeKab;
|
||||
addrData.district_id = byAll[0].kodeKec;
|
||||
addrData.village_id = byAll[0].kodeKel;
|
||||
}
|
||||
|
||||
let byKel = await RegionModels.whereLike({
|
||||
nmKelurahan: addrData.village_text,
|
||||
});
|
||||
if (byAll.length < 1 && byKel.length > 0) {
|
||||
addrData.state_id = byKel[0].kodeProv;
|
||||
addrData.city_id = byKel[0].kodeKab;
|
||||
addrData.district_id = byKel[0].kodeKec;
|
||||
addrData.village_id = byKel[0].kodeKel;
|
||||
}
|
||||
|
||||
if (addrData.state_id === null) {
|
||||
let byPr = await RegionModels.whereLike({ nmProvinsiKel: addrData.state_text });
|
||||
if (byPr.length > 0) { addrData.state_id = byPr[0].kodeProv; }
|
||||
}
|
||||
|
||||
if (addrData.state_id !== null && addrData.city_id === null) {
|
||||
let byKt = await RegionModels.whereLike({ nmKotamadyaKel: addrData.city_text });
|
||||
if (byKt.length > 0) { addrData.city_id = byKt[0].kodeKab; }
|
||||
}
|
||||
|
||||
if (addrData.state_id !== null && addrData.city_id !== null && addrData.district_id === null) {
|
||||
let byKc = await RegionModels.whereLike({ nmKecamatanKel: addrData.district_text });
|
||||
if (byKc.length > 0) { addrData.district_id = byKc[0].kodeKec; }
|
||||
}
|
||||
|
||||
if (addrData.state_id !== null && addrData.city_id !== null && addrData.district_id !== null && addrData.village_id === null) {
|
||||
let byKl = await RegionModels.whereLike({ nmKelurahan: addrData.village_text });
|
||||
if (byKl.length > 0) { addrData.village_id = byKl[0].kodeKel; }
|
||||
}
|
||||
|
||||
resolve({type: 'sc', respData, addrData});
|
||||
}
|
||||
} else {
|
||||
resolve({type: 'fail', respData});
|
||||
}
|
||||
} catch (e) {
|
||||
let respData = {}
|
||||
if (respRegion) {
|
||||
if (respRegion.status != 200) {
|
||||
respData.data = respRegion.data;
|
||||
} else {
|
||||
respData.msg = e.message;
|
||||
}
|
||||
} else if (typeof e.response != 'undefined') {
|
||||
respData.data = e.response;
|
||||
} else {
|
||||
respData.msg = e.message;
|
||||
}
|
||||
resolve({type: 'error', respData});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static formurlencoded(apiUrl, data) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
let respApi = null;
|
||||
try {
|
||||
let params = new url.URLSearchParams();
|
||||
for (let row of data.names || []) {
|
||||
params.append('names[]', row);
|
||||
}
|
||||
for (let row of data.photos || []) {
|
||||
params.append('photos[]', row);
|
||||
}
|
||||
const axInstance = axios.create();
|
||||
axios.defaults.timeout = 3000;
|
||||
axios.defaults.crossDomain = true;
|
||||
respApi = await axInstance.post(apiUrl, params, {
|
||||
headers: {
|
||||
'content-type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
});
|
||||
let respData = respApi.data || {};
|
||||
if (respData.meta.code == 200) {
|
||||
resolve({type: 'sc', respData});
|
||||
} else {
|
||||
resolve({type: 'fail', respData});
|
||||
}
|
||||
} catch (e) {
|
||||
let respData = {}
|
||||
if (respApi) {
|
||||
if (respApi.status != 200) {
|
||||
respData.data = respApi.data;
|
||||
} else {
|
||||
respData.msg = e.message;
|
||||
}
|
||||
} else if (typeof e.response != 'undefined') {
|
||||
respData.data = e.response;
|
||||
} else {
|
||||
respData.msg = e.message;
|
||||
}
|
||||
resolve({type: 'error', respData});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
module.exports = LibCurl;
|
||||
573
library/LibDevice.js
Normal file
573
library/LibDevice.js
Normal file
@ -0,0 +1,573 @@
|
||||
const LibHelper = require('./LibHelper');
|
||||
|
||||
class LibDevice {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Buffer} buffer
|
||||
* @param {Object} opts
|
||||
* @returns {Object} me
|
||||
* must return at least => obj.protocol_name
|
||||
*/
|
||||
static identifyProtocolFromBuffer(buffer, opts = {}) {
|
||||
let ori_hex_str = '';
|
||||
if (typeof opts.skip_buffer != 'undefined' && opts.skip_buffer === true) {
|
||||
ori_hex_str = buffer;
|
||||
} else {
|
||||
ori_hex_str = buffer.toString('hex'); // 16
|
||||
}
|
||||
|
||||
const data = ori_hex_str;
|
||||
const me = {
|
||||
ori_string: data,
|
||||
};
|
||||
|
||||
// eelinkCustom
|
||||
if (opts.isEelinkCustom) {
|
||||
me.protocol_name = 'eelinkCustom';
|
||||
|
||||
me.field0 = data.slice(0, 2); // system status flag
|
||||
me.field1 = data.slice(2, 18); // IMEI
|
||||
me.field2 = data.slice(18, 34); // IMSI
|
||||
me.field3 = data.slice(34, 42); // Time
|
||||
me.field4 = data.slice(42, 58); // Location
|
||||
me.field5 = data.slice(58, 62); // Altitude
|
||||
me.field6 = data.slice(62, 66); // Heading
|
||||
me.field7 = data.slice(66, 68); // Velocity
|
||||
me.field8 = data.slice(68, 70); // Power Input Voltage
|
||||
me.field9 = data.slice(70, 72); // GPIO Status
|
||||
me.field10 = data.slice(72, 74); // Time Seconds
|
||||
me.field11 = data.slice(74, 76); // RSSI
|
||||
me.field12 = data.slice(76, 78); // Sequence Counter
|
||||
me.field13 = data.slice(78, 80); // Vendor ID
|
||||
me.field14 = data.slice(80, 82); // GPS Satellites
|
||||
me.field15 = data.slice(82, 90); // Accumulators
|
||||
|
||||
me.ori_string = data;
|
||||
}
|
||||
// gt06
|
||||
else if (data.slice(0, 4) == '7878') {
|
||||
me.protocol_name = 'gt06';
|
||||
|
||||
me.start = data.slice(0, 4);
|
||||
me.length = parseInt(data.slice(4, 6), 16);
|
||||
me.protocol_id = data.slice(6, 8);
|
||||
me.serial_number = data.slice(-12, -8)
|
||||
me.error_check = data.slice(-8, -4)
|
||||
me.finish = data.slice(-4); // data.substr(6 + me.length * 2, 4);
|
||||
|
||||
if (me.finish != '0d0a') {
|
||||
throw 'finish code incorrect!';
|
||||
}
|
||||
|
||||
me.ori_string = data;
|
||||
}
|
||||
// gt02a
|
||||
else if (data.slice(0, 4) == '6868') {
|
||||
me.protocol_name = 'gt02a';
|
||||
}
|
||||
// tk103
|
||||
// else if (data.indexOf("B")) {
|
||||
// me.protocol_name = 'tk103';
|
||||
// }
|
||||
// eelink
|
||||
else if (data.slice(0, 4) == '6767') {
|
||||
me.protocol_name = 'eelink';
|
||||
|
||||
me.start = data.slice(0, 4);
|
||||
me.pid = data.slice(4, 6);
|
||||
me.size = parseInt(data.slice(6, 10), 16);
|
||||
me.sequence = parseInt(data.slice(10, 4), 16);
|
||||
|
||||
me.ori_string = data;
|
||||
}
|
||||
// unknown
|
||||
else {
|
||||
me.protocol_name = 'unknown';
|
||||
}
|
||||
|
||||
return me;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Object} me
|
||||
* @param {String} device_id
|
||||
* @returns
|
||||
* must return at least => act.cmd,act.action_type,act.device_id
|
||||
*/
|
||||
static gt06Action(me, device_id = null) {
|
||||
const act = {}
|
||||
|
||||
// Login message
|
||||
if (me.protocol_id == '01') {
|
||||
act.action_type = 'login';
|
||||
|
||||
act.cmd = 'login_request';
|
||||
act.action = 'login_request';
|
||||
act.device_id = me.ori_string.slice(8).slice(0, 16).padStart(16, '0');
|
||||
|
||||
act.buffer_resp = LibDevice.gt06AuthorizeResp(me);
|
||||
}
|
||||
// Location data
|
||||
else if (me.protocol_id == '12') {
|
||||
act.action_type = 'location';
|
||||
|
||||
act.cmd = 'ping';
|
||||
act.action = 'ping';
|
||||
act.device_id = device_id; // because device_id only sent when login
|
||||
|
||||
// content data
|
||||
// me.ori_string.slice(8).slice(0, 52); // me.ori_string.substr(8, me.length * 2);
|
||||
|
||||
// gps information
|
||||
act.gps_string = me.ori_string.slice(8).slice(0, 36);
|
||||
act.gps_data = LibDevice.gt06ParseLocation(me, act.gps_string);
|
||||
// if (!act.gps_data) {
|
||||
// //Something bad happened
|
||||
// _this.do_log('GPS Data can\'t be parsed. Discarding packet...');
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// lbs information
|
||||
act.lbs_string = me.ori_string.slice(8).slice(36, 52);
|
||||
act.lbs_data = LibDevice.gt06ParseLbs(me, act.lbs_string);
|
||||
}
|
||||
// Status information
|
||||
else if (me.protocol_id == '13') {
|
||||
act.action_type = 'heartbeat';
|
||||
|
||||
act.cmd = 'heartbeat';
|
||||
act.action = 'heartbeat';
|
||||
act.device_id = device_id; // because device_id only sent when login
|
||||
|
||||
// status information
|
||||
act.stts_string = me.ori_string.slice(8).slice(0, 10);
|
||||
act.stts_data = LibDevice.gt06ParseHeartbeat(me, act.stts_string);
|
||||
|
||||
act.buffer_resp = LibDevice.gt06HeartbeatResp(me);
|
||||
}
|
||||
// String information
|
||||
else if (me.protocol_id == '15') {
|
||||
// act.action_type = 'other'; // 'heartbeat';
|
||||
// act.cmd = 'other'; // 'heartbeat';
|
||||
// act.action = 'other'; // 'heartbeat';
|
||||
// act.device_id = ''; // device_id; // because device_id only sent when login
|
||||
|
||||
act.action_type = 'heartbeat';
|
||||
|
||||
act.cmd = 'heartbeat';
|
||||
act.action = 'heartbeat';
|
||||
act.device_id = device_id; // because device_id only sent when login
|
||||
|
||||
// status information
|
||||
act.stts_string = me.ori_string.slice(8).slice(0, 10);
|
||||
act.stts_data = LibDevice.gt06ParseHeartbeat(me, act.stts_string);
|
||||
|
||||
act.buffer_resp = LibDevice.gt06HeartbeatResp(me);
|
||||
}
|
||||
// Alarm data
|
||||
else if (me.protocol_id == '16' || me.protocol_id == '18') {
|
||||
act.action_type = 'alarm';
|
||||
|
||||
act.cmd = 'alarm';
|
||||
act.action = 'alarm';
|
||||
act.device_id = device_id; // because device_id only sent when login
|
||||
|
||||
// content data
|
||||
// me.ori_string.slice(8).slice(0, 64); // me.ori_string.substr(8, me.length * 2);
|
||||
|
||||
// gps information
|
||||
act.gps_string = me.ori_string.slice(8).slice(0, 36);
|
||||
act.gps_data = LibDevice.gt06ParseLocation(me, act.gps_string);
|
||||
|
||||
// lbs information
|
||||
act.lbs_string = me.ori_string.slice(8).slice(36, 54);
|
||||
act.lbs_data = LibDevice.gt06ParseLbs(me, act.lbs_string);
|
||||
|
||||
// status information
|
||||
act.stts_string = me.ori_string.slice(8).slice(54, 64);
|
||||
act.stts_data = LibDevice.gt06ParseStatusAlarm(me, act.stts_string);
|
||||
|
||||
act.buffer_resp = LibDevice.gt06AlarmResp(me);
|
||||
}
|
||||
// GPS, query address information by phone number
|
||||
else if (me.protocol_id == '1A') {
|
||||
act.action_type = 'other';
|
||||
|
||||
act.cmd = 'other';
|
||||
act.action = 'other';
|
||||
act.device_id = '';
|
||||
}
|
||||
// Command information sent by the server to the terminal
|
||||
else if (me.protocol_id == '80') {
|
||||
act.action_type = 'other';
|
||||
|
||||
act.cmd = 'other';
|
||||
act.action = 'other';
|
||||
act.device_id = '';
|
||||
} else {
|
||||
act.action_type = 'other';
|
||||
|
||||
act.cmd = 'other';
|
||||
act.action = 'other';
|
||||
act.device_id = '';
|
||||
}
|
||||
|
||||
return act;
|
||||
}
|
||||
|
||||
static gt06AuthorizeResp(me) {
|
||||
return Buffer.from("787805010001d9dc0d0a", "hex");
|
||||
}
|
||||
|
||||
static gt06ParseLocation(me, gps_string = null) {
|
||||
if (!gps_string) {
|
||||
gps_string = me.ori_string.slice(8).slice(0, 36);
|
||||
}
|
||||
|
||||
let year = (parseInt(gps_string.slice(0, 2), 16) + '').padStart(2, 0);
|
||||
year = '20' + year;
|
||||
let month = (parseInt(gps_string.slice(2, 4), 16) + '').padStart(2, 0);
|
||||
let day = (parseInt(gps_string.slice(4, 6), 16) + '').padStart(2, 0);
|
||||
let hour = (parseInt(gps_string.slice(6, 8), 16) + '').padStart(2, 0);
|
||||
let minute = (parseInt(gps_string.slice(8, 10), 16) + '').padStart(2, 0);
|
||||
let second = (parseInt(gps_string.slice(10, 12), 16) + '').padStart(2, 0);
|
||||
|
||||
let ob1 = LibHelper.hex2bin(gps_string.slice(32, 34)); // orientation_byte1
|
||||
let ob1_bit7 = ob1.slice(0, 1);
|
||||
let ob1_bit6 = ob1.slice(1, 2);
|
||||
let ob1_bit5 = ob1.slice(2, 3); // 0 (realtime GPS) or differential positioning
|
||||
let ob1_bit4 = ob1.slice(3, 4); // 1 (GPS has been positioned)
|
||||
let ob1_bit3 = ob1.slice(4, 5); // 0 (east longitude) || 1 (west longitude)
|
||||
let ob1_bit2 = ob1.slice(5, 6); // 1 (north latitude) || 0 (south latitude)
|
||||
let ob1_bit1 = ob1.slice(6, 7);
|
||||
let ob1_bit0 = ob1.slice(7, 8);
|
||||
let ob2 = LibHelper.hex2bin(gps_string.slice(34, 36)); // orientation_byte2
|
||||
// let ob2_bit7 = ob2.slice(0, 1);
|
||||
// let ob2_bit6 = ob2.slice(1, 2);
|
||||
// let ob2_bit5 = ob2.slice(2, 3);
|
||||
// let ob2_bit4 = ob2.slice(3, 4);
|
||||
// let ob2_bit3 = ob2.slice(4, 5);
|
||||
// let ob2_bit2 = ob2.slice(5, 6);
|
||||
// let ob2_bit1 = ob2.slice(6, 7);
|
||||
// let ob2_bit0 = ob2.slice(7, 8);
|
||||
|
||||
let lat_wind = ''; // wind direction N,S
|
||||
let lng_wind = ''; // wind direction W,E
|
||||
if (ob1_bit3 == 1) {
|
||||
lng_wind = 'W';
|
||||
}
|
||||
if (ob1_bit3 == 0) {
|
||||
lng_wind = 'E';
|
||||
}
|
||||
if (ob1_bit2 == 1) {
|
||||
lat_wind = 'N';
|
||||
}
|
||||
if (ob1_bit2 == 0) {
|
||||
lat_wind = 'S';
|
||||
}
|
||||
|
||||
const data = {
|
||||
'date_raw': gps_string.slice(0, 12),
|
||||
'date': `${year}-${month}-${day} ${hour}:${minute}:${second}`,
|
||||
'quantity_pos_satellites_raw': gps_string.slice(12, 14),
|
||||
'quantity_pos_satellites_c': parseInt(gps_string.slice(12, 13), 16), // length of gps information
|
||||
'quantity_pos_satellites_b': parseInt(gps_string.slice(13, 14), 16), // number of positioning satellites
|
||||
realtime_dif_gps: ob1_bit5, // 0 (realtime GPS) or differential positioning
|
||||
positioning_gps: ob1_bit6, // 1 (GPS has been positioned)
|
||||
'latitude_raw': gps_string.slice(14, 22),
|
||||
'longitude_raw': gps_string.slice(22, 30),
|
||||
lat_wind_direction: lat_wind,
|
||||
lng_wind_direction: lng_wind,
|
||||
'latitude': LibDevice.gt06Hex2DMM(gps_string.slice(14, 22), lat_wind),
|
||||
'longitude': LibDevice.gt06Hex2DMM(gps_string.slice(22, 30), lng_wind),
|
||||
'speed': parseInt(gps_string.slice(30, 32), 16), // km/h
|
||||
'orientation_raw': gps_string.slice(32, 36),
|
||||
'orientation': parseInt(`${ob1_bit1}${ob1_bit0}${ob2}`, 2), // -360 ~ 360 derajat
|
||||
};
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// more accurate
|
||||
static gt06Hex2DMM(hex, direction) {
|
||||
hex = parseInt(hex, 16);
|
||||
// convert hexadecimal to Degrees Minutes.m (DMM)
|
||||
let a = parseInt(hex, 10) // to decimal values
|
||||
let b = a / 30000.0
|
||||
let degrees = b / 60
|
||||
let minutes = b % 60
|
||||
// convert DMM to Decimal Degrees (DD)
|
||||
// let d = minutes / 60
|
||||
// let dd = degrees + d
|
||||
// add - follow wind direction
|
||||
if (direction == "S" || direction == "W" || direction == "s" || direction == "w") {
|
||||
degrees = degrees * -1;
|
||||
}
|
||||
return degrees;
|
||||
}
|
||||
// ga akurat
|
||||
static gt06Hex2DMM1(hex, direction) {
|
||||
hex = parseInt(hex, 16);
|
||||
// convert hexadecimal to Degrees Minutes.m (DMM)
|
||||
let a = parseInt(hex, 10) // to decimal values
|
||||
let b = a / 30000.0
|
||||
let degrees = b / 60
|
||||
let minutes = b % 60
|
||||
// convert DMM to Decimal Degrees (DD)
|
||||
let d = minutes / 60
|
||||
let dd = degrees + d
|
||||
// add - follow wind direction
|
||||
if (direction == "S" || direction == "W" || direction == "s" || direction == "w") {
|
||||
dd = dd * -1;
|
||||
}
|
||||
return dd;
|
||||
}
|
||||
|
||||
static gt06HeartbeatResp(me) {
|
||||
return Buffer.from("787805130001d9dc0d0a", "hex");
|
||||
}
|
||||
|
||||
static gt06ParseHeartbeat(me, stts_string) {
|
||||
if (!stts_string) {
|
||||
stts_string = me.ori_string.slice(8).slice(0, 10);
|
||||
}
|
||||
|
||||
let terminal_info_raw = stts_string.slice(0, 2);
|
||||
let tib1 = LibHelper.hex2bin(terminal_info_raw); // terminal_info_byte1
|
||||
let tib1_bit7 = tib1.slice(0, 1);
|
||||
let tib1_bit6 = tib1.slice(1, 2);
|
||||
let tib1_bit5 = tib1.slice(2, 3);
|
||||
let tib1_bit4 = tib1.slice(3, 4);
|
||||
let tib1_bit3 = tib1.slice(4, 5);
|
||||
let tib1_bit2 = tib1.slice(5, 6);
|
||||
let tib1_bit1 = tib1.slice(6, 7);
|
||||
let tib1_bit0 = tib1.slice(7, 8);
|
||||
|
||||
/**
|
||||
* 0: No Power (shutdown)
|
||||
* 1: Extremely Low Battery (not enough for calling or sending text messages, etc.)
|
||||
* 2: Very Low Battery (Low Battery Alarm)
|
||||
* 3: Low Battery (can be used normally)
|
||||
* 4: Medium
|
||||
* 5: High
|
||||
* 6: Very High
|
||||
*/
|
||||
let voltage_level = stts_string.slice(2, 4);
|
||||
|
||||
let gsm_signal_strength = stts_string.slice(4, 6);
|
||||
|
||||
let alarm_stts = stts_string.slice(6, 8); // former bit: terminal alarm status (suitable for alarm packet and electronic fence project)
|
||||
let language = stts_string.slice(8, 10); // latter bit: the current language used in the terminal
|
||||
|
||||
const data = {
|
||||
terminal_info_raw: terminal_info_raw,
|
||||
terminal_info_byte: tib1,
|
||||
terminal_info: {
|
||||
oil_electricity: tib1_bit7, // 1: oil and electricity disconnected, 0: gas oil and electricity connected
|
||||
gps_tracking: tib1_bit6, // 1: GPS tracking is on, 0: GPS tracking is off
|
||||
stts: `${tib1_bit5}${tib1_bit4}${tib1_bit3}`, // 100: SOS, 011: Low Battery Alarm, 010: Power Cut Alarm, 001: Shock Alarm, 000: Normal
|
||||
charge: tib1_bit2, // 1: Charge On, 0: Charge Off
|
||||
acc: tib1_bit1, // 1: ACC high, 0: ACC Low
|
||||
is_active: tib1_bit0, // 1: Activated, 0: Deactivated
|
||||
},
|
||||
voltage_level,
|
||||
gsm_signal_strength, // 0x00: no signal; 0x01: extremely weak signal; 0x02: very weak signal; 0x03: good signal; 0x04: strong signal.
|
||||
alarm_stts, // 0x00: normal, 0x01: SOS, 0x02: Power Cut Alarm, 0x03: Shock Alarm, 0x04: Fence In Alarm, 0x05: Fence Out Alarm
|
||||
language, // 0x01: Chinese, 0x02: English
|
||||
};
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static gt06ParseLbs(me, lbs_string) {
|
||||
let mcc_raw = null,
|
||||
mcc = null, // mobile country code
|
||||
mnc_raw = null,
|
||||
mnc = null, // mobile network code
|
||||
lac_raw = null,
|
||||
lac = null, // location area code
|
||||
cellID_raw = null,
|
||||
cellID = null, // cell tower id
|
||||
lbs_length_raw = null,
|
||||
lbs_length = null;
|
||||
|
||||
// from location
|
||||
if (lbs_string.length == 16) {
|
||||
mcc_raw = lbs_string.slice(0, 4);
|
||||
mnc_raw = lbs_string.slice(4, 6);
|
||||
lac_raw = lbs_string.slice(6, 10);
|
||||
cellID_raw = lbs_string.slice(10, 16);
|
||||
}
|
||||
// from alarm
|
||||
else if (lbs_string.length == 18) {
|
||||
lbs_length_raw = lbs_string.slice(0, 2);
|
||||
mcc_raw = lbs_string.slice(2, 6);
|
||||
mnc_raw = lbs_string.slice(6, 8);
|
||||
lac_raw = lbs_string.slice(8, 12);
|
||||
cellID_raw = lbs_string.slice(12, 18);
|
||||
}
|
||||
|
||||
if (lbs_length_raw) {
|
||||
lbs_length = parseInt(lbs_length_raw, 16);
|
||||
}
|
||||
if (mnc_raw && mcc_raw && lac_raw && cellID_raw) {
|
||||
mnc = parseInt(mnc_raw, 16);
|
||||
mcc = parseInt(mcc_raw, 16);
|
||||
lac = parseInt(lac_raw, 16);
|
||||
cellID = parseInt(cellID_raw, 16);
|
||||
}
|
||||
|
||||
const data = {
|
||||
lbs_length_raw,
|
||||
lbs_length,
|
||||
mcc_raw,
|
||||
mcc,
|
||||
mnc_raw,
|
||||
mnc,
|
||||
lac_raw,
|
||||
lac,
|
||||
cellID_raw,
|
||||
cellID,
|
||||
};
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static gt06ParseStatusAlarm(me, stts_string) {
|
||||
if (!stts_string) {
|
||||
stts_string = me.ori_string.slice(8).slice(54, 10);
|
||||
}
|
||||
|
||||
let terminal_info_raw = stts_string.slice(0, 2);
|
||||
let tib1 = LibHelper.hex2bin(terminal_info_raw); // terminal_info_byte1
|
||||
let tib1_bit7 = tib1.slice(0, 1);
|
||||
let tib1_bit6 = tib1.slice(1, 2);
|
||||
let tib1_bit5 = tib1.slice(2, 3);
|
||||
let tib1_bit4 = tib1.slice(3, 4);
|
||||
let tib1_bit3 = tib1.slice(4, 5);
|
||||
let tib1_bit2 = tib1.slice(5, 6);
|
||||
let tib1_bit1 = tib1.slice(6, 7);
|
||||
let tib1_bit0 = tib1.slice(7, 8);
|
||||
|
||||
/**
|
||||
* 0: No Power (shutdown)
|
||||
* 1: Extremely Low Battery (not enough for calling or sending text messages, etc.)
|
||||
* 2: Very Low Battery (Low Battery Alarm)
|
||||
* 3: Low Battery (can be used normally)
|
||||
* 4: Medium
|
||||
* 5: High
|
||||
* 6: Very High
|
||||
*/
|
||||
let voltage_level = stts_string.slice(2, 4);
|
||||
|
||||
let gsm_signal_strength = stts_string.slice(4, 6);
|
||||
|
||||
let alarm_stts = stts_string.slice(6, 8); // former bit: terminal alarm status (suitable for alarm packet and electronic fence project)
|
||||
let language = stts_string.slice(8, 10); // latter bit: the current language used in the terminal
|
||||
|
||||
const data = {
|
||||
terminal_info_raw: terminal_info_raw,
|
||||
terminal_info_byte: tib1,
|
||||
terminal_info: {
|
||||
oil_electricity: tib1_bit7, // 1: oil and electricity disconnected, 0: gas oil and electricity connected
|
||||
gps_tracking: tib1_bit6, // 1: GPS tracking is on, 0: GPS tracking is off
|
||||
stts: `${tib1_bit5}${tib1_bit4}${tib1_bit3}`, // 100: SOS, 011: Low Battery Alarm, 010: Power Cut Alarm, 001: Shock Alarm, 000: Normal
|
||||
charge: tib1_bit2, // 1: Charge On, 0: Charge Off
|
||||
acc: tib1_bit1, // 1: ACC high, 0: ACC Low
|
||||
is_active: tib1_bit0, // 1: Activated, 0: Deactivated
|
||||
},
|
||||
voltage_level,
|
||||
gsm_signal_strength, // 0x00: no signal; 0x01: extremely weak signal; 0x02: very weak signal; 0x03: good signal; 0x04: strong signal.
|
||||
alarm_stts, // 0x00: normal, 0x01: SOS, 0x02: Power Cut Alarm, 0x03: Shock Alarm, 0x04: Fence In Alarm, 0x05: Fence Out Alarm
|
||||
language, // 0x01: Chinese, 0x02: English
|
||||
};
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static gt06AlarmResp(me) {
|
||||
return Buffer.from("787805160001d9dc0d0a", "hex");
|
||||
}
|
||||
|
||||
static eelinkCustomAction(me, device_id = null) {
|
||||
const act = {
|
||||
device_id: me.field1,
|
||||
action_type: 'exist_data',
|
||||
cmd: 'exist_data',
|
||||
}
|
||||
|
||||
if (me.field3 == '60000000' || me.field3 == '00000000') {
|
||||
act.action_type = 'no_data_since_hardware_reset';
|
||||
act.cmd = 'no_data_since_hardware_reset';
|
||||
|
||||
// act.buffer_resp = Buffer.from('LOAD');
|
||||
// act.buffer_resp = 'LOAD';
|
||||
// act.buffer_resp = Buffer.from(`6767${me.field0}000A0001${me.field1}01`, 'hex');
|
||||
// act.buffer_resp = `6767${me.field0}000A0001${me.field1}01`;
|
||||
}
|
||||
|
||||
if (act.action_type !== 'exist_data') return act;
|
||||
|
||||
act.flag = me.field0;
|
||||
act.imsi = me.field2; // International Mobile Subscriber Identifier
|
||||
|
||||
act.time_string = me.field3;
|
||||
const time_data = {};
|
||||
time_data.year = '20' + (parseInt(me.field3.slice(0, 1), 16) + 10);
|
||||
time_data.month = '' + parseInt(me.field3.slice(1, 2), 16);
|
||||
if (time_data.month.length === 1) { time_data.month = '0' + time_data.month; }
|
||||
time_data.day = '' + parseInt(me.field3.slice(2, 4), 16);
|
||||
if (time_data.day.length === 1) { time_data.day = '0' + time_data.day; }
|
||||
time_data.hour = '' + parseInt(me.field3.slice(4, 6), 16);
|
||||
if (time_data.hour.length === 1) { time_data.hour = '0' + time_data.hour; }
|
||||
time_data.minute = '' + parseInt(me.field3.slice(6, 8), 16);
|
||||
if (time_data.minute.length === 1) { time_data.minute = '0' + time_data.minute; }
|
||||
act.time_data = time_data;
|
||||
|
||||
/**
|
||||
* north => -9000000 to +9000000, can 1 or 2 digits of degree
|
||||
* west => -18000000 to +18000000, usually 3 digits degree
|
||||
*/
|
||||
act.location_string = me.field4;
|
||||
// original value is expressed as signed hex value, so we must to conversion to decimal
|
||||
// using two complement
|
||||
let north = ~parseInt(me.field4.slice(0, 8), 16);
|
||||
let west = ~parseInt(me.field4.slice(8, 16), 16);
|
||||
// add magnitude
|
||||
north = north * -1 + 1;
|
||||
west = west * -1 + 1;
|
||||
// convert to string
|
||||
north += '';
|
||||
west += '';
|
||||
// remove - from string at first and save to temporary variable
|
||||
let signedNorth = '';
|
||||
if (north.indexOf('-') === 0) {
|
||||
north = north.slice(1);
|
||||
signedNorth = '-';
|
||||
}
|
||||
let signedWest = '';
|
||||
if (west.indexOf('-') === 0) {
|
||||
west = west.slice(1);
|
||||
signedWest = '-';
|
||||
}
|
||||
// separate DDM => Degree Decimal Minutes
|
||||
let northDegree = north.slice(0, 1);
|
||||
let northDecimalMinute = north.slice(1, 7);
|
||||
if (north.length === 8) {
|
||||
northDegree = north.slice(0, 2);
|
||||
northDecimalMinute = north.slice(2, 7);
|
||||
}
|
||||
let westDegree = west.slice(0, 3);
|
||||
let westDecimalMinute = west.slice(3, 7);
|
||||
// convert DDM to DD (Decimal Degrees)
|
||||
act.latitude = '' + signedNorth + northDegree + '.' + ('' + (northDecimalMinute/60)).replace('.', '');
|
||||
act.longitude = '' + signedWest + westDegree + '.' + ('' + (westDecimalMinute/60)).replace('.', '');
|
||||
|
||||
return act;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = LibDevice;
|
||||
45
library/LibFile.js
Normal file
45
library/LibFile.js
Normal file
@ -0,0 +1,45 @@
|
||||
const fs = require('fs');
|
||||
const path = require("path");
|
||||
|
||||
const STORAGE_PATH = path.join(__dirname, '../files/storage/');
|
||||
|
||||
class LibFile {
|
||||
static save(filename, dirpath, rawBase64) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const cleanBase64 = rawBase64.replace(/^data:(image|application)\/(png|jpg|jpeg);base64,/, '');
|
||||
const saveLocation = `${STORAGE_PATH}${dirpath}/${filename}`;
|
||||
|
||||
LibFile.ensureDirectoryExistence(saveLocation);
|
||||
|
||||
fs.writeFile(saveLocation, cleanBase64, 'base64', function (err) {
|
||||
if (err) return reject(err);
|
||||
resolve(`${dirpath}/${filename}`);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
static ensureDirectoryExistence(filePath) {
|
||||
var dirname = path.dirname(filePath);
|
||||
if (fs.existsSync(dirname)) {
|
||||
return true;
|
||||
}
|
||||
LibFile.ensureDirectoryExistence(dirname);
|
||||
fs.mkdirSync(dirname);
|
||||
}
|
||||
|
||||
static remove(filenamepath) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const removeLocation = `${STORAGE_PATH}${filenamepath}`;
|
||||
// if not exists mean, have been deleted
|
||||
if (!fs.existsSync(removeLocation)) return resolve(true);
|
||||
// if exist mean not deleted
|
||||
fs.unlink(removeLocation, function(err) {
|
||||
if (err) return reject(err);
|
||||
resolve(true);
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = LibFile;
|
||||
72
library/LibFirebase.js
Normal file
72
library/LibFirebase.js
Normal file
@ -0,0 +1,72 @@
|
||||
const fs = require('fs');
|
||||
const path = require("path");
|
||||
const firebase = require('firebase-admin');
|
||||
|
||||
// const account = fs.readFileSync(path.join(__dirname, '../files/keys/bingcorp-tracker-firebase-adminsdk-7tcet-7399bbf4fc.json')); // jadinya buffer
|
||||
const account = require(path.join(__dirname, '../files/keys/bingcorp-tracker-firebase-adminsdk-7tcet-7399bbf4fc.json')); // langsung json
|
||||
const firebaseAdmin = firebase.initializeApp({
|
||||
credential: firebase.credential.cert(account),
|
||||
databaseURL: 'https://bingcorp-tracker-default-rtdb.asia-southeast1.firebasedatabase.app',
|
||||
});
|
||||
|
||||
class LibFirebase {
|
||||
|
||||
static upLocActiveOrder = 1;
|
||||
static upPhotoOtwDrop = 2;
|
||||
static upLocIdle = 3;
|
||||
static scTfMoney = 6; // success transfer pocket money to driver
|
||||
static blastNotif = 7; // notif per 6jam semua driver baik ada order / kaga
|
||||
|
||||
firebaseAdmin = null;
|
||||
|
||||
firebaseInit() {
|
||||
const firebase = require('firebase-admin');
|
||||
// const account = fs.readFileSync(path.join(__dirname, '../files/keys/bingcorp-tracker-firebase-adminsdk-7tcet-7399bbf4fc.json')); // jadinya buffer
|
||||
const account = require(path.join(__dirname, '../files/keys/bingcorp-tracker-firebase-adminsdk-7tcet-7399bbf4fc.json')); // langsung json
|
||||
return firebase.initializeApp({
|
||||
credential: firebase.credential.cert(account),
|
||||
databaseURL: 'https://bingcorp-tracker-default-rtdb.asia-southeast1.firebasedatabase.app',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* https://firebase.google.com/docs/cloud-messaging/send-message
|
||||
* https://firebase.google.com/docs/reference/admin/node/firebase-admin.messaging.messaging#messagingsendtodevice
|
||||
*
|
||||
* https://firebase.google.com/docs/reference/admin/node/firebase-admin.messaging.messagingoptions.md#messagingoptions_interface
|
||||
*/
|
||||
sendToDevice(registrationToken = '', payload = {}, options = {}) {
|
||||
// const obj = this;
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
// console.log('check value => ', obj.firebaseAdmin);
|
||||
// if (obj.firebaseAdmin === null) {
|
||||
// // obj.firebaseAdmin = this.firebaseInit();
|
||||
// obj.firebaseAdmin = 'udah di assign';
|
||||
// }
|
||||
// console.log('check value => ', obj.firebaseAdmin);
|
||||
if (!options['priority']) {
|
||||
options.priority = 'normal';
|
||||
}
|
||||
if (!options['timeToLive']) {
|
||||
options.timeToLive = 60 * 60 * 24;
|
||||
}
|
||||
const response = await firebaseAdmin.messaging().sendToDevice(registrationToken, payload, options);
|
||||
resolve(response);
|
||||
// {
|
||||
// results: [ { error: [FirebaseMessagingError] } ],
|
||||
// canonicalRegistrationTokenCount: 0,
|
||||
// failureCount: 1,
|
||||
// successCount: 0,
|
||||
// multicastId: 7795206774601661000
|
||||
// }
|
||||
} catch (err) {
|
||||
console.log('error sending message');
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = LibFirebase;
|
||||
97
library/LibHelper.js
Normal file
97
library/LibHelper.js
Normal file
@ -0,0 +1,97 @@
|
||||
class LibHelper {
|
||||
|
||||
static EARTH_RADIUS_M = 6371000;
|
||||
static EARTH_RADIUS_KM = 6371;
|
||||
static EARTH_RADIUS_MILES = 3959; // 3958.756 || 3959 || 3963
|
||||
|
||||
static setErrMsg(msg) {
|
||||
if (process.env.NODE_ENV == 'development') {
|
||||
return msg;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the great-circle distance between two points, with
|
||||
* the Haversine formula.
|
||||
* @param float latitudeFrom Latitude of start point in [deg decimal]
|
||||
* @param float longitudeFrom Longitude of start point in [deg decimal]
|
||||
* @param float latitudeTo Latitude of target point in [deg decimal]
|
||||
* @param float longitudeTo Longitude of target point in [deg decimal]
|
||||
* @param float earthRadius Mean earth radius in [m]
|
||||
* @return float Distance between points in [m] (same as earthRadius)
|
||||
* reference: https://stackoverflow.com/questions/14750275/haversine-formula-with-php
|
||||
* tolak ukur: tarik garis lurus
|
||||
* more accurate using km/meters than miles i think ~ rafifmulia
|
||||
*/
|
||||
static haversineGreatCircleDistance(latFrom, lngFrom, latTo, lngTo, earthRadius = LibHelper.EARTH_RADIUS_M) {
|
||||
// convert from degrees to radians
|
||||
let latFromRads = LibHelper.degsToRads(latFrom);
|
||||
let lngFromRads = LibHelper.degsToRads(lngFrom);
|
||||
let latToRads = LibHelper.degsToRads(latTo);
|
||||
let lngToRads = LibHelper.degsToRads(lngTo);
|
||||
|
||||
let latDelta = latToRads - latFromRads;
|
||||
let lngDelta = lngToRads - lngFromRads;
|
||||
|
||||
let angle = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(latDelta / 2), 2) + Math.cos(latFromRads) * Math.cos(latToRads) * Math.pow(Math.sin(lngDelta / 2), 2)));
|
||||
let distance = angle * earthRadius;
|
||||
|
||||
return distance;
|
||||
}
|
||||
|
||||
// reference: https://www.codegrepper.com/code-examples/javascript/deg2rad+javascript
|
||||
// degrees to radians
|
||||
static degsToRads(deg) {
|
||||
return (deg * Math.PI) / 180.0;
|
||||
}
|
||||
// radians to degrees
|
||||
static radsToDegs(rad) {
|
||||
return rad * 180.0 / Math.PI;
|
||||
}
|
||||
|
||||
// round up with precisions digits => 4 == (10000)
|
||||
// reference: https://stackoverflow.com/questions/11832914/how-to-round-to-at-most-2-decimal-places-if-necessary
|
||||
static milesToKm(miles, precisionDigits = 10000) {
|
||||
return Math.round(((miles * 1.609) + Number.EPSILON) * precisionDigits) / precisionDigits;
|
||||
}
|
||||
static milesToMeters(miles, precisionDigits = 10000) {
|
||||
return Math.round(((miles * 1609) + Number.EPSILON) * precisionDigits) / precisionDigits;
|
||||
}
|
||||
static milesToMiles(meters, precisionDigits = 10000) {
|
||||
return Math.round(((meters) + Number.EPSILON) * precisionDigits) / precisionDigits;
|
||||
}
|
||||
static kmToMiles(km, precisionDigits = 10000) {
|
||||
return Math.round(((km / 1.609) + Number.EPSILON) * precisionDigits) / precisionDigits;
|
||||
}
|
||||
static kmToMeters(km, precisionDigits = 10000) {
|
||||
return Math.round(((km * 1000) + Number.EPSILON) * precisionDigits) / precisionDigits;
|
||||
}
|
||||
static kmToKm(meters, precisionDigits = 10000) {
|
||||
return Math.round(((meters) + Number.EPSILON) * precisionDigits) / precisionDigits;
|
||||
}
|
||||
static metersToKm(meters, precisionDigits = 10000) {
|
||||
return Math.round(((meters / 1000) + Number.EPSILON) * precisionDigits) / precisionDigits;
|
||||
}
|
||||
static metersToMiles(meters, precisionDigits = 10000) {
|
||||
return Math.round(((meters / 1609) + Number.EPSILON) * precisionDigits) / precisionDigits;
|
||||
}
|
||||
static metersToMeters(meters, precisionDigits = 10000) {
|
||||
return Math.round(((meters) + Number.EPSILON) * precisionDigits) / precisionDigits;
|
||||
}
|
||||
|
||||
static hex2bin(hex) {
|
||||
return ("00000000" + (parseInt(hex, 16)).toString(2)).slice(-8);
|
||||
}
|
||||
|
||||
static splitEvery4Char(str) {
|
||||
if (str) {
|
||||
str = '' + str;
|
||||
let splitEvery4Char = str.match(/.{1,4}/g)
|
||||
return splitEvery4Char.join(' ')
|
||||
}
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = LibHelper;
|
||||
140
library/LibImgGeotagging.js
Normal file
140
library/LibImgGeotagging.js
Normal file
@ -0,0 +1,140 @@
|
||||
const fs = require('fs');
|
||||
const path = require("path");
|
||||
const moment = require('moment');
|
||||
const Jimp = require("jimp");
|
||||
|
||||
/**
|
||||
* download ttf font => https://www.fontspace.com/search?q=open%20sans
|
||||
* load custom font on jimp => https://github.com/oliver-moran/jimp/issues/375
|
||||
* convert ttf to (fnt,png) => https://github.com/oliver-moran/jimp/issues/1002 => https://ttf2fnt.com/
|
||||
* neon color palette => https://icolorpalette.com/color/neon-green
|
||||
* reference => https://libgdx.com/wiki/graphics/2d/fonts/bitmap-fonts
|
||||
*/
|
||||
const FONT_OPEN_SANS_NEON_100 = path.resolve(__dirname, '../files/public/OpenSans-B9K8.ttf_neon_100/neon_100.fnt');
|
||||
const FONT_OPEN_SANS_NEON_64 = path.resolve(__dirname, '../files/public/OpenSans-B9K8.ttf_neon_64/neon_64.fnt');
|
||||
const FONT_OPEN_SANS_NEON_32 = path.resolve(__dirname, '../files/public/OpenSans-B9K8.ttf_neon_32/neon_32.fnt');
|
||||
const FONT_OPEN_SANS_NEON_16 = path.resolve(__dirname, '../files/public/OpenSans-B9K8.ttf_neon_16/neon_16.fnt');
|
||||
|
||||
class LibImgGeotagging {
|
||||
|
||||
static create(tempFileName = '', tempFileNamePath = '', photo = 'base64', { fulladdress, lat, lng, photo_at }) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
const JimpLoadedFile = await Jimp.read(Buffer.from(photo.replace(/^data:(image|application)\/(png|jpg|jpeg);base64,/, ''), 'base64'));
|
||||
JimpLoadedFile.quality(60);
|
||||
fulladdress = (fulladdress) ? decodeURIComponent(fulladdress) : null;
|
||||
let theAddr = fulladdress || '';
|
||||
let text = moment.unix(photo_at).format('DD MMMM YYYY HH:mm:ss') + ' WIB' + ' ' + lat + ',' + lng + ' ' + theAddr;
|
||||
let startX = 10;
|
||||
let startY = 10;
|
||||
// example 00
|
||||
// const JimpLoadFont = await Jimp.loadFont(Jimp.FONT_SANS_64_WHITE);
|
||||
// await JimpLoadedFile
|
||||
// // .print(JimpLoadFont, startX, JimpLoadedFile.bitmap.height-450, { text }, JimpLoadedFile.bitmap.width)
|
||||
// .print(JimpLoadFont, startX, startY, { text, alignmentX: Jimp.HORIZONTAL_ALIGN_LEFT, alignmentY: Jimp.VERTICAL_ALIGN_BOTTOM }, JimpLoadedFile.bitmap.width * 75 / 100, JimpLoadedFile.bitmap.height)
|
||||
// // .print(JimpLoadFont, startX, startY, { text: moment.unix(photo_at).format('YYYY-MM-DD HH:mm:ss') + ' WIB', alignmentX: Jimp.HORIZONTAL_ALIGN_LEFT, alignmentY: Jimp.VERTICAL_ALIGN_BOTTOM }, JimpLoadedFile.bitmap.width, JimpLoadedFile.bitmap.height)
|
||||
// // .print(JimpLoadFont, startX, startY, { text: '' + lat + ',' + lng, alignmentX: Jimp.HORIZONTAL_ALIGN_LEFT, alignmentY: Jimp.VERTICAL_ALIGN_BOTTOM }, JimpLoadedFile.bitmap.width, JimpLoadedFile.bitmap.height)
|
||||
// // .print(JimpLoadFont, startX, startY, { text: `${theAddr}`, alignmentX: Jimp.HORIZONTAL_ALIGN_LEFT, alignmentY: Jimp.VERTICAL_ALIGN_BOTTOM }, JimpLoadedFile.bitmap.width, JimpLoadedFile.bitmap.height)
|
||||
// .writeAsync(tempFileNamePath);
|
||||
|
||||
// example with set background with opacity 01
|
||||
// const imgBg = Buffer.from(fs.readFileSync(path.resolve(__dirname, '../files/public/img_white_bg.png'))).toString('base64');
|
||||
// const JimpImgBg = await Jimp.read(Buffer.from(imgBg.replace(/^data:(image|application)\/(png|jpg|jpeg);base64,/, ''), 'base64'));
|
||||
// const JimpLoadFont = (JimpLoadedFile.bitmap.height > 1000) ? await Jimp.loadFont(Jimp.FONT_SANS_32_BLACK) : await Jimp.loadFont(Jimp.FONT_SANS_32_BLACK);
|
||||
// await JimpLoadedFile
|
||||
// .composite(JimpImgBg, 0, JimpLoadedFile.bitmap.height-200, { mode: Jimp.BLEND_SOURCE_OVER, opacitySource: 0.5, opacityDest: 1 })
|
||||
// .print(JimpLoadFont, startX, startY, { text, alignmentX: Jimp.HORIZONTAL_ALIGN_LEFT, alignmentY: Jimp.VERTICAL_ALIGN_BOTTOM }, JimpLoadedFile.bitmap.width * percent / 100, JimpLoadedFile.bitmap.height)
|
||||
// .writeAsync(tempFileNamePath);
|
||||
|
||||
// write geotagging based on size
|
||||
// https://www.pixel.web.id/ukuran-foto-sesuai-standar/
|
||||
// font color => https://www.fontspace.com/search?q=open%20sans
|
||||
/**
|
||||
* kalo height <= 600px maka metode maka jadikan satu baru menggunakan alignmentX dan Y
|
||||
* kalo height > 600px maka metode print pecah jadi 3 baris dan pake minus(-) di startY, misal -300 -200 -100
|
||||
*/
|
||||
console.log('image width => ', JimpLoadedFile.bitmap.width, ' || ', 'image height => ', JimpLoadedFile.bitmap.height);
|
||||
const percent = (JimpLoadedFile.bitmap.height > 1000) ? 75 : 50;
|
||||
if (JimpLoadedFile.bitmap.width > 3000) {
|
||||
startY = -10;
|
||||
const JimpLoadFont = (JimpLoadedFile.bitmap.height > 1000) ? await Jimp.loadFont(FONT_OPEN_SANS_NEON_100) : await Jimp.loadFont(FONT_OPEN_SANS_NEON_64);
|
||||
await JimpLoadedFile
|
||||
.print(JimpLoadFont, startX, startY, { text, alignmentX: Jimp.HORIZONTAL_ALIGN_LEFT, alignmentY: Jimp.VERTICAL_ALIGN_BOTTOM }, JimpLoadedFile.bitmap.width * percent / 100, JimpLoadedFile.bitmap.height)
|
||||
.writeAsync(tempFileNamePath);
|
||||
} else if (JimpLoadedFile.bitmap.width > 2400) {
|
||||
startY = -10;
|
||||
const JimpLoadFont = (JimpLoadedFile.bitmap.height > 1000) ? await Jimp.loadFont(FONT_OPEN_SANS_NEON_64) : await Jimp.loadFont(FONT_OPEN_SANS_NEON_64);
|
||||
await JimpLoadedFile
|
||||
.print(JimpLoadFont, startX, startY, { text, alignmentX: Jimp.HORIZONTAL_ALIGN_LEFT, alignmentY: Jimp.VERTICAL_ALIGN_BOTTOM }, JimpLoadedFile.bitmap.width * percent / 100, JimpLoadedFile.bitmap.height)
|
||||
.writeAsync(tempFileNamePath);
|
||||
} else if (JimpLoadedFile.bitmap.width > 2100) {
|
||||
startY = -10;
|
||||
const JimpLoadFont = (JimpLoadedFile.bitmap.height > 1000) ? await Jimp.loadFont(FONT_OPEN_SANS_NEON_64) : await Jimp.loadFont(FONT_OPEN_SANS_NEON_64);
|
||||
await JimpLoadedFile
|
||||
.print(JimpLoadFont, startX, startY, { text, alignmentX: Jimp.HORIZONTAL_ALIGN_LEFT, alignmentY: Jimp.VERTICAL_ALIGN_BOTTOM }, JimpLoadedFile.bitmap.width * percent / 100, JimpLoadedFile.bitmap.height)
|
||||
.writeAsync(tempFileNamePath);
|
||||
} else if (JimpLoadedFile.bitmap.width > 1800) {
|
||||
// done testing
|
||||
startY = -10;
|
||||
const JimpLoadFont = (JimpLoadedFile.bitmap.height > 1000) ? await Jimp.loadFont(FONT_OPEN_SANS_NEON_64) : await Jimp.loadFont(FONT_OPEN_SANS_NEON_32);
|
||||
await JimpLoadedFile
|
||||
.print(JimpLoadFont, startX, startY, { text, alignmentX: Jimp.HORIZONTAL_ALIGN_LEFT, alignmentY: Jimp.VERTICAL_ALIGN_BOTTOM }, JimpLoadedFile.bitmap.width * percent / 100, JimpLoadedFile.bitmap.height)
|
||||
.writeAsync(tempFileNamePath);
|
||||
} else if (JimpLoadedFile.bitmap.width > 1650) {
|
||||
startY = -10;
|
||||
const JimpLoadFont = (JimpLoadedFile.bitmap.height > 1000) ? await Jimp.loadFont(FONT_OPEN_SANS_NEON_32) : await Jimp.loadFont(FONT_OPEN_SANS_NEON_32);
|
||||
await JimpLoadedFile
|
||||
.print(JimpLoadFont, startX, startY, { text, alignmentX: Jimp.HORIZONTAL_ALIGN_LEFT, alignmentY: Jimp.VERTICAL_ALIGN_BOTTOM }, JimpLoadedFile.bitmap.width * percent / 100, JimpLoadedFile.bitmap.height)
|
||||
.writeAsync(tempFileNamePath);
|
||||
} else if (JimpLoadedFile.bitmap.width > 1500) {
|
||||
startY = -10;
|
||||
const JimpLoadFont = (JimpLoadedFile.bitmap.height > 1000) ? await Jimp.loadFont(FONT_OPEN_SANS_NEON_32) : await Jimp.loadFont(FONT_OPEN_SANS_NEON_32);
|
||||
await JimpLoadedFile
|
||||
.print(JimpLoadFont, startX, startY, { text, alignmentX: Jimp.HORIZONTAL_ALIGN_LEFT, alignmentY: Jimp.VERTICAL_ALIGN_BOTTOM }, JimpLoadedFile.bitmap.width * percent / 100, JimpLoadedFile.bitmap.height)
|
||||
.writeAsync(tempFileNamePath);
|
||||
} else if (JimpLoadedFile.bitmap.width > 1200) {
|
||||
startY = -10;
|
||||
const JimpLoadFont = (JimpLoadedFile.bitmap.height > 1000) ? await Jimp.loadFont(FONT_OPEN_SANS_NEON_32) : await Jimp.loadFont(FONT_OPEN_SANS_NEON_32);
|
||||
await JimpLoadedFile
|
||||
.print(JimpLoadFont, startX, startY, { text, alignmentX: Jimp.HORIZONTAL_ALIGN_LEFT, alignmentY: Jimp.VERTICAL_ALIGN_BOTTOM }, JimpLoadedFile.bitmap.width * percent / 100, JimpLoadedFile.bitmap.height)
|
||||
.writeAsync(tempFileNamePath);
|
||||
} else if (JimpLoadedFile.bitmap.width > 900) {
|
||||
// done testing
|
||||
startY = -10;
|
||||
const JimpLoadFont = (JimpLoadedFile.bitmap.height > 1000) ? await Jimp.loadFont(FONT_OPEN_SANS_NEON_32) : await Jimp.loadFont(FONT_OPEN_SANS_NEON_32);
|
||||
await JimpLoadedFile
|
||||
.print(JimpLoadFont, startX, startY, { text, alignmentX: Jimp.HORIZONTAL_ALIGN_LEFT, alignmentY: Jimp.VERTICAL_ALIGN_BOTTOM }, JimpLoadedFile.bitmap.width * percent / 100, JimpLoadedFile.bitmap.height)
|
||||
.writeAsync(tempFileNamePath);
|
||||
} else if (JimpLoadedFile.bitmap.width > 750) {
|
||||
// done testing
|
||||
startX = 8;
|
||||
startY = -8;
|
||||
const JimpLoadFont = (JimpLoadedFile.bitmap.height > 1000) ? await Jimp.loadFont(FONT_OPEN_SANS_NEON_32) : await Jimp.loadFont(FONT_OPEN_SANS_NEON_16);
|
||||
await JimpLoadedFile
|
||||
.print(JimpLoadFont, startX, startY, { text, alignmentX: Jimp.HORIZONTAL_ALIGN_LEFT, alignmentY: Jimp.VERTICAL_ALIGN_BOTTOM }, JimpLoadedFile.bitmap.width * percent / 100, JimpLoadedFile.bitmap.height)
|
||||
.writeAsync(tempFileNamePath);
|
||||
} else if (JimpLoadedFile.bitmap.width > 400) {
|
||||
startX = 5;
|
||||
startY = -5;
|
||||
const JimpLoadFont = (JimpLoadedFile.bitmap.height > 1000) ? await Jimp.loadFont(FONT_OPEN_SANS_NEON_16) : await Jimp.loadFont(FONT_OPEN_SANS_NEON_16);
|
||||
await JimpLoadedFile
|
||||
.print(JimpLoadFont, startX, startY, { text, alignmentX: Jimp.HORIZONTAL_ALIGN_LEFT, alignmentY: Jimp.VERTICAL_ALIGN_BOTTOM }, JimpLoadedFile.bitmap.width * percent / 100, JimpLoadedFile.bitmap.height)
|
||||
.writeAsync(tempFileNamePath);
|
||||
} else {
|
||||
startX = 5;
|
||||
startY = -5;
|
||||
const JimpLoadFont = (JimpLoadedFile.bitmap.height > 1000) ? await Jimp.loadFont(FONT_OPEN_SANS_NEON_16) : await Jimp.loadFont(FONT_OPEN_SANS_NEON_16);
|
||||
await JimpLoadedFile
|
||||
.print(JimpLoadFont, startX, startY, { text, alignmentX: Jimp.HORIZONTAL_ALIGN_LEFT, alignmentY: Jimp.VERTICAL_ALIGN_BOTTOM }, JimpLoadedFile.bitmap.width * percent / 100, JimpLoadedFile.bitmap.height)
|
||||
.writeAsync(tempFileNamePath);
|
||||
}
|
||||
resolve({type: 'sc'});
|
||||
} catch (e) {
|
||||
reject({type: 'err', e});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = LibImgGeotagging;
|
||||
55
library/LibJwt.js
Normal file
55
library/LibJwt.js
Normal file
@ -0,0 +1,55 @@
|
||||
const jwt = require('jsonwebtoken');
|
||||
const cryptojs = require("crypto-js");
|
||||
|
||||
const confJwt = {
|
||||
algorithm: 'HS256',
|
||||
issuer: 'BINGCORP',
|
||||
subject: 'APP_DRIVER',
|
||||
audience: 'MOBILE',
|
||||
// expiresIn: '365d',
|
||||
};
|
||||
const SECRET_KEY = 'BINGCORP';
|
||||
const KEY_ENC = 'dRgUjXn2r5u8x/A?D(G+KbPeShVmYp3s'; // just random string
|
||||
|
||||
class LibJwt {
|
||||
|
||||
static async createToken(data) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// mengapa data perlu di enc lagi ? karena di jwt data tersebut hanya di base64_enc
|
||||
let cipherText = cryptojs.AES.encrypt(JSON.stringify(data), KEY_ENC).toString();
|
||||
let payload = {
|
||||
cipherText,
|
||||
};
|
||||
|
||||
jwt.sign(payload, SECRET_KEY, confJwt, function (err, token) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return false;
|
||||
}
|
||||
resolve({ type: 'success', token });
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
static async verifyToken(token) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
jwt.verify(token, SECRET_KEY, confJwt, function (err, decoded) {
|
||||
if (err) {
|
||||
resolve({ type: 'fail', message: err.message });
|
||||
return false;
|
||||
}
|
||||
const { cipherText } = decoded;
|
||||
let bytes = cryptojs.AES.decrypt(cipherText, KEY_ENC);
|
||||
let dataDecoded = JSON.parse(bytes.toString(cryptojs.enc.Utf8));
|
||||
resolve({ type: 'success', data: dataDecoded });
|
||||
})
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = LibJwt;
|
||||
28
library/LibLogReqResApi.js
Normal file
28
library/LibLogReqResApi.js
Normal file
@ -0,0 +1,28 @@
|
||||
const LibWinston = require('./LibWinston');
|
||||
const Logger = LibWinston.initialize('req_res_api');
|
||||
|
||||
class LibLogReqResApi {
|
||||
|
||||
static async log(req, apiRes) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
delete req.body.photo;
|
||||
const payload = {
|
||||
url: req.originalUrl,
|
||||
auth: req.auth,
|
||||
params: req.params,
|
||||
query: req.query,
|
||||
body: req.body,
|
||||
resp: apiRes,
|
||||
};
|
||||
Logger.log('info', `${JSON.stringify(payload)}`);
|
||||
resolve(true);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = LibLogReqResApi;
|
||||
1163
library/LibMail.js
Normal file
1163
library/LibMail.js
Normal file
File diff suppressed because it is too large
Load Diff
206
library/LibMysqlHelper.js
Normal file
206
library/LibMysqlHelper.js
Normal file
@ -0,0 +1,206 @@
|
||||
const db = require(`../config/dbMysqlConn`);
|
||||
// const Promise = require("bluebird");
|
||||
|
||||
class MysqlHelpers {
|
||||
static async insert (table, data) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const query = `INSERT INTO ${table} SET ?;`;
|
||||
db.getConnection(function (err, conn) {
|
||||
if (err) {
|
||||
conn.release();
|
||||
reject(err);
|
||||
return false;
|
||||
}
|
||||
conn.beginTransaction(async function (err) {
|
||||
if (err) {
|
||||
conn.release();
|
||||
reject(err);
|
||||
return false;
|
||||
}
|
||||
conn.query(query, data, function (err, result) {
|
||||
if (err) {
|
||||
conn.rollback(async function () {
|
||||
conn.release();
|
||||
reject(err);
|
||||
});
|
||||
return false;
|
||||
}
|
||||
// Number(result.insertId);
|
||||
conn.commit(async function (err) {
|
||||
if (err) {
|
||||
conn.release();
|
||||
reject(err);
|
||||
return false;
|
||||
}
|
||||
conn.release();
|
||||
resolve(result);
|
||||
return true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
})
|
||||
}
|
||||
static async update (table, data, colId, valId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const query = `UPDATE ${table} SET ? WHERE ${colId} = ?;`;
|
||||
db.getConnection(function (err, conn) {
|
||||
if (err) {
|
||||
conn.release();
|
||||
reject(err);
|
||||
return false;
|
||||
}
|
||||
conn.beginTransaction(async function (err) {
|
||||
if (err) {
|
||||
conn.release();
|
||||
reject(err);
|
||||
return false;
|
||||
}
|
||||
conn.query(query, [data, valId], function (err, result) {
|
||||
if (err) {
|
||||
conn.rollback(async function () {
|
||||
conn.release();
|
||||
reject(err);
|
||||
});
|
||||
return false;
|
||||
}
|
||||
conn.commit(async function (err) {
|
||||
if (err) {
|
||||
conn.release();
|
||||
reject(err);
|
||||
return false;
|
||||
}
|
||||
conn.release();
|
||||
resolve(result);
|
||||
return true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
})
|
||||
}
|
||||
static async delete (table, colId, valId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const query = `DELETE FROM ${table} WHERE ${colId} = ?;`;
|
||||
db.getConnection(function (err, conn) {
|
||||
if (err) {
|
||||
conn.release();
|
||||
reject(err);
|
||||
return false;
|
||||
}
|
||||
conn.beginTransaction(async function (err) {
|
||||
if (err) {
|
||||
conn.release();
|
||||
reject(err);
|
||||
return false;
|
||||
}
|
||||
conn.query(query, [valId], function (err, result) {
|
||||
if (err) {
|
||||
conn.rollback(async function () {
|
||||
conn.release();
|
||||
reject(err);
|
||||
});
|
||||
return false;
|
||||
}
|
||||
conn.commit(async function (err) {
|
||||
if (err) {
|
||||
conn.release();
|
||||
reject(err);
|
||||
return false;
|
||||
}
|
||||
conn.release();
|
||||
resolve(result);
|
||||
return true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
})
|
||||
}
|
||||
static async createConnection () {
|
||||
return new Promise((resolve, reject) => {
|
||||
db.getConnection(function (err, conn) {
|
||||
if (err) {
|
||||
if (conn) conn.release();
|
||||
reject(err);
|
||||
return false;
|
||||
}
|
||||
resolve(conn);
|
||||
});
|
||||
})
|
||||
}
|
||||
static async getDbMysqlConn () {
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve(db);
|
||||
})
|
||||
}
|
||||
static async releaseConnection (conn) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (conn) conn.release();
|
||||
resolve(true);
|
||||
})
|
||||
}
|
||||
static async createTrx (conn) {
|
||||
return new Promise((resolve, reject) => {
|
||||
conn.beginTransaction(async function (err) {
|
||||
if (err) {
|
||||
conn.release();
|
||||
reject(err);
|
||||
return false;
|
||||
}
|
||||
resolve(conn);
|
||||
});
|
||||
})
|
||||
}
|
||||
static async queryTrx (conn, query = '', params = []) {
|
||||
return new Promise((resolve, reject) => {
|
||||
conn.query(query, params, function (err, result) {
|
||||
if (err) {
|
||||
conn.rollback(async function () {
|
||||
conn.release();
|
||||
reject(err);
|
||||
});
|
||||
return false;
|
||||
}
|
||||
// Number(result.insertId);
|
||||
resolve(result);
|
||||
});
|
||||
})
|
||||
}
|
||||
static async query (conn, query = '', params = []) {
|
||||
return new Promise((resolve, reject) => {
|
||||
conn.query(query, params, function (err, result) {
|
||||
if (err) return reject(err);
|
||||
// Number(result.insertId);
|
||||
resolve(result);
|
||||
});
|
||||
})
|
||||
}
|
||||
static async commit (conn) {
|
||||
return new Promise((resolve, reject) => {
|
||||
conn.commit(async function (err) {
|
||||
if (err) {
|
||||
conn.release();
|
||||
reject(err);
|
||||
return false;
|
||||
}
|
||||
conn.release();
|
||||
resolve(true);
|
||||
});
|
||||
})
|
||||
}
|
||||
static async rollback (conn) {
|
||||
return new Promise((resolve, reject) => {
|
||||
conn.rollback(async function () {
|
||||
conn.release();
|
||||
reject(err);
|
||||
});
|
||||
return false;
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MysqlHelpers;
|
||||
39
library/LibPassword.js
Normal file
39
library/LibPassword.js
Normal file
@ -0,0 +1,39 @@
|
||||
const bcrypt = require('bcrypt');
|
||||
|
||||
const saltRounds = 10;
|
||||
|
||||
class LibPassword {
|
||||
|
||||
static async hashPw(plainTextPassword) {
|
||||
return new Promise((resolve, reject) => {
|
||||
bcrypt.genSalt(saltRounds, function (err, salt) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return false;
|
||||
}
|
||||
bcrypt.hash(plainTextPassword, salt, function (err, hash) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return false;
|
||||
}
|
||||
resolve(hash);
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
static async checkPw(hash, plainText) {
|
||||
return new Promise((resolve, reject) => {
|
||||
bcrypt.compare(plainText, hash, function (err, result) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return false;
|
||||
}
|
||||
resolve(result);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = LibPassword;
|
||||
82
library/LibQueueBlastOrder.js
Normal file
82
library/LibQueueBlastOrder.js
Normal file
@ -0,0 +1,82 @@
|
||||
const path = require('path');
|
||||
const RedisConn = require('./LibRedisConn');
|
||||
const QueueMQ = require('bullmq').Queue;
|
||||
const QueueSchedulerMQ = require('bullmq').QueueScheduler;
|
||||
const WorkerMQ = require('bullmq').Worker;
|
||||
// const JobMQ = require('bullmq').Job;
|
||||
const LibWinston = require('./LibWinston');
|
||||
const EventEmitter = require('events');
|
||||
|
||||
const queueName = process.env.REDIS_QUEUE_BLAST_ORDER;
|
||||
const theQueue = new QueueMQ(queueName, { connection: RedisConn });
|
||||
const theQueueScheduler = new QueueSchedulerMQ(queueName, { connection: RedisConn });
|
||||
|
||||
const Logger = LibWinston.initialize(queueName);
|
||||
|
||||
class MyEmitter extends EventEmitter { };
|
||||
const myEmitter = new MyEmitter();
|
||||
|
||||
/**
|
||||
* bisa multiple queue tanpa new Queue(name) lagi, tinggal dibedain aja nama processornya ketika queue.add(nameProcessor)
|
||||
* worker hanya memproses nama sesuai dengan nameProcessor bukan nama ketika new Queue(name)
|
||||
* concurrency di worker adalah maksimal yang bisa diproses dalam satu waktu bersamaan
|
||||
* attempts di queue adalah maksimal percobaan ketika gagal
|
||||
*/
|
||||
|
||||
const LibQueueBlastOrder = {
|
||||
name: queueName,
|
||||
queue: theQueue,
|
||||
addQueue: function (data) {
|
||||
this.queue.add(queueName, data, {
|
||||
removeOnComplete: false, // false just for debugging
|
||||
delay: data.delay,
|
||||
});
|
||||
},
|
||||
pauseQueue: function () {
|
||||
this.queue.pause();
|
||||
},
|
||||
resumeQueue: function () {
|
||||
this.queue.resume();
|
||||
},
|
||||
getJobs: async function () {
|
||||
return await this.queue.getJobs();
|
||||
},
|
||||
getRepeatableJobs: async function () {
|
||||
return await this.queue.getRepeatableJobs();
|
||||
},
|
||||
setWorker: function () {
|
||||
try {
|
||||
const processorFile = path.join(__dirname, '../workers/BlastOrderWorker.js');
|
||||
const schedulerWorker = new WorkerMQ(queueName, processorFile, {
|
||||
connection: RedisConn,
|
||||
concurrency: 1,
|
||||
})
|
||||
schedulerWorker.on('completed', async (job, returnValue) => {
|
||||
Logger.log('info', `${queueName} completed: ${JSON.stringify(returnValue)}`);
|
||||
})
|
||||
schedulerWorker.on('failed', async (job, failedReason) => {
|
||||
Logger.log('error', `${queueName} failed: ${JSON.stringify(failedReason)}`);
|
||||
});
|
||||
schedulerWorker.on('error', async (errMsg) => {
|
||||
Logger.log('error', `${queueName} error: ${JSON.stringify(errMsg)}`);
|
||||
});
|
||||
myEmitter.on(queueName + 'ShutDown', () => {
|
||||
// gracefull shutdown
|
||||
schedulerWorker.close();
|
||||
});
|
||||
} catch (e) {
|
||||
Logger.log('error', `${queueName} worker_error: ${JSON.stringify(e, Object.getOwnPropertyNames(e))}`);
|
||||
}
|
||||
},
|
||||
drainQueue: function () {
|
||||
this.queue.drain();
|
||||
},
|
||||
obliterateQueue: function () {
|
||||
this.queue.obliterate();
|
||||
},
|
||||
workerShutDown: function () {
|
||||
myEmitter.emit(queueName + 'ShutDown');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = LibQueueBlastOrder
|
||||
9
library/LibRedisConn.js
Normal file
9
library/LibRedisConn.js
Normal file
@ -0,0 +1,9 @@
|
||||
const Redis = require('ioredis');
|
||||
const redisConn = new Redis({
|
||||
port: process.env.REDIS_PORT,
|
||||
host: process.env.REDIS_HOST,
|
||||
maxRetriesPerRequest: null,
|
||||
enableReadyCheck: false,
|
||||
});
|
||||
|
||||
module.exports = redisConn;
|
||||
93
library/LibSchedulerDrvBlastNotif.js
Normal file
93
library/LibSchedulerDrvBlastNotif.js
Normal file
@ -0,0 +1,93 @@
|
||||
const path = require('path');
|
||||
const RedisConn = require('./LibRedisConn');
|
||||
const QueueMQ = require('bullmq').Queue;
|
||||
const QueueSchedulerMQ = require('bullmq').QueueScheduler;
|
||||
const WorkerMQ = require('bullmq').Worker;
|
||||
// const JobMQ = require('bullmq').Job;
|
||||
const LibWinston = require('./LibWinston');
|
||||
const EventEmitter = require('events');
|
||||
|
||||
const schedulerName = process.env.REDIS_SCHEDULER_DRV_BLAST_NOTIF;
|
||||
const theQueue = new QueueMQ(schedulerName, { connection: RedisConn });
|
||||
const theQueueScheduler = new QueueSchedulerMQ(schedulerName, { connection: RedisConn });
|
||||
|
||||
const Logger = LibWinston.initialize(schedulerName);
|
||||
|
||||
class MyEmitter extends EventEmitter { };
|
||||
const myEmitter = new MyEmitter();
|
||||
|
||||
/**
|
||||
* bisa multiple queue tanpa new Queue(name) lagi, tinggal dibedain aja nama processornya ketika queue.add(nameProcessor)
|
||||
* worker hanya memproses nama sesuai dengan nameProcessor bukan nama ketika new Queue(name)
|
||||
* concurrency di worker adalah maksimal yang bisa diproses dalam satu waktu bersamaan
|
||||
* attempts di queue adalah maksimal percobaan ketika gagal
|
||||
*/
|
||||
|
||||
const LibSchedulerDrvBlastNotif = {
|
||||
name: schedulerName,
|
||||
queue: theQueue,
|
||||
runQueueScheduler: function (data) {
|
||||
// * * * * * // every minute
|
||||
// */3 * * * * // every 3 minute
|
||||
// 0 */1 * * * // every hour
|
||||
this.queue.add(schedulerName, data, {
|
||||
removeOnComplete: false, // false just for debugging
|
||||
repeat: {
|
||||
cron: process.env.SCHEDULE_DRV_BLAST_NOTIF_TIME,
|
||||
},
|
||||
attempts: 0,
|
||||
});
|
||||
},
|
||||
addQueue: function (data) {
|
||||
this.queue.add(schedulerName, data, {
|
||||
removeOnComplete: false, // false just for debugging
|
||||
});
|
||||
},
|
||||
pauseQueue: function () {
|
||||
this.queue.pause();
|
||||
},
|
||||
resumeQueue: function () {
|
||||
this.queue.resume();
|
||||
},
|
||||
getJobs: async function () {
|
||||
return await this.queue.getJobs();
|
||||
},
|
||||
getRepeatableJobs: async function () {
|
||||
return await this.queue.getRepeatableJobs();
|
||||
},
|
||||
setWorker: function () {
|
||||
try {
|
||||
const processorFile = path.join(__dirname, '../workers/DrvBlastNotifWorker.js');
|
||||
const schedulerWorker = new WorkerMQ(schedulerName, processorFile, {
|
||||
connection: RedisConn,
|
||||
concurrency: 1,
|
||||
})
|
||||
schedulerWorker.on('completed', async (job, returnValue) => {
|
||||
Logger.log('info', `${schedulerName} completed: ${JSON.stringify(returnValue)}`);
|
||||
})
|
||||
schedulerWorker.on('failed', async (job, failedReason) => {
|
||||
Logger.log('error', `${schedulerName} failed: ${JSON.stringify(failedReason)}`);
|
||||
});
|
||||
schedulerWorker.on('error', async (errMsg) => {
|
||||
Logger.log('error', `${schedulerName} error: ${JSON.stringify(errMsg)}`);
|
||||
});
|
||||
myEmitter.on(schedulerName + 'ShutDown', () => {
|
||||
// gracefull shutdown
|
||||
schedulerWorker.close();
|
||||
});
|
||||
} catch (e) {
|
||||
Logger.log('error', `${schedulerName} worker_error: ${JSON.stringify(e, Object.getOwnPropertyNames(e))}`);
|
||||
}
|
||||
},
|
||||
drainQueue: function () {
|
||||
this.queue.drain();
|
||||
},
|
||||
obliterateQueue: function () {
|
||||
this.queue.obliterate();
|
||||
},
|
||||
workerShutDown: function () {
|
||||
myEmitter.emit(schedulerName + 'ShutDown');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = LibSchedulerDrvBlastNotif
|
||||
93
library/LibSchedulerDrvUpLoc.js
Normal file
93
library/LibSchedulerDrvUpLoc.js
Normal file
@ -0,0 +1,93 @@
|
||||
const path = require('path');
|
||||
const RedisConn = require('./LibRedisConn');
|
||||
const QueueMQ = require('bullmq').Queue;
|
||||
const QueueSchedulerMQ = require('bullmq').QueueScheduler;
|
||||
const WorkerMQ = require('bullmq').Worker;
|
||||
// const JobMQ = require('bullmq').Job;
|
||||
const LibWinston = require('./LibWinston');
|
||||
const EventEmitter = require('events');
|
||||
|
||||
const schedulerName = process.env.REDIS_SCHEDULER_DRV_UP_LOC;
|
||||
const theQueue = new QueueMQ(schedulerName, { connection: RedisConn });
|
||||
const theQueueScheduler = new QueueSchedulerMQ(schedulerName, { connection: RedisConn });
|
||||
|
||||
const Logger = LibWinston.initialize(schedulerName);
|
||||
|
||||
class MyEmitter extends EventEmitter { };
|
||||
const myEmitter = new MyEmitter();
|
||||
|
||||
/**
|
||||
* bisa multiple queue tanpa new Queue(name) lagi, tinggal dibedain aja nama processornya ketika queue.add(nameProcessor)
|
||||
* worker hanya memproses nama sesuai dengan nameProcessor bukan nama ketika new Queue(name)
|
||||
* concurrency di worker adalah maksimal yang bisa diproses dalam satu waktu bersamaan
|
||||
* attempts di queue adalah maksimal percobaan ketika gagal
|
||||
*/
|
||||
|
||||
const LibSchedulerDrvUpLoc = {
|
||||
name: schedulerName,
|
||||
queue: theQueue,
|
||||
runQueueScheduler: function (data) {
|
||||
// * * * * * // every minute
|
||||
// */3 * * * * // every 3 minute
|
||||
// 0 */1 * * * // every hour
|
||||
this.queue.add(schedulerName, data, {
|
||||
removeOnComplete: false, // false just for debugging
|
||||
repeat: {
|
||||
cron: process.env.SCHEDULE_DRV_UP_LOC_TIME,
|
||||
},
|
||||
attempts: 0,
|
||||
});
|
||||
},
|
||||
addQueue: function (data) {
|
||||
this.queue.add(schedulerName, data, {
|
||||
removeOnComplete: false, // false just for debugging
|
||||
});
|
||||
},
|
||||
pauseQueue: function () {
|
||||
this.queue.pause();
|
||||
},
|
||||
resumeQueue: function () {
|
||||
this.queue.resume();
|
||||
},
|
||||
getJobs: async function () {
|
||||
return await this.queue.getJobs();
|
||||
},
|
||||
getRepeatableJobs: async function () {
|
||||
return await this.queue.getRepeatableJobs();
|
||||
},
|
||||
setWorker: function () {
|
||||
try {
|
||||
const processorFile = path.join(__dirname, '../workers/DrvUpLocWorker.js');
|
||||
const schedulerWorker = new WorkerMQ(schedulerName, processorFile, {
|
||||
connection: RedisConn,
|
||||
concurrency: 1,
|
||||
})
|
||||
schedulerWorker.on('completed', async (job, returnValue) => {
|
||||
Logger.log('info', `${schedulerName} completed: ${JSON.stringify(returnValue)}`);
|
||||
})
|
||||
schedulerWorker.on('failed', async (job, failedReason) => {
|
||||
Logger.log('error', `${schedulerName} failed: ${JSON.stringify(failedReason)}`);
|
||||
});
|
||||
schedulerWorker.on('error', async (errMsg) => {
|
||||
Logger.log('error', `${schedulerName} error: ${JSON.stringify(errMsg)}`);
|
||||
});
|
||||
myEmitter.on(schedulerName + 'ShutDown', () => {
|
||||
// gracefull shutdown
|
||||
schedulerWorker.close();
|
||||
});
|
||||
} catch (e) {
|
||||
Logger.log('error', `${schedulerName} worker_error: ${JSON.stringify(e, Object.getOwnPropertyNames(e))}`);
|
||||
}
|
||||
},
|
||||
drainQueue: function () {
|
||||
this.queue.drain();
|
||||
},
|
||||
obliterateQueue: function () {
|
||||
this.queue.obliterate();
|
||||
},
|
||||
workerShutDown: function () {
|
||||
myEmitter.emit(schedulerName + 'ShutDown');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = LibSchedulerDrvUpLoc
|
||||
93
library/LibSchedulerDrvUpLocIdle.js
Normal file
93
library/LibSchedulerDrvUpLocIdle.js
Normal file
@ -0,0 +1,93 @@
|
||||
const path = require('path');
|
||||
const RedisConn = require('./LibRedisConn');
|
||||
const QueueMQ = require('bullmq').Queue;
|
||||
const QueueSchedulerMQ = require('bullmq').QueueScheduler;
|
||||
const WorkerMQ = require('bullmq').Worker;
|
||||
// const JobMQ = require('bullmq').Job;
|
||||
const LibWinston = require('./LibWinston');
|
||||
const EventEmitter = require('events');
|
||||
|
||||
const schedulerName = process.env.REDIS_SCHEDULER_DRV_UP_LOC_IDLE;
|
||||
const theQueue = new QueueMQ(schedulerName, { connection: RedisConn });
|
||||
const theQueueScheduler = new QueueSchedulerMQ(schedulerName, { connection: RedisConn });
|
||||
|
||||
const Logger = LibWinston.initialize(schedulerName);
|
||||
|
||||
class MyEmitter extends EventEmitter { };
|
||||
const myEmitter = new MyEmitter();
|
||||
|
||||
/**
|
||||
* bisa multiple queue tanpa new Queue(name) lagi, tinggal dibedain aja nama processornya ketika queue.add(nameProcessor)
|
||||
* worker hanya memproses nama sesuai dengan nameProcessor bukan nama ketika new Queue(name)
|
||||
* concurrency di worker adalah maksimal yang bisa diproses dalam satu waktu bersamaan
|
||||
* attempts di queue adalah maksimal percobaan ketika gagal
|
||||
*/
|
||||
|
||||
const LibSchedulerDrvUpLocIdle = {
|
||||
name: schedulerName,
|
||||
queue: theQueue,
|
||||
runQueueScheduler: function (data) {
|
||||
// * * * * * // every minute
|
||||
// */3 * * * * // every 3 minute
|
||||
// 0 */1 * * * // every hour
|
||||
this.queue.add(schedulerName, data, {
|
||||
removeOnComplete: false, // false just for debugging
|
||||
repeat: {
|
||||
cron: process.env.SCHEDULE_DRV_UP_LOC_IDLE_TIME,
|
||||
},
|
||||
attempts: 0,
|
||||
});
|
||||
},
|
||||
addQueue: function (data) {
|
||||
this.queue.add(schedulerName, data, {
|
||||
removeOnComplete: false, // false just for debugging
|
||||
});
|
||||
},
|
||||
pauseQueue: function () {
|
||||
this.queue.pause();
|
||||
},
|
||||
resumeQueue: function () {
|
||||
this.queue.resume();
|
||||
},
|
||||
getJobs: async function () {
|
||||
return await this.queue.getJobs();
|
||||
},
|
||||
getRepeatableJobs: async function () {
|
||||
return await this.queue.getRepeatableJobs();
|
||||
},
|
||||
setWorker: function () {
|
||||
try {
|
||||
const processorFile = path.join(__dirname, '../workers/DrvUpLocIdleWorker.js');
|
||||
const schedulerWorker = new WorkerMQ(schedulerName, processorFile, {
|
||||
connection: RedisConn,
|
||||
concurrency: 1,
|
||||
})
|
||||
schedulerWorker.on('completed', async (job, returnValue) => {
|
||||
Logger.log('info', `${schedulerName} completed: ${JSON.stringify(returnValue)}`);
|
||||
})
|
||||
schedulerWorker.on('failed', async (job, failedReason) => {
|
||||
Logger.log('error', `${schedulerName} failed: ${JSON.stringify(failedReason)}`);
|
||||
});
|
||||
schedulerWorker.on('error', async (errMsg) => {
|
||||
Logger.log('error', `${schedulerName} error: ${JSON.stringify(errMsg)}`);
|
||||
});
|
||||
myEmitter.on(schedulerName + 'ShutDown', () => {
|
||||
// gracefull shutdown
|
||||
schedulerWorker.close();
|
||||
});
|
||||
} catch (e) {
|
||||
Logger.log('error', `${schedulerName} worker_error: ${JSON.stringify(e, Object.getOwnPropertyNames(e))}`);
|
||||
}
|
||||
},
|
||||
drainQueue: function () {
|
||||
this.queue.drain();
|
||||
},
|
||||
obliterateQueue: function () {
|
||||
this.queue.obliterate();
|
||||
},
|
||||
workerShutDown: function () {
|
||||
myEmitter.emit(schedulerName + 'ShutDown');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = LibSchedulerDrvUpLocIdle
|
||||
93
library/LibSchedulerDrvUpPhoto.js
Normal file
93
library/LibSchedulerDrvUpPhoto.js
Normal file
@ -0,0 +1,93 @@
|
||||
const path = require('path');
|
||||
const RedisConn = require('./LibRedisConn');
|
||||
const QueueMQ = require('bullmq').Queue;
|
||||
const QueueSchedulerMQ = require('bullmq').QueueScheduler;
|
||||
const WorkerMQ = require('bullmq').Worker;
|
||||
// const JobMQ = require('bullmq').Job;
|
||||
const LibWinston = require('./LibWinston');
|
||||
const EventEmitter = require('events');
|
||||
|
||||
const schedulerName = process.env.REDIS_SCHEDULER_DRV_UP_PHOTO;
|
||||
const theQueue = new QueueMQ(schedulerName, { connection: RedisConn });
|
||||
const theQueueScheduler = new QueueSchedulerMQ(schedulerName, { connection: RedisConn });
|
||||
|
||||
const Logger = LibWinston.initialize(schedulerName);
|
||||
|
||||
class MyEmitter extends EventEmitter { };
|
||||
const myEmitter = new MyEmitter();
|
||||
|
||||
/**
|
||||
* bisa multiple queue tanpa new Queue(name) lagi, tinggal dibedain aja nama processornya ketika queue.add(nameProcessor)
|
||||
* worker hanya memproses nama sesuai dengan nameProcessor bukan nama ketika new Queue(name)
|
||||
* concurrency di worker adalah maksimal yang bisa diproses dalam satu waktu bersamaan
|
||||
* attempts di queue adalah maksimal percobaan ketika gagal
|
||||
*/
|
||||
|
||||
const LibSchedulerDrvUpPhoto = {
|
||||
name: schedulerName,
|
||||
queue: theQueue,
|
||||
runQueueScheduler: function (data) {
|
||||
// * * * * * // every minute
|
||||
// */3 * * * * // every 3 minute
|
||||
// 0 */1 * * * // every hour
|
||||
this.queue.add(schedulerName, data, {
|
||||
removeOnComplete: false, // false just for debugging
|
||||
repeat: {
|
||||
cron: process.env.SCHEDULE_DRV_UP_PHOTO_TIME,
|
||||
},
|
||||
attempts: 0,
|
||||
});
|
||||
},
|
||||
addQueue: function (data) {
|
||||
this.queue.add(schedulerName, data, {
|
||||
removeOnComplete: false, // false just for debugging
|
||||
});
|
||||
},
|
||||
pauseQueue: function () {
|
||||
this.queue.pause();
|
||||
},
|
||||
resumeQueue: function () {
|
||||
this.queue.resume();
|
||||
},
|
||||
getJobs: async function () {
|
||||
return await this.queue.getJobs();
|
||||
},
|
||||
getRepeatableJobs: async function () {
|
||||
return await this.queue.getRepeatableJobs();
|
||||
},
|
||||
setWorker: function () {
|
||||
try {
|
||||
const processorFile = path.join(__dirname, '../workers/DrvUpPhotoWorker.js');
|
||||
const schedulerWorker = new WorkerMQ(schedulerName, processorFile, {
|
||||
connection: RedisConn,
|
||||
concurrency: 1,
|
||||
})
|
||||
schedulerWorker.on('completed', async (job, returnValue) => {
|
||||
Logger.log('info', `${schedulerName} completed: ${JSON.stringify(returnValue)}`);
|
||||
})
|
||||
schedulerWorker.on('failed', async (job, failedReason) => {
|
||||
Logger.log('error', `${schedulerName} failed: ${JSON.stringify(failedReason)}`);
|
||||
});
|
||||
schedulerWorker.on('error', async (errMsg) => {
|
||||
Logger.log('error', `${schedulerName} error: ${JSON.stringify(errMsg)}`);
|
||||
});
|
||||
myEmitter.on(schedulerName + 'ShutDown', () => {
|
||||
// gracefull shutdown
|
||||
schedulerWorker.close();
|
||||
});
|
||||
} catch (e) {
|
||||
Logger.log('error', `${schedulerName} worker_error: ${JSON.stringify(e, Object.getOwnPropertyNames(e))}`);
|
||||
}
|
||||
},
|
||||
drainQueue: function () {
|
||||
this.queue.drain();
|
||||
},
|
||||
obliterateQueue: function () {
|
||||
this.queue.obliterate();
|
||||
},
|
||||
workerShutDown: function () {
|
||||
myEmitter.emit(schedulerName + 'ShutDown');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = LibSchedulerDrvUpPhoto
|
||||
133
library/LibSchedulerGpsTrackerWakeUp.js
Normal file
133
library/LibSchedulerGpsTrackerWakeUp.js
Normal file
@ -0,0 +1,133 @@
|
||||
const path = require('path');
|
||||
const RedisConn = require('./LibRedisConn');
|
||||
const QueueMQ = require('bullmq').Queue;
|
||||
const QueueSchedulerMQ = require('bullmq').QueueScheduler;
|
||||
const WorkerMQ = require('bullmq').Worker;
|
||||
// const JobMQ = require('bullmq').Job;
|
||||
const LibWinston = require('./LibWinston');
|
||||
const EventEmitter = require('events');
|
||||
|
||||
const schedulerName = process.env.REDIS_SCHEDULER_GPS_TRACKER_WAKEUP;
|
||||
const theQueue = new QueueMQ(schedulerName, { connection: RedisConn });
|
||||
const theQueueScheduler = new QueueSchedulerMQ(schedulerName, { connection: RedisConn });
|
||||
|
||||
const Logger = LibWinston.initialize(schedulerName);
|
||||
|
||||
class MyEmitter extends EventEmitter { };
|
||||
const myEmitter = new MyEmitter();
|
||||
|
||||
/**
|
||||
* bisa multiple queue tanpa new Queue(name) lagi, tinggal dibedain aja nama processornya ketika queue.add(nameProcessor)
|
||||
* worker hanya memproses nama sesuai dengan nameProcessor bukan nama ketika new Queue(name)
|
||||
* concurrency di worker adalah maksimal yang bisa diproses dalam satu waktu bersamaan
|
||||
* attempts di queue adalah maksimal percobaan ketika gagal
|
||||
*/
|
||||
|
||||
const LibSchedulerDrvUpLocIdle = {
|
||||
name: schedulerName,
|
||||
queue: theQueue,
|
||||
runQueueScheduler: function (data) {
|
||||
// * * * * * // every minute
|
||||
// */3 * * * * // every 3 minute
|
||||
// 0 */1 * * * // every hour
|
||||
this.queue.add(schedulerName, data, {
|
||||
removeOnComplete: false, // false just for debugging
|
||||
repeat: {
|
||||
cron: process.env.SCHEDULE_GPS_TRACKER_WAKEUP_TIME,
|
||||
},
|
||||
attempts: 0,
|
||||
});
|
||||
},
|
||||
addQueue: function (data) {
|
||||
this.queue.add(schedulerName, data, {
|
||||
removeOnComplete: false, // false just for debugging
|
||||
});
|
||||
},
|
||||
pauseQueue: function () {
|
||||
this.queue.pause();
|
||||
},
|
||||
resumeQueue: function () {
|
||||
this.queue.resume();
|
||||
},
|
||||
getJobs: async function () {
|
||||
return await this.queue.getJobs();
|
||||
},
|
||||
getRepeatableJobs: async function () {
|
||||
return await this.queue.getRepeatableJobs();
|
||||
},
|
||||
setWorker: function () {
|
||||
try {
|
||||
// const processorFile = path.join(__dirname, '../workers/GpsTrackerWakeUp.js');
|
||||
const schedulerWorker = new WorkerMQ(schedulerName, worker, {
|
||||
connection: RedisConn,
|
||||
concurrency: 1,
|
||||
})
|
||||
schedulerWorker.on('completed', async (job, returnValue) => {
|
||||
Logger.log('info', `${schedulerName} completed: ${JSON.stringify(returnValue)}`);
|
||||
})
|
||||
schedulerWorker.on('failed', async (job, failedReason) => {
|
||||
Logger.log('error', `${schedulerName} failed: ${JSON.stringify(failedReason)}`);
|
||||
});
|
||||
schedulerWorker.on('error', async (errMsg) => {
|
||||
Logger.log('error', `${schedulerName} error: ${JSON.stringify(errMsg)}`);
|
||||
});
|
||||
myEmitter.on(schedulerName + 'ShutDown', () => {
|
||||
// gracefull shutdown
|
||||
schedulerWorker.close();
|
||||
});
|
||||
} catch (e) {
|
||||
Logger.log('error', `${schedulerName} worker_error: ${JSON.stringify(e, Object.getOwnPropertyNames(e))}`);
|
||||
}
|
||||
},
|
||||
drainQueue: function () {
|
||||
this.queue.drain();
|
||||
},
|
||||
obliterateQueue: function () {
|
||||
this.queue.obliterate();
|
||||
},
|
||||
workerShutDown: function () {
|
||||
myEmitter.emit(schedulerName + 'ShutDown');
|
||||
}
|
||||
}
|
||||
|
||||
const netConn = require('../config/netConn');
|
||||
async function worker (job) {
|
||||
try {
|
||||
// const now = moment().unix();
|
||||
Logger.log('info', `${schedulerName} running: ${JSON.stringify(job.opts)}`);
|
||||
|
||||
// (async function() {
|
||||
try {
|
||||
let loop = process.env.SCHEDULE_GPS_TRACKER_WAKEUP_MAX_LOOP;
|
||||
for (let x = 0; x < loop; x++) {
|
||||
for (const device_id in netConn) {
|
||||
const c = netConn[device_id];
|
||||
// c.write(Buffer.from("DWXX#", "hex"));
|
||||
c.write("DWXX#");
|
||||
console.log('looking up information location');
|
||||
// c.on('data', function(data) {
|
||||
// console.log('worker ', data);
|
||||
// });
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
Logger.log('error', `${schedulerName} error: ${JSON.stringify(e)}`);
|
||||
}
|
||||
// })();
|
||||
|
||||
return {
|
||||
type: 'success',
|
||||
message: 'success run scheduler gps tracker wakeup worker',
|
||||
data: null,
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
type: 'error',
|
||||
message: e.message,
|
||||
e: e,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = LibSchedulerDrvUpLocIdle
|
||||
93
library/LibSchedulerReverseGeocode.js
Normal file
93
library/LibSchedulerReverseGeocode.js
Normal file
@ -0,0 +1,93 @@
|
||||
const path = require('path');
|
||||
const RedisConn = require('./LibRedisConn');
|
||||
const QueueMQ = require('bullmq').Queue;
|
||||
const QueueSchedulerMQ = require('bullmq').QueueScheduler;
|
||||
const WorkerMQ = require('bullmq').Worker;
|
||||
// const JobMQ = require('bullmq').Job;
|
||||
const LibWinston = require('./LibWinston');
|
||||
const EventEmitter = require('events');
|
||||
|
||||
const schedulerName = process.env.REDIS_SCHEDULER_REVERSE_GEO;
|
||||
const theQueue = new QueueMQ(schedulerName, { connection: RedisConn });
|
||||
const theQueueScheduler = new QueueSchedulerMQ(schedulerName, { connection: RedisConn });
|
||||
|
||||
const Logger = LibWinston.initialize(schedulerName);
|
||||
|
||||
class MyEmitter extends EventEmitter { };
|
||||
const myEmitter = new MyEmitter();
|
||||
|
||||
/**
|
||||
* bisa multiple queue tanpa new Queue(name) lagi, tinggal dibedain aja nama processornya ketika queue.add(nameProcessor)
|
||||
* worker hanya memproses nama sesuai dengan nameProcessor bukan nama ketika new Queue(name)
|
||||
* concurrency di worker adalah maksimal yang bisa diproses dalam satu waktu bersamaan
|
||||
* attempts di queue adalah maksimal percobaan ketika gagal
|
||||
*/
|
||||
|
||||
const LibSchedulerReverseGeocode = {
|
||||
name: schedulerName,
|
||||
queue: theQueue,
|
||||
runQueueScheduler: function (data) {
|
||||
// * * * * * // every minute
|
||||
// */3 * * * * // every 3 minute
|
||||
// 0 */1 * * * // every hour
|
||||
this.queue.add(schedulerName, data, {
|
||||
removeOnComplete: false, // false just for debugging
|
||||
repeat: {
|
||||
cron: process.env.SCHEDULE_REVERSE_GEO_TIME,
|
||||
},
|
||||
attempts: 0,
|
||||
});
|
||||
},
|
||||
addQueue: function (data) {
|
||||
this.queue.add(schedulerName, data, {
|
||||
removeOnComplete: false, // false just for debugging
|
||||
});
|
||||
},
|
||||
pauseQueue: function () {
|
||||
this.queue.pause();
|
||||
},
|
||||
resumeQueue: function () {
|
||||
this.queue.resume();
|
||||
},
|
||||
getJobs: async function () {
|
||||
return await this.queue.getJobs();
|
||||
},
|
||||
getRepeatableJobs: async function () {
|
||||
return await this.queue.getRepeatableJobs();
|
||||
},
|
||||
setWorker: function () {
|
||||
try {
|
||||
const processorFile = path.join(__dirname, '../workers/ReverseGeocodeWorker.js');
|
||||
const schedulerWorker = new WorkerMQ(schedulerName, processorFile, {
|
||||
connection: RedisConn,
|
||||
concurrency: 1,
|
||||
})
|
||||
schedulerWorker.on('completed', async (job, returnValue) => {
|
||||
Logger.log('info', `${schedulerName} completed: ${JSON.stringify(returnValue)}`);
|
||||
})
|
||||
schedulerWorker.on('failed', async (job, failedReason) => {
|
||||
Logger.log('error', `${schedulerName} failed: ${JSON.stringify(failedReason)}`);
|
||||
});
|
||||
schedulerWorker.on('error', async (errMsg) => {
|
||||
Logger.log('error', `${schedulerName} error: ${JSON.stringify(errMsg)}`);
|
||||
});
|
||||
myEmitter.on(schedulerName + 'ShutDown', () => {
|
||||
// gracefull shutdown
|
||||
schedulerWorker.close();
|
||||
});
|
||||
} catch (e) {
|
||||
Logger.log('error', `${schedulerName} worker_error: ${JSON.stringify(e, Object.getOwnPropertyNames(e))}`);
|
||||
}
|
||||
},
|
||||
drainQueue: function () {
|
||||
this.queue.drain();
|
||||
},
|
||||
obliterateQueue: function () {
|
||||
this.queue.obliterate();
|
||||
},
|
||||
workerShutDown: function () {
|
||||
myEmitter.emit(schedulerName + 'ShutDown');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = LibSchedulerReverseGeocode
|
||||
53
library/LibWinston.js
Normal file
53
library/LibWinston.js
Normal file
@ -0,0 +1,53 @@
|
||||
const path = require('path');
|
||||
const winston = require('winston');
|
||||
|
||||
class LibWinston {
|
||||
|
||||
static initialize(filename) {
|
||||
winston.addColors(this.getColours());
|
||||
return winston.createLogger({
|
||||
level: this.getAccessLevel(),
|
||||
levels: this.getLevels(),
|
||||
exitOnError: false,
|
||||
format: winston.format.combine(
|
||||
// winston.format.colorize(),
|
||||
winston.format.timestamp(),
|
||||
winston.format.json(),
|
||||
),
|
||||
transports: [
|
||||
new winston.transports.File({ filename: path.join(__dirname, '../logs', `${filename}.log`), level: this.getAccessLevel(), maxsize: '10000000', maxFiles: '10' }),
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
static getLevels() {
|
||||
return {
|
||||
error: 0,
|
||||
warn: 1,
|
||||
info: 2,
|
||||
http: 3,
|
||||
debug: 4,
|
||||
};
|
||||
}
|
||||
|
||||
static getColours() {
|
||||
return {
|
||||
error: 'red',
|
||||
warn: 'yellow',
|
||||
info: 'green',
|
||||
http: 'magenta',
|
||||
debug: 'white',
|
||||
};
|
||||
}
|
||||
|
||||
static getAccessLevel() {
|
||||
if (process.env.NODE_ENV == 'production') {
|
||||
return 'info';
|
||||
}
|
||||
// development
|
||||
return 'debug';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = LibWinston;
|
||||
Reference in New Issue
Block a user