initialize
This commit is contained in:
commit
99b6232436
24 changed files with 3872 additions and 0 deletions
9
.dockerignore
Normal file
9
.dockerignore
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
.git
|
||||||
|
node_modules
|
||||||
|
.DS_Store
|
||||||
|
.gitignore
|
||||||
|
*.log
|
||||||
|
*.md
|
||||||
|
logs
|
||||||
|
priceslot.*
|
||||||
|
debug.*
|
||||||
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
.env
|
||||||
|
**/.DS_Store
|
||||||
|
logs
|
||||||
|
node_modules
|
||||||
|
*.zip
|
||||||
|
*.log
|
||||||
8
README.md
Normal file
8
README.md
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
change directory:
|
||||||
|
$ cd ./server
|
||||||
|
|
||||||
|
install dependencies:
|
||||||
|
$ npm install
|
||||||
|
|
||||||
|
run the app:
|
||||||
|
$ DEBUG=server:* PORT=36530 npm start
|
||||||
128
app.js
Normal file
128
app.js
Normal file
|
|
@ -0,0 +1,128 @@
|
||||||
|
var createError = require("http-errors");
|
||||||
|
var express = require("express");
|
||||||
|
var path = require("path");
|
||||||
|
var cookieParser = require("cookie-parser");
|
||||||
|
// var morgan = require("morgan");
|
||||||
|
|
||||||
|
var indexRouter = require("./routes/index");
|
||||||
|
var usersRouter = require("./routes/users");
|
||||||
|
// const { createLogger, format, transports } = require('winston');
|
||||||
|
|
||||||
|
const {
|
||||||
|
logger,
|
||||||
|
Log,
|
||||||
|
getTestSpreadSheet,
|
||||||
|
GoogleFunctions,
|
||||||
|
PluginsManager,
|
||||||
|
} = require("./lib/common");
|
||||||
|
const { SyncText } = require("./lib/sync_text");
|
||||||
|
const { CronJobs } = require("./cron-jobs");
|
||||||
|
const { google } = require("googleapis");
|
||||||
|
const { EventEmitter } = require("stream");
|
||||||
|
|
||||||
|
require("dotenv").config();
|
||||||
|
|
||||||
|
Log.debug(process.env.TEST_SHEET_ID);
|
||||||
|
|
||||||
|
var app = express();
|
||||||
|
|
||||||
|
// view engine setup
|
||||||
|
app.set("views", path.join(__dirname, "views"));
|
||||||
|
app.set("view engine", "jade");
|
||||||
|
|
||||||
|
app
|
||||||
|
// .use(require("morgan")("combined", { stream: logger.stream }))
|
||||||
|
.use(express.json())
|
||||||
|
.use(express.urlencoded({ extended: false }))
|
||||||
|
.use(cookieParser())
|
||||||
|
.use(express.static(path.join(__dirname, "public")));
|
||||||
|
|
||||||
|
app.use("/", indexRouter);
|
||||||
|
app.use("/users", usersRouter);
|
||||||
|
|
||||||
|
Log.debug(`running on port ${process.env.PORT}`);
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// CRON JOB (Trigger)
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
var cronTasks =
|
||||||
|
CronJobs.MAXIMUM_CRON_JOBS > 0 ? new Array(CronJobs.MAXIMUM_CRON_JOBS) : [];
|
||||||
|
|
||||||
|
const auth = GoogleFunctions.auth();
|
||||||
|
const sheet = GoogleFunctions.SpreadSheets(auth);
|
||||||
|
|
||||||
|
// let heartbeatTask = CronJobs.doEveryMinute(() => {
|
||||||
|
// Log.debug(`All running => ${JSON.stringify(cronTasks)}`);
|
||||||
|
// cronTasks[0].exec_times += 1;
|
||||||
|
// if(cronTasks[0].exec_times >= 1){
|
||||||
|
// Log.debug("stop hb");
|
||||||
|
// this.emit('stop');
|
||||||
|
// }
|
||||||
|
// }, 'hb');
|
||||||
|
|
||||||
|
// heartbeatTask.on('stop', () => heartbeatTask.stop());
|
||||||
|
|
||||||
|
// cronTasks[0] = {
|
||||||
|
// task: heartbeatTask,
|
||||||
|
// exec_times: 0
|
||||||
|
// };
|
||||||
|
|
||||||
|
// getTestSpreadSheet(sheet);
|
||||||
|
// const tha_sheets = GoogleFunctions.getAllSheetNamesByCountry(sheet, "tha", (sheetDetail) => {
|
||||||
|
// return sheetDetail.data.sheets.map((sheet) => {
|
||||||
|
// return {
|
||||||
|
// name: sheet.properties.title,
|
||||||
|
// id: sheet.properties.sheetId,
|
||||||
|
// };
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
|
// GoogleFunctions.getCountrySheetByName(sheet, "tha", "PriceSlot1");
|
||||||
|
// GoogleFunctions.getPriceSlotValues(sheet, "tha");
|
||||||
|
|
||||||
|
// v3 !!!!
|
||||||
|
// GoogleFunctions.syncProfilePrice(sheet, "tha", false);
|
||||||
|
//
|
||||||
|
|
||||||
|
// Test Taobin SyncText
|
||||||
|
SyncText.run(sheet, "uae", false);
|
||||||
|
|
||||||
|
const pm = new PluginsManager(cronTasks, CronJobs);
|
||||||
|
pm.load();
|
||||||
|
|
||||||
|
pm.checkScript();
|
||||||
|
// pm.runScript("heartbeat.js");
|
||||||
|
|
||||||
|
// Expect variable from plugins
|
||||||
|
let endpointMap;
|
||||||
|
// Object.keys(pm.scripts).forEach((scriptName) => {
|
||||||
|
|
||||||
|
// });
|
||||||
|
|
||||||
|
for (let scriptName of Object.keys(pm.scripts)) {
|
||||||
|
Log.debug(`running ${scriptName}`);
|
||||||
|
eval(pm.scripts[scriptName]);
|
||||||
|
pm.createEndpoint(app, express, scriptName, endpointMap);
|
||||||
|
endpointMap = undefined; // reset
|
||||||
|
}
|
||||||
|
|
||||||
|
// pm.loop()
|
||||||
|
|
||||||
|
// catch 404 and forward to error handler
|
||||||
|
app.use(function (req, res, next) {
|
||||||
|
next(createError(404));
|
||||||
|
});
|
||||||
|
|
||||||
|
// error handler
|
||||||
|
app.use(function (err, req, res, next) {
|
||||||
|
// set locals, only providing error in development
|
||||||
|
res.locals.message = err.message;
|
||||||
|
res.locals.error = req.app.get("env") === "development" ? err : {};
|
||||||
|
|
||||||
|
// render the error page
|
||||||
|
res.status(err.status || 500);
|
||||||
|
res.render("error");
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = app;
|
||||||
90
bin/www
Executable file
90
bin/www
Executable file
|
|
@ -0,0 +1,90 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module dependencies.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var app = require('../app');
|
||||||
|
var debug = require('debug')('server:server');
|
||||||
|
var http = require('http');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get port from environment and store in Express.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var port = normalizePort(process.env.PORT || '3000');
|
||||||
|
app.set('port', port);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create HTTP server.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var server = http.createServer(app);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listen on provided port, on all network interfaces.
|
||||||
|
*/
|
||||||
|
|
||||||
|
server.listen(port);
|
||||||
|
server.on('error', onError);
|
||||||
|
server.on('listening', onListening);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize a port into a number, string, or false.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function normalizePort(val) {
|
||||||
|
var port = parseInt(val, 10);
|
||||||
|
|
||||||
|
if (isNaN(port)) {
|
||||||
|
// named pipe
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (port >= 0) {
|
||||||
|
// port number
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event listener for HTTP server "error" event.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function onError(error) {
|
||||||
|
if (error.syscall !== 'listen') {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
var bind = typeof port === 'string'
|
||||||
|
? 'Pipe ' + port
|
||||||
|
: 'Port ' + port;
|
||||||
|
|
||||||
|
// handle specific listen errors with friendly messages
|
||||||
|
switch (error.code) {
|
||||||
|
case 'EACCES':
|
||||||
|
console.error(bind + ' requires elevated privileges');
|
||||||
|
process.exit(1);
|
||||||
|
break;
|
||||||
|
case 'EADDRINUSE':
|
||||||
|
console.error(bind + ' is already in use');
|
||||||
|
process.exit(1);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event listener for HTTP server "listening" event.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function onListening() {
|
||||||
|
var addr = server.address();
|
||||||
|
var bind = typeof addr === 'string'
|
||||||
|
? 'pipe ' + addr
|
||||||
|
: 'port ' + addr.port;
|
||||||
|
debug('Listening on ' + bind);
|
||||||
|
}
|
||||||
6
config.js
Normal file
6
config.js
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
require("dotenv").config();
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
PORT: 36530 || process.env.PORT,
|
||||||
|
DEBUG: true || process.env.DEBUG,
|
||||||
|
};
|
||||||
37
cron-jobs.js
Normal file
37
cron-jobs.js
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
const config = require("./config");
|
||||||
|
|
||||||
|
const cron = require("node-cron");
|
||||||
|
const { Log } = require("./lib/common");
|
||||||
|
|
||||||
|
// CRON JOB GUIDE
|
||||||
|
// # ┌────────────── วินาที (optional)
|
||||||
|
// # │ ┌──────────── นาที
|
||||||
|
// # │ │ ┌────────── ชั่วโมง
|
||||||
|
// # │ │ │ ┌──────── วันที่
|
||||||
|
// # │ │ │ │ ┌────── เดือน
|
||||||
|
// # │ │ │ │ │ ┌──── วันในสัปดาห์นั้น ๆ
|
||||||
|
// # │ │ │ │ │ │
|
||||||
|
// # │ │ │ │ │ │
|
||||||
|
// # * * * * * *
|
||||||
|
|
||||||
|
const CronScheduleList = {
|
||||||
|
everyMin: "* * * * *",
|
||||||
|
midNight: "0 0 * * *",
|
||||||
|
};
|
||||||
|
|
||||||
|
const CronJobs = {
|
||||||
|
MAXIMUM_CRON_JOBS: -1,
|
||||||
|
getAllRunning: cron.getTasks(),
|
||||||
|
doEveryMinute: (task, id) => {
|
||||||
|
if(config.DEBUG) Log.debug(`verify cron ${cron.validate(CronScheduleList.everyMin)}`);
|
||||||
|
return cron.schedule(CronScheduleList.everyMin, task, { name: id });
|
||||||
|
},
|
||||||
|
doEveryMidnight: (task, id) => {
|
||||||
|
if(config.DEBUG) Log.debug(`verify cron ${cron.validate(CronScheduleList.midNight)}`);
|
||||||
|
return cron.schedule(CronScheduleList.midNight, task, { name: id });
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
CronJobs,
|
||||||
|
};
|
||||||
1407
lib/common.js
Normal file
1407
lib/common.js
Normal file
File diff suppressed because it is too large
Load diff
215
lib/sync_text.js
Normal file
215
lib/sync_text.js
Normal file
|
|
@ -0,0 +1,215 @@
|
||||||
|
const {
|
||||||
|
logger,
|
||||||
|
Log,
|
||||||
|
getTestSpreadSheet,
|
||||||
|
GoogleFunctions,
|
||||||
|
PluginsManager,
|
||||||
|
getCountrySheetByName,
|
||||||
|
diff2DArraysCustom,
|
||||||
|
saveJsonToFile,
|
||||||
|
} = require("./common");
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// get source sheet --> tha
|
||||||
|
// get remote sheet --> input
|
||||||
|
//
|
||||||
|
|
||||||
|
class TextTable {
|
||||||
|
constructor() {
|
||||||
|
// [ Language,... ]
|
||||||
|
this.supportedLanguages = [];
|
||||||
|
// { category: [ rows ] }
|
||||||
|
this.data = {};
|
||||||
|
// update tracker
|
||||||
|
// { category: { row_number: value } }
|
||||||
|
this.update = {};
|
||||||
|
//
|
||||||
|
this.ordered_rows = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
add_category(category) {
|
||||||
|
this.data[category] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
add_row(category, row) {
|
||||||
|
this.data[category].push(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
add_ordered_row(row) {
|
||||||
|
this.ordered_rows.push(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
add_update(category, row_number, value) {
|
||||||
|
this.update[category][row_number.toString()] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
size() {
|
||||||
|
let size = 0;
|
||||||
|
for (let category in this.data) {
|
||||||
|
size += this.data[category].length;
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Differs between two TextTable objects,
|
||||||
|
* return what this table has to update
|
||||||
|
*
|
||||||
|
* @param {TextTable} another_table
|
||||||
|
*
|
||||||
|
* @returns {Map}
|
||||||
|
*/
|
||||||
|
diff_by_category(another_table) {
|
||||||
|
let column_to_focus = {};
|
||||||
|
for (let [idx, lang] of Object.entries(another_table.supportedLanguages)) {
|
||||||
|
if (this.supportedLanguages.includes(lang)) {
|
||||||
|
column_to_focus[lang] = {
|
||||||
|
target: idx,
|
||||||
|
source: this.supportedLanguages.indexOf(lang),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// need add entire category
|
||||||
|
Object.keys(another_table.data).forEach((category) => {
|
||||||
|
if (!this.data[category]) {
|
||||||
|
this.data[category] = another_table.data[category];
|
||||||
|
this.update[category] = {};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// iter through map category first
|
||||||
|
for (let category of Object.keys(this.data)) {
|
||||||
|
let another_table_data = another_table.data[category];
|
||||||
|
if (another_table_data != null) {
|
||||||
|
// Log.debug(
|
||||||
|
// `[${category}] source: ${this.data[category].length}, target: ${another_table_data.length}`,
|
||||||
|
// );
|
||||||
|
let equal_length =
|
||||||
|
this.data[category].length == another_table_data.length;
|
||||||
|
let need_update =
|
||||||
|
another_table_data.length > this.data[category].length;
|
||||||
|
|
||||||
|
if (need_update) {
|
||||||
|
Log.debug(`[${category}] need update`);
|
||||||
|
|
||||||
|
// loop check
|
||||||
|
for (let i = 0; i < another_table_data.length; i++) {
|
||||||
|
if (
|
||||||
|
this.data[category][i] &&
|
||||||
|
this.data[category][i] != another_table_data[i]
|
||||||
|
) {
|
||||||
|
// this.update[category][i] = another_table_data[i];
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// check by column
|
||||||
|
|
||||||
|
for (let [k, v] of Object.entries(column_to_focus)) {
|
||||||
|
let source_column = v.source;
|
||||||
|
let target_column = v.target;
|
||||||
|
if (
|
||||||
|
this.data[category][i][source_column] !=
|
||||||
|
another_table_data[i][target_column]
|
||||||
|
) {
|
||||||
|
Log.debug(
|
||||||
|
`[${category}] ${source_column} != ${target_column} -- ${this.data[category][i][source_column]} != ${another_table_data[i][target_column]}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (!this.data[category][i]) {
|
||||||
|
// this.update[category][i] = another_table_data[i];
|
||||||
|
// search line number
|
||||||
|
let index = another_table_data.ordered_rows.indexOf(
|
||||||
|
another_table_data[i],
|
||||||
|
);
|
||||||
|
Log.debug(
|
||||||
|
`[${category}] index: ${index} --> ${another_table_data[i]}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (this.update[category]) {
|
||||||
|
Log.debug(`[${category}] update **NEW**`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.err(
|
||||||
|
`[${category}] source: ${this.data[category].length}, target: 0`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildTable(raw_data) {
|
||||||
|
var table = new TextTable();
|
||||||
|
|
||||||
|
// build table
|
||||||
|
Log.debug("Building table...");
|
||||||
|
|
||||||
|
let current_category;
|
||||||
|
for (let [idx, row] of Object.entries(raw_data)) {
|
||||||
|
if ((row[0] == "TextID" || row[1] == "Note") && row.length >= 3) {
|
||||||
|
// expect header
|
||||||
|
table.supportedLanguages = row.slice(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (row.length) {
|
||||||
|
case 2:
|
||||||
|
// detect category
|
||||||
|
table.add_category(row[1].trim());
|
||||||
|
current_category = row[1].trim();
|
||||||
|
Log.debug(`Found Category: '${current_category}'`);
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// detect row
|
||||||
|
let text_id = row[0];
|
||||||
|
if (text_id.toString().length > 0 && current_category != null) {
|
||||||
|
// add to table
|
||||||
|
table.add_row(current_category, row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log.debug(`${idx}/${row.length}: ${row}`);
|
||||||
|
table.add_ordered_row(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log.debug(`Table built --> ${JSON.stringify(table)}`);
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
|
// process
|
||||||
|
async function syncTaobinText(sheet, country_short, is_test_mode) {
|
||||||
|
var source = await getCountrySheetByName(sheet, "tha", "Taobin-Text");
|
||||||
|
var remote = await getCountrySheetByName(sheet, country_short, "Taobin-Text");
|
||||||
|
|
||||||
|
// build table
|
||||||
|
let sourceTable = buildTable(source);
|
||||||
|
let remoteTable = buildTable(remote);
|
||||||
|
Log.debug(`source: ${sourceTable.size()}`);
|
||||||
|
Log.debug(`remote: ${remoteTable.size()}`);
|
||||||
|
|
||||||
|
// remoteTable.diff_by_category(sourceTable);
|
||||||
|
|
||||||
|
if (sourceTable.size() != remoteTable.size()) {
|
||||||
|
// detect missing rows
|
||||||
|
} else {
|
||||||
|
// detect similarity
|
||||||
|
}
|
||||||
|
|
||||||
|
let diffs = diff2DArraysCustom(remote, source);
|
||||||
|
|
||||||
|
saveJsonToFile(`diff_tha_${country_short}.json`, diffs);
|
||||||
|
|
||||||
|
// Log.debug(`diffs: ${JSON.stringify(diffs)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const SyncText = {
|
||||||
|
version: "1.0.0_250725",
|
||||||
|
name: "SyncText",
|
||||||
|
run: syncTaobinText,
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
SyncText,
|
||||||
|
};
|
||||||
1794
package-lock.json
generated
Normal file
1794
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
21
package.json
Normal file
21
package.json
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"name": "server",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"start": "node ./bin/www"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"cookie-parser": "~1.4.4",
|
||||||
|
"debug": "~2.6.9",
|
||||||
|
"dotenv": "^16.4.7",
|
||||||
|
"express": "~4.16.1",
|
||||||
|
"googleapis": "^148.0.0",
|
||||||
|
"http-errors": "~1.6.3",
|
||||||
|
"jade": "~1.11.0",
|
||||||
|
"morgan": "~1.9.1",
|
||||||
|
"node-cron": "^3.0.3",
|
||||||
|
"winston": "^3.17.0",
|
||||||
|
"winston-daily-rotate-file": "^5.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
31
plugins/heartbeat.js
Normal file
31
plugins/heartbeat.js
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
// continue;
|
||||||
|
|
||||||
|
const heartbeatApiInfo = {
|
||||||
|
version: 1,
|
||||||
|
changelogs: [
|
||||||
|
"17/4/25, create endpoint map @Pakin"
|
||||||
|
],
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
let heartbeatTask = CronJobs.doEveryMinute(() => {
|
||||||
|
Log.debug("[hb] test heartbeat");
|
||||||
|
Log.debug(`[hb] current running tasks: ${JSON.stringify(CronJobs.getAllRunning.size)}`);
|
||||||
|
heartbeatTask.stop();
|
||||||
|
}, 'heartbeat');
|
||||||
|
|
||||||
|
heartbeatTask.on('stop-heartbeat', () => heartbeatTask.stop());
|
||||||
|
|
||||||
|
cronTasks[0] = heartbeatTask;
|
||||||
|
|
||||||
|
endpointMap = {
|
||||||
|
'': function(req, res, next){
|
||||||
|
|
||||||
|
res.locals.title = "heartbeat";
|
||||||
|
res.locals.version = heartbeatApiInfo.version;
|
||||||
|
res.locals.task_num = CronJobs.getAllRunning.size;
|
||||||
|
|
||||||
|
res.render("heartbeat");
|
||||||
|
},
|
||||||
|
};
|
||||||
9
plugins/syncProfilePriceSlot8AOT.js
Normal file
9
plugins/syncProfilePriceSlot8AOT.js
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
|
||||||
|
|
||||||
|
let syncProfilePriceSlot8Task = CronJobs.doEveryMidnight(() => {
|
||||||
|
|
||||||
|
}, 'sync-price-slot-aot');
|
||||||
|
|
||||||
|
syncProfilePriceSlot8Task.on('stop-sync-price-slot-aot', () => syncProfilePriceSlot8Task.stop());
|
||||||
|
|
||||||
|
cronTasks.push(syncProfilePriceSlot8Task);
|
||||||
37
plugins/syncProfilePriceSlotSheet.js
Normal file
37
plugins/syncProfilePriceSlotSheet.js
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
const syncThaiProfilePriceSlotApiInfo = {
|
||||||
|
version: 1,
|
||||||
|
semver: "1.0.1",
|
||||||
|
changelogs: [
|
||||||
|
"17/4/25, create endpoint map @Pakin",
|
||||||
|
"29/5/25, fix price slot sync incorrect @Pakin",
|
||||||
|
],
|
||||||
|
desc: "Sync price between sheets expected sheet with prefix `PriceSlot` for country `tha` (Thai), | does not allow modification to function while running except reloaded, Endpoints:\n/run/now,/run/test\n",
|
||||||
|
};
|
||||||
|
|
||||||
|
let syncProfilePriceSlotsTask = CronJobs.doEveryMidnight(() => {
|
||||||
|
GoogleFunctions.syncProfilePrice(sheet, "tha", false);
|
||||||
|
}, "sync-gg-price_slot_tha");
|
||||||
|
|
||||||
|
syncProfilePriceSlotsTask.on("stop-sync-gg-price_slot_tha_test", () =>
|
||||||
|
syncProfilePriceSlotsTask.stop(),
|
||||||
|
);
|
||||||
|
|
||||||
|
cronTasks.push(syncProfilePriceSlotsTask);
|
||||||
|
|
||||||
|
endpointMap = {
|
||||||
|
"": function (req, res, next) {
|
||||||
|
res.locals.title = "Sync Thai Price Slots";
|
||||||
|
res.locals.version = syncThaiProfilePriceSlotApiInfo.version;
|
||||||
|
res.locals.desc = syncThaiProfilePriceSlotApiInfo.desc;
|
||||||
|
|
||||||
|
res.render("apiInfo");
|
||||||
|
},
|
||||||
|
"run/now": function (req, res, next) {
|
||||||
|
GoogleFunctions.syncProfilePrice(sheet, "tha", false);
|
||||||
|
res.send("Force run `Sync Thai Price Slots`");
|
||||||
|
},
|
||||||
|
"run/test": function (req, res, next) {
|
||||||
|
GoogleFunctions.syncProfilePrice(sheet, "tha", true);
|
||||||
|
res.send("Force run `Sync Thai Price Slots` test mode");
|
||||||
|
},
|
||||||
|
};
|
||||||
8
public/stylesheets/style.css
Normal file
8
public/stylesheets/style.css
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
body {
|
||||||
|
padding: 50px;
|
||||||
|
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #00B7FF;
|
||||||
|
}
|
||||||
9
routes/index.js
Normal file
9
routes/index.js
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
var express = require('express');
|
||||||
|
var router = express.Router();
|
||||||
|
|
||||||
|
/* GET home page. */
|
||||||
|
router.get('/', function(req, res, next) {
|
||||||
|
res.render('index', { title: 'Express' });
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
14
routes/pm.js
Normal file
14
routes/pm.js
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
var express = require('express');
|
||||||
|
var router = express.Router();
|
||||||
|
|
||||||
|
router.get('/', function(req, res, next){
|
||||||
|
res.send('Plugin Manager, mounted on `plugins` folder, try add new `.js` file to this folder and run `/reload`')
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/reload', function(req, res, next){
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/update', function(req, res, next){
|
||||||
|
|
||||||
|
});
|
||||||
9
routes/users.js
Normal file
9
routes/users.js
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
var express = require('express');
|
||||||
|
var router = express.Router();
|
||||||
|
|
||||||
|
/* GET users listing. */
|
||||||
|
router.get('/', function(req, res, next) {
|
||||||
|
res.send('respond with a resource');
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
2
run.test.sh
Executable file
2
run.test.sh
Executable file
|
|
@ -0,0 +1,2 @@
|
||||||
|
#!/bin/bash
|
||||||
|
DEBUG=server:* PORT=36530 npm start
|
||||||
7
views/apiInfo.jade
Normal file
7
views/apiInfo.jade
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
extends layout
|
||||||
|
|
||||||
|
block content
|
||||||
|
h1 #{title}
|
||||||
|
h2 version #{version}
|
||||||
|
p Description:
|
||||||
|
p #{desc}
|
||||||
6
views/error.jade
Normal file
6
views/error.jade
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
extends layout
|
||||||
|
|
||||||
|
block content
|
||||||
|
h1= message
|
||||||
|
h2= error.status
|
||||||
|
pre #{error.stack}
|
||||||
7
views/heartbeat.jade
Normal file
7
views/heartbeat.jade
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
extends layout
|
||||||
|
|
||||||
|
block content
|
||||||
|
h1 #{title}
|
||||||
|
h2 version #{version}
|
||||||
|
p Description:
|
||||||
|
p heartbeat runs every minutes, current running: #{task_num}
|
||||||
5
views/index.jade
Normal file
5
views/index.jade
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
extends layout
|
||||||
|
|
||||||
|
block content
|
||||||
|
h1= title
|
||||||
|
p Welcome to #{title}
|
||||||
7
views/layout.jade
Normal file
7
views/layout.jade
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
doctype html
|
||||||
|
html
|
||||||
|
head
|
||||||
|
title= title
|
||||||
|
link(rel='stylesheet', href='/stylesheets/style.css')
|
||||||
|
body
|
||||||
|
block content
|
||||||
Loading…
Add table
Add a link
Reference in a new issue