Initial commit

This commit is contained in:
meusinfirmary
2025-04-22 14:31:37 +07:00
commit b7e852126c
115 changed files with 23188 additions and 0 deletions

38
library/LibBullAdapter.js Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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;

View 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

File diff suppressed because it is too large Load Diff

206
library/LibMysqlHelper.js Normal file
View 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
View 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;

View 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
View 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;

View 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

View 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

View 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

View 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

View 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

View 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
View 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;