mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
Merge branch 'devel' into release-3.4.1
This commit is contained in:
@@ -461,6 +461,14 @@ export default defineConfig({
|
||||
text: "jam:offline",
|
||||
link: "/community-packages/offline",
|
||||
},
|
||||
{
|
||||
text: "dupontbertrand:cluster",
|
||||
link: "/community-packages/cluster",
|
||||
},
|
||||
{
|
||||
text: "dupontbertrand:mail-preview",
|
||||
link: "/community-packages/mail-preview",
|
||||
},
|
||||
],
|
||||
collapsed: true,
|
||||
},
|
||||
|
||||
152
v3-docs/docs/community-packages/cluster.md
Normal file
152
v3-docs/docs/community-packages/cluster.md
Normal file
@@ -0,0 +1,152 @@
|
||||
# Cluster
|
||||
|
||||
- `Who maintains the package` – [Bertrand Dupont](https://github.com/dupontbertrand)
|
||||
|
||||
[[toc]]
|
||||
|
||||
## What Is It?
|
||||
|
||||
A Meteor 3 compatible fork of [`meteorhacks:cluster`](https://github.com/meteorhacks/cluster), providing multi-core support, load balancing, and service discovery for Meteor apps.
|
||||
|
||||
The original package was abandoned around 2016. This fork ports it to Meteor 3 with async/await, modern dependencies, and updated MongoDB driver — while preserving the same public API.
|
||||
|
||||
::: warning
|
||||
This is a **compatibility / migration bridge**, not a new recommended scaling architecture. If you only need multi-core CPU utilization, an external process manager like [PM2](https://pm2.keymetrics.io/) is simpler. This package is for apps that relied on `meteorhacks:cluster` features like service discovery, DDP-aware proxying, or `Cluster.discoverConnection()`.
|
||||
:::
|
||||
|
||||
## How to Download It?
|
||||
|
||||
```bash
|
||||
meteor add dupontbertrand:cluster
|
||||
```
|
||||
|
||||
## How to Use It?
|
||||
|
||||
### Multi-Core (Simplest Use Case)
|
||||
|
||||
Just set the `CLUSTER_WORKERS_COUNT` environment variable:
|
||||
|
||||
```bash
|
||||
# Use all available CPU cores
|
||||
CLUSTER_WORKERS_COUNT=auto meteor run
|
||||
|
||||
# Or specify a number
|
||||
CLUSTER_WORKERS_COUNT=4 meteor run
|
||||
```
|
||||
|
||||
The package forks child processes automatically. No code changes needed.
|
||||
|
||||
### Service Discovery + Load Balancing
|
||||
|
||||
For multi-instance deployments with automatic service registration and DDP-aware load balancing:
|
||||
|
||||
```bash
|
||||
export CLUSTER_DISCOVERY_URL="mongodb://your-mongo-host/cluster"
|
||||
export CLUSTER_SERVICE="web"
|
||||
export CLUSTER_WORKERS_COUNT=2
|
||||
```
|
||||
|
||||
Instances register themselves in MongoDB and discover each other automatically. No need to reconfigure a load balancer when adding or removing instances.
|
||||
|
||||
### Microservices
|
||||
|
||||
Connect to other services in the cluster by name:
|
||||
|
||||
```js
|
||||
// Server
|
||||
const serviceConnection = Cluster.discoverConnection('payments');
|
||||
|
||||
// Call methods on the remote service
|
||||
const result = await serviceConnection.callAsync('processPayment', data);
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### `Cluster.connect(discoveryUrl, [options])`
|
||||
|
||||
Connect to a discovery backend (currently MongoDB).
|
||||
|
||||
```js
|
||||
Cluster.connect('mongodb://host/db');
|
||||
```
|
||||
|
||||
Usually configured via the `CLUSTER_DISCOVERY_URL` environment variable instead of calling directly.
|
||||
|
||||
### `Cluster.register(serviceName, [options])`
|
||||
|
||||
Register this instance as a named service.
|
||||
|
||||
```js
|
||||
Cluster.register('web');
|
||||
```
|
||||
|
||||
Options:
|
||||
- `endpoint` — the URL other instances use to reach this one (defaults to `ROOT_URL`)
|
||||
- `balancer` — URL of the balancer (defaults to `CLUSTER_BALANCER_URL`)
|
||||
- `uiService` — the service that serves the UI (defaults to the registered service name)
|
||||
|
||||
Usually configured via the `CLUSTER_SERVICE` environment variable.
|
||||
|
||||
### `Cluster.discoverConnection(serviceName, [ddpOptions])`
|
||||
|
||||
Get a DDP connection to a named service. The connection automatically tracks healthy instances via the discovery backend.
|
||||
|
||||
```js
|
||||
const conn = Cluster.discoverConnection('analytics');
|
||||
const data = await conn.callAsync('getReport', params);
|
||||
```
|
||||
|
||||
### `Cluster.allowPublicAccess(serviceList)`
|
||||
|
||||
Allow public (non-authenticated) access to specific services.
|
||||
|
||||
```js
|
||||
Cluster.allowPublicAccess(['web', 'api']);
|
||||
```
|
||||
|
||||
Usually configured via the `CLUSTER_PUBLIC_SERVICES` environment variable (comma-separated).
|
||||
|
||||
## Environment Variables
|
||||
|
||||
| Variable | Description | Default |
|
||||
| -------- | ----------- | ------- |
|
||||
| `CLUSTER_WORKERS_COUNT` | Number of worker processes (`auto` = all cores) | 1 (no clustering) |
|
||||
| `CLUSTER_DISCOVERY_URL` | MongoDB URL for service discovery | — |
|
||||
| `CLUSTER_SERVICE` | Name to register this instance as | — |
|
||||
| `CLUSTER_ENDPOINT_URL` | URL for other instances to reach this one | `ROOT_URL` |
|
||||
| `CLUSTER_BALANCER_URL` | URL of the load balancer | — |
|
||||
| `CLUSTER_PUBLIC_SERVICES` | Comma-separated list of public services | — |
|
||||
| `CLUSTER_UI_SERVICE` | Service that serves the UI | same as `CLUSTER_SERVICE` |
|
||||
|
||||
## What Changed From meteorhacks:cluster
|
||||
|
||||
This fork preserves the original API. Under the hood:
|
||||
|
||||
- MongoDB discovery backend rewritten in **async/await** (was Fibers/wrapAsync)
|
||||
- MongoDB driver updated from 1.4.x to **6.12.0**
|
||||
- `underscore` replaced with native ES2015+
|
||||
- npm dependencies updated (`cookies`, `http-proxy`, `portscanner`)
|
||||
- Fixed `Buffer()` deprecation and a pre-existing IPC listener bug
|
||||
- Balancer made transport-aware for compatibility with pluggable transports ([#14231](https://github.com/meteor/meteor/pull/14231))
|
||||
- Test suite adapted for Meteor 3
|
||||
|
||||
Full changelog: [METEOR3-PORT.md](https://github.com/dupontbertrand/cluster/blob/master/METEOR3-PORT.md)
|
||||
|
||||
## Known Limitations
|
||||
|
||||
- The balancer uses `OverShadowServerEvent` to intercept HTTP/WS traffic at the `httpServer` level. This is invasive and could break if Meteor changes its listener initialization order.
|
||||
- No Windows testing yet.
|
||||
- The worker pool uses `child_process.fork` with per-worker ports (not `node:cluster`).
|
||||
|
||||
## Compatibility
|
||||
|
||||
- Meteor 3.4+
|
||||
- Node 22
|
||||
- Tested on Linux
|
||||
|
||||
## Sources
|
||||
|
||||
- **Atmosphere:** [dupontbertrand:cluster](https://atmospherejs.com/dupontbertrand/cluster)
|
||||
- **GitHub:** [dupontbertrand/cluster](https://github.com/dupontbertrand/cluster)
|
||||
- **Original package:** [meteorhacks/cluster](https://github.com/meteorhacks/cluster)
|
||||
- **Forum Discussion:** [Memory usage and sessions quadruple after upgrading to Meteor 3](https://forums.meteor.com/t/memory-usage-and-sessions-quadruple-after-upgrading-to-meteor-3/64496)
|
||||
@@ -40,6 +40,14 @@ Please bear in mind if you are adding a package to this list, try providing as m
|
||||
- [`jam:soft-delete`](./soft-delete.md), An easy way to add soft deletes to your Meteor app
|
||||
- [`jam:archive`](./archive.md),
|
||||
|
||||
#### Scaling / Clustering
|
||||
|
||||
- [`dupontbertrand:cluster`](./cluster.md), Meteor 3 fork of meteorhacks:cluster — multi-core, load balancing, service discovery
|
||||
|
||||
#### Developer tools
|
||||
|
||||
- [`dupontbertrand:mail-preview`](./mail-preview.md), Zero-config dev-mode mail preview UI — view captured emails at `/__meteor_mail__/`
|
||||
|
||||
#### Utilities
|
||||
|
||||
- [`jam:offline`](./offline.md), An easy way to give your Meteor app offline capabilities and make it feel instant
|
||||
|
||||
123
v3-docs/docs/community-packages/mail-preview.md
Normal file
123
v3-docs/docs/community-packages/mail-preview.md
Normal file
@@ -0,0 +1,123 @@
|
||||
# Mail Preview
|
||||
|
||||
- `Who maintains the package` – [Bertrand Dupont](https://github.com/dupontbertrand)
|
||||
|
||||
[[toc]]
|
||||
|
||||
## What Is It?
|
||||
|
||||
A zero-config, dev-mode mail preview UI for Meteor. Every email sent via `Email.sendAsync()` is captured and displayed in a browser UI at `/__meteor_mail__/`.
|
||||
|
||||
Inspired by similar features in Rails (Action Mailer Preview), Phoenix (Swoosh), Laravel (Mailtrap), and Django (console backend).
|
||||
|
||||
This is a `devOnly` package — it is **automatically excluded from production builds**. Zero overhead in production, no need to remove it before deploying.
|
||||
|
||||
## How to Download It?
|
||||
|
||||
```bash
|
||||
meteor add dupontbertrand:mail-preview
|
||||
```
|
||||
|
||||
That's it. No configuration needed.
|
||||
|
||||
## How to Use It?
|
||||
|
||||
1. Start your Meteor app in development mode (`meteor run`)
|
||||
2. Trigger any email — password reset, verification, enrollment, or your own `Email.sendAsync()` calls
|
||||
3. Open `http://localhost:3000/__meteor_mail__/` in your browser
|
||||
|
||||
### What You Get
|
||||
|
||||
- **Mail list** — live-updating list of all captured emails (polls every 2s, no page reload)
|
||||
- **Mail detail** — view each email with tabs for **HTML render**, **Plain Text**, and **HTML Source**
|
||||
- **Clickable links** — verification, password reset, and enrollment links work directly from the preview
|
||||
- **JSON API** — programmatic access for testing and tooling
|
||||
- **Clear all** — one-click button to clear captured mails
|
||||
|
||||
### Example: Capturing an Accounts Email
|
||||
|
||||
```js
|
||||
// Server — nothing special needed, just use Meteor's built-in accounts
|
||||
import { Accounts } from 'meteor/accounts-base';
|
||||
|
||||
// When a user registers, Meteor sends a verification email.
|
||||
// mail-preview captures it automatically.
|
||||
Accounts.sendVerificationEmail(userId);
|
||||
```
|
||||
|
||||
Then open `/__meteor_mail__/` to see the captured email, click the verification link, and it works.
|
||||
|
||||
### Example: Custom Emails with MJML
|
||||
|
||||
```js
|
||||
// Server
|
||||
import { Email } from 'meteor/email';
|
||||
import mjml2html from 'mjml';
|
||||
|
||||
const { html } = mjml2html(`
|
||||
<mjml>
|
||||
<mj-body>
|
||||
<mj-section>
|
||||
<mj-column>
|
||||
<mj-text font-size="20px" color="#333">Hello from Meteor!</mj-text>
|
||||
<mj-button background-color="#0d6efd" href="https://meteor.com">
|
||||
Visit Meteor
|
||||
</mj-button>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
</mj-body>
|
||||
</mjml>
|
||||
`);
|
||||
|
||||
await Email.sendAsync({
|
||||
from: 'noreply@example.com',
|
||||
to: 'user@example.com',
|
||||
subject: 'Welcome!',
|
||||
html,
|
||||
});
|
||||
```
|
||||
|
||||
The MJML-rendered email is captured and displayed with full HTML rendering in the preview UI.
|
||||
|
||||
## JSON API
|
||||
|
||||
For programmatic access (useful in tests or tooling):
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
| -------- | ------------------------------ | ------------------------ |
|
||||
| `GET` | `/__meteor_mail__/api/mails` | List all captured mails |
|
||||
| `GET` | `/__meteor_mail__/api/mails/:id` | Get a single mail |
|
||||
| `DELETE` | `/__meteor_mail__/api/mails` | Clear all captured mails |
|
||||
|
||||
### Example: Using the API in Tests
|
||||
|
||||
```js
|
||||
// In a test, after triggering an email:
|
||||
const res = await fetch('http://localhost:3000/__meteor_mail__/api/mails');
|
||||
const { mails } = await res.json();
|
||||
|
||||
assert.equal(mails[0].subject, 'Verify your email');
|
||||
assert.ok(mails[0].text.includes('verify-email'));
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
The package uses `Email.hookSend()` to intercept outgoing emails and store them in memory (up to 50 — oldest are evicted). A middleware mounted via `WebApp.rawConnectHandlers` serves the preview UI.
|
||||
|
||||
- **Dev mode only** — guarded by `Meteor.isDevelopment` and `devOnly: true` in `package.js`
|
||||
- **No SMTP needed** — emails are captured before they reach any transport
|
||||
- **No external dependencies** — uses only Meteor core packages (`email`, `webapp`, `ecmascript`)
|
||||
- **Works alongside `MAIL_URL`** — if set, emails are still sent normally; the hook captures a copy
|
||||
|
||||
## Compatibility
|
||||
|
||||
- Meteor 3.4+
|
||||
- Works with `accounts-password` emails (verification, reset password, enrollment)
|
||||
- Works with custom `Email.sendAsync()` / `Email.send()` calls
|
||||
- Compatible with Rspack bundler
|
||||
|
||||
## Sources
|
||||
|
||||
- **Atmosphere:** [dupontbertrand:mail-preview](https://atmospherejs.com/dupontbertrand/mail-preview)
|
||||
- **GitHub:** [dupontbertrand/meteor-mail-preview](https://github.com/dupontbertrand/meteor-mail-preview)
|
||||
- **Forum Discussion:** [Built-in Mail Preview UI for Dev Mode](https://forums.meteor.com/t/built-in-mail-preview-ui-for-dev-mode/64489)
|
||||
Reference in New Issue
Block a user