From eb3aec82ae72d5d78382802057e4745da8ffd750 Mon Sep 17 00:00:00 2001 From: vjrj Date: Tue, 14 Jul 2026 11:32:19 +0200 Subject: [PATCH] =?UTF-8?q?meteor3:=20purge=20dead=20client=20packages=20?= =?UTF-8?q?=E2=80=94=20web=20UI=20renders=20on=203.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit alanning:roles removed (client crashed the whole bundle; plain user.roles checks instead), selaias:cookie-consent replaced by in-repo React banner, publish-performant-counts vendored with countAsync, Meteor.autorun dropped (App)/Tracker.autorun (FiresMap), and the map publications not covered by the REST smoke (activefiresmyloc, activefiresunionmyloc, fireAlerts, oauth.verifyConfiguration) converted to async APIs. Verified in browser: / and /fires render with map + cookie banner, zero uncaught exceptions. REST smoke byte-identical (12/12). --- .meteor/packages | 4 +- .meteor/versions | 5 +-- UPGRADE.md | 25 +++++++++++ .../api/ActiveFires/server/publications.js | 10 ++--- .../ActiveFiresUnion/server/publications.js | 6 +-- imports/api/FireAlerts/server/publications.js | 4 +- imports/api/OAuth/server/methods.js | 8 ++-- imports/startup/client/i18n.js | 22 ++------- imports/startup/server/facts.js | 19 ++++++-- .../components/CookieConsent/CookieConsent.js | 45 +++++++++++++++++++ .../CookieConsent/CookieConsent.scss | 20 +++++++++ imports/ui/layouts/App/App.js | 11 ++--- imports/ui/pages/FiresMap/FiresMap.js | 3 +- packages/publish-performant-counts/client.js | 9 ++++ packages/publish-performant-counts/package.js | 16 +++++++ packages/publish-performant-counts/server.js | 37 +++++++++++++++ 16 files changed, 194 insertions(+), 50 deletions(-) create mode 100644 imports/ui/components/CookieConsent/CookieConsent.js create mode 100644 imports/ui/components/CookieConsent/CookieConsent.scss create mode 100644 packages/publish-performant-counts/client.js create mode 100644 packages/publish-performant-counts/package.js create mode 100644 packages/publish-performant-counts/server.js diff --git a/.meteor/packages b/.meteor/packages index a921a4b..d5ece8d 100644 --- a/.meteor/packages +++ b/.meteor/packages @@ -16,7 +16,6 @@ ecmascript@0.16.10 # Enable ECMAScript2015+ syntax in app code shell-server@0.6.1 # Server-side component of the `meteor shell` command react-meteor-data -alanning:roles fourseven:scss@5.0.0 accounts-base@3.0.3 accounts-password@3.0.3 @@ -33,14 +32,13 @@ dynamic-import@0.7.4 static-html@1.4.0 alexwine:bootstrap-4 -selaias:cookie-consent # Cookie consent gadicc:blaze-react-component # Add braze to react 255kb:meteor-status # Connect status # mixmax:smart-disconnect # Disconnect on lost focus (disabled to get notif) flowkey:raven # js errors vjrj:piwik # Stats mdg:geolocation -natestrauser:publish-performant-counts +tcef:publish-performant-counts ostrio:mailer # mailer # babrahams:constellation # dev utility percolate:migrations # db migrations diff --git a/.meteor/versions b/.meteor/versions index 4325a12..93a4c58 100644 --- a/.meteor/versions +++ b/.meteor/versions @@ -5,7 +5,6 @@ accounts-github@1.5.1 accounts-google@1.4.1 accounts-oauth@1.4.5 accounts-password@3.0.3 -alanning:roles@1.2.10 aldeed:collection2@4.2.0 aldeed:schema-deny@4.0.2 aldeed:simple-schema@1.13.1 @@ -75,7 +74,6 @@ mongo-decimal@0.2.0 mongo-dev-server@1.1.1 mongo-id@1.0.9 mongo-livedata@1.0.13 -natestrauser:publish-performant-counts@0.1.2 nimble:restivus@0.8.12 npm-mongo@6.10.0 nspangler:autoreconnect@0.0.1 @@ -83,7 +81,6 @@ oauth@3.0.0 oauth2@1.3.3 observe-sequence@2.0.1 ordered-dict@1.2.0 -ostrio:cookies@2.8.0 ostrio:mailer@2.5.0 ostrio:meteor-root@1.0.7 percolate:migrations@2.0.1 @@ -99,7 +96,6 @@ reload@1.3.2 retry@1.1.1 reywood:publish-composite@1.6.0 routepolicy@1.1.2 -selaias:cookie-consent@0.4.0 service-configuration@1.3.5 sha@1.0.10 shell-server@0.6.1 @@ -111,6 +107,7 @@ standard-minifier-css@1.9.3 standard-minifier-js@3.0.0 static-html@1.4.0 static-html-tools@1.0.0 +tcef:publish-performant-counts@0.2.0 templating@1.4.4 templating-compiler@2.0.1 templating-runtime@2.0.2 diff --git a/UPGRADE.md b/UPGRADE.md index 2d86cab..23e7ce6 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -251,6 +251,31 @@ MONGO_CONTAINER=tcef-mongo7 MONGO_SHELL=mongosh MONGO_PORT=27019 ./smoke/smoke.s `WebApp.connectHandlers` handler serving the same static page list (the per-fire section of the old handler was behind `firesMapEnabled = false`, i.e. dead code, and was dropped). Verified serving on dev. +- ✅ **Dead client packages purged — the web UI now RENDERS on Meteor 3.** + First-ever browser verification of this branch (the smoke only covers REST) + found a chain of ancient Blaze-era packages whose client code threw at + bundle load, killing the whole app (`meteorInstall is not defined`): + - **alanning:roles 1.2.10** (client crash, and its server + `Roles.userIsInRole` is sync) → package removed; the app barely used it: + `facts.js` now checks the plain `user.roles` field (admin set cached at + startup) and App.js takes `roles` from the user doc (prop was unused). + Upgrading to roles v4 was rejected: needs a prod data migration. + - **selaias:cookie-consent** (its `Cookies` global is gone) → removed; + replaced by in-repo React `imports/ui/components/CookieConsent` reusing + the same i18n keys; `CookieConsent.init` dropped from `i18n.js`. + - **natestrauser:publish-performant-counts** server `Counter._publishCursor` + uses sync `cursor.count()` → **vendored** as + `packages/publish-performant-counts` (`tcef:publish-performant-counts`) + with `countAsync` and an async-aware interval; same public API. + - `Meteor.autorun` (removed in Meteor 3) → dropped (App.js debug) / + `Tracker.autorun` (FiresMap). + - Map/server publications not exercised by the REST smoke converted to + async: `activefiresmyloc`, `activefiresunionmyloc`, `fireAlerts` + (countAsync/fetchAsync/awaited firesUnion), and `oauth.verifyConfiguration` + (findOneAsync). `migrations.js` bodies stay sync **by design** (control is + pinned at v18; prod data arrives pre-migrated) — tracked as debt. + Verified in a real browser: `/` and `/fires` render (map, search, layers, + cookie banner), zero uncaught exceptions; REST smoke byte-identical. ### Still TODO before the web (not just REST) is fully deployable on 3.x - **React 16 → 18** render root + verify ancient react-* libs. diff --git a/imports/api/ActiveFires/server/publications.js b/imports/api/ActiveFires/server/publications.js index 03878b3..66c9991 100644 --- a/imports/api/ActiveFires/server/publications.js +++ b/imports/api/ActiveFires/server/publications.js @@ -16,7 +16,7 @@ Meteor.publish('activefirestotal', function total() { return counter; }); -const activefires = (northEastLng, northEastLat, southWestLng, southWestLat, withMarks) => { +const activefires = async (northEastLng, northEastLat, southWestLng, southWestLat, withMarks) => { const fires = ActiveFires.find({ ourid: { $geoWithin: { @@ -36,10 +36,10 @@ const activefires = (northEastLng, northEastLat, southWestLng, southWestLat, wit } }); - if (Meteor.isDevelopment) console.log(`Active fires total: ${fires.count()}`); + if (Meteor.isDevelopment) console.log(`Active fires total: ${await fires.countAsync()}`); - if (withMarks && fires.fetch().length > 0) { - const union = firesUnion(fires); + if (withMarks && (await fires.fetchAsync()).length > 0) { + const union = await firesUnion(fires); const falsePos = whichAreFalsePositives(FalsePositives, union); const industries = whichAreFalsePositives(Industries, union); return [fires, falsePos, industries]; @@ -48,7 +48,7 @@ const activefires = (northEastLng, northEastLat, southWestLng, southWestLat, wit return fires; }; -Meteor.publish('activefiresmyloc', function activeInMyLoc(northEastLng, northEastLat, southWestLng, southWestLat, withMarks) { +Meteor.publish('activefiresmyloc', async function activeInMyLoc(northEastLng, northEastLat, southWestLng, southWestLat, withMarks) { // latitude -90 and 90 and the longitude between -180 and 180 check(northEastLng, NumberBetween(-180, 180)); check(southWestLat, NumberBetween(-90, 90)); diff --git a/imports/api/ActiveFiresUnion/server/publications.js b/imports/api/ActiveFiresUnion/server/publications.js index 6a126d8..f3d8db7 100644 --- a/imports/api/ActiveFiresUnion/server/publications.js +++ b/imports/api/ActiveFiresUnion/server/publications.js @@ -16,7 +16,7 @@ Meteor.publish('activefiresuniontotal', function total() { return counter; }); -const activeFiresUnion = (b1, b2, c1, c2, withMarks) => { +const activeFiresUnion = async (b1, b2, c1, c2, withMarks) => { // a --- b // c --- d const geometry = { @@ -46,7 +46,7 @@ const activeFiresUnion = (b1, b2, c1, c2, withMarks) => { } }); - if (Meteor.isDevelopment) console.log(`Active fires union total: ${fires.count()}`); + if (Meteor.isDevelopment) console.log(`Active fires union total: ${await fires.countAsync()}`); /* if (withMarks && fires.fetch().length > 0) { @@ -59,7 +59,7 @@ const activeFiresUnion = (b1, b2, c1, c2, withMarks) => { return fires; }; -Meteor.publish('activefiresunionmyloc', function activeInMyLoc(northEastLng, northEastLat, southWestLng, southWestLat, withMarks) { +Meteor.publish('activefiresunionmyloc', async function activeInMyLoc(northEastLng, northEastLat, southWestLng, southWestLat, withMarks) { // latitude -90 and 90 and the longitude between -180 and 180 check(northEastLng, NumberBetween(-180, 180)); check(southWestLat, NumberBetween(-90, 90)); diff --git a/imports/api/FireAlerts/server/publications.js b/imports/api/FireAlerts/server/publications.js index 6642f35..a3d5350 100644 --- a/imports/api/FireAlerts/server/publications.js +++ b/imports/api/FireAlerts/server/publications.js @@ -6,7 +6,7 @@ import { check } from 'meteor/check'; import { NumberBetween } from '/imports/modules/server/other-checks'; import FireAlerts from '../FireAlerts'; -Meteor.publish('fireAlerts', function fireAlerts(northEastLng, northEastLat, southWestLng, southWestLat) { +Meteor.publish('fireAlerts', async function fireAlerts(northEastLng, northEastLat, southWestLng, southWestLat) { // latitude -90 and 90 and the longitude between -180 and 180 check(northEastLng, NumberBetween(-180, 180)); check(southWestLat, NumberBetween(-90, 90)); @@ -37,6 +37,6 @@ Meteor.publish('fireAlerts', function fireAlerts(northEastLng, northEastLat, sou track: 1 } }); - if (Meteor.isDevelopment) console.log(`Neighbour alerts total: ${fires.count()}`); + if (Meteor.isDevelopment) console.log(`Neighbour alerts total: ${await fires.countAsync()}`); return fires; }); diff --git a/imports/api/OAuth/server/methods.js b/imports/api/OAuth/server/methods.js index 6696da4..3f1df74 100644 --- a/imports/api/OAuth/server/methods.js +++ b/imports/api/OAuth/server/methods.js @@ -4,16 +4,16 @@ import { ServiceConfiguration } from 'meteor/service-configuration'; import rateLimit from '../../../modules/rate-limit'; Meteor.methods({ - 'oauth.verifyConfiguration': function oauthVerifyConfiguration(services) { + 'oauth.verifyConfiguration': async function oauthVerifyConfiguration(services) { check(services, Array); try { const verifiedServices = []; - services.forEach((service) => { - if (ServiceConfiguration.configurations.findOne({ service })) { + for (const service of services) { + if (await ServiceConfiguration.configurations.findOneAsync({ service })) { verifiedServices.push(service); } - }); + } return verifiedServices.sort(); } catch (exception) { throw new Meteor.Error('500', exception); diff --git a/imports/startup/client/i18n.js b/imports/startup/client/i18n.js index 384053f..a66caf3 100644 --- a/imports/startup/client/i18n.js +++ b/imports/startup/client/i18n.js @@ -1,4 +1,4 @@ -/* global CookieConsent Intl */ +/* global Intl */ import i18n from 'i18next'; import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; @@ -89,23 +89,9 @@ i18n.use(backend) i18nReady.set(true); - // cookies eu consent - const cookiesOpt = { - cookieTitle: t('Uso de Cookies'), - cookieMessage: t('Utilizamos cookies para asegurar un mejor uso de nuestra web. Si continúas navegando, consideramos que aceptas su uso'), - /* cookieMessage: t('Uso de Cookies'), - cookieMessageImply: t('Utilizamos cookies para asegurar un mejor uso de nuestra web. Si continúas navegando, consideramos que aceptas su uso'), */ - showLink: false, - position: 'bottom', - linkText: 'Lee más', - linkRouteName: '/privacy', - acceptButtonText: t('Aceptar'), - html: false, - expirationInDays: 70, - forceShow: false - }; - - CookieConsent.init(cookiesOpt); + // Cookie consent banner: selaias:cookie-consent (Blaze, dead on Meteor 3) + // replaced by the React component imports/ui/components/CookieConsent, + // which reuses the same i18n keys. }); Meteor.subscribe('userData'); // lang is there diff --git a/imports/startup/server/facts.js b/imports/startup/server/facts.js index 02781f7..ac3e9df 100644 --- a/imports/startup/server/facts.js +++ b/imports/startup/server/facts.js @@ -1,5 +1,18 @@ -/* global Facts */ -import { Roles } from 'meteor/alanning:roles'; import { Meteor } from 'meteor/meteor'; +import { Facts } from 'meteor/facts-base'; -Facts.setUserIdFilter(userId => Roles.userIsInRole(userId, ['admin'])); +/* + * alanning:roles removed (1.x is dead on Meteor 3 — crashes the whole client + * bundle at load — and 4.x needs a data migration we don't want). Admin check + * against the plain `user.roles` field, cached at startup: facts are + * dev/admin instrumentation and the admin set essentially never changes. + */ +const adminIds = new Set(); + +Meteor.startup(async () => { + await Meteor.users + .find({ roles: 'admin' }, { fields: { _id: 1 } }) + .forEachAsync(u => adminIds.add(u._id)); +}); + +Facts.setUserIdFilter(userId => adminIds.has(userId)); diff --git a/imports/ui/components/CookieConsent/CookieConsent.js b/imports/ui/components/CookieConsent/CookieConsent.js new file mode 100644 index 0000000..3b7a7e2 --- /dev/null +++ b/imports/ui/components/CookieConsent/CookieConsent.js @@ -0,0 +1,45 @@ +import React, { Component } from 'react'; +import i18n from 'i18next'; +import './CookieConsent.scss'; + +/* + * Minimal cookie-consent banner replacing selaias:cookie-consent (Blaze, + * dead on Meteor 3: its `Cookies` global is gone and it crashed the whole + * client bundle at load). Same behavior: informative banner, dismissed once. + */ +const STORAGE_KEY = 'tcef-cookie-consent'; + +class CookieConsent extends Component { + constructor(props) { + super(props); + let accepted = false; + try { + accepted = window.localStorage.getItem(STORAGE_KEY) === 'yes'; + } catch (e) { /* storage disabled: show banner every time */ } + this.state = { accepted }; + this.accept = this.accept.bind(this); + } + + accept() { + try { + window.localStorage.setItem(STORAGE_KEY, 'yes'); + } catch (e) { /* ignore */ } + this.setState({ accepted: true }); + } + + render() { + if (this.state.accepted) return null; + return ( +
+ + {i18n.t('Utilizamos cookies para asegurar un mejor uso de nuestra web. Si continúas navegando, consideramos que aceptas su uso')} + + +
+ ); + } +} + +export default CookieConsent; diff --git a/imports/ui/components/CookieConsent/CookieConsent.scss b/imports/ui/components/CookieConsent/CookieConsent.scss new file mode 100644 index 0000000..c1f4cfd --- /dev/null +++ b/imports/ui/components/CookieConsent/CookieConsent.scss @@ -0,0 +1,20 @@ +.CookieConsent { + position: fixed; + bottom: 0; + left: 0; + right: 0; + z-index: 2000; + display: flex; + align-items: center; + justify-content: center; + gap: 1em; + padding: 0.75em 1em; + background: rgba(0, 0, 0, 0.85); + color: #fff; + font-size: 0.9em; + + button { + margin-left: 1em; + white-space: nowrap; + } +} diff --git a/imports/ui/layouts/App/App.js b/imports/ui/layouts/App/App.js index 4aeef3a..11f7d74 100644 --- a/imports/ui/layouts/App/App.js +++ b/imports/ui/layouts/App/App.js @@ -9,7 +9,6 @@ import { I18nextProvider } from 'react-i18next'; import { Helmet } from 'react-helmet'; import { Meteor } from 'meteor/meteor'; import { withTracker } from 'meteor/react-meteor-data'; -import { Roles } from 'meteor/alanning:roles'; // https://github.com/gadicc/meteor-blaze-react-component/ import Blaze from 'meteor/gadicc:blaze-react-component'; // i18n @@ -48,6 +47,7 @@ import About from '../../pages/About/About'; import Privacy from '../../pages/Privacy/Privacy'; import License from '../../pages/License/License'; import Credits from '../../pages/Credits/Credits'; +import CookieConsent from '../../components/CookieConsent/CookieConsent'; import Footer from '../../components/Footer/Footer'; import Feedback from '../../components/Feedback/Feedback'; import ReSendEmail from '../../components/ReSendEmail/ReSendEmail'; @@ -151,7 +151,7 @@ const App = props => (