Add a mechanism to self-upgrade the workbook.
It's not completely seamless, but it should work adequately well. The workbook (aka client) inserts the workbook version into the http request body. The server code compares that version number against its minimum supported client version. If the client is too old, an error message is sent back to the client. When the client receives the "Obsolete" error message, it launches the https://<server>:<port>/template URL in the default browser, which enables the user to save the downloaded new workbook file.
This commit is contained in:
parent
2454393a1d
commit
22c2375f44
Binary file not shown.
@ -16,6 +16,12 @@ Function makeHttpRequest(method As String, route As String, doc As String, ByRef
|
||||
Exit Function
|
||||
End If
|
||||
|
||||
' Inject the user's name and the current version of this file into the request body.
|
||||
Set json = JsonConverter.ParseJson(doc)
|
||||
json("version") = shConfig.Range("version").Value
|
||||
json("username") = Application.UserName
|
||||
doc = JsonConverter.ConvertToJson(json)
|
||||
|
||||
Dim server As String
|
||||
server = shConfig.Range("server").Value
|
||||
|
||||
@ -32,6 +38,17 @@ Function makeHttpRequest(method As String, route As String, doc As String, ByRef
|
||||
Debug.Print Timer - t; "sec): "; Left(wr, 200)
|
||||
End With
|
||||
|
||||
' This is a poor man's self-upgrade mechanism for this application.
|
||||
If Mid(wr, 1, 24) = "Obsolete client workbook" Then
|
||||
If MsgBox("Your workbook is an older version and needs to be upgraded. Download now? This workbook will be closed so your download can overwrite it.", vbYesNo + vbQuestion) = vbYes Then
|
||||
ActiveWorkbook.FollowHyperlink server & "/template"
|
||||
ActiveWorkbook.Close False
|
||||
Else
|
||||
errorMsg = "You won't be able to use this workbook until you upgrade it. Please download the new one the next time you're prompted."
|
||||
Exit Function
|
||||
End If
|
||||
End If
|
||||
|
||||
If Mid(wr, 1, 1) <> "{" Or _
|
||||
Mid(wr, 2, 5) = "error" Or _
|
||||
Mid(wr, 1, 6) = "<body>" Or _
|
||||
@ -40,7 +57,7 @@ Function makeHttpRequest(method As String, route As String, doc As String, ByRef
|
||||
errorMsg = "Unexpected Result from Server: " & wr
|
||||
Exit Function
|
||||
End If
|
||||
|
||||
|
||||
If Mid(wr, 1, 6) = "null" Then
|
||||
errorMsg = "API route not implemented."
|
||||
Exit Function
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
119
index.js
119
index.js
@ -1,5 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const minSupportedVersion = '1.9.2';
|
||||
|
||||
require('dotenv').config();
|
||||
const express = require('express');
|
||||
var https = require('https');
|
||||
@ -51,7 +53,16 @@ function error() {
|
||||
return `\x1b[91mERROR\x1b[0m|`
|
||||
}
|
||||
|
||||
function process_route(route, description, path, res, callback) {
|
||||
function supported_version(v) {
|
||||
if (v === undefined) return false;
|
||||
|
||||
const formatVersion = function(v) {
|
||||
return v.split('.').map((x) => ('00000'+x).slice(-5)).join('.');
|
||||
}
|
||||
return formatVersion(v) >= formatVersion(minSupportedVersion);
|
||||
}
|
||||
|
||||
function process_route(route, description, path, req, res, callback) {
|
||||
console.log(`\x1b[96m${"▼".repeat(120)}\x1b[0m`)
|
||||
console.log(`${timestamp()} ${route} (${description})`)
|
||||
if (!path) {
|
||||
@ -59,6 +70,12 @@ function process_route(route, description, path, res, callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!supported_version(req.body.version)) {
|
||||
console.log(`${timestamp()} ${error()}${req.body.username ?? 'Anonymous'} is using a too-old version of the client - ${req.body.version}`);
|
||||
res.send(`Obsolete client workbook, version ${req.body.version}`);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`${timestamp()} SQL file: ${path}`)
|
||||
fs.readFile(path, 'utf8', function(err, data) {
|
||||
if (!err) {
|
||||
@ -98,13 +115,19 @@ function build_where(req, res) {
|
||||
return where;
|
||||
}
|
||||
|
||||
server.get('/', function(_, res) {
|
||||
process_route('GET /', 'Test if server is up.', undefined, res,
|
||||
server.get('/', function(req, res) {
|
||||
process_route('GET /', 'Test if server is up.', undefined, req, res,
|
||||
function(_) {
|
||||
res.send('node.js express is up and running');
|
||||
});
|
||||
})
|
||||
|
||||
server.get('/template', function(_, res) {
|
||||
console.log('Downloading a template for someone.');
|
||||
const file = `${__dirname}/Master Template.xlsm`;
|
||||
res.download(file);
|
||||
})
|
||||
|
||||
server.get('/login', (_, res) => res.sendFile(process.env.wd + 'msauth.html'))
|
||||
|
||||
server.get('/logs', (_, res) => res.sendFile(process.env.wd + 'changes.log'))
|
||||
@ -114,8 +137,8 @@ server.get('/pgbadger', (_, res) => res.sendFile(process.env.wd + 'logs.html'))
|
||||
server.get('/totals', (_, res) => res.sendFile(process.env.wd + 'totals.log'))
|
||||
|
||||
server.get('/get_pool', bodyParser.json(), function(req, res) {
|
||||
process_route('GET /get_pool', 'Get all data for one DSM.', './route_sql/get_pool.sql', res,
|
||||
function(arg) {
|
||||
process_route('GET /get_pool', 'Get all data for one DSM.', './route_sql/get_pool.sql', req, res,
|
||||
function(sql) {
|
||||
if (req.body.quota_rep) {
|
||||
// ensure backward compatibility
|
||||
console.log(`${timestamp()} Converting old format… ${JSON.stringify(req.body)}`);
|
||||
@ -125,41 +148,41 @@ server.get('/get_pool', bodyParser.json(), function(req, res) {
|
||||
var where = build_where(req, res);
|
||||
if (!where) return;
|
||||
|
||||
var sql = arg.replace(new RegExp("where_clause", 'g'), where);
|
||||
sql = sql.replace(new RegExp("where_clause", 'g'), where);
|
||||
Postgres.FirstRow(sql, res)
|
||||
});
|
||||
})
|
||||
|
||||
server.get('/scenario_package', bodyParser.json(), function(req, res) {
|
||||
process_route('GET /scenario_package', 'Get all data for a given scenario.', './route_sql/scenario_package.sql', res,
|
||||
function(arg) {
|
||||
process_route('GET /scenario_package', 'Get all data for a given scenario.', './route_sql/scenario_package.sql', req, res,
|
||||
function(sql) {
|
||||
var where = build_where(req, res);
|
||||
if (!where) return;
|
||||
|
||||
var sql = arg.replace(new RegExp("where_clause", 'g'), where)
|
||||
sql = sql.replace(new RegExp("where_clause", 'g'), where)
|
||||
Postgres.FirstRow(sql, res)
|
||||
});
|
||||
})
|
||||
|
||||
server.get('/swap_fit', bodyParser.json(), function(req, res) {
|
||||
process_route('GET /swap_fit', 'Obsolete.', './route_sql/swap_fit.sql', res,
|
||||
function(arg) {
|
||||
process_route('GET /swap_fit', 'Obsolete.', './route_sql/swap_fit.sql', req, res,
|
||||
function(sql) {
|
||||
var where = build_where(req, res);
|
||||
if (!where) return;
|
||||
|
||||
var sql = arg.replace(new RegExp("where_clause", 'g'), where);
|
||||
sql = sql.replace(new RegExp("where_clause", 'g'), where);
|
||||
sql = sql.replace(new RegExp("replace_new_mold", 'g'), req.body.new_mold);
|
||||
Postgres.FirstRow(sql, res)
|
||||
});
|
||||
})
|
||||
|
||||
server.post('/swap', bodyParser.json(), function(req, res) {
|
||||
process_route('POST /swap', 'Obsolete.', './route_sql/swap_post.sql', res,
|
||||
function(arg) {
|
||||
process_route('POST /swap', 'Obsolete.', './route_sql/swap_post.sql', req, res,
|
||||
function(sql) {
|
||||
var where = build_where(req, res);
|
||||
if (!where) return;
|
||||
|
||||
var sql = arg.replace(new RegExp("where_clause", 'g'), where);
|
||||
sql = sql.replace(new RegExp("where_clause", 'g'), where);
|
||||
sql = sql.replace(new RegExp("swap_doc", 'g'), JSON.stringify(req.body.swap));
|
||||
sql = sql.replace(new RegExp("replace_version", 'g'), req.body.scenario.version);
|
||||
sql = sql.replace(new RegExp("replace_source", 'g'), req.body.source);
|
||||
@ -169,12 +192,12 @@ server.post('/swap', bodyParser.json(), function(req, res) {
|
||||
})
|
||||
|
||||
server.post('/cust_swap', bodyParser.json(), function(req, res) {
|
||||
process_route('POST /cust_swap', 'Obsolete.', './route_sql/swap_cust.sql', res,
|
||||
function(arg) {
|
||||
process_route('POST /cust_swap', 'Obsolete.', './route_sql/swap_cust.sql', req, res,
|
||||
function(sql) {
|
||||
var where = build_where(req, res);
|
||||
if (!where) return;
|
||||
|
||||
var sql = arg.replace(new RegExp("where_clause", 'g'), where);
|
||||
sql = sql.replace(new RegExp("where_clause", 'g'), where);
|
||||
sql = sql.replace(new RegExp("swap_doc", 'g'), JSON.stringify(req.body.swap));
|
||||
sql = sql.replace(new RegExp("replace_version", 'g'), req.body.scenario.version);
|
||||
sql = sql.replace(new RegExp("replace_source", 'g'), req.body.source);
|
||||
@ -184,19 +207,19 @@ server.post('/cust_swap', bodyParser.json(), function(req, res) {
|
||||
})
|
||||
|
||||
server.get('/list_changes', bodyParser.json(), function(req, res) {
|
||||
process_route('GET /list_changes', 'Get a list of adjustments made to DSM\'s pool.', './route_sql/list_changes.sql', res,
|
||||
function(arg) {
|
||||
process_route('GET /list_changes', 'Get a list of adjustments made to DSM\'s pool.', './route_sql/list_changes.sql', req, res,
|
||||
function(sql) {
|
||||
var where = build_where(req, res);
|
||||
if (!where) return;
|
||||
var sql = arg.replace(new RegExp("where_clause", 'g'), where);
|
||||
sql = sql.replace(new RegExp("where_clause", 'g'), where);
|
||||
Postgres.FirstRow(sql, res)
|
||||
});
|
||||
})
|
||||
|
||||
server.get('/undo_change', bodyParser.json(), function(req, res) {
|
||||
process_route('GET /undo_change', 'Remove an adjustment from the DSM\'s pool.', './route_sql/undo.sql', res,
|
||||
function(arg) {
|
||||
var sql = arg.replace(new RegExp("replace_id", 'g'), JSON.stringify(req.body.logid))
|
||||
process_route('GET /undo_change', 'Remove an adjustment from the DSM\'s pool.', './route_sql/undo.sql', req, res,
|
||||
function(sql) {
|
||||
sql = sql.replace(new RegExp("replace_id", 'g'), JSON.stringify(req.body.logid))
|
||||
Postgres.FirstRow(sql, res)
|
||||
});
|
||||
})
|
||||
@ -204,13 +227,13 @@ server.get('/undo_change', bodyParser.json(), function(req, res) {
|
||||
//deprecating this route, just use _vp for volume and prive
|
||||
/*
|
||||
server.post('/addmonth_v', bodyParser.json(), function(req, res) {
|
||||
process_route('POST /add_month_v', 'Obsolete.', './route_sql/addmonth_vd.sql', res,
|
||||
function(arg) {
|
||||
process_route('POST /add_month_v', 'Obsolete.', './route_sql/addmonth_vd.sql', req, res,
|
||||
function(sql) {
|
||||
var where = build_where(req, res);
|
||||
if (!where) return;
|
||||
|
||||
req.body.stamp = new Date().toISOString()
|
||||
var sql = arg.replace(new RegExp("scenario = target_scenario", 'g'), where);
|
||||
sql = sql.replace(new RegExp("scenario = target_scenario", 'g'), where);
|
||||
sql = sql.replace(new RegExp("target_increment", 'g'), req.body.qty);
|
||||
sql = sql.replace(new RegExp("target_month", 'g'), req.body.month);
|
||||
sql = sql.replace(new RegExp("replace_version", 'g'), req.body.scenario.version);
|
||||
@ -222,13 +245,13 @@ server.post('/addmonth_v', bodyParser.json(), function(req, res) {
|
||||
*/
|
||||
|
||||
server.post('/addmonth_vp', bodyParser.json(), function(req, res) {
|
||||
process_route('POST /add_month_vp', 'Add volume and pricing for a new month in the forecast.', './route_sql/addmonth_vupd.sql', res,
|
||||
function(arg) {
|
||||
process_route('POST /add_month_vp', 'Add volume and pricing for a new month in the forecast.', './route_sql/addmonth_vupd.sql', req, res,
|
||||
function(sql) {
|
||||
var where = build_where(req, res);
|
||||
if (!where) return;
|
||||
|
||||
req.body.stamp = new Date().toISOString()
|
||||
var sql = arg.replace(new RegExp("where_clause", 'g'), where);
|
||||
sql = sql.replace(new RegExp("where_clause", 'g'), where);
|
||||
sql = sql.replace(new RegExp("target_volume", 'g'), req.body.qty);
|
||||
sql = sql.replace(new RegExp("target_price", 'g'), req.body.amount);
|
||||
sql = sql.replace(new RegExp("target_month", 'g'), req.body.month);
|
||||
@ -240,13 +263,13 @@ server.post('/addmonth_vp', bodyParser.json(), function(req, res) {
|
||||
})
|
||||
|
||||
server.post('/scale_v', bodyParser.json(), function(req, res) {
|
||||
process_route('POST /scale_v', 'Scale the volume for the given scenario.', './route_sql/scale_vd.sql', res,
|
||||
function(arg) {
|
||||
process_route('POST /scale_v', 'Scale the volume for the given scenario.', './route_sql/scale_vd.sql', req, res,
|
||||
function(sql) {
|
||||
var where = build_where(req, res);
|
||||
if (!where) return;
|
||||
|
||||
req.body.stamp = new Date().toISOString()
|
||||
var sql = arg.replace(new RegExp("where_clause", 'g'), where);
|
||||
sql = sql.replace(new RegExp("where_clause", 'g'), where);
|
||||
sql = sql.replace(new RegExp("incr_qty", 'g'), req.body.qty);
|
||||
sql = sql.replace(new RegExp("replace_version", 'g'), req.body.scenario.version);
|
||||
sql = sql.replace(new RegExp("replace_source", 'g'), req.body.source);
|
||||
@ -256,13 +279,13 @@ server.post('/scale_v', bodyParser.json(), function(req, res) {
|
||||
})
|
||||
|
||||
server.post('/scale_p', bodyParser.json(), function(req, res) {
|
||||
process_route('POST /scale_p', 'Scale price for the given scenario.', './route_sql/scale_pd.sql', res,
|
||||
function(arg) {
|
||||
process_route('POST /scale_p', 'Scale price for the given scenario.', './route_sql/scale_pd.sql', req, res,
|
||||
function(sql) {
|
||||
var where = build_where(req, res);
|
||||
if (!where) return;
|
||||
|
||||
req.body.stamp = new Date().toISOString()
|
||||
var sql = arg.replace(new RegExp("where_clause", 'g'), where);
|
||||
sql = sql.replace(new RegExp("where_clause", 'g'), where);
|
||||
sql = sql.replace(new RegExp("target_increment", 'g'), req.body.amount);
|
||||
sql = sql.replace(new RegExp("replace_version", 'g'), req.body.scenario.version);
|
||||
sql = sql.replace(new RegExp("replace_source", 'g'), req.body.source);
|
||||
@ -272,13 +295,13 @@ server.post('/scale_p', bodyParser.json(), function(req, res) {
|
||||
})
|
||||
|
||||
server.post('/scale_vp', bodyParser.json(), function(req, res) {
|
||||
process_route('POST /scale_vp', 'Scale volume and price for the given scenario.', './route_sql/scale_vupd.sql', res,
|
||||
function(arg) {
|
||||
process_route('POST /scale_vp', 'Scale volume and price for the given scenario.', './route_sql/scale_vupd.sql', req, res,
|
||||
function(sql) {
|
||||
var where = build_where(req, res);
|
||||
if (!where) return;
|
||||
|
||||
req.body.stamp = new Date().toISOString()
|
||||
var sql = arg.replace(new RegExp("where_clause", 'g'), where);
|
||||
sql = sql.replace(new RegExp("where_clause", 'g'), where);
|
||||
sql = sql.replace(new RegExp("target_vol", 'g'), req.body.qty);
|
||||
sql = sql.replace(new RegExp("target_prc", 'g'), req.body.amount);
|
||||
sql = sql.replace(new RegExp("replace_version", 'g'), req.body.scenario.version);
|
||||
@ -289,13 +312,13 @@ server.post('/scale_vp', bodyParser.json(), function(req, res) {
|
||||
})
|
||||
|
||||
server.post('/scale_vp_sales', bodyParser.json(), function(req, res) {
|
||||
process_route('POST /scale_vp_sales', 'Obsolete.', './route_sql/scale_vupd.sql', res,
|
||||
function(arg) {
|
||||
process_route('POST /scale_vp_sales', 'Obsolete.', './route_sql/scale_vupd.sql', req, res,
|
||||
function(sql) {
|
||||
var where = build_where(req, res);
|
||||
if (!where) return;
|
||||
|
||||
req.body.stamp = new Date().toISOString()
|
||||
var sql = arg.replace(new RegExp("where_clause", 'g'), where);
|
||||
sql = sql.replace(new RegExp("where_clause", 'g'), where);
|
||||
sql = sql.replace(new RegExp("target_vol", 'g'), req.body.qty);
|
||||
sql = sql.replace(new RegExp("target_prc", 'g'), req.body.amount);
|
||||
sql = sql.replace(new RegExp("replace_version", 'g'), req.body.scenario.version);
|
||||
@ -306,13 +329,13 @@ server.post('/scale_vp_sales', bodyParser.json(), function(req, res) {
|
||||
})
|
||||
|
||||
server.post('/new_part', bodyParser.json(), function(req, res) {
|
||||
process_route('POST /new_part', 'Obsolete.', './route_sql/new_part.sql', res,
|
||||
function(arg) {
|
||||
process_route('POST /new_part', 'Obsolete.', './route_sql/new_part.sql', req, res,
|
||||
function(sql) {
|
||||
var where = build_where(req, res);
|
||||
if (!where) return;
|
||||
|
||||
req.body.stamp = new Date().toISOString()
|
||||
var sql = arg.replace(new RegExp("where_clause", 'g'), where);
|
||||
sql = sql.replace(new RegExp("where_clause", 'g'), where);
|
||||
sql = sql.replace(new RegExp("target_vol", 'g'), req.body.qty);
|
||||
sql = sql.replace(new RegExp("target_prc", 'g'), req.body.amount);
|
||||
sql = sql.replace(new RegExp("replace_request", 'g'), JSON.stringify(req.body));
|
||||
@ -324,15 +347,15 @@ server.post('/new_part', bodyParser.json(), function(req, res) {
|
||||
})
|
||||
|
||||
server.post('/new_basket', bodyParser.json(), function(req, res) {
|
||||
process_route('POST /new_basket', 'Add new part and/or customer.', './route_sql/new_basket.sql', res,
|
||||
function(arg) {
|
||||
process_route('POST /new_basket', 'Add new part and/or customer.', './route_sql/new_basket.sql', req, res,
|
||||
function(sql) {
|
||||
req.body.scenario.iter.push("adj volume"); //intercept the request body and force in a "adj volume" at position 1, only a "copy" iteration is being used
|
||||
|
||||
var where = build_where(req, res);
|
||||
if (!where) return;
|
||||
|
||||
req.body.stamp = new Date().toISOString()
|
||||
var sql = arg.replace(new RegExp("where_clause", 'g'), where);
|
||||
sql = sql.replace(new RegExp("where_clause", 'g'), where);
|
||||
sql = sql.replace(new RegExp("target_vol", 'g'), req.body.qty);
|
||||
sql = sql.replace(new RegExp("target_prc", 'g'), req.body.amount);
|
||||
sql = sql.replace(new RegExp("replace_request", 'g'), JSON.stringify(req.body));
|
||||
|
||||
Loading…
Reference in New Issue
Block a user