diff --git a/Development/libs/phantomjs b/Development/libs/phantomjs new file mode 100755 index 0000000..e62f343 Binary files /dev/null and b/Development/libs/phantomjs differ diff --git a/Development/libs/ultima-ng-9.0.0.zip b/Development/libs/ultima-ng-9.0.0.zip deleted file mode 100644 index 208b463..0000000 Binary files a/Development/libs/ultima-ng-9.0.0.zip and /dev/null differ diff --git a/Development/server/.tmp/export_69e28118a9673b08101159b1.csv b/Development/server/.tmp/export_69e28118a9673b08101159b1.csv deleted file mode 100644 index 2cedcc4..0000000 --- a/Development/server/.tmp/export_69e28118a9673b08101159b1.csv +++ /dev/null @@ -1 +0,0 @@ -jobId,orderNumber,jobName,sessionId,fileName,pilotName,timestampUtc,gpsTime,lat,lon,utmX,utmY,alt_m,groundSpeed_ms,heading,crossTrackError_m,lockedLine,hdop,satsInView,correctionId,waasId,sprayStat,flowRateApplied_Lmin,flowRateRequired_Lmin,appRateRequired_Lha,appRateApplied_Lha,swathWidth_m,boomPressure_psi,sprayOnLag_s,sprayOffLag_s,pulsesPerLitre,windSpeed_ms,windDir_deg,temp_c,humidity_pct diff --git a/Development/server/.tmp/export_69e28122a9673b08101159bc.csv b/Development/server/.tmp/export_69e28122a9673b08101159bc.csv deleted file mode 100644 index 2cedcc4..0000000 --- a/Development/server/.tmp/export_69e28122a9673b08101159bc.csv +++ /dev/null @@ -1 +0,0 @@ -jobId,orderNumber,jobName,sessionId,fileName,pilotName,timestampUtc,gpsTime,lat,lon,utmX,utmY,alt_m,groundSpeed_ms,heading,crossTrackError_m,lockedLine,hdop,satsInView,correctionId,waasId,sprayStat,flowRateApplied_Lmin,flowRateRequired_Lmin,appRateRequired_Lha,appRateApplied_Lha,swathWidth_m,boomPressure_psi,sprayOnLag_s,sprayOffLag_s,pulsesPerLitre,windSpeed_ms,windDir_deg,temp_c,humidity_pct diff --git a/Development/server/middlewares/app_validator.js b/Development/server/middlewares/app_validator.js index 11c4df2..c0130d4 100644 --- a/Development/server/middlewares/app_validator.js +++ b/Development/server/middlewares/app_validator.js @@ -11,7 +11,6 @@ const { AppAuthError, AppMembershipError, AppParamError } = require('../helpers/app_error.js'), { SubType, SubFields } = require('../model/subscription.js'), Vehicle = require('../model/vehicle.js'), - bcrypt = require('bcryptjs'), ObjectId = require('mongodb').ObjectId; const USE_SUBSCRIPTION = env.ENABLE_SUBSCRIPTION; @@ -30,8 +29,7 @@ function isSecuredRoute(routePath, method) { { path: '/exists', method: 'POST' }, { path: '/countries', method: 'GET' }, { path: '/testAuth', method: 'ALL' }, - { path: '/stPmtWH_EP', method: 'POST' }, // Stripe webhook endpoint - authenticated via signature verification - { path: '/api/v1/', method: 'ALL' } // Public data-export API — authenticated via X-API-Key header instead + { path: '/stPmtWH_EP', method: 'POST' } // Stripe webhook endpoint - authenticated via signature verification ]; if (env.INV_IMG_VIR_DIR) { nonSecurePaths.push({ path: env.INV_IMG_VIR_DIR, method: 'ALL' }); @@ -211,52 +209,7 @@ async function checkRqUsageLimits(req, res, next) { } -/** - * API-Key middleware for the public data-export API (/api/v1/ routes). - * - * Reads the X-API-Key header, looks up a candidate key by its prefix (first 8 chars), - * then bcrypt-compares the full key. On success, sets req.uid identical to checkUser - * so all existing controller ownership filters work without modification. - * - * lastUsedAt is updated fire-and-forget to avoid adding latency to every request. - */ -async function checkApiKey(req, res, next) { - const ApiKey = require('../model/api_key'); // lazy require to avoid circular dependency risk - const rawKey = req.headers['x-api-key']; - if (!rawKey || rawKey.length < 8) return AppAuthError.throw(); - - const prefix = rawKey.substring(0, 8); - // Find active keys matching this prefix (should be at most one, prefix is not a unique index - // to avoid leaking timing info about key existence) - const candidates = await ApiKey.find({ prefix, active: true }).limit(5).lean(); - if (!candidates.length) AppAuthError.throw(); - - let matched = null; - for (const candidate of candidates) { - const ok = await bcrypt.compare(rawKey, candidate.keyHash); - if (ok) { matched = candidate; break; } - } - if (!matched) AppAuthError.throw(); - - // Mirror what checkUser sets — controllers need req.uid and nothing else - req.uid = matched.owner.toString(); - req.apiKeyId = matched._id; - - // Fire-and-forget lastUsedAt update (do not await — avoids adding DB latency to request) - ApiKey.updateOne({ _id: matched._id }, { $set: { lastUsedAt: new Date() } }).catch(() => {}); - - // Load owner's userInfo from cache (same path as checkUser) - const userInfo = cache.get(req.uid); - if (userInfo && !userInfo[Fields.MARKED_DELETE]) { - req.userInfo = userInfo; - } else { - req.userInfo = await cache.loadUser(req.uid); - } - - return next && next(); -} - module.exports = { - isSecuredRoute, getUserInfo, checkUser, checkApiKey, checkACsLimits, checkUsageLimits, + isSecuredRoute, getUserInfo, checkUser, checkACsLimits, checkUsageLimits, checkRqAnySubscription, checkRqPkgSubscription, checkRqACsLimits, checkRqUsageLimits, }; diff --git a/Development/server/model/application.js b/Development/server/model/application.js index 8079713..9874381 100644 --- a/Development/server/model/application.js +++ b/Development/server/model/application.js @@ -38,8 +38,6 @@ const schema = new Schema({ totalSprayMat: { type: Number, required: false }, // Total Sprayed material amount. Always in metric (L/Ha or Kg/Ha) totalSprayMatUnit: { type: Number, required: false }, // 1 or 4 - avgSpraySpeed: { type: Number, required: false }, // Average ground speed (m/s) during spray-on periods, computed at import time - status: { type: Number, required: true, default: 1 }, // -1: was cancelled - to be deleted soon, 0: error, 1: created, 2: in progress, 3: done proStatus: { type: Number, default: 0 }, // 0: not fully processed (disrupted while reading or processing files). 1: with data, 2: no data. +10 if items were updated errorMsg: { type: String }, diff --git a/Development/server/model/index.js b/Development/server/model/index.js index dbb64e4..7c079c2 100644 --- a/Development/server/model/index.js +++ b/Development/server/model/index.js @@ -28,5 +28,7 @@ module.exports = { LogPayment: require('./log_payment'), Partner: require('./partner').Partner, PartnerSystemUser: require('./partner').PartnerSystemUser, - PartnerLogTracker: require('./partner_log_tracker') -} + PartnerLogTracker: require('./partner_log_tracker'), + + +}; diff --git a/Development/server/public/partner-dlq-monitor.html b/Development/server/public/partner-dlq-monitor.html new file mode 100644 index 0000000..8935cf0 --- /dev/null +++ b/Development/server/public/partner-dlq-monitor.html @@ -0,0 +1,535 @@ + + + + + + Partner DLQ Monitor + + + +
+

🔄 Partner DLQ Monitor

+ +
+
+ +
+
+

DLQ Messages

+
-
+
+
+

Failed Tasks

+
-
+
+
+

Processing

+
-
+
+
+

Downloaded

+
-
+
+
+

Processed

+
-
+
+
+

Archived

+
-
+
+
+ +
+

Actions

+
+ + + + +
+
+ +
+

Recent Failures

+
+
Loading...
+
+
+ +
+ Last updated: Never +
+
+ + + + diff --git a/Development/server/routes/index.js b/Development/server/routes/index.js index 4b0d5b8..f1c46e3 100644 --- a/Development/server/routes/index.js +++ b/Development/server/routes/index.js @@ -30,7 +30,4 @@ module.exports = function (app) { require('./log_payment')(app); require('./partner')(app); require('./health')(app); - // Data Export public API (X-API-Key auth) and key management (JWT auth) - require('./api_pub')(app); - require('./api_keys')(app); }; \ No newline at end of file diff --git a/Development/server/workers/job_worker.js b/Development/server/workers/job_worker.js index 045d10e..f14295c 100644 --- a/Development/server/workers/job_worker.js +++ b/Development/server/workers/job_worker.js @@ -525,7 +525,6 @@ function work(impMsg, redelivered, cb) { appl.totalSprayMat = appData.totalSprayMat; appl.totalSprayMatUnit = appData.totalSprayMatUnit; } - if (utils.isNumber(appData.avgSpraySpeed)) appl.avgSpraySpeed = appData.avgSpraySpeed; // m/s, average ground speed during spray-on periods appl.startDateTime = appData.startDateTime.format('YYYYMMDDTHHmmss'); appl.endDateTime = appData.endDateTime.format('YYYYMMDDTHHmmss'); } @@ -941,7 +940,6 @@ async function getUsageLimits(user) { function importData(dataPath, appId, job, cb) { let appData, totalSprays = 0, totalSprLength = 0, avgRates = [], totalTurnTime = 0, totalSprayTime = 0, totalFlightTime = 0, totalSprMats = 0, dataFiles = [], sprMatsUnit; - let totalSpeedAcc = 0, totalSpeedCount = 0; // for avgSpraySpeed const importInfo = []; const begin = Date.now(); // DEBUG - Measering total import data time @@ -1043,11 +1041,6 @@ function importData(dataPath, appId, job, cb) { if (utils.isNumber(data.sprayTime)) totalSprayTime += data.sprayTime; if (utils.isNumber(data.totalTime)) totalFlightTime += data.totalTime; - if (utils.isNumber(data.spraySpeedCount) && data.spraySpeedCount > 0 && utils.isNumber(data.avgSpraySpeed)) { - totalSpeedAcc += data.avgSpraySpeed * data.spraySpeedCount; - totalSpeedCount += data.spraySpeedCount; - } - if (data.avgRate) avgRates.push(data.avgRate); @@ -1086,8 +1079,7 @@ function importData(dataPath, appId, job, cb) { totalTurnTime: totalTurnTime, totalFlightTime: totalFlightTime, totalSprayMat: totalSprMats, - totalSprayMatUnit: sprMatsUnit, - avgSpraySpeed: totalSpeedCount > 0 ? totalSpeedAcc / totalSpeedCount : null + totalSprayMatUnit: sprMatsUnit } const duration = Date.now() - begin; @@ -1306,7 +1298,6 @@ function importDataFiles(fileItems, appId, job, cb) { (skip the segments within the same line) */ let turnTime = { line: null, at: null, nextOff: false, total: 0 }, timeDif = 0, totalSprTime = 0, totalTime = 0; - let totalSpeedAcc = 0, spraySpeedCount = 0; // for avgSpraySpeed let prevTime = -999, prevSprTime = -999; let record; for (let i = 0; i < importInfo.records.length; i++) { @@ -1335,10 +1326,6 @@ function importDataFiles(fileItems, appId, job, cb) { if (timeDif > 0 && timeDif <= 120) totalSprTime += timeDif; } - if (record.sprayStat !== 3 && utils.isNumber(record.grSpeed)) { - totalSpeedAcc += record.grSpeed; - spraySpeedCount++; - } prevSprTime = record.gpsTime; } @@ -1378,8 +1365,6 @@ function importDataFiles(fileItems, appId, job, cb) { importInfo.turnTime = turnTime.total; importInfo.sprayTime = totalSprTime; importInfo.totalTime = totalTime; - importInfo.avgSpraySpeed = spraySpeedCount > 0 ? totalSpeedAcc / spraySpeedCount : null; // m/s - importInfo.spraySpeedCount = spraySpeedCount; callback(); },