fix: fire ids leaked into URLs as ObjectID("...") on Mongo 7

Fire collections use idGeneration:'MONGO', so _id is a Mongo.ObjectID whose
toString() is ObjectID("<hex>"), not the bare hex. Interpolating _id into a
URL produced /fire/archive/ObjectID("c0..."), which no route matches -> the
fire-detail page 404'd. New hexId() helper returns the 24-char hex for an
ObjectID (and passes plain strings through); applied at the three string-context
sites: the map-marker click URL (MarkListeners), the active->archive redirect,
and the comments referenceId (Fires.js). Left falsePositives.insert untouched —
its check() expects a Meteor.Collection.ObjectID, so it takes the object.

Verified in browser: /fire/archive/<hex> and /fire/active/<hex> render the
detail page (map + comments), URL stays clean hex, no ObjectID( anywhere.
This commit is contained in:
vjrj 2026-07-17 18:31:59 +02:00
parent c6c22643c1
commit 9feaca9d0b
3 changed files with 22 additions and 3 deletions

15
imports/api/Common/id.js Normal file
View file

@ -0,0 +1,15 @@
/*
* Fire/collection docs use `idGeneration: 'MONGO'`, so their `_id` is a
* `Mongo.ObjectID`, whose `.toString()` yields `ObjectID("<hex>")` NOT the
* bare hex. Interpolating an `_id` straight into a URL or any string therefore
* produces `.../fire/archive/ObjectID("...")`, which the routes can't match.
*
* Use `hexId()` wherever an id becomes part of a string (URLs, comment
* reference ids, DOM ids). It returns the 24-char hex for an ObjectID and
* passes plain-string ids through unchanged. Do NOT use it for DDP method
* args whose `check()` expects `Meteor.Collection.ObjectID` pass the object.
*/
export const hexId = id =>
(id && typeof id.toHexString === 'function' ? id.toHexString() : id);
export default hexId;

View file

@ -1,4 +1,7 @@
import { hexId } from '/imports/api/Common/id';
export const onMarkClick = (history, nasa, id) => {
// console.log('onClick fired');
history.push(`/fire/${nasa ? 'active' : 'alert'}/${id}`);
// hexId: `id` is a Mongo.ObjectID whose toString() is `ObjectID("...")`.
history.push(`/fire/${nasa ? 'active' : 'alert'}/${hexId(id)}`);
};

View file

@ -18,6 +18,7 @@ import FiresCollection from '/imports/api/Fires/Fires';
import FireList from '/imports/ui/components/Maps/FireList';
import FromNow from '/imports/ui/components/FromNow/FromNow';
import { dateLongFormat, dateYYYYMMDD } from '/imports/api/Common/dates';
import { hexId } from '/imports/api/Common/id';
import CommentsBox from '/imports/ui/components/Comments/CommentsBox';
import FalsePositiveTypes from '/imports/api/FalsePositives/FalsePositiveTypes';
import FalsePositivesCollection, { falsePositivesRemap } from '/imports/api/FalsePositives/FalsePositives';
@ -41,7 +42,7 @@ class Fire extends React.Component {
// console.log(`Next when ${nextProps.when}`);
if (nextProps.fire && (nextProps.alert || nextProps.active || nextProps.fromHash)) {
// change url to archive with new _id
nextProps.history.replace(`/fire/archive/${nextProps.fire._id}`);
nextProps.history.replace(`/fire/archive/${hexId(nextProps.fire._id)}`);
}
this.setState({
loading: nextProps.loading,
@ -232,7 +233,7 @@ class Fire extends React.Component {
</ul>
</div>
<div className="comments-section">
<CommentsBox referenceId={`fire-${fire._id}`} />
<CommentsBox referenceId={`fire-${hexId(fire._id)}`} />
</div>
</Fragment>
}