create-meith 0.19.0 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.mjs +73 -46
- package/package.json +1 -1
- package/src/bin.ts +1 -1
- package/src/index.ts +1 -1
- package/src/scaffold.ts +72 -44
package/dist/bin.mjs
CHANGED
|
@@ -25,7 +25,8 @@ var MATERIALIZED_AT_ROOT = [
|
|
|
25
25
|
];
|
|
26
26
|
var VERCEL_BUILD_COMMAND = `community migrate && forum-web build ${AT_ROOT_FLAG}`;
|
|
27
27
|
var TICK_PATH = "/api/system/tick";
|
|
28
|
-
var TICK_SCHEDULE = "
|
|
28
|
+
var TICK_SCHEDULE = "0 3 * * *";
|
|
29
|
+
var RESEND_SENDER_MAILBOX = "noreply";
|
|
29
30
|
var MATERIALIZED_PUBLIC = [
|
|
30
31
|
"placeholder-logo.png",
|
|
31
32
|
"placeholder-logo.svg",
|
|
@@ -278,15 +279,17 @@ BLOB_READ_WRITE_TOKEN=
|
|
|
278
279
|
# board refuses it on Vercel; 587 with STARTTLS may work, but an API does not
|
|
279
280
|
# depend on the platform's egress rules staying as they are.
|
|
280
281
|
#
|
|
281
|
-
# MAIL_FROM is
|
|
282
|
-
#
|
|
283
|
-
#
|
|
284
|
-
#
|
|
282
|
+
# MAIL_FROM is optional where a provider publishes the domain it sends from:
|
|
283
|
+
# with RESEND_API_KEY and RESEND_EMAIL_DOMAIN both set, the board sends from
|
|
284
|
+
# noreply@ that domain and this can stay empty. Set it to send from another
|
|
285
|
+
# address \u2014 it must be at a domain the provider has verified for you, and an
|
|
286
|
+
# address set here always wins over the derived one.
|
|
285
287
|
MAIL_FROM=
|
|
286
288
|
|
|
287
289
|
# Add the Resend integration to the project from Vercel's marketplace and it
|
|
288
|
-
# publishes its key under
|
|
289
|
-
#
|
|
290
|
+
# publishes its key and its sending domain under these names, which the board
|
|
291
|
+
# reads: with RESEND_API_KEY set, and either RESEND_EMAIL_DOMAIN or a MAIL_FROM
|
|
292
|
+
# beside it, the board sends over Resend's HTTPS API and
|
|
290
293
|
# needs neither of the two variables below. The mail driver itself is a plain
|
|
291
294
|
# JSON-over-HTTPS sender and is not Resend-specific \u2014 this is one injected name
|
|
292
295
|
# bridged to the generic pair, not a provider baked into the board.
|
|
@@ -330,7 +333,7 @@ var VERCEL_DERIVED_DRIVERS = [
|
|
|
330
333
|
"MAIL_DRIVER=http"
|
|
331
334
|
];
|
|
332
335
|
var VERCEL_PROMPTED_ENV = ["AUTH_SECRET", "CRON_SECRET"];
|
|
333
|
-
var
|
|
336
|
+
var VERCEL_MARKETPLACE_PRODUCTS = [
|
|
334
337
|
{ type: "integration", integrationSlug: "neon", productSlug: "neon", protocol: "storage" },
|
|
335
338
|
{
|
|
336
339
|
type: "integration",
|
|
@@ -338,7 +341,13 @@ var VERCEL_MARKETPLACE_STORES = [
|
|
|
338
341
|
productSlug: "upstash-kv",
|
|
339
342
|
protocol: "storage"
|
|
340
343
|
},
|
|
341
|
-
{ type: "blob" }
|
|
344
|
+
{ type: "blob" },
|
|
345
|
+
{
|
|
346
|
+
type: "integration",
|
|
347
|
+
integrationSlug: "resend",
|
|
348
|
+
productSlug: "resend-email",
|
|
349
|
+
protocol: "messaging"
|
|
350
|
+
}
|
|
342
351
|
];
|
|
343
352
|
function deployButtonUrl(templateRepositoryUrl) {
|
|
344
353
|
const params = new URLSearchParams([
|
|
@@ -348,10 +357,10 @@ function deployButtonUrl(templateRepositoryUrl) {
|
|
|
348
357
|
["env", VERCEL_PROMPTED_ENV.join(",")],
|
|
349
358
|
[
|
|
350
359
|
"envDescription",
|
|
351
|
-
"Two secrets, generated rather than chosen \u2014 32 characters or more each. Everything else the board reads from the database, cache
|
|
360
|
+
"Two secrets, generated rather than chosen \u2014 32 characters or more each. Everything else the board reads from the database, cache, blob store and mail provider this form links."
|
|
352
361
|
],
|
|
353
362
|
["envLink", `${templateRepositoryUrl}/blob/main/.env.example`],
|
|
354
|
-
["
|
|
363
|
+
["products", JSON.stringify(VERCEL_MARKETPLACE_PRODUCTS)],
|
|
355
364
|
["skippable-integrations", "1"]
|
|
356
365
|
]);
|
|
357
366
|
return `https://vercel.com/new/clone?${params.toString()}`;
|
|
@@ -429,6 +438,7 @@ import { defineForumConfig } from '@meith/web/config'
|
|
|
429
438
|
import {
|
|
430
439
|
BROWSER_THEME_COLOR,
|
|
431
440
|
DARK_TOKENS,
|
|
441
|
+
defaultMessages,
|
|
432
442
|
defaultTheme,
|
|
433
443
|
LIGHT_TOKENS,
|
|
434
444
|
} from '@meith/theme-default'
|
|
@@ -443,6 +453,7 @@ export default defineForumConfig({
|
|
|
443
453
|
tokens: { light: LIGHT_TOKENS, dark: DARK_TOKENS },
|
|
444
454
|
browserThemeColor: BROWSER_THEME_COLOR,
|
|
445
455
|
theme: defaultTheme,
|
|
456
|
+
messages: defaultMessages,
|
|
446
457
|
},
|
|
447
458
|
},
|
|
448
459
|
defaultTheme: 'default',
|
|
@@ -1007,18 +1018,20 @@ A forum, built on [Meith](${repositoryUrl}), running as Vercel functions.
|
|
|
1007
1018
|
Vercel's SDK, which authenticates with the deployment's own OIDC identity, so
|
|
1008
1019
|
there is no token to copy. This is what used to be four hand-typed \`S3_*\`
|
|
1009
1020
|
secrets.
|
|
1021
|
+
- **A Resend mail account**, attached the same way, which publishes
|
|
1022
|
+
\`RESEND_API_KEY\` and \`RESEND_EMAIL_DOMAIN\`. The board reads both names
|
|
1023
|
+
directly: its mail driver already speaks Resend's request shape, so there is
|
|
1024
|
+
nothing to adapt, and the sending domain is what the sender is built from.
|
|
1010
1025
|
- **A Vercel project** carrying \`vercel.json\` \u2014 the build command
|
|
1011
1026
|
\`${VERCEL_BUILD_COMMAND}\`,
|
|
1012
1027
|
which applies the schema before it builds, materializes the board's app at
|
|
1013
1028
|
the project root so the artefact lands where Vercel reads it, and the cron
|
|
1014
1029
|
entry that drives the tick.
|
|
1015
1030
|
|
|
1016
|
-
**Mail
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
board boots and runs without mail, and delivers nothing, silently, until it is
|
|
1021
|
-
done.
|
|
1031
|
+
**Mail needs no variables after the deploy.** Resend publishes both its key
|
|
1032
|
+
and its sending domain, and the board sends from \`${RESEND_SENDER_MAILBOX}@\`
|
|
1033
|
+
that domain \u2014 see *Mail* below to send from a different address, and for the
|
|
1034
|
+
one case that does need you: a domain Resend has not verified yet.
|
|
1022
1035
|
|
|
1023
1036
|
## What to type into the deploy form
|
|
1024
1037
|
|
|
@@ -1069,26 +1082,30 @@ Vercel* below for why that matters \u2014 set \`FILESTORE_DRIVER=s3\` and add
|
|
|
1069
1082
|
project's environment settings, with \`S3_ENDPOINT\` for a bucket that is not AWS
|
|
1070
1083
|
(\`S3_REGION=auto\` for R2). The same board runs either way.
|
|
1071
1084
|
|
|
1072
|
-
## Mail
|
|
1085
|
+
## Mail
|
|
1086
|
+
|
|
1087
|
+
**There is nothing to set.** The Resend the deploy form added publishes two
|
|
1088
|
+
names into the project: \`RESEND_API_KEY\`, and \`RESEND_EMAIL_DOMAIN\` \u2014 the
|
|
1089
|
+
domain it sends from. The board reads both, sends from
|
|
1090
|
+
\`${RESEND_SENDER_MAILBOX}@\` that domain, and posts over Resend's HTTPS API.
|
|
1073
1091
|
|
|
1074
|
-
|
|
1075
|
-
|
|
1092
|
+
**If Resend refuses the messages**, that domain is not verified yet. Resend
|
|
1093
|
+
will not send from a domain it has not verified, whoever set the address, so
|
|
1094
|
+
verify it from the Resend dashboard \u2014 the deploy cannot do that step for you,
|
|
1095
|
+
because it is Resend confirming you own the domain. This is the one thing here
|
|
1096
|
+
that can need attention, and it announces itself: the test button below says
|
|
1097
|
+
so rather than the board failing quietly.
|
|
1076
1098
|
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
2. Verify that domain in the Resend dashboard if you have not already. Resend
|
|
1081
|
-
refuses to send from an address at a domain it has not verified.
|
|
1082
|
-
3. Add \`MAIL_FROM\` to the project's environment settings \u2014 an address at that
|
|
1083
|
-
verified domain, and the only mail value you ever type. The deploy form does
|
|
1084
|
-
not ask for it, because a sender address is not something you can know
|
|
1085
|
-
before there is a provider to verify it.
|
|
1086
|
-
4. Redeploy, or let the next push redeploy.
|
|
1099
|
+
**To send from a different address**, set \`MAIL_FROM\` in the project's
|
|
1100
|
+
environment settings and redeploy. It must be at a domain Resend has verified,
|
|
1101
|
+
for the same reason. An address you set always wins over the derived one.
|
|
1087
1102
|
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1103
|
+
A board with the key but no verified domain \u2014 which is what you get if you
|
|
1104
|
+
remove the integration's \`RESEND_EMAIL_DOMAIN\` without putting a
|
|
1105
|
+
\`MAIL_FROM\` in its place \u2014 does not guess a sender. It stays on the log
|
|
1106
|
+
driver and delivers nothing, which is the honest outcome: a guessed sender at
|
|
1107
|
+
an unverified domain would be refused by Resend anyway, one message at a
|
|
1108
|
+
time.
|
|
1092
1109
|
|
|
1093
1110
|
The board is not tied to Resend. Its mail driver is a plain JSON-over-HTTPS
|
|
1094
1111
|
sender that posts \`{from, to, subject, text, html, reply_to}\` with a bearer
|
|
@@ -1097,8 +1114,10 @@ it needs no adapter. Any provider with the same shape works: set
|
|
|
1097
1114
|
\`MAIL_HTTP_ENDPOINT\`, \`MAIL_HTTP_TOKEN\` and \`MAIL_DRIVER=http\` in the
|
|
1098
1115
|
project's environment settings, and set the first two **together** \u2014 either
|
|
1099
1116
|
one on its own stands the Resend bridge down, so a key issued for Resend is
|
|
1100
|
-
never presented to an endpoint you chose.
|
|
1101
|
-
|
|
1117
|
+
never presented to an endpoint you chose. Setting \`MAIL_DRIVER\` to anything
|
|
1118
|
+
but \`http\` stands the bridge down too, for the same reason: a board that
|
|
1119
|
+
moved to SMTP must not send through its new provider from Resend's domain.
|
|
1120
|
+
Delete \`RESEND_API_KEY\` once you have moved off Resend.
|
|
1102
1121
|
|
|
1103
1122
|
Check it worked: sign in as the administrator and use the test button on
|
|
1104
1123
|
**/admin \u2192 Settings \u2192 Mail**.
|
|
@@ -1122,15 +1141,23 @@ drains; nothing here runs it on its own, because there is no worker process on
|
|
|
1122
1141
|
a function platform. Two things about it are worth knowing **before** you
|
|
1123
1142
|
deploy rather than after:
|
|
1124
1143
|
|
|
1125
|
-
- **
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1144
|
+
- **This ships a daily schedule, because Hobby refuses anything faster.** A
|
|
1145
|
+
Hobby plan rejects a cron expression that would run more than once a day \u2014
|
|
1146
|
+
the deployment fails outright rather than being slowed down \u2014 so
|
|
1147
|
+
\`vercel.json\` carries \`${TICK_SCHEDULE}\` and deploys anywhere. On a
|
|
1148
|
+
paid plan, edit it to \`* * * * *\` and the board ticks every minute.
|
|
1149
|
+
|
|
1150
|
+
A daily tick loses nothing permanently: tasks are written so a missed run
|
|
1151
|
+
delays work rather than dropping it, and a password reset is sent as the
|
|
1152
|
+
request is handled rather than waiting for a tick. What it does delay is
|
|
1153
|
+
everything the tick drives \u2014 a new post is not findable in search, and a
|
|
1154
|
+
notification is not sent, until the next run.
|
|
1155
|
+
|
|
1156
|
+
**To keep a fast tick without paying**, drive \`${TICK_PATH}\` from anything
|
|
1157
|
+
that can call a URL on a schedule \u2014 a GitHub Actions workflow, a systemd
|
|
1158
|
+
timer, an uptime pinger \u2014 presenting \`TICK_SECRET\` instead of
|
|
1159
|
+
\`CRON_SECRET\`. The endpoint accepts either, so the Vercel cron and an
|
|
1160
|
+
outside scheduler can both drive it.
|
|
1134
1161
|
- **\`maxDuration = 300\` is validated when the project builds, not when the
|
|
1135
1162
|
function runs.** A plan that does not allow 300 seconds therefore **fails the
|
|
1136
1163
|
deployment** rather than clamping the request. With Fluid Compute \u2014 the
|
|
@@ -1329,7 +1356,7 @@ async function run(argv, version) {
|
|
|
1329
1356
|
}
|
|
1330
1357
|
|
|
1331
1358
|
// src/bin.ts
|
|
1332
|
-
var result = await run(process.argv.slice(2), "0.
|
|
1359
|
+
var result = await run(process.argv.slice(2), "0.21.0");
|
|
1333
1360
|
for (const line of result.lines) {
|
|
1334
1361
|
if (result.code === 0) console.log(line);
|
|
1335
1362
|
else console.error(line);
|
package/package.json
CHANGED
package/src/bin.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { run } from './cli'
|
|
3
3
|
|
|
4
|
-
const result = await run(process.argv.slice(2), '0.
|
|
4
|
+
const result = await run(process.argv.slice(2), '0.21.0')
|
|
5
5
|
for (const line of result.lines) {
|
|
6
6
|
if (result.code === 0) console.log(line)
|
|
7
7
|
else console.error(line)
|
package/src/index.ts
CHANGED
package/src/scaffold.ts
CHANGED
|
@@ -33,7 +33,9 @@ export const VERCEL_BUILD_COMMAND = `community migrate && forum-web build ${AT_R
|
|
|
33
33
|
|
|
34
34
|
export const TICK_PATH = '/api/system/tick'
|
|
35
35
|
|
|
36
|
-
export const TICK_SCHEDULE = '
|
|
36
|
+
export const TICK_SCHEDULE = '0 3 * * *'
|
|
37
|
+
|
|
38
|
+
export const RESEND_SENDER_MAILBOX = 'noreply'
|
|
37
39
|
|
|
38
40
|
export const MATERIALIZED_PUBLIC = [
|
|
39
41
|
'placeholder-logo.png',
|
|
@@ -302,15 +304,17 @@ BLOB_READ_WRITE_TOKEN=
|
|
|
302
304
|
# board refuses it on Vercel; 587 with STARTTLS may work, but an API does not
|
|
303
305
|
# depend on the platform's egress rules staying as they are.
|
|
304
306
|
#
|
|
305
|
-
# MAIL_FROM is
|
|
306
|
-
#
|
|
307
|
-
#
|
|
308
|
-
#
|
|
307
|
+
# MAIL_FROM is optional where a provider publishes the domain it sends from:
|
|
308
|
+
# with RESEND_API_KEY and RESEND_EMAIL_DOMAIN both set, the board sends from
|
|
309
|
+
# noreply@ that domain and this can stay empty. Set it to send from another
|
|
310
|
+
# address — it must be at a domain the provider has verified for you, and an
|
|
311
|
+
# address set here always wins over the derived one.
|
|
309
312
|
MAIL_FROM=
|
|
310
313
|
|
|
311
314
|
# Add the Resend integration to the project from Vercel's marketplace and it
|
|
312
|
-
# publishes its key under
|
|
313
|
-
#
|
|
315
|
+
# publishes its key and its sending domain under these names, which the board
|
|
316
|
+
# reads: with RESEND_API_KEY set, and either RESEND_EMAIL_DOMAIN or a MAIL_FROM
|
|
317
|
+
# beside it, the board sends over Resend's HTTPS API and
|
|
314
318
|
# needs neither of the two variables below. The mail driver itself is a plain
|
|
315
319
|
# JSON-over-HTTPS sender and is not Resend-specific — this is one injected name
|
|
316
320
|
# bridged to the generic pair, not a provider baked into the board.
|
|
@@ -359,7 +363,7 @@ const VERCEL_DERIVED_DRIVERS = [
|
|
|
359
363
|
|
|
360
364
|
export const VERCEL_PROMPTED_ENV = ['AUTH_SECRET', 'CRON_SECRET'] as const
|
|
361
365
|
|
|
362
|
-
export const
|
|
366
|
+
export const VERCEL_MARKETPLACE_PRODUCTS = [
|
|
363
367
|
{ type: 'integration', integrationSlug: 'neon', productSlug: 'neon', protocol: 'storage' },
|
|
364
368
|
{
|
|
365
369
|
type: 'integration',
|
|
@@ -368,6 +372,12 @@ export const VERCEL_MARKETPLACE_STORES = [
|
|
|
368
372
|
protocol: 'storage',
|
|
369
373
|
},
|
|
370
374
|
{ type: 'blob' },
|
|
375
|
+
{
|
|
376
|
+
type: 'integration',
|
|
377
|
+
integrationSlug: 'resend',
|
|
378
|
+
productSlug: 'resend-email',
|
|
379
|
+
protocol: 'messaging',
|
|
380
|
+
},
|
|
371
381
|
] as const
|
|
372
382
|
|
|
373
383
|
export function deployButtonUrl(templateRepositoryUrl: string): string {
|
|
@@ -378,10 +388,10 @@ export function deployButtonUrl(templateRepositoryUrl: string): string {
|
|
|
378
388
|
['env', VERCEL_PROMPTED_ENV.join(',')],
|
|
379
389
|
[
|
|
380
390
|
'envDescription',
|
|
381
|
-
'Two secrets, generated rather than chosen — 32 characters or more each. Everything else the board reads from the database, cache
|
|
391
|
+
'Two secrets, generated rather than chosen — 32 characters or more each. Everything else the board reads from the database, cache, blob store and mail provider this form links.',
|
|
382
392
|
],
|
|
383
393
|
['envLink', `${templateRepositoryUrl}/blob/main/.env.example`],
|
|
384
|
-
['
|
|
394
|
+
['products', JSON.stringify(VERCEL_MARKETPLACE_PRODUCTS)],
|
|
385
395
|
['skippable-integrations', '1'],
|
|
386
396
|
])
|
|
387
397
|
|
|
@@ -463,6 +473,7 @@ import { defineForumConfig } from '@meith/web/config'
|
|
|
463
473
|
import {
|
|
464
474
|
BROWSER_THEME_COLOR,
|
|
465
475
|
DARK_TOKENS,
|
|
476
|
+
defaultMessages,
|
|
466
477
|
defaultTheme,
|
|
467
478
|
LIGHT_TOKENS,
|
|
468
479
|
} from '@meith/theme-default'
|
|
@@ -477,6 +488,7 @@ export default defineForumConfig({
|
|
|
477
488
|
tokens: { light: LIGHT_TOKENS, dark: DARK_TOKENS },
|
|
478
489
|
browserThemeColor: BROWSER_THEME_COLOR,
|
|
479
490
|
theme: defaultTheme,
|
|
491
|
+
messages: defaultMessages,
|
|
480
492
|
},
|
|
481
493
|
},
|
|
482
494
|
defaultTheme: 'default',
|
|
@@ -1068,18 +1080,20 @@ A forum, built on [Meith](${repositoryUrl}), running as Vercel functions.
|
|
|
1068
1080
|
Vercel's SDK, which authenticates with the deployment's own OIDC identity, so
|
|
1069
1081
|
there is no token to copy. This is what used to be four hand-typed \`S3_*\`
|
|
1070
1082
|
secrets.
|
|
1083
|
+
- **A Resend mail account**, attached the same way, which publishes
|
|
1084
|
+
\`RESEND_API_KEY\` and \`RESEND_EMAIL_DOMAIN\`. The board reads both names
|
|
1085
|
+
directly: its mail driver already speaks Resend's request shape, so there is
|
|
1086
|
+
nothing to adapt, and the sending domain is what the sender is built from.
|
|
1071
1087
|
- **A Vercel project** carrying \`vercel.json\` — the build command
|
|
1072
1088
|
\`${VERCEL_BUILD_COMMAND}\`,
|
|
1073
1089
|
which applies the schema before it builds, materializes the board's app at
|
|
1074
1090
|
the project root so the artefact lands where Vercel reads it, and the cron
|
|
1075
1091
|
entry that drives the tick.
|
|
1076
1092
|
|
|
1077
|
-
**Mail
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
board boots and runs without mail, and delivers nothing, silently, until it is
|
|
1082
|
-
done.
|
|
1093
|
+
**Mail needs no variables after the deploy.** Resend publishes both its key
|
|
1094
|
+
and its sending domain, and the board sends from \`${RESEND_SENDER_MAILBOX}@\`
|
|
1095
|
+
that domain — see *Mail* below to send from a different address, and for the
|
|
1096
|
+
one case that does need you: a domain Resend has not verified yet.
|
|
1083
1097
|
|
|
1084
1098
|
## What to type into the deploy form
|
|
1085
1099
|
|
|
@@ -1130,26 +1144,30 @@ Vercel* below for why that matters — set \`FILESTORE_DRIVER=s3\` and add
|
|
|
1130
1144
|
project's environment settings, with \`S3_ENDPOINT\` for a bucket that is not AWS
|
|
1131
1145
|
(\`S3_REGION=auto\` for R2). The same board runs either way.
|
|
1132
1146
|
|
|
1133
|
-
## Mail
|
|
1147
|
+
## Mail
|
|
1148
|
+
|
|
1149
|
+
**There is nothing to set.** The Resend the deploy form added publishes two
|
|
1150
|
+
names into the project: \`RESEND_API_KEY\`, and \`RESEND_EMAIL_DOMAIN\` — the
|
|
1151
|
+
domain it sends from. The board reads both, sends from
|
|
1152
|
+
\`${RESEND_SENDER_MAILBOX}@\` that domain, and posts over Resend's HTTPS API.
|
|
1134
1153
|
|
|
1135
|
-
|
|
1136
|
-
|
|
1154
|
+
**If Resend refuses the messages**, that domain is not verified yet. Resend
|
|
1155
|
+
will not send from a domain it has not verified, whoever set the address, so
|
|
1156
|
+
verify it from the Resend dashboard — the deploy cannot do that step for you,
|
|
1157
|
+
because it is Resend confirming you own the domain. This is the one thing here
|
|
1158
|
+
that can need attention, and it announces itself: the test button below says
|
|
1159
|
+
so rather than the board failing quietly.
|
|
1137
1160
|
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
2. Verify that domain in the Resend dashboard if you have not already. Resend
|
|
1142
|
-
refuses to send from an address at a domain it has not verified.
|
|
1143
|
-
3. Add \`MAIL_FROM\` to the project's environment settings — an address at that
|
|
1144
|
-
verified domain, and the only mail value you ever type. The deploy form does
|
|
1145
|
-
not ask for it, because a sender address is not something you can know
|
|
1146
|
-
before there is a provider to verify it.
|
|
1147
|
-
4. Redeploy, or let the next push redeploy.
|
|
1161
|
+
**To send from a different address**, set \`MAIL_FROM\` in the project's
|
|
1162
|
+
environment settings and redeploy. It must be at a domain Resend has verified,
|
|
1163
|
+
for the same reason. An address you set always wins over the derived one.
|
|
1148
1164
|
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1165
|
+
A board with the key but no verified domain — which is what you get if you
|
|
1166
|
+
remove the integration's \`RESEND_EMAIL_DOMAIN\` without putting a
|
|
1167
|
+
\`MAIL_FROM\` in its place — does not guess a sender. It stays on the log
|
|
1168
|
+
driver and delivers nothing, which is the honest outcome: a guessed sender at
|
|
1169
|
+
an unverified domain would be refused by Resend anyway, one message at a
|
|
1170
|
+
time.
|
|
1153
1171
|
|
|
1154
1172
|
The board is not tied to Resend. Its mail driver is a plain JSON-over-HTTPS
|
|
1155
1173
|
sender that posts \`{from, to, subject, text, html, reply_to}\` with a bearer
|
|
@@ -1158,8 +1176,10 @@ it needs no adapter. Any provider with the same shape works: set
|
|
|
1158
1176
|
\`MAIL_HTTP_ENDPOINT\`, \`MAIL_HTTP_TOKEN\` and \`MAIL_DRIVER=http\` in the
|
|
1159
1177
|
project's environment settings, and set the first two **together** — either
|
|
1160
1178
|
one on its own stands the Resend bridge down, so a key issued for Resend is
|
|
1161
|
-
never presented to an endpoint you chose.
|
|
1162
|
-
|
|
1179
|
+
never presented to an endpoint you chose. Setting \`MAIL_DRIVER\` to anything
|
|
1180
|
+
but \`http\` stands the bridge down too, for the same reason: a board that
|
|
1181
|
+
moved to SMTP must not send through its new provider from Resend's domain.
|
|
1182
|
+
Delete \`RESEND_API_KEY\` once you have moved off Resend.
|
|
1163
1183
|
|
|
1164
1184
|
Check it worked: sign in as the administrator and use the test button on
|
|
1165
1185
|
**/admin → Settings → Mail**.
|
|
@@ -1183,15 +1203,23 @@ drains; nothing here runs it on its own, because there is no worker process on
|
|
|
1183
1203
|
a function platform. Two things about it are worth knowing **before** you
|
|
1184
1204
|
deploy rather than after:
|
|
1185
1205
|
|
|
1186
|
-
- **
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1206
|
+
- **This ships a daily schedule, because Hobby refuses anything faster.** A
|
|
1207
|
+
Hobby plan rejects a cron expression that would run more than once a day —
|
|
1208
|
+
the deployment fails outright rather than being slowed down — so
|
|
1209
|
+
\`vercel.json\` carries \`${TICK_SCHEDULE}\` and deploys anywhere. On a
|
|
1210
|
+
paid plan, edit it to \`* * * * *\` and the board ticks every minute.
|
|
1211
|
+
|
|
1212
|
+
A daily tick loses nothing permanently: tasks are written so a missed run
|
|
1213
|
+
delays work rather than dropping it, and a password reset is sent as the
|
|
1214
|
+
request is handled rather than waiting for a tick. What it does delay is
|
|
1215
|
+
everything the tick drives — a new post is not findable in search, and a
|
|
1216
|
+
notification is not sent, until the next run.
|
|
1217
|
+
|
|
1218
|
+
**To keep a fast tick without paying**, drive \`${TICK_PATH}\` from anything
|
|
1219
|
+
that can call a URL on a schedule — a GitHub Actions workflow, a systemd
|
|
1220
|
+
timer, an uptime pinger — presenting \`TICK_SECRET\` instead of
|
|
1221
|
+
\`CRON_SECRET\`. The endpoint accepts either, so the Vercel cron and an
|
|
1222
|
+
outside scheduler can both drive it.
|
|
1195
1223
|
- **\`maxDuration = 300\` is validated when the project builds, not when the
|
|
1196
1224
|
function runs.** A plan that does not allow 300 seconds therefore **fails the
|
|
1197
1225
|
deployment** rather than clamping the request. With Fluid Compute — the
|