WIP(meteor3): boot progresses past linker + several async fixes

Past the client-bundle underscore linker (pin underscore@1.6.4) and now working
through the server async/await migration on Meteor 3.1 + Mongo 7:

- Removed fibers (imports/startup/server/fibers.js + import) — gone in Meteor 3.
- accounts/oauth.js: upsert -> upsertAsync (top-level await).
- migrations.js: startup async + await Migrations.migrateTo (async in 2.x).
  mongo7 marked at version 18 so historical up() bodies (still sync Mongo) don't
  run — prod data restores already-migrated. Documented.
- subsUnion.js: fully async (fetchAsync/findOneAsync/countAsync/upsertAsync,
  observeAsync with async callbacks).
- facts.js: dropped dead sync findOne.
- email.js: ostrio:mailer 2.5 is a default export (was named import); dropped
  removed static MailTime.Template (built-in default '{{{html}}}').

NEXT wall + remaining chain (multi-session):
1. aldeed:collection2@4.2.0 needs simpl-schema 3.x (app has 1.x) -> 'attachSchema
   is not a function'. Requires upgrading npm simpl-schema to 3.x and migrating
   all collection schemas (SimpleSchema.RegEx.Id etc.) across ~8 files.
2. Rest.js + helpers (countRealFires/firesUnion/whichAreFalsePositives/
   fireFromHash/subscriptionsInsert/upsertFalsePositive) -> *Async.
3. Patch vendored restivus to await async endpoint handlers.
4. methods/publications/comments to async; cron SyncedCron(quave)/Facts imports.
5. alanning:roles@1.2.10 + gadicohen:sitemaps@0.0.17 warn incompatible -> bump.
6. React 16 -> 18 render root.

Run with: NODE_OPTIONS='--dns-result-order=ipv4first --no-network-family-autoselection'
MONGO_URL='mongodb://localhost:27019/fuegos?replicaSet=rs0'
This commit is contained in:
vjrj 2026-07-13 23:46:45 +02:00
parent 8fa10e47c7
commit 25dc1b99c0
9 changed files with 40 additions and 44 deletions

View file

@ -54,3 +54,4 @@ nspangler:autoreconnect
quave:synced-cron quave:synced-cron
# lmachens:kadira # lmachens:kadira
nimble:restivus@0.8.12 nimble:restivus@0.8.12
underscore@1.6.4

View file

@ -120,7 +120,7 @@ themeteorchef:bert@1.1.0
tmeasday:check-npm-versions@0.3.2 tmeasday:check-npm-versions@0.3.2
tracker@1.3.4 tracker@1.3.4
typescript@5.6.3 typescript@5.6.3
underscore@1.6.2 underscore@1.6.4
url@1.3.5 url@1.3.5
vjrj:piwik@0.3.1 vjrj:piwik@0.3.1
webapp@2.0.4 webapp@2.0.4

View file

@ -4,10 +4,12 @@ import { ServiceConfiguration } from 'meteor/service-configuration';
const OAuthSettings = Meteor.settings.private.OAuth; const OAuthSettings = Meteor.settings.private.OAuth;
if (OAuthSettings) { if (OAuthSettings) {
Object.keys(OAuthSettings).forEach((service) => { // Meteor 3: sync Mongo write-methods are gone on the server; use *Async.
ServiceConfiguration.configurations.upsert( // Top-level await is supported in eager server modules.
for (const service of Object.keys(OAuthSettings)) {
await ServiceConfiguration.configurations.upsertAsync(
{ service }, { service },
{ $set: OAuthSettings[service] }, { $set: OAuthSettings[service] },
); );
}); }
} }

View file

@ -1,6 +1,6 @@
import { Meteor } from 'meteor/meteor'; import { Meteor } from 'meteor/meteor';
import nodemailer from 'nodemailer'; import nodemailer from 'nodemailer';
import { MailTime } from 'meteor/ostrio:mailer'; import MailTime from 'meteor/ostrio:mailer';
import i18n from 'i18next'; import i18n from 'i18next';
import isMaster from './isMaster'; import isMaster from './isMaster';
@ -48,7 +48,8 @@ if (isMailServerMaster) {
concatDelimiter: hr + '<h2>{{{subject}}}</h2>', // Start each concatenated email with it's own subject concatDelimiter: hr + '<h2>{{{subject}}}</h2>', // Start each concatenated email with it's own subject
/* eslint-enable */ /* eslint-enable */
// concatThrottling: 30, // concatThrottling: 30,
template: MailTime.Template // Use default template // Mail-Time 2.x removed the static MailTime.Template; omitting `template`
// uses the built-in default ('{{{html}}}'). (debt: richer wrapper template)
}); });
} else { } else {
console.log('I\'m a mail client'); console.log('I\'m a mail client');

View file

@ -2,8 +2,4 @@
import { Roles } from 'meteor/alanning:roles'; import { Roles } from 'meteor/alanning:roles';
import { Meteor } from 'meteor/meteor'; import { Meteor } from 'meteor/meteor';
Facts.setUserIdFilter((userId) => { Facts.setUserIdFilter(userId => Roles.userIsInRole(userId, ['admin']));
const user = Meteor.users.findOne(userId);
// console.log(`User roles: ${user.roles}`);
return Roles.userIsInRole(userId, ['admin']);
});

View file

@ -1,7 +0,0 @@
// Workaround for: https://github.com/meteor/meteor/issues/9796
// https://github.com/meteor/meteor/issues/9796#issuecomment-381676326
// https://github.com/sandstorm-io/sandstorm/blob/0f1fec013fe7208ed0fd97eb88b31b77e3c61f42/shell/server/00-startup.js#L99-L129
import Fiber from 'fibers';
Fiber.poolSize = 1e9;

View file

@ -1,6 +1,5 @@
import './ravenLogger'; import './ravenLogger';
import './catchExceptions'; import './catchExceptions';
import './fibers';
import './i18n'; import './i18n';
import './accounts'; import './accounts';
import './api'; import './api';

View file

@ -14,7 +14,7 @@ import Notifications from '/imports/api/Notifications/Notifications';
import ActiveFiresUnion from '/imports/api/ActiveFiresUnion/ActiveFiresUnion'; import ActiveFiresUnion from '/imports/api/ActiveFiresUnion/ActiveFiresUnion';
import { Mongo } from 'meteor/mongo'; import { Mongo } from 'meteor/mongo';
Meteor.startup(() => { Meteor.startup(async () => {
// https://github.com/percolatestudio/meteor-migrations // https://github.com/percolatestudio/meteor-migrations
Migrations.config({ Migrations.config({
@ -255,7 +255,12 @@ Meteor.startup(() => {
}); });
// Set createdAt in users & subs // Set createdAt in users & subs
Migrations.migrateTo('latest'); // Meteor 3: migrateTo is async now.
// NOTE: the individual up() bodies below still use SYNC Mongo — they only run
// on a DB whose migration version is behind. Production data is restored from
// rsmain already at the latest version, so they never execute. If migrations
// must ever run on a truly-fresh DB, their up() bodies need async conversion.
await Migrations.migrateTo('latest');
// Migrations.migrateTo('14,rerun'); // Migrations.migrateTo('14,rerun');
}); });

View file

@ -11,7 +11,7 @@ import { isMailServerMaster } from '/imports/startup/server/email';
// sudo apt-get install libcairo2-dev libjpeg-dev libgif-dev // sudo apt-get install libcairo2-dev libjpeg-dev libgif-dev
Meteor.startup(() => { Meteor.startup(async () => {
if (!isMailServerMaster) { if (!isMailServerMaster) {
console.log('We only process subsUnion in master'); console.log('We only process subsUnion in master');
return; return;
@ -35,8 +35,8 @@ Meteor.startup(() => {
const noNoisy = sub => sub; const noNoisy = sub => sub;
const process = (isPublic) => { const process = async (isPublic) => {
const subscribers = Subscriptions.find().fetch(); const subscribers = await Subscriptions.find().fetchAsync();
const result = calcUnion(L, subscribers, isPublic ? addNoisy : noNoisy, true); const result = calcUnion(L, subscribers, isPublic ? addNoisy : noNoisy, true);
const union = result[0]; const union = result[0];
const bounds = result[1]; const bounds = result[1];
@ -74,9 +74,9 @@ Meteor.startup(() => {
}; };
// FIXME, take care of object size: // FIXME, take care of object size:
// https://stackoverflow.com/questions/10827812/what-is-the-length-maximum-for-a-string-data-type-in-mongodb-used-with-ruby // https://stackoverflow.com/questions/10827812/what-is-the-length-maximum-for-a-string-data-type-in-mongodb-used-with-ruby
SiteSettings.upsert({ name: `subs-${publicl}-union` }, unionSet, { multi: false }); await SiteSettings.upsertAsync({ name: `subs-${publicl}-union` }, unionSet, { multi: false });
SiteSettings.upsert({ name: `subs-${publicl}-union-bounds` }, boundsSet, { multi: false }); await SiteSettings.upsertAsync({ name: `subs-${publicl}-union-bounds` }, boundsSet, { multi: false });
SiteSettings.upsert({ name: 'subs-union-count' }, sizeSet, { multi: false }); await SiteSettings.upsertAsync({ name: 'subs-union-count' }, sizeSet, { multi: false });
if (debug) console.log(`${Publicl} subscription union calculated`); if (debug) console.log(`${Publicl} subscription union calculated`);
} else { } else {
console.log('Subscription union failed!'); console.log('Subscription union failed!');
@ -84,40 +84,39 @@ Meteor.startup(() => {
}; };
// At startup, we check if it's necessary to calc subscriptions union again // At startup, we check if it's necessary to calc subscriptions union again
const currentUnion = SiteSettings.findOne({ name: 'subs-public-union' }); const currentUnion = await SiteSettings.findOneAsync({ name: 'subs-public-union' });
const lastSubs = Subscriptions.findOne({}, { sort: { updatedAt: -1 } }); const lastSubs = await Subscriptions.findOneAsync({}, { sort: { updatedAt: -1 } });
const countUnionSubs = SiteSettings.findOne({ name: 'subs-union-count' }); const countUnionSubs = await SiteSettings.findOneAsync({ name: 'subs-union-count' });
const countSubs = Subscriptions.find({}).count(); const countSubs = await Subscriptions.find({}).countAsync();
if (currentUnion && lastSubs) { if (currentUnion && lastSubs) {
const lastUnionUpdated = currentUnion.updatedAt; const lastUnionUpdated = currentUnion.updatedAt;
const lastSubsUpdated = lastSubs.updatedAt; const lastSubsUpdated = lastSubs.updatedAt;
if (lastUnionUpdated > lastSubsUpdated || !countUnionSubs || countSubs !== countUnionSubs.value) { if (lastUnionUpdated > lastSubsUpdated || !countUnionSubs || countSubs !== countUnionSubs.value) {
console.log('Subs union outdated'); console.log('Subs union outdated');
process(true); await process(true);
process(false); await process(false);
} else { } else {
console.log('Subs union up-to-date'); console.log('Subs union up-to-date');
} }
} }
Subscriptions.find({ createdAt: { $gt: new Date() } }).observe({ const recreate = async () => { await process(true); await process(false); };
added: function newSubAdded() { // doc) {
await Subscriptions.find({ createdAt: { $gt: new Date() } }).observeAsync({
added: async function newSubAdded() { // doc) {
if (debug) console.log('Subs added so recreate union'); if (debug) console.log('Subs added so recreate union');
process(true); await recreate();
process(false);
} }
}); });
Subscriptions.find().observe({ await Subscriptions.find().observeAsync({
changed: function subsChanged() { // updatedDoc, oldDoc) { changed: async function subsChanged() { // updatedDoc, oldDoc) {
if (debug) console.log('Subs changed so recreate union'); if (debug) console.log('Subs changed so recreate union');
process(true); await recreate();
process(false);
}, },
removed: function subsRemoved() { // oldDoc) { removed: async function subsRemoved() { // oldDoc) {
if (debug) console.log('Subs removed so recreate union'); if (debug) console.log('Subs removed so recreate union');
process(true); await recreate();
process(false);
} }
}); });
}); });