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