I've only ever done it in Amazon AWS using a Lambda script (as that provides a public SSL certificate, and you pay for runtime, which is very cheap).
This is some node.js code that I wrote. I hope I have removed enough to prevent getting myself in trouble while leaving it as helpful.
const debug=true;
const secret = process.env.secret;
const validator = process.env.validator;
const sql = "INSERT INTO dim_meraki_wifi (apiVer, type, clientMac, seenTime, ssid, rssi, lat, lng, unc, x, y);
// Create a pool of database connections
const { Client } = require('pg')
exports.handler = (event, context, callback) => {
//context.callbackWaitsForEmptyEventLoop = false;
// Return the validator
if(event.httpMethod=='GET') {
if(debug) console.log('Processing GET request');
callback(null, {
statusCode: '200',
body: validator,
headers: {'Content-Type': 'text/plain'}
});
return;
}
// We only process POST methods after processing the GET
if(event.httpMethod !='POST') {
if(debug) console.log('Processing POST request');
callback(null, {
statusCode: '403',
body: "request not recognized",
headers: {'Content-Type': 'text/plain'}
});
return;
}
const apiData = JSON.parse(event.body);
if(debug) {
console.log('apiData:');
console.log(apiData);
console.log(apiData.data.observations);
}
// Check the right secret has been supplied
if(apiData.secret != secret) {
console.log("secret invalid: "+ apiData.secret);
callback(null, {
statusCode: '403',
body: "INVALID SECRET",
headers: {'Content-Type': 'text/plain'}
});
return;
}
// Check which version of API data we have been given
if(apiData.version=='2.0') {
let key;
const o = apiData.data.observations;
if(apiData.type == "DevicesSeen" || apiData.type == "BluetoothDevicesSeen") {
if(debug) console.log('Processing V2 request: '+ apiData.type);
for (key in o) {
if (o.hasOwnProperty(key)) {
if (!o[key].location) break;
if (o[key].seenEpoch == null || o[key].seenEpoch == 0) break; // # This probe is useless, so ignore it
}
// Put item into PostgreSQL
const values=[apiData.version,apiData.type,o[key].clientMac,o[key].seenTime,o[key].ssid,o[key].rssi,o[key].location.lat,o[key].location.lng,o[key].location.unc,o[key].location.x[0],o[key].location.y[0]];
if(debug) console.log('Processing SQL: '+sql+' '+values);
const client = new Client();
client.connect();
client.query(sql,values,(err, res) => {
if (err) console.log('client.query():error: '+err);
else {
if(debug) console.log('client.query():success: '+JSON.stringify(res));
}
client.end();
});
}
}
} else if(apiData.version=='3.0') {
let x;
const o = apiData.data.observations;
// Process V3 WiFi observations
if(apiData.type == "WiFi") {
if(debug) console.log('Processing V3 request: '+ apiData.type);
// Lop through all the observations
for (x in o) {
// Skip observations with no locations
if(o[x].locations.length==0) continue;
// Loop through all the locations in the observation
let y;
for(y in o[x].locations) {
const location=o[x].locations[y];
// Put item into PostgreSQL
const values=[apiData.version,apiData.type,o[x].clientMac,location.time,o[x].ssid,o[x].latestRecord['nearestApRssi'],location.lat,location.lng,location.variance,location.x,location.y];
if(debug) console.log('Processing SQL: '+sql+' '+values);
const client = new Client();
client.connect();
client.query(sql,values,(err, res) => {
if (err) console.log('client.query():error: '+err);
else {
if(debug) console.log('client.query():success: '+JSON.stringify(res));
}
client.end();
});
}
}
}
} else {
console.log("Receiver is written for version 2.0 or 3.0 and will not accept other versions. The POST data was sent with version: "+ apiData.version);
callback(null, {
statusCode: '403',
body: "Invalid location API version",
headers: {'Content-Type': 'text/plain'}
});
return;
}
// Respond to client with success
callback(null, {
statusCode: '200',
body: "Location API data received",
headers: {'Content-Type': 'text/plain'}
});
};