create-meith 0.37.2 → 0.37.4
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 +117 -473
- package/package.json +1 -1
- package/src/bin.ts +1 -1
- package/src/scaffold-extension.ts +8 -9
- package/src/scaffold.ts +108 -463
package/src/scaffold.ts
CHANGED
|
@@ -496,7 +496,7 @@ save-exact=true
|
|
|
496
496
|
*
|
|
497
497
|
* Adding a theme is: \`npm install\` it, add a line here, redeploy. Adding a
|
|
498
498
|
* plugin is the same, through board.plugins.json and meith.plugins.ts —
|
|
499
|
-
* see docs/
|
|
499
|
+
* see docs/extensions/plugins.md.
|
|
500
500
|
*/
|
|
501
501
|
import { defineForumConfig } from '@meith/web/config'
|
|
502
502
|
import {
|
|
@@ -537,7 +537,7 @@ export default defineForumConfig({
|
|
|
537
537
|
// them. A plugin that does not fit that convention can be added here by hand instead —
|
|
538
538
|
// keep it out of board.plugins.json so a regenerate does not drop it.
|
|
539
539
|
//
|
|
540
|
-
// docs/
|
|
540
|
+
// docs/extensions/plugins.md explains both.
|
|
541
541
|
|
|
542
542
|
import type { InstalledPlugin } from '@meith/web/config'
|
|
543
543
|
|
|
@@ -677,7 +677,7 @@ jobs:
|
|
|
677
677
|
# \`npm install\` below), so a build here is heavier than \`Dockerfile.prebuilt\`'s
|
|
678
678
|
# thin delta — that image, pulled rather than built, is the trade the advanced
|
|
679
679
|
# path takes for a low-spec build server or a faster deploy (see \`README.md\`
|
|
680
|
-
# and, in the meith repository, docs/
|
|
680
|
+
# and, in the meith repository, docs/operations/docker-compose.md,
|
|
681
681
|
# "Custom boards").
|
|
682
682
|
#
|
|
683
683
|
# Two stages, not three: unlike the official image, this does not prune down
|
|
@@ -776,7 +776,7 @@ ENTRYPOINT ["./docker-entrypoint.sh"]
|
|
|
776
776
|
#
|
|
777
777
|
# FROM the published framework base image — deps + framework layers only,
|
|
778
778
|
# locked to this exact release (see the meith repository's
|
|
779
|
-
# docs/
|
|
779
|
+
# docs/operations/docker-compose.md, "Custom boards", and docker/Dockerfile.base for what
|
|
780
780
|
# it is and is not). This board's own Dockerfile only ever installs its own
|
|
781
781
|
# delta on top of it — a new plugin's own dependency, typically nothing more
|
|
782
782
|
# — which is what keeps a rebuild after \`npm install some-plugin\` a matter
|
|
@@ -1120,7 +1120,7 @@ services:
|
|
|
1120
1120
|
# service the variables the file names, so a Scheduled Task running
|
|
1121
1121
|
# \`meith backup\` in this container would never see them without these
|
|
1122
1122
|
# lines — see the meith repository's
|
|
1123
|
-
# docs/
|
|
1123
|
+
# docs/operations/coolify.md, "Set up backups".
|
|
1124
1124
|
- BACKUP_S3_BUCKET=\${BACKUP_S3_BUCKET:-}
|
|
1125
1125
|
- BACKUP_S3_REGION=\${BACKUP_S3_REGION:-}
|
|
1126
1126
|
- BACKUP_S3_ACCESS_KEY_ID=\${BACKUP_S3_ACCESS_KEY_ID:-}
|
|
@@ -1146,7 +1146,7 @@ services:
|
|
|
1146
1146
|
# @meith/worker is not published (see the meith repository's
|
|
1147
1147
|
# docs/contributing/release.md), so there is no compiled worker binary a scaffolded
|
|
1148
1148
|
# board can run — this drives the tick the alternative way the meith
|
|
1149
|
-
# repository documents in docs/
|
|
1149
|
+
# repository documents in docs/operations/docker-compose.md, "Running the tick without
|
|
1150
1150
|
# a second set of credentials": a small loop calling /api/system/tick.
|
|
1151
1151
|
worker:
|
|
1152
1152
|
image: alpine:3.24@sha256:28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
|
|
@@ -1293,7 +1293,7 @@ services:
|
|
|
1293
1293
|
# service the variables the file names, so a Scheduled Task running
|
|
1294
1294
|
# \`meith backup\` in this container would never see them without these
|
|
1295
1295
|
# lines — see the meith repository's
|
|
1296
|
-
# docs/
|
|
1296
|
+
# docs/operations/coolify.md, "Set up backups".
|
|
1297
1297
|
- BACKUP_S3_BUCKET=\${BACKUP_S3_BUCKET:-}
|
|
1298
1298
|
- BACKUP_S3_REGION=\${BACKUP_S3_REGION:-}
|
|
1299
1299
|
- BACKUP_S3_ACCESS_KEY_ID=\${BACKUP_S3_ACCESS_KEY_ID:-}
|
|
@@ -1319,7 +1319,7 @@ services:
|
|
|
1319
1319
|
# @meith/worker is not published (see the meith repository's
|
|
1320
1320
|
# docs/contributing/release.md), so there is no compiled worker binary a scaffolded
|
|
1321
1321
|
# board can run — this drives the tick the alternative way the meith
|
|
1322
|
-
# repository documents in docs/
|
|
1322
|
+
# repository documents in docs/operations/docker-compose.md, "Running the tick without
|
|
1323
1323
|
# a second set of credentials": a small loop calling /api/system/tick.
|
|
1324
1324
|
worker:
|
|
1325
1325
|
image: alpine:3.24@sha256:28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
|
|
@@ -1358,7 +1358,7 @@ volumes:
|
|
|
1358
1358
|
# secrets for you: every value Coolify would have filled in — the database
|
|
1359
1359
|
# password, AUTH_SECRET, TICK_SECRET, the board's own address — comes from
|
|
1360
1360
|
# a \`.env\` beside this file instead. See the meith repository's
|
|
1361
|
-
# docs/
|
|
1361
|
+
# docs/operations/docker-compose.md, which this file is
|
|
1362
1362
|
# the last step of.
|
|
1363
1363
|
#
|
|
1364
1364
|
# \`docker compose\` only auto-discovers a file literally named
|
|
@@ -1374,7 +1374,7 @@ volumes:
|
|
|
1374
1374
|
# to GitHub and let \`.github/workflows/build.yml\` do it, or run
|
|
1375
1375
|
# \`docker build -f Dockerfile.prebuilt ...\` by hand) and change the two
|
|
1376
1376
|
# \`build: .\` lines below to \`image: <that image>:<version>\` — the
|
|
1377
|
-
# substitution docs/
|
|
1377
|
+
# substitution docs/operations/docker-compose.md, "Building
|
|
1378
1378
|
# somewhere else", walks through.
|
|
1379
1379
|
services:
|
|
1380
1380
|
postgres:
|
|
@@ -1456,7 +1456,7 @@ services:
|
|
|
1456
1456
|
APP_URL: \${APP_URL:-http://localhost:3000}
|
|
1457
1457
|
# One reverse proxy (Caddy, in the guide's own walkthrough) sits in
|
|
1458
1458
|
# front of \`web\` — see "Count your proxies" in
|
|
1459
|
-
# docs/
|
|
1459
|
+
# docs/operations/docker-compose.md.
|
|
1460
1460
|
TRUSTED_PROXY_HOPS: \${TRUSTED_PROXY_HOPS:-1}
|
|
1461
1461
|
# Leaving this at \`log\` does not mean no mail: it means the board
|
|
1462
1462
|
# decides, from the installer on first run or from
|
|
@@ -1519,7 +1519,7 @@ services:
|
|
|
1519
1519
|
depends_on:
|
|
1520
1520
|
- web
|
|
1521
1521
|
|
|
1522
|
-
# Off by default — see docs/
|
|
1522
|
+
# Off by default — see docs/operations/scaling.md before setting
|
|
1523
1523
|
# CACHE_DRIVER=redis above. This is the server it needs.
|
|
1524
1524
|
redis:
|
|
1525
1525
|
profiles: ['redis']
|
|
@@ -1549,264 +1549,91 @@ volumes:
|
|
|
1549
1549
|
'README.md',
|
|
1550
1550
|
`# ${name}
|
|
1551
1551
|
|
|
1552
|
-
A
|
|
1553
|
-
|
|
1554
|
-
## Deploy
|
|
1555
|
-
|
|
1556
|
-
Three paths onto a server, all ending at the same \`/install\`. **Quick
|
|
1557
|
-
start** onto [Coolify](https://coolify.io) is the default and needs nothing
|
|
1558
|
-
but a push; **advanced/prebuilt** moves the build off the server, onto
|
|
1559
|
-
GitHub Actions, for a low-spec build server or a faster deploy; **without a
|
|
1560
|
-
panel** is the same four containers run by hand, with your own \`.env\` and
|
|
1561
|
-
reverse proxy, and no Coolify at all. Pick one — a board only ever runs one
|
|
1562
|
-
of them at a time.
|
|
1563
|
-
|
|
1564
|
-
### Quick start (default)
|
|
1565
|
-
|
|
1566
|
-
Coolify builds the image itself, from this repository, every time it
|
|
1567
|
-
deploys — there is nothing to push anywhere first and no image tag to paste
|
|
1568
|
-
in. Two steps:
|
|
1569
|
-
|
|
1570
|
-
1. **Push this repository to GitHub.**
|
|
1571
|
-
|
|
1572
|
-
2. **Point Coolify at \`docker-compose.yaml\`** — a **Public Git repository**
|
|
1573
|
-
resource with **Docker Compose** as its build pack, this repository as its
|
|
1574
|
-
source. The name is Coolify's own default, so its **Compose file** field is
|
|
1575
|
-
already right when the form opens, and the file already carries Coolify's
|
|
1576
|
-
own "magic variables" for \`AUTH_SECRET\`, \`TICK_SECRET\` and the database
|
|
1577
|
-
password, generated on the first deploy and never typed in. Nothing else to
|
|
1578
|
-
set: \`docker-compose.yaml\` builds \`web\` and \`migrate\` from \`Dockerfile\`
|
|
1579
|
-
itself, so there is no \`MEITH_IMAGE\` here at all.
|
|
1580
|
-
|
|
1581
|
-
3. **Deploy, then \`/install\` on your own domain.** Coolify issues the
|
|
1582
|
-
certificate; the installer from there is the one
|
|
1583
|
-
[docs/getting-started/deployment/coolify.md](${repositoryUrl}/blob/main/docs/getting-started/deployment/coolify.md#4-run-the-installer)
|
|
1584
|
-
walks through, screen for screen. It seals itself when it finishes, and
|
|
1585
|
-
\`/install\` answers 404 from then on — run it **against the database you
|
|
1586
|
-
are going to keep**. Every push to \`main\` after this is picked up the next
|
|
1587
|
-
time Coolify's own **Redeploy** button runs — pushing alone does not
|
|
1588
|
-
rebuild it.
|
|
1589
|
-
|
|
1590
|
-
The trade for that zero setup is a heavier build: \`Dockerfile\` installs this
|
|
1591
|
-
board's full dependency closure on the server itself, on every deploy, rather
|
|
1592
|
-
than starting from a warm base image. A 2 GB VPS can OOM on it. If that is
|
|
1593
|
-
your server, use the advanced path below instead.
|
|
1594
|
-
|
|
1595
|
-
A quick-start board never needs \`Dockerfile.prebuilt\`,
|
|
1596
|
-
\`docker-compose.prebuilt.yaml\` or \`.github/workflows/build.yml\` — delete all
|
|
1597
|
-
three.
|
|
1598
|
-
|
|
1599
|
-
### Advanced / prebuilt — for a low-spec server or a faster deploy
|
|
1600
|
-
|
|
1601
|
-
Something else builds the image ahead of time; the server only ever pulls
|
|
1602
|
-
one. Three steps, nothing to configure by hand beyond one value only you know:
|
|
1603
|
-
|
|
1604
|
-
1. **Push this repository to GitHub.** \`.github/workflows/build.yml\` builds
|
|
1605
|
-
\`Dockerfile.prebuilt\` on every push to \`main\` and pushes the result to your
|
|
1606
|
-
own GitHub Container Registry, \`ghcr.io/<you>/${name}\` — using only the
|
|
1607
|
-
\`GITHUB_TOKEN\` every GitHub Actions run already carries. No secret to
|
|
1608
|
-
add, no registry account beyond the GitHub account you already have.
|
|
1609
|
-
|
|
1610
|
-
That build is the thing step 2 waits on: open the repository's
|
|
1611
|
-
**Actions** tab and let the run finish, because its **Summary** is where
|
|
1612
|
-
the exact image to paste into step 2 comes from. The Summary also links
|
|
1613
|
-
the package itself, to check it is public — a build from a public
|
|
1614
|
-
repository usually lands public already, and a private one fails
|
|
1615
|
-
Coolify's pull with an authentication error no operator can act on.
|
|
1616
|
-
|
|
1617
|
-
2. **Point Coolify at \`docker-compose.prebuilt.yaml\`** — a
|
|
1618
|
-
**Public Git repository** resource with **Docker Compose** as its build
|
|
1619
|
-
pack, this repository as its source, and its **Compose file** field
|
|
1620
|
-
changed from Coolify's default of \`docker-compose.yaml\` to
|
|
1621
|
-
\`docker-compose.prebuilt.yaml\`. That file carries Coolify's own "magic
|
|
1622
|
-
variables" for \`AUTH_SECRET\`, \`TICK_SECRET\` and the database password,
|
|
1623
|
-
generated on the first deploy and never typed in. The one thing Coolify
|
|
1624
|
-
cannot generate is the image step 1 just pushed: set \`MEITH_IMAGE\` in the
|
|
1625
|
-
resource's own environment to one of the two values that run's Summary
|
|
1626
|
-
printed (\`docker-compose.prebuilt.yaml\` refuses to start without it, with
|
|
1627
|
-
a message saying why). \`ghcr.io/<you>/${name}:\${{ github.sha }}\` names
|
|
1628
|
-
that one build and nothing else, ever; \`ghcr.io/<you>/${name}:latest\`
|
|
1629
|
-
follows \`main\` instead, so installing a plugin later is a push and a
|
|
1630
|
-
**Redeploy** — the trade this path takes, at the cost of an unrelated
|
|
1631
|
-
redeploy pulling whatever \`main\` most recently built.
|
|
1632
|
-
|
|
1633
|
-
3. **Deploy, then \`/install\` on your own domain.** Same installer, same
|
|
1634
|
-
[docs/getting-started/deployment/coolify.md](${repositoryUrl}/blob/main/docs/getting-started/deployment/coolify.md#4-run-the-installer)
|
|
1635
|
-
walk-through, same one-time seal. Every push to \`main\` after this rebuilds
|
|
1636
|
-
the image; Coolify's own **Redeploy** button is what actually pulls it —
|
|
1637
|
-
pushing alone does not.
|
|
1638
|
-
|
|
1639
|
-
No Docker Hub, no paid CI: GitHub Actions' free tier and GHCR are the whole
|
|
1640
|
-
build side of this, for a board of any size.
|
|
1641
|
-
|
|
1642
|
-
**Building it yourself**: works on any machine with Docker, if you would
|
|
1643
|
-
rather not use GitHub Actions for the build — push the result wherever
|
|
1644
|
-
\`docker-compose.prebuilt.yaml\`'s \`MEITH_IMAGE\` can reach.
|
|
1552
|
+
A community board built on [Meith](${repositoryUrl}).
|
|
1645
1553
|
|
|
1646
|
-
|
|
1647
|
-
docker build -f Dockerfile.prebuilt --build-arg MEITH_VERSION=$(node -p "require('./package.json').dependencies['@meith/web']") -t ${name} .
|
|
1648
|
-
\`\`\`
|
|
1554
|
+
## Choose a deployment
|
|
1649
1555
|
|
|
1650
|
-
|
|
1556
|
+
| Route | File | Where the image builds |
|
|
1557
|
+
|---|---|---|
|
|
1558
|
+
| Quick start with Coolify | \`docker-compose.yaml\` | Your server |
|
|
1559
|
+
| Advanced / prebuilt | \`docker-compose.prebuilt.yaml\` | GitHub Actions or another build machine |
|
|
1560
|
+
| Docker Compose without a panel | \`docker-compose.byhand.yaml\` | Your server |
|
|
1651
1561
|
|
|
1652
|
-
|
|
1653
|
-
four containers deployed with nothing generating secrets for you: a \`.env\`
|
|
1654
|
-
you write yourself, a port published for the reverse proxy you already run,
|
|
1655
|
-
and \`docker compose up -d --build\` in place of a panel's Deploy button.
|
|
1656
|
-
[docs/getting-started/deployment/docker-compose.md](${repositoryUrl}/blob/main/docs/getting-started/deployment/docker-compose.md)
|
|
1657
|
-
is the full walkthrough this file is the last step of, including the
|
|
1658
|
-
\`.env\` this repository does not carry — nothing here belongs in git. Delete
|
|
1659
|
-
this file if you know you will only ever deploy through Coolify; keep it,
|
|
1660
|
-
and it needs nothing else changed, if you later want to move away from
|
|
1661
|
-
Coolify without changing how the board itself is built.
|
|
1562
|
+
Use one route for a deployment. The [Coolify guide](${repositoryUrl}/blob/main/docs/operations/coolify.md) and [Docker Compose guide](${repositoryUrl}/blob/main/docs/operations/docker-compose.md) cover prerequisites, secrets, domains and recovery.
|
|
1662
1563
|
|
|
1663
|
-
|
|
1564
|
+
### Quick start with Coolify
|
|
1664
1565
|
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
depend on yet. Deploy some other way and something still has to call that
|
|
1671
|
-
route (or run \`meith task:run\`) every minute, or nothing catches up
|
|
1672
|
-
and nothing errors.
|
|
1566
|
+
1. Push this repository to GitHub.
|
|
1567
|
+
2. Create a Git repository resource in Coolify with the Docker Compose build pack and \`/docker-compose.yaml\` as the Compose file.
|
|
1568
|
+
3. Assign the board's domain and deploy. Coolify supplies database and authentication secrets; save a protected recovery copy.
|
|
1569
|
+
4. Confirm \`postgres\` is healthy, \`migrate\` exits successfully, and \`web\` and \`worker\` run.
|
|
1570
|
+
5. Open \`/install\`, unlock with \`AUTH_SECRET\`, and create the board and its first administrator. The installer seals itself and returns 404 after completion.
|
|
1673
1571
|
|
|
1674
|
-
|
|
1572
|
+
A push alone does not rebuild this route. Use Coolify's **Redeploy** after pushing. If the server cannot complete the build, use the prebuilt route.
|
|
1675
1573
|
|
|
1676
|
-
|
|
1677
|
-
npm install
|
|
1678
|
-
npm run dev
|
|
1679
|
-
\`\`\`
|
|
1574
|
+
### Advanced / prebuilt
|
|
1680
1575
|
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1576
|
+
1. Let \`.github/workflows/build.yml\` finish in GitHub Actions. It builds and publishes your board's image.
|
|
1577
|
+
2. Make that image accessible to Coolify and select \`/docker-compose.prebuilt.yaml\`.
|
|
1578
|
+
3. Set \`MEITH_IMAGE\` to the exact image from the workflow summary. The commit tag uses \`\${{ github.sha }}\`; \`:latest\` follows later builds and can change on redeploy.
|
|
1579
|
+
4. Deploy and complete \`/install\` as above.
|
|
1684
1580
|
|
|
1685
|
-
|
|
1686
|
-
\`DATABASE_URL\` and the two secrets in it, then:
|
|
1581
|
+
For a local image build, the build argument comes from the board's pinned package:
|
|
1687
1582
|
|
|
1688
1583
|
\`\`\`sh
|
|
1689
|
-
|
|
1690
|
-
echo "<password>" | npm run meith -- user:create --username <name> --email <address> --group administrators
|
|
1584
|
+
docker build -f Dockerfile.prebuilt --build-arg MEITH_VERSION=$(node -p "require('./package.json').dependencies['@meith/web']") -t ${name} .
|
|
1691
1585
|
\`\`\`
|
|
1692
1586
|
|
|
1693
|
-
|
|
1587
|
+
After changing the board, wait for its new image and update \`MEITH_IMAGE\` if pinned to a commit, then redeploy.
|
|
1694
1588
|
|
|
1695
|
-
|
|
1696
|
-
is named here so the bundler can see it; nothing is found by scanning a
|
|
1697
|
-
directory at runtime.
|
|
1698
|
-
- **\`/admin\`** — settings, forums, groups, members, themes, maintenance. An
|
|
1699
|
-
administrator re-enters their password to get in, and again for anything
|
|
1700
|
-
destructive.
|
|
1701
|
-
- **\`npm run meith -- --help\`** — the operator CLI. Everything the panel does
|
|
1702
|
-
and a few things it cannot, without a browser.
|
|
1589
|
+
## Run locally
|
|
1703
1590
|
|
|
1704
|
-
|
|
1591
|
+
\`\`\`sh
|
|
1592
|
+
npm install
|
|
1593
|
+
npm run dev
|
|
1594
|
+
\`\`\`
|
|
1705
1595
|
|
|
1706
|
-
|
|
1707
|
-
built into the image. In this repository:
|
|
1596
|
+
Open \`http://localhost:3000\`. Without \`DATABASE_URL\`, this is a read-only fixture preview. For persistent registration and posting, follow [Create a writable local board](${repositoryUrl}/blob/main/docs/operations/local-board.md).
|
|
1708
1597
|
|
|
1709
|
-
|
|
1710
|
-
registers it:
|
|
1598
|
+
## Configure the community
|
|
1711
1599
|
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1600
|
+
- \`meith.config.ts\` registers themes and board configuration.
|
|
1601
|
+
- \`board.plugins.json\` and \`meith.plugins.ts\` register installed plugins.
|
|
1602
|
+
- \`/admin\` manages forums, members, permissions and settings.
|
|
1603
|
+
- \`npm run meith -- --help\` lists operator commands.
|
|
1715
1604
|
|
|
1716
|
-
|
|
1717
|
-
(\`npm run meith -- plugin:remove <key>\` reverses it). A **theme** is
|
|
1718
|
-
\`npm install --save-exact @meith/theme-midnight\`, then an entry in
|
|
1719
|
-
\`meith.config.ts\`'s \`themes\` map following the shape of the \`default\` one
|
|
1720
|
-
already there — set \`defaultTheme\` to its key to make it the board's
|
|
1721
|
-
default.
|
|
1605
|
+
Before inviting members, [test email delivery](${repositoryUrl}/blob/main/docs/operations/mail.md), verify [scheduled work](${repositoryUrl}/blob/main/docs/operations/scheduled-tasks.md), and [configure backups](${repositoryUrl}/blob/main/docs/operations/backups.md). The log mail driver delivers nothing. This deployment's worker calls the web application's tick endpoint. For a manual development run, use \`npm run meith -- task:run\`.
|
|
1722
1606
|
|
|
1723
|
-
|
|
1724
|
-
not rebuild. Quick start builds the new image on that redeploy; advanced/prebuilt
|
|
1725
|
-
waits for \`.github/workflows/build.yml\` to finish first, and Redeploy is
|
|
1726
|
-
what actually pulls the result.
|
|
1607
|
+
## Install an extension
|
|
1727
1608
|
|
|
1728
|
-
|
|
1729
|
-
**Admin → System** (**Version & migrations**) in the browser, or:
|
|
1609
|
+
Add a plugin from this checkout:
|
|
1730
1610
|
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1611
|
+
\`\`\`sh
|
|
1612
|
+
npm run meith -- plugin:add @meith/plugin-dues
|
|
1613
|
+
\`\`\`
|
|
1734
1614
|
|
|
1735
|
-
|
|
1736
|
-
for the full guide.
|
|
1615
|
+
Commit the package and registry changes, build and deploy, then apply plugin migrations with \`meith upgrade\` against the deployed board. Follow [Install plugins and themes](${repositoryUrl}/blob/main/docs/operations/installing.md) for the full procedure and theme registration. Installing a package into a running container does not make it part of the next deployment.
|
|
1737
1616
|
|
|
1738
1617
|
## Upgrading
|
|
1739
1618
|
|
|
1740
|
-
\`.github/workflows/update.yml\`
|
|
1741
|
-
whenever you press **Run workflow** on the Actions tab — it checks for a new
|
|
1742
|
-
Meith release and opens a pull request that moves every \`@meith/*\` package
|
|
1743
|
-
and \`next\` together, and rewrites the deploy files this scaffold owns
|
|
1744
|
-
(\`Dockerfile\`, the compose files, the workflows) to the new release's
|
|
1745
|
-
shape. A file you have edited yourself is never rewritten; the run's log
|
|
1746
|
-
names any it left for you. One-time setup: under
|
|
1747
|
-
**Settings → Actions → General**, enable **Allow GitHub Actions to create
|
|
1748
|
-
and approve pull requests**, or the workflow cannot open one.
|
|
1619
|
+
\`.github/workflows/update.yml\` checks weekly and opens an update pull request. It also supports **Run workflow**. Enable **Allow GitHub Actions to create and approve pull requests** under **Settings → Actions → General**.
|
|
1749
1620
|
|
|
1750
|
-
|
|
1751
|
-
release notes it links, take a backup first, and press **Redeploy** in
|
|
1752
|
-
Coolify after the merge — pushing alone does not rebuild. Once the new
|
|
1753
|
-
version serves, run \`npm run meith -- upgrade\` against it for the plugin
|
|
1754
|
-
migrations.
|
|
1621
|
+
Review the release notes, take a backup, and inspect any scaffold files the updater left for manual reconciliation. Merge, rebuild and redeploy; then run \`meith upgrade\` for plugin migrations. Core migrations run through the deployment's migration service. Migrations are forward-only; recovery uses a backup.
|
|
1755
1622
|
|
|
1756
|
-
|
|
1623
|
+
To prepare the update locally:
|
|
1757
1624
|
|
|
1758
1625
|
\`\`\`sh
|
|
1759
1626
|
npx create-meith@latest update
|
|
1760
|
-
git commit -am "Update Meith"
|
|
1761
|
-
git push
|
|
1762
1627
|
\`\`\`
|
|
1763
1628
|
|
|
1764
|
-
|
|
1765
|
-
rewrite neither of them can do:
|
|
1629
|
+
The updater moves package pins and supported deployment files together. Its package update includes these commands; running them alone does not update deployment files:
|
|
1766
1630
|
|
|
1767
1631
|
\`\`\`sh
|
|
1768
1632
|
npm install --save-exact @meith/web@latest @meith/cli@latest @meith/theme-default@latest
|
|
1769
1633
|
npm install --save-exact next@$(node -p "require('./node_modules/@meith/web/package.json').dependencies.next")
|
|
1770
1634
|
\`\`\`
|
|
1771
1635
|
|
|
1772
|
-
|
|
1773
|
-
the npm commands alone never bump it: upgrading only the \`@meith/*\` packages
|
|
1774
|
-
leaves the board's own pin on the old Next while \`@meith/web\` depends on the
|
|
1775
|
-
new one, which npm resolves by installing both — the build then runs on one
|
|
1776
|
-
version while everything reading \`package.json\` sees the other. Reading the
|
|
1777
|
-
version out of the freshly installed \`@meith/web\` is what keeps the two the
|
|
1778
|
-
same without anybody having to know the number.
|
|
1779
|
-
|
|
1780
|
-
\`next\` and \`@meith/web\` move together or not at all, which is why one
|
|
1781
|
-
updater owns the whole move and no dependency bot bumps either on its own.
|
|
1782
|
-
What Dependabot *does* keep current is this repository's own GitHub Actions —
|
|
1783
|
-
\`.github/dependabot.yml\` opens a weekly pull request bumping the actions
|
|
1784
|
-
pinned under \`.github/workflows\`, a safe, independent update the updater
|
|
1785
|
-
leaves to it.
|
|
1786
|
-
|
|
1787
|
-
On the quick-start path there is no version to keep in sync by hand:
|
|
1788
|
-
\`Dockerfile\` runs \`npm install\` straight from this \`package.json\` on every
|
|
1789
|
-
build, so a rebuild always picks up whatever is pinned there. On the
|
|
1790
|
-
advanced/prebuilt path, that \`package.json\` change is the whole pin:
|
|
1791
|
-
\`Dockerfile.prebuilt\`'s own \`FROM\` line takes the version as a build argument,
|
|
1792
|
-
and \`.github/workflows/build.yml\` reads it straight out of \`package.json\`'s
|
|
1793
|
-
own \`@meith/web\` dependency when it rebuilds — nothing in
|
|
1794
|
-
\`Dockerfile.prebuilt\` itself to keep in sync by hand. \`--save-exact\` matters
|
|
1795
|
-
either way: npm's default \`save-prefix\` is \`^\`, and a caret range is not a
|
|
1796
|
-
legal Docker image tag for the advanced path — without it, this exact command
|
|
1797
|
-
would write \`"^0.18.0"\` and the next \`Dockerfile.prebuilt\` build would fail
|
|
1798
|
-
with \`invalid reference format\` instead of building. This
|
|
1799
|
-
project's own \`.npmrc\` sets \`save-exact=true\` for the same reason, so an
|
|
1800
|
-
\`npm install\` of anything else here — a plugin, say — stays pinned too; the
|
|
1801
|
-
build workflow also refuses to build from anything but an exact version, as
|
|
1802
|
-
a second line of defense. Once the rebuilt image is deployed, run
|
|
1803
|
-
\`npm run meith -- upgrade\` against it for the plugin migrations — see
|
|
1804
|
-
[the operator CLI](${repositoryUrl}/blob/main/docs/guides/operations/operating.md#the-operator-cli)
|
|
1805
|
-
for running it against this deployment.
|
|
1806
|
-
|
|
1807
|
-
Migrations are forward-only. Recovery is by restore, so take a backup first —
|
|
1808
|
-
there is no down migration to undo a destructive one, and a button that pretended
|
|
1809
|
-
otherwise would be worse than its absence.
|
|
1636
|
+
Keep Next.js aligned with \`@meith/web\`. Use \`--save-exact\`: a caret range is not a legal Docker image tag. Read [Upgrade Meith](${repositoryUrl}/blob/main/docs/operations/upgrading.md) before applying the change.
|
|
1810
1637
|
`,
|
|
1811
1638
|
)
|
|
1812
1639
|
|
|
@@ -1860,277 +1687,95 @@ ${AT_ROOT_IGNORES}
|
|
|
1860
1687
|
function vercelReadme({ name, repositoryUrl, templateRepositoryUrl }: VercelTreeOptions): string {
|
|
1861
1688
|
return `# ${name}
|
|
1862
1689
|
|
|
1863
|
-
A
|
|
1690
|
+
A community board built on [Meith](${repositoryUrl}), deployed as Vercel functions.
|
|
1864
1691
|
|
|
1865
1692
|
[](${deployButtonUrl(templateRepositoryUrl)})
|
|
1866
1693
|
|
|
1867
|
-
##
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
pooled connection string as \`DATABASE_URL\` and the direct one as
|
|
1873
|
-
\`DATABASE_URL_UNPOOLED\`.
|
|
1874
|
-
- **An Upstash Redis store**, attached the same way, for the shared cache. It
|
|
1875
|
-
publishes \`KV_URL\`, which the board reads as \`REDIS_URL\` — \`KV_REST_API_URL\`
|
|
1876
|
-
beside it is an HTTPS endpoint and is not used for this.
|
|
1877
|
-
- **A Vercel Blob store** for uploads, which publishes \`BLOB_STORE_ID\` into the
|
|
1878
|
-
project by itself. That is the whole credential: the board hands the id to
|
|
1879
|
-
Vercel's SDK, which authenticates with the deployment's own OIDC identity, so
|
|
1880
|
-
there is no token to copy. This is what used to be four hand-typed \`S3_*\`
|
|
1881
|
-
secrets.
|
|
1882
|
-
- **A Resend mail account**, attached the same way, which publishes
|
|
1883
|
-
\`RESEND_API_KEY\` and \`RESEND_EMAIL_DOMAIN\`. The board reads both names
|
|
1884
|
-
directly: its mail driver already speaks Resend's request shape, so there is
|
|
1885
|
-
nothing to adapt, and the sending domain is what the sender is built from.
|
|
1886
|
-
- **A Vercel project** carrying \`vercel.json\` — the build command
|
|
1887
|
-
\`${VERCEL_BUILD_COMMAND}\`,
|
|
1888
|
-
which applies the schema before it builds, materializes the board's app at
|
|
1889
|
-
the project root so the artefact lands where Vercel reads it, and the cron
|
|
1890
|
-
entry that drives the tick.
|
|
1891
|
-
|
|
1892
|
-
**Mail needs no variables after the deploy.** Resend publishes both its key
|
|
1893
|
-
and its sending domain, and the board sends from \`${RESEND_SENDER_MAILBOX}@\`
|
|
1894
|
-
that domain — see *Mail* below to send from a different address, and for the
|
|
1895
|
-
one case that does need you: a domain Resend has not verified yet.
|
|
1896
|
-
|
|
1897
|
-
## What to type into the deploy form
|
|
1898
|
-
|
|
1899
|
-
**Two secrets**, generated rather than chosen. Thirty-two characters is a floor
|
|
1900
|
-
the board enforces at boot, not a suggestion:
|
|
1694
|
+
## 1. Connect services and set secrets
|
|
1695
|
+
|
|
1696
|
+
The deployment template requests Neon PostgreSQL, Upstash Redis, Vercel Blob and Resend. Keep access to these service accounts and review their current limits and pricing.
|
|
1697
|
+
|
|
1698
|
+
Generate two independent secrets:
|
|
1901
1699
|
|
|
1902
1700
|
\`\`\`sh
|
|
1903
|
-
openssl rand -hex 32
|
|
1904
|
-
openssl rand -hex 32
|
|
1701
|
+
openssl rand -hex 32
|
|
1702
|
+
openssl rand -hex 32
|
|
1905
1703
|
\`\`\`
|
|
1906
1704
|
|
|
1907
|
-
\`
|
|
1908
|
-
cannot be told to send another — the caller is the platform, so this one has to
|
|
1909
|
-
be an environment variable both ends can read, and cannot be something the
|
|
1910
|
-
board makes up for itself. Note that this floor is stricter than the 16
|
|
1911
|
-
characters Vercel's own cron documentation suggests — a value generated by
|
|
1912
|
-
following those instructions is refused here, and the fix is a longer secret.
|
|
1705
|
+
Use them for \`AUTH_SECRET\` and \`CRON_SECRET\`. Each must be at least 32 characters. Keep a recovery copy of the original \`AUTH_SECRET\`; it seals stored secrets.
|
|
1913
1706
|
|
|
1914
|
-
|
|
1915
|
-
in outgoing mail. It stays in the environment deliberately: a copy of the
|
|
1916
|
-
database is then not enough to forge either.
|
|
1917
|
-
|
|
1918
|
-
**That is the whole form.** Everything else the board works out from the stores
|
|
1919
|
-
this button just linked to the project:
|
|
1707
|
+
The platform derives the following defaults when you have not supplied explicit overrides:
|
|
1920
1708
|
|
|
1921
1709
|
\`\`\`ini
|
|
1922
1710
|
${VERCEL_DERIVED_DRIVERS.join('\n')}
|
|
1923
1711
|
\`\`\`
|
|
1924
1712
|
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
\`
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
If a
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
If you would rather keep uploads somewhere you hold yourself — see *Leaving
|
|
1941
|
-
Vercel* below for why that matters — set \`FILESTORE_DRIVER=s3\` and add
|
|
1942
|
-
\`S3_BUCKET\`, \`S3_REGION\`, \`S3_ACCESS_KEY_ID\` and \`S3_SECRET_ACCESS_KEY\` in the
|
|
1943
|
-
project's environment settings, with \`S3_ENDPOINT\` for a bucket that is not AWS
|
|
1944
|
-
(\`S3_REGION=auto\` for R2). The same board runs either way.
|
|
1713
|
+
| Service value | Meith uses it for |
|
|
1714
|
+
|---|---|
|
|
1715
|
+
| \`DATABASE_URL\` | Runtime database connection |
|
|
1716
|
+
| \`DATABASE_URL_UNPOOLED\`, falling back to \`POSTGRES_URL_NON_POOLING\` | \`DIRECT_DATABASE_URL\` for migrations and installer locks |
|
|
1717
|
+
| \`KV_URL\` | \`REDIS_URL\`; the Redis protocol connection, not the HTTP REST endpoint |
|
|
1718
|
+
| \`BLOB_STORE_ID\` | Upload storage authenticated through the deployment identity |
|
|
1719
|
+
| \`RESEND_API_KEY\`, \`RESEND_EMAIL_DOMAIN\` | HTTP mail credentials and sender |
|
|
1720
|
+
|
|
1721
|
+
If a required service configuration is missing, Meith refuses to boot rather than guess. Inspect the named variables in the deployment log. See [Vercel configuration](${repositoryUrl}/blob/main/docs/operations/vercel-configuration.md) for explicit overrides.
|
|
1722
|
+
|
|
1723
|
+
## 2. Deploy and install
|
|
1724
|
+
|
|
1725
|
+
The build command is \`${VERCEL_BUILD_COMMAND}\`. It applies core migrations before building. Keep preview deployments on a separate database if they must not migrate production.
|
|
1726
|
+
|
|
1727
|
+
When the deployment succeeds, open \`/install\`. Unlock with \`AUTH_SECRET\`, confirm the permanent public board address, and create the first administrator. Installation seals the route; \`/install\` then returns 404.
|
|
1945
1728
|
|
|
1946
1729
|
## Mail
|
|
1947
1730
|
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
A board with the key but no verified domain — which is what you get if you
|
|
1965
|
-
remove the integration's \`RESEND_EMAIL_DOMAIN\` without putting a
|
|
1966
|
-
\`MAIL_FROM\` in its place — does not guess a sender. It stays on the log
|
|
1967
|
-
driver and delivers nothing, which is the honest outcome: a guessed sender at
|
|
1968
|
-
an unverified domain would be refused by Resend anyway, one message at a
|
|
1969
|
-
time.
|
|
1970
|
-
|
|
1971
|
-
The board is not tied to Resend. Its mail driver is a plain JSON-over-HTTPS
|
|
1972
|
-
sender that posts \`{from, to, subject, text, html, reply_to}\` with a bearer
|
|
1973
|
-
token — Resend's \`POST /emails\` happens to be exactly that shape, which is why
|
|
1974
|
-
it needs no adapter. Any provider with the same shape works: set
|
|
1975
|
-
\`MAIL_HTTP_ENDPOINT\`, \`MAIL_HTTP_TOKEN\` and \`MAIL_DRIVER=http\` in the
|
|
1976
|
-
project's environment settings, and set the first two **together** — either
|
|
1977
|
-
one on its own stands the Resend bridge down, so a key issued for Resend is
|
|
1978
|
-
never presented to an endpoint you chose. Setting \`MAIL_DRIVER\` to anything
|
|
1979
|
-
but \`http\` stands the bridge down too, for the same reason: a board that
|
|
1980
|
-
moved to SMTP must not send through its new provider from Resend's domain.
|
|
1981
|
-
Delete \`RESEND_API_KEY\` once you have moved off Resend.
|
|
1982
|
-
|
|
1983
|
-
Check it worked: sign in as the administrator and use the test button on
|
|
1984
|
-
**/admin → Settings → Mail**.
|
|
1985
|
-
|
|
1986
|
-
## First run: \`/install\`
|
|
1987
|
-
|
|
1988
|
-
The build applies migrations, but an empty schema is not yet a board. Open
|
|
1989
|
-
\`https://<your-deployment>/install\` once the first deploy is green. It asks for
|
|
1990
|
-
the board's name and address and for the first administrator's username, email
|
|
1991
|
-
and password, creates the board and that account, and then **seals itself**:
|
|
1992
|
-
\`/install\` answers 404 from then on. Run it against the database you intend to
|
|
1993
|
-
keep — the screens are the ones
|
|
1994
|
-
[docs/getting-started/deployment/docker-compose.md](${repositoryUrl}/blob/main/docs/getting-started/deployment/docker-compose.md#6-install-it)
|
|
1995
|
-
walks through.
|
|
1996
|
-
|
|
1997
|
-
## The tick
|
|
1998
|
-
|
|
1999
|
-
\`vercel.json\` asks Vercel to call \`${TICK_PATH}\` on \`${TICK_SCHEDULE}\`. That
|
|
2000
|
-
route is how bans expire, digests send, mail leaves the outbox and the queue
|
|
2001
|
-
drains; nothing here runs it on its own, because there is no worker process on
|
|
2002
|
-
a function platform. Two things about it are worth knowing **before** you
|
|
2003
|
-
deploy rather than after:
|
|
2004
|
-
|
|
2005
|
-
- **This ships a daily schedule, because Hobby refuses anything faster.** A
|
|
2006
|
-
Hobby plan rejects a cron expression that would run more than once a day —
|
|
2007
|
-
the deployment fails outright rather than being slowed down — so
|
|
2008
|
-
\`vercel.json\` carries \`${TICK_SCHEDULE}\` and deploys anywhere. On a
|
|
2009
|
-
paid plan, edit it to \`* * * * *\` and the board ticks every minute.
|
|
2010
|
-
|
|
2011
|
-
A daily tick loses nothing permanently: tasks are written so a missed run
|
|
2012
|
-
delays work rather than dropping it, and a password reset is sent as the
|
|
2013
|
-
request is handled rather than waiting for a tick. What it does delay is
|
|
2014
|
-
everything the tick drives — a new post is not findable in search, and a
|
|
2015
|
-
notification is not sent, until the next run.
|
|
2016
|
-
|
|
2017
|
-
**To keep a fast tick without paying**, drive \`${TICK_PATH}\` from anything
|
|
2018
|
-
that can call a URL on a schedule — a GitHub Actions workflow, a systemd
|
|
2019
|
-
timer, an uptime pinger — presenting \`TICK_SECRET\` instead of
|
|
2020
|
-
\`CRON_SECRET\`. The endpoint accepts either, so the Vercel cron and an
|
|
2021
|
-
outside scheduler can both drive it.
|
|
2022
|
-
- **\`maxDuration = 300\` is validated when the project builds, not when the
|
|
2023
|
-
function runs.** A plan that does not allow 300 seconds therefore **fails the
|
|
2024
|
-
deployment** rather than clamping the request. With Fluid Compute — the
|
|
2025
|
-
default for new projects — Hobby allows 300 and this builds as written. With
|
|
2026
|
-
Fluid Compute switched off, Hobby caps a function at 60 seconds and the build
|
|
2027
|
-
fails. Turn Fluid Compute back on.
|
|
2028
|
-
|
|
2029
|
-
A tick that reaches the tasks and runs them answers \`200\` even when one of them
|
|
2030
|
-
threw, with \`ok: false\` and the failure named in \`ran\`. That is deliberate:
|
|
2031
|
-
schedulers retry non-2xx answers, and a task that fails every time would turn
|
|
2032
|
-
each retry into another attempt against whatever it is failing against.
|
|
1731
|
+
Mail needs no variables after the deploy when the Resend integration supplies both \`RESEND_API_KEY\` and \`RESEND_EMAIL_DOMAIN\`. The default sender is \`${RESEND_SENDER_MAILBOX}@\` followed by that domain. Verify the sending domain with Resend and send a test from **Admin → Settings → Mail**.
|
|
1732
|
+
|
|
1733
|
+
To send from a different address, set \`MAIL_FROM\` and redeploy. The address must be allowed by your provider. Missing sender configuration can leave mail on the log driver, which delivers nothing.
|
|
1734
|
+
|
|
1735
|
+
For another HTTP provider, set \`MAIL_DRIVER=http\`, \`MAIL_HTTP_ENDPOINT\`, \`MAIL_HTTP_TOKEN\` and \`MAIL_FROM\`; endpoint and token must be supplied together. See [Email configuration](${repositoryUrl}/blob/main/docs/operations/mail.md).
|
|
1736
|
+
|
|
1737
|
+
## 3. Verify scheduled work and hosting limits
|
|
1738
|
+
|
|
1739
|
+
\`vercel.json\` calls \`${TICK_PATH}\` on \`${TICK_SCHEDULE}\`: once a day. That cadence can delay notifications, search indexing and queued work. Time-sensitive plugin work can miss its useful delivery window.
|
|
1740
|
+
|
|
1741
|
+
Choose a cadence supported by your current plan, or use an external scheduler authenticated with an independently generated \`TICK_SECRET\`. The endpoint accepts \`CRON_SECRET\` or \`TICK_SECRET\`. A paid plan may provide more scheduling options; check [Vercel cron limits](https://vercel.com/docs/cron-jobs/usage-and-pricing).
|
|
1742
|
+
|
|
1743
|
+
The tick declares \`maxDuration = 300\`. Check the project's Fluid Compute setting and [function duration limit](https://vercel.com/docs/functions/configuring-functions/duration) before deploying; an unsupported duration can fail deployment. Upload limits also come from the platform, regardless of the board's attachment settings.
|
|
1744
|
+
|
|
1745
|
+
Inspect task results, not only HTTP status: a tick can return 200 with \`ok: false\` and failed tasks in \`ran\`. Follow [Scheduled tasks](${repositoryUrl}/blob/main/docs/operations/scheduled-tasks.md) and test real posting, uploads and email before inviting members.
|
|
2033
1746
|
|
|
2034
1747
|
## Upgrading
|
|
2035
1748
|
|
|
2036
|
-
\`.github/workflows/update.yml\`
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
and \`next\` together. A file you have edited yourself is never rewritten; the
|
|
2040
|
-
run's log names any it left for you. One-time setup: under
|
|
2041
|
-
**Settings → Actions → General**, enable **Allow GitHub Actions to create and
|
|
2042
|
-
approve pull requests**, or the workflow cannot open one. Read the release
|
|
2043
|
-
notes the pull request links and take a backup before merging; Vercel
|
|
2044
|
-
rebuilds on the merge, and the build command applies the new migrations
|
|
2045
|
-
before it builds.
|
|
1749
|
+
\`.github/workflows/update.yml\` opens a weekly update pull request and supports **Run workflow**. Enable **Allow GitHub Actions to create and approve pull requests** under **Settings → Actions → General**.
|
|
1750
|
+
|
|
1751
|
+
Read the release notes and take a backup before merging. Vercel redeploys the merged code and applies core migrations during the build. Apply plugin migrations with the operator CLI using the deployment's environment. Migrations are forward-only.
|
|
2046
1752
|
|
|
2047
|
-
|
|
1753
|
+
To prepare the update locally:
|
|
2048
1754
|
|
|
2049
1755
|
\`\`\`sh
|
|
2050
1756
|
npx create-meith@latest update
|
|
2051
|
-
git commit -am "Update Meith"
|
|
2052
|
-
git push
|
|
2053
1757
|
\`\`\`
|
|
2054
1758
|
|
|
2055
|
-
|
|
1759
|
+
The updater also reconciles supported deployment files. Its package update keeps Meith and Next.js aligned:
|
|
2056
1760
|
|
|
2057
1761
|
\`\`\`sh
|
|
2058
1762
|
npm install --save-exact @meith/web@latest @meith/cli@latest @meith/theme-default@latest
|
|
2059
1763
|
npm install --save-exact next@$(node -p "require('./node_modules/@meith/web/package.json').dependencies.next")
|
|
2060
1764
|
\`\`\`
|
|
2061
1765
|
|
|
2062
|
-
|
|
2063
|
-
installed here.
|
|
2064
|
-
|
|
2065
|
-
The second command is not optional. This board pins \`next\` itself — Vercel
|
|
2066
|
-
reads that pin to pick its Next.js builder — and the npm commands alone never
|
|
2067
|
-
bump it. Upgrading only the \`@meith/*\` packages leaves two versions of Next
|
|
2068
|
-
installed, the board built with one and the platform configured for the
|
|
2069
|
-
other. Reading the version out of the freshly installed \`@meith/web\` keeps
|
|
2070
|
-
them the same without anybody having to know the number.
|
|
2071
|
-
|
|
2072
|
-
Migrations are forward-only. Recovery is by restore, so take a backup first —
|
|
2073
|
-
there is no down migration to undo a destructive one.
|
|
1766
|
+
See [Upgrade Meith](${repositoryUrl}/blob/main/docs/operations/upgrading.md) for validation and recovery.
|
|
2074
1767
|
|
|
2075
1768
|
## Leaving Vercel
|
|
2076
1769
|
|
|
2077
|
-
|
|
2078
|
-
is not portable: Neon and Upstash hand out ordinary Postgres and Redis strings
|
|
2079
|
-
that any host accepts, but a Vercel Blob store is reachable only through Vercel's
|
|
2080
|
-
own API and there is no bucket to sync out of it. **The uploads are the thing you
|
|
2081
|
-
have to carry out deliberately, and \`meith backup\` is how.**
|
|
1770
|
+
Run backups from a checkout with the correct hosted database credentials and PostgreSQL tools. Set \`FILESTORE_DRIVER=blob\` and a store's \`BLOB_READ_WRITE_TOKEN\`; a local CLI cannot use the deployment's identity. Blob backups include uploads **by default**.
|
|
2082
1771
|
|
|
2083
|
-
|
|
2084
|
-
default** — it walks the Blob store, pulls every object, and puts them in the
|
|
2085
|
-
bundle beside the database dump. This is the opposite of the \`s3\` default, which
|
|
2086
|
-
skips them, because a bucket has its own backup story you can drive yourself and
|
|
2087
|
-
a Blob store does not:
|
|
1772
|
+
With that environment selected and a writable output directory:
|
|
2088
1773
|
|
|
2089
1774
|
\`\`\`sh
|
|
2090
|
-
|
|
2091
|
-
DIRECT_DATABASE_URL=… # Neon's DATABASE_URL_UNPOOLED
|
|
2092
|
-
FILESTORE_DRIVER=blob
|
|
2093
|
-
BLOB_READ_WRITE_TOKEN=… # create one on the store; see below
|
|
2094
|
-
npm run meith -- backup
|
|
2095
|
-
\`\`\`
|
|
2096
|
-
|
|
2097
|
-
Run that from a checkout of this repository, with those four values in the
|
|
2098
|
-
environment — the CLI talks to Neon and to the Blob store over the network, so
|
|
2099
|
-
it does not have to run on Vercel.
|
|
2100
|
-
|
|
2101
|
-
That last one is the one value this route asks you to make by hand, and only
|
|
2102
|
-
here. On the deployment the board reaches the store with \`BLOB_STORE_ID\` and
|
|
2103
|
-
the deployment's OIDC identity, which a command on your own machine does not
|
|
2104
|
-
have. Open the store under **Storage**, create a read-write token, and use it
|
|
2105
|
-
for the backup; the board itself never needs it. The bundle it writes holds the dump *and*
|
|
2106
|
-
every object. Check the last line it prints: if it says *no uploads*, the
|
|
2107
|
-
uploads are not in the bundle and restoring it gives a board whose posts have
|
|
2108
|
-
broken images.
|
|
2109
|
-
|
|
2110
|
-
Restoring puts them wherever the *restoring* board's \`FILESTORE_DRIVER\` points,
|
|
2111
|
-
so the same bundle moves the board either onward or away:
|
|
2112
|
-
|
|
2113
|
-
\`\`\`sh
|
|
2114
|
-
# onto a self-hosted board with a bucket
|
|
2115
|
-
FILESTORE_DRIVER=s3 S3_BUCKET=… RESTORE_DATABASE_URL=… npm run meith -- restore bundle.tar.gz
|
|
2116
|
-
|
|
2117
|
-
# onto a board that keeps uploads on its own disk
|
|
2118
|
-
RESTORE_DATABASE_URL=… npm run meith -- restore bundle.tar.gz --uploads-dir ./uploads
|
|
1775
|
+
npm run meith -- backup --out ./board-backup.tar.gz --uploads include
|
|
2119
1776
|
\`\`\`
|
|
2120
1777
|
|
|
2121
|
-
|
|
2122
|
-
the attachments with it, and there is no second copy anywhere unless you made
|
|
2123
|
-
one.
|
|
2124
|
-
|
|
2125
|
-
## Somewhere other than Vercel
|
|
2126
|
-
|
|
2127
|
-
Everything above is one deployment shape.
|
|
2128
|
-
[docs/getting-started/deployment/docker-compose.md](${repositoryUrl}/blob/main/docs/getting-started/deployment/docker-compose.md) is the
|
|
2129
|
-
same board as containers you run yourself, and \`npx create-meith <name>\`
|
|
2130
|
-
scaffolds that shape instead — a Dockerfile, a compose file and a workflow that
|
|
2131
|
-
builds the image. [docs/guides/operations/scaling.md](${repositoryUrl}/blob/main/docs/guides/operations/scaling.md)
|
|
2132
|
-
explains why the drivers above are what they are, and why an S3-compatible
|
|
2133
|
-
bucket is the portable choice for uploads everywhere but here.
|
|
1778
|
+
Check the result and bundle manifest. Preserve the original \`AUTH_SECRET\` separately. Restore into an empty destination and verify attachments and sign-in before switching traffic or deleting the old services. Follow [Move away from Vercel](${repositoryUrl}/blob/main/docs/operations/leaving-vercel.md) for the complete procedure.
|
|
2134
1779
|
`
|
|
2135
1780
|
}
|
|
2136
1781
|
|