mirror of
https://github.com/bckelley/cgm-remote-monitor.git
synced 2026-08-24 03:14:12 -05:00
feat: add UUID_HANDLING feature flag for GET/DELETE by identifier
- env.js: Add UUID_HANDLING env var (default: false)
- query.js: Add UUID detection in normalizeIdValue()
- When UUID_HANDLING=true and _id is UUID, search by identifier field
- Returns searchByIdentifier flag to redirect query
- treatments.js: Move queryOpts inside query_for() for env access
- entries.js: Same pattern for entries collection
When UUID_HANDLING=true:
- GET /treatments/{uuid} searches by identifier field
- DELETE /treatments/{uuid} deletes by identifier field
- Same behavior for entries collection
When UUID_HANDLING=false (default):
- UUID _id values return empty results (safe, no crash)
- Maintains backwards compatibility
Refs: uuid-feature-flag, uuid-query-impl from uuid-identifier-lookup.md
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
+15
-14
@@ -163,7 +163,21 @@ function storage (env, ctx) {
|
||||
}
|
||||
|
||||
function query_for (opts) {
|
||||
return find_options(opts, storage.queryOpts);
|
||||
// Build queryOpts inside function to access env.uuidHandling
|
||||
var queryOpts = {
|
||||
walker: {
|
||||
date: parseInt
|
||||
, sgv: parseInt
|
||||
, filtered: parseInt
|
||||
, unfiltered: parseInt
|
||||
, rssi: parseInt
|
||||
, noise: parseInt
|
||||
, mbg: parseInt
|
||||
}
|
||||
, useEpoch: true
|
||||
, uuidHandling: env.uuidHandling
|
||||
};
|
||||
return find_options(opts, queryOpts);
|
||||
}
|
||||
|
||||
// closure to represent the API
|
||||
@@ -247,19 +261,6 @@ function storage (env, ctx) {
|
||||
return api;
|
||||
}
|
||||
|
||||
storage.queryOpts = {
|
||||
walker: {
|
||||
date: parseInt
|
||||
, sgv: parseInt
|
||||
, filtered: parseInt
|
||||
, unfiltered: parseInt
|
||||
, rssi: parseInt
|
||||
, noise: parseInt
|
||||
, mbg: parseInt
|
||||
}
|
||||
, useEpoch: true
|
||||
};
|
||||
|
||||
// expose module
|
||||
storage.storage = storage;
|
||||
module.exports = storage;
|
||||
|
||||
@@ -75,6 +75,11 @@ function setSSL () {
|
||||
env.secureHstsHeaderPreload = readENVTruthy("SECURE_HSTS_HEADER_PRELOAD", false);
|
||||
env.secureCsp = readENVTruthy("SECURE_CSP", false);
|
||||
env.secureCspReportOnly = readENVTruthy("SECURE_CSP_REPORT_ONLY", false);
|
||||
|
||||
// UUID handling for AID clients (Loop, Trio, AAPS, xDrip+)
|
||||
// When true: UUID _id values are normalized to 'identifier' field
|
||||
// When false: UUID _id values are rejected on write, ignored on read
|
||||
env.uuidHandling = readENVTruthy("UUID_HANDLING", false);
|
||||
}
|
||||
|
||||
// A little ugly, but we don't want to read the secret into a var
|
||||
|
||||
+32
-8
@@ -4,6 +4,7 @@ const traverse = require('traverse');
|
||||
const ObjectID = require('mongodb-legacy').ObjectId;
|
||||
const moment = require('moment');
|
||||
const OBJECT_ID_HEX_RE = /^[0-9a-fA-F]{24}$/;
|
||||
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
||||
|
||||
const TWO_DAYS = 172800000;
|
||||
/**
|
||||
@@ -89,32 +90,55 @@ function enforceDateFilter (query, opts) {
|
||||
/**
|
||||
* Helper to set ObjectID type for `_id` queries.
|
||||
* Forces anything named `_id` to be the `ObjectID` type.
|
||||
* When opts.uuidHandling is true, UUID _id values search by identifier field.
|
||||
*/
|
||||
function updateIdQuery (query) {
|
||||
function updateIdQuery (query, opts) {
|
||||
if (!Object.prototype.hasOwnProperty.call(query, '_id')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof query._id === 'string') {
|
||||
query._id = normalizeIdValue(query._id);
|
||||
var result = normalizeIdValue(query._id, opts);
|
||||
if (result.searchByIdentifier) {
|
||||
// UUID detected with uuidHandling enabled - search by identifier instead
|
||||
query.identifier = result.value;
|
||||
delete query._id;
|
||||
} else {
|
||||
query._id = result.value;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (query._id && typeof query._id === 'object') {
|
||||
traverse(query._id).forEach(function (x) {
|
||||
if (this.isLeaf) {
|
||||
this.update(normalizeIdValue(x));
|
||||
var result = normalizeIdValue(x, opts);
|
||||
// For complex queries (like $in), we only handle ObjectIDs
|
||||
// UUID handling in complex queries would require more work
|
||||
this.update(result.value);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeIdValue (value) {
|
||||
/**
|
||||
* Normalize an _id value for MongoDB queries.
|
||||
* @param {string} value - The _id value to normalize
|
||||
* @param {Object} opts - Options including uuidHandling flag
|
||||
* @returns {Object} { value: normalized, searchByIdentifier: boolean }
|
||||
*/
|
||||
function normalizeIdValue (value, opts) {
|
||||
if (typeof value === 'string' && OBJECT_ID_HEX_RE.test(value)) {
|
||||
return new ObjectID(value);
|
||||
return { value: new ObjectID(value), searchByIdentifier: false };
|
||||
}
|
||||
|
||||
return value;
|
||||
// Check if it's a UUID and uuidHandling is enabled
|
||||
if (typeof value === 'string' && UUID_RE.test(value) && opts && opts.uuidHandling) {
|
||||
return { value: value, searchByIdentifier: true };
|
||||
}
|
||||
|
||||
// Unknown format - return as-is (will likely return 0 results)
|
||||
return { value: value, searchByIdentifier: false };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -142,8 +166,8 @@ function create (params, opts) {
|
||||
enforceDateFilter(query, opts);
|
||||
}
|
||||
|
||||
// Help queries for _id.
|
||||
updateIdQuery(query);
|
||||
// Help queries for _id (pass opts for UUID handling)
|
||||
updateIdQuery(query, opts);
|
||||
|
||||
//console.info('query:', query);
|
||||
// Ready for mongodb.find( ) and friends.
|
||||
|
||||
+14
-13
@@ -216,7 +216,20 @@ function storage (env, ctx) {
|
||||
}
|
||||
|
||||
function query_for (opts) {
|
||||
return find_options(opts, storage.queryOpts);
|
||||
// Build queryOpts inside function to access env.uuidHandling
|
||||
var queryOpts = {
|
||||
walker: {
|
||||
insulin: parseInt
|
||||
, carbs: parseInt
|
||||
, glucose: parseInt
|
||||
, notes: find_options.parseRegEx
|
||||
, eventType: find_options.parseRegEx
|
||||
, enteredBy: find_options.parseRegEx
|
||||
}
|
||||
, dateField: 'created_at'
|
||||
, uuidHandling: env.uuidHandling
|
||||
};
|
||||
return find_options(opts, queryOpts);
|
||||
}
|
||||
|
||||
function remove (opts, fn) {
|
||||
@@ -459,16 +472,4 @@ function prepareData(obj) {
|
||||
return results;
|
||||
}
|
||||
|
||||
storage.queryOpts = {
|
||||
walker: {
|
||||
insulin: parseInt
|
||||
, carbs: parseInt
|
||||
, glucose: parseInt
|
||||
, notes: find_options.parseRegEx
|
||||
, eventType: find_options.parseRegEx
|
||||
, enteredBy: find_options.parseRegEx
|
||||
}
|
||||
, dateField: 'created_at'
|
||||
};
|
||||
|
||||
module.exports = storage;
|
||||
|
||||
Reference in New Issue
Block a user