create-meith 0.37.3 → 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/dist/bin.mjs
CHANGED
|
@@ -460,7 +460,7 @@ save-exact=true
|
|
|
460
460
|
*
|
|
461
461
|
* Adding a theme is: \`npm install\` it, add a line here, redeploy. Adding a
|
|
462
462
|
* plugin is the same, through board.plugins.json and meith.plugins.ts \u2014
|
|
463
|
-
* see docs/
|
|
463
|
+
* see docs/extensions/plugins.md.
|
|
464
464
|
*/
|
|
465
465
|
import { defineForumConfig } from '@meith/web/config'
|
|
466
466
|
import {
|
|
@@ -500,7 +500,7 @@ export default defineForumConfig({
|
|
|
500
500
|
// them. A plugin that does not fit that convention can be added here by hand instead \u2014
|
|
501
501
|
// keep it out of board.plugins.json so a regenerate does not drop it.
|
|
502
502
|
//
|
|
503
|
-
// docs/
|
|
503
|
+
// docs/extensions/plugins.md explains both.
|
|
504
504
|
|
|
505
505
|
import type { InstalledPlugin } from '@meith/web/config'
|
|
506
506
|
|
|
@@ -635,7 +635,7 @@ jobs:
|
|
|
635
635
|
# \`npm install\` below), so a build here is heavier than \`Dockerfile.prebuilt\`'s
|
|
636
636
|
# thin delta \u2014 that image, pulled rather than built, is the trade the advanced
|
|
637
637
|
# path takes for a low-spec build server or a faster deploy (see \`README.md\`
|
|
638
|
-
# and, in the meith repository, docs/
|
|
638
|
+
# and, in the meith repository, docs/operations/docker-compose.md,
|
|
639
639
|
# "Custom boards").
|
|
640
640
|
#
|
|
641
641
|
# Two stages, not three: unlike the official image, this does not prune down
|
|
@@ -733,7 +733,7 @@ ENTRYPOINT ["./docker-entrypoint.sh"]
|
|
|
733
733
|
#
|
|
734
734
|
# FROM the published framework base image \u2014 deps + framework layers only,
|
|
735
735
|
# locked to this exact release (see the meith repository's
|
|
736
|
-
# docs/
|
|
736
|
+
# docs/operations/docker-compose.md, "Custom boards", and docker/Dockerfile.base for what
|
|
737
737
|
# it is and is not). This board's own Dockerfile only ever installs its own
|
|
738
738
|
# delta on top of it \u2014 a new plugin's own dependency, typically nothing more
|
|
739
739
|
# \u2014 which is what keeps a rebuild after \`npm install some-plugin\` a matter
|
|
@@ -1072,7 +1072,7 @@ services:
|
|
|
1072
1072
|
# service the variables the file names, so a Scheduled Task running
|
|
1073
1073
|
# \`meith backup\` in this container would never see them without these
|
|
1074
1074
|
# lines \u2014 see the meith repository's
|
|
1075
|
-
# docs/
|
|
1075
|
+
# docs/operations/coolify.md, "Set up backups".
|
|
1076
1076
|
- BACKUP_S3_BUCKET=\${BACKUP_S3_BUCKET:-}
|
|
1077
1077
|
- BACKUP_S3_REGION=\${BACKUP_S3_REGION:-}
|
|
1078
1078
|
- BACKUP_S3_ACCESS_KEY_ID=\${BACKUP_S3_ACCESS_KEY_ID:-}
|
|
@@ -1098,7 +1098,7 @@ services:
|
|
|
1098
1098
|
# @meith/worker is not published (see the meith repository's
|
|
1099
1099
|
# docs/contributing/release.md), so there is no compiled worker binary a scaffolded
|
|
1100
1100
|
# board can run \u2014 this drives the tick the alternative way the meith
|
|
1101
|
-
# repository documents in docs/
|
|
1101
|
+
# repository documents in docs/operations/docker-compose.md, "Running the tick without
|
|
1102
1102
|
# a second set of credentials": a small loop calling /api/system/tick.
|
|
1103
1103
|
worker:
|
|
1104
1104
|
image: alpine:3.24@sha256:28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
|
|
@@ -1244,7 +1244,7 @@ services:
|
|
|
1244
1244
|
# service the variables the file names, so a Scheduled Task running
|
|
1245
1245
|
# \`meith backup\` in this container would never see them without these
|
|
1246
1246
|
# lines \u2014 see the meith repository's
|
|
1247
|
-
# docs/
|
|
1247
|
+
# docs/operations/coolify.md, "Set up backups".
|
|
1248
1248
|
- BACKUP_S3_BUCKET=\${BACKUP_S3_BUCKET:-}
|
|
1249
1249
|
- BACKUP_S3_REGION=\${BACKUP_S3_REGION:-}
|
|
1250
1250
|
- BACKUP_S3_ACCESS_KEY_ID=\${BACKUP_S3_ACCESS_KEY_ID:-}
|
|
@@ -1270,7 +1270,7 @@ services:
|
|
|
1270
1270
|
# @meith/worker is not published (see the meith repository's
|
|
1271
1271
|
# docs/contributing/release.md), so there is no compiled worker binary a scaffolded
|
|
1272
1272
|
# board can run \u2014 this drives the tick the alternative way the meith
|
|
1273
|
-
# repository documents in docs/
|
|
1273
|
+
# repository documents in docs/operations/docker-compose.md, "Running the tick without
|
|
1274
1274
|
# a second set of credentials": a small loop calling /api/system/tick.
|
|
1275
1275
|
worker:
|
|
1276
1276
|
image: alpine:3.24@sha256:28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
|
|
@@ -1308,7 +1308,7 @@ volumes:
|
|
|
1308
1308
|
# secrets for you: every value Coolify would have filled in \u2014 the database
|
|
1309
1309
|
# password, AUTH_SECRET, TICK_SECRET, the board's own address \u2014 comes from
|
|
1310
1310
|
# a \`.env\` beside this file instead. See the meith repository's
|
|
1311
|
-
# docs/
|
|
1311
|
+
# docs/operations/docker-compose.md, which this file is
|
|
1312
1312
|
# the last step of.
|
|
1313
1313
|
#
|
|
1314
1314
|
# \`docker compose\` only auto-discovers a file literally named
|
|
@@ -1324,7 +1324,7 @@ volumes:
|
|
|
1324
1324
|
# to GitHub and let \`.github/workflows/build.yml\` do it, or run
|
|
1325
1325
|
# \`docker build -f Dockerfile.prebuilt ...\` by hand) and change the two
|
|
1326
1326
|
# \`build: .\` lines below to \`image: <that image>:<version>\` \u2014 the
|
|
1327
|
-
# substitution docs/
|
|
1327
|
+
# substitution docs/operations/docker-compose.md, "Building
|
|
1328
1328
|
# somewhere else", walks through.
|
|
1329
1329
|
services:
|
|
1330
1330
|
postgres:
|
|
@@ -1406,7 +1406,7 @@ services:
|
|
|
1406
1406
|
APP_URL: \${APP_URL:-http://localhost:3000}
|
|
1407
1407
|
# One reverse proxy (Caddy, in the guide's own walkthrough) sits in
|
|
1408
1408
|
# front of \`web\` \u2014 see "Count your proxies" in
|
|
1409
|
-
# docs/
|
|
1409
|
+
# docs/operations/docker-compose.md.
|
|
1410
1410
|
TRUSTED_PROXY_HOPS: \${TRUSTED_PROXY_HOPS:-1}
|
|
1411
1411
|
# Leaving this at \`log\` does not mean no mail: it means the board
|
|
1412
1412
|
# decides, from the installer on first run or from
|
|
@@ -1469,7 +1469,7 @@ services:
|
|
|
1469
1469
|
depends_on:
|
|
1470
1470
|
- web
|
|
1471
1471
|
|
|
1472
|
-
# Off by default \u2014 see docs/
|
|
1472
|
+
# Off by default \u2014 see docs/operations/scaling.md before setting
|
|
1473
1473
|
# CACHE_DRIVER=redis above. This is the server it needs.
|
|
1474
1474
|
redis:
|
|
1475
1475
|
profiles: ['redis']
|
|
@@ -1498,264 +1498,91 @@ volumes:
|
|
|
1498
1498
|
"README.md",
|
|
1499
1499
|
`# ${name}
|
|
1500
1500
|
|
|
1501
|
-
A
|
|
1502
|
-
|
|
1503
|
-
## Deploy
|
|
1504
|
-
|
|
1505
|
-
Three paths onto a server, all ending at the same \`/install\`. **Quick
|
|
1506
|
-
start** onto [Coolify](https://coolify.io) is the default and needs nothing
|
|
1507
|
-
but a push; **advanced/prebuilt** moves the build off the server, onto
|
|
1508
|
-
GitHub Actions, for a low-spec build server or a faster deploy; **without a
|
|
1509
|
-
panel** is the same four containers run by hand, with your own \`.env\` and
|
|
1510
|
-
reverse proxy, and no Coolify at all. Pick one \u2014 a board only ever runs one
|
|
1511
|
-
of them at a time.
|
|
1512
|
-
|
|
1513
|
-
### Quick start (default)
|
|
1514
|
-
|
|
1515
|
-
Coolify builds the image itself, from this repository, every time it
|
|
1516
|
-
deploys \u2014 there is nothing to push anywhere first and no image tag to paste
|
|
1517
|
-
in. Two steps:
|
|
1518
|
-
|
|
1519
|
-
1. **Push this repository to GitHub.**
|
|
1520
|
-
|
|
1521
|
-
2. **Point Coolify at \`docker-compose.yaml\`** \u2014 a **Public Git repository**
|
|
1522
|
-
resource with **Docker Compose** as its build pack, this repository as its
|
|
1523
|
-
source. The name is Coolify's own default, so its **Compose file** field is
|
|
1524
|
-
already right when the form opens, and the file already carries Coolify's
|
|
1525
|
-
own "magic variables" for \`AUTH_SECRET\`, \`TICK_SECRET\` and the database
|
|
1526
|
-
password, generated on the first deploy and never typed in. Nothing else to
|
|
1527
|
-
set: \`docker-compose.yaml\` builds \`web\` and \`migrate\` from \`Dockerfile\`
|
|
1528
|
-
itself, so there is no \`MEITH_IMAGE\` here at all.
|
|
1529
|
-
|
|
1530
|
-
3. **Deploy, then \`/install\` on your own domain.** Coolify issues the
|
|
1531
|
-
certificate; the installer from there is the one
|
|
1532
|
-
[docs/getting-started/deployment/coolify.md](${repositoryUrl}/blob/main/docs/getting-started/deployment/coolify.md#4-run-the-installer)
|
|
1533
|
-
walks through, screen for screen. It seals itself when it finishes, and
|
|
1534
|
-
\`/install\` answers 404 from then on \u2014 run it **against the database you
|
|
1535
|
-
are going to keep**. Every push to \`main\` after this is picked up the next
|
|
1536
|
-
time Coolify's own **Redeploy** button runs \u2014 pushing alone does not
|
|
1537
|
-
rebuild it.
|
|
1538
|
-
|
|
1539
|
-
The trade for that zero setup is a heavier build: \`Dockerfile\` installs this
|
|
1540
|
-
board's full dependency closure on the server itself, on every deploy, rather
|
|
1541
|
-
than starting from a warm base image. A 2 GB VPS can OOM on it. If that is
|
|
1542
|
-
your server, use the advanced path below instead.
|
|
1543
|
-
|
|
1544
|
-
A quick-start board never needs \`Dockerfile.prebuilt\`,
|
|
1545
|
-
\`docker-compose.prebuilt.yaml\` or \`.github/workflows/build.yml\` \u2014 delete all
|
|
1546
|
-
three.
|
|
1547
|
-
|
|
1548
|
-
### Advanced / prebuilt \u2014 for a low-spec server or a faster deploy
|
|
1549
|
-
|
|
1550
|
-
Something else builds the image ahead of time; the server only ever pulls
|
|
1551
|
-
one. Three steps, nothing to configure by hand beyond one value only you know:
|
|
1552
|
-
|
|
1553
|
-
1. **Push this repository to GitHub.** \`.github/workflows/build.yml\` builds
|
|
1554
|
-
\`Dockerfile.prebuilt\` on every push to \`main\` and pushes the result to your
|
|
1555
|
-
own GitHub Container Registry, \`ghcr.io/<you>/${name}\` \u2014 using only the
|
|
1556
|
-
\`GITHUB_TOKEN\` every GitHub Actions run already carries. No secret to
|
|
1557
|
-
add, no registry account beyond the GitHub account you already have.
|
|
1558
|
-
|
|
1559
|
-
That build is the thing step 2 waits on: open the repository's
|
|
1560
|
-
**Actions** tab and let the run finish, because its **Summary** is where
|
|
1561
|
-
the exact image to paste into step 2 comes from. The Summary also links
|
|
1562
|
-
the package itself, to check it is public \u2014 a build from a public
|
|
1563
|
-
repository usually lands public already, and a private one fails
|
|
1564
|
-
Coolify's pull with an authentication error no operator can act on.
|
|
1565
|
-
|
|
1566
|
-
2. **Point Coolify at \`docker-compose.prebuilt.yaml\`** \u2014 a
|
|
1567
|
-
**Public Git repository** resource with **Docker Compose** as its build
|
|
1568
|
-
pack, this repository as its source, and its **Compose file** field
|
|
1569
|
-
changed from Coolify's default of \`docker-compose.yaml\` to
|
|
1570
|
-
\`docker-compose.prebuilt.yaml\`. That file carries Coolify's own "magic
|
|
1571
|
-
variables" for \`AUTH_SECRET\`, \`TICK_SECRET\` and the database password,
|
|
1572
|
-
generated on the first deploy and never typed in. The one thing Coolify
|
|
1573
|
-
cannot generate is the image step 1 just pushed: set \`MEITH_IMAGE\` in the
|
|
1574
|
-
resource's own environment to one of the two values that run's Summary
|
|
1575
|
-
printed (\`docker-compose.prebuilt.yaml\` refuses to start without it, with
|
|
1576
|
-
a message saying why). \`ghcr.io/<you>/${name}:\${{ github.sha }}\` names
|
|
1577
|
-
that one build and nothing else, ever; \`ghcr.io/<you>/${name}:latest\`
|
|
1578
|
-
follows \`main\` instead, so installing a plugin later is a push and a
|
|
1579
|
-
**Redeploy** \u2014 the trade this path takes, at the cost of an unrelated
|
|
1580
|
-
redeploy pulling whatever \`main\` most recently built.
|
|
1581
|
-
|
|
1582
|
-
3. **Deploy, then \`/install\` on your own domain.** Same installer, same
|
|
1583
|
-
[docs/getting-started/deployment/coolify.md](${repositoryUrl}/blob/main/docs/getting-started/deployment/coolify.md#4-run-the-installer)
|
|
1584
|
-
walk-through, same one-time seal. Every push to \`main\` after this rebuilds
|
|
1585
|
-
the image; Coolify's own **Redeploy** button is what actually pulls it \u2014
|
|
1586
|
-
pushing alone does not.
|
|
1587
|
-
|
|
1588
|
-
No Docker Hub, no paid CI: GitHub Actions' free tier and GHCR are the whole
|
|
1589
|
-
build side of this, for a board of any size.
|
|
1590
|
-
|
|
1591
|
-
**Building it yourself**: works on any machine with Docker, if you would
|
|
1592
|
-
rather not use GitHub Actions for the build \u2014 push the result wherever
|
|
1593
|
-
\`docker-compose.prebuilt.yaml\`'s \`MEITH_IMAGE\` can reach.
|
|
1501
|
+
A community board built on [Meith](${repositoryUrl}).
|
|
1594
1502
|
|
|
1595
|
-
|
|
1596
|
-
docker build -f Dockerfile.prebuilt --build-arg MEITH_VERSION=$(node -p "require('./package.json').dependencies['@meith/web']") -t ${name} .
|
|
1597
|
-
\`\`\`
|
|
1503
|
+
## Choose a deployment
|
|
1598
1504
|
|
|
1599
|
-
|
|
1505
|
+
| Route | File | Where the image builds |
|
|
1506
|
+
|---|---|---|
|
|
1507
|
+
| Quick start with Coolify | \`docker-compose.yaml\` | Your server |
|
|
1508
|
+
| Advanced / prebuilt | \`docker-compose.prebuilt.yaml\` | GitHub Actions or another build machine |
|
|
1509
|
+
| Docker Compose without a panel | \`docker-compose.byhand.yaml\` | Your server |
|
|
1600
1510
|
|
|
1601
|
-
|
|
1602
|
-
four containers deployed with nothing generating secrets for you: a \`.env\`
|
|
1603
|
-
you write yourself, a port published for the reverse proxy you already run,
|
|
1604
|
-
and \`docker compose up -d --build\` in place of a panel's Deploy button.
|
|
1605
|
-
[docs/getting-started/deployment/docker-compose.md](${repositoryUrl}/blob/main/docs/getting-started/deployment/docker-compose.md)
|
|
1606
|
-
is the full walkthrough this file is the last step of, including the
|
|
1607
|
-
\`.env\` this repository does not carry \u2014 nothing here belongs in git. Delete
|
|
1608
|
-
this file if you know you will only ever deploy through Coolify; keep it,
|
|
1609
|
-
and it needs nothing else changed, if you later want to move away from
|
|
1610
|
-
Coolify without changing how the board itself is built.
|
|
1511
|
+
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.
|
|
1611
1512
|
|
|
1612
|
-
|
|
1513
|
+
### Quick start with Coolify
|
|
1613
1514
|
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
depend on yet. Deploy some other way and something still has to call that
|
|
1620
|
-
route (or run \`meith task:run\`) every minute, or nothing catches up
|
|
1621
|
-
and nothing errors.
|
|
1515
|
+
1. Push this repository to GitHub.
|
|
1516
|
+
2. Create a Git repository resource in Coolify with the Docker Compose build pack and \`/docker-compose.yaml\` as the Compose file.
|
|
1517
|
+
3. Assign the board's domain and deploy. Coolify supplies database and authentication secrets; save a protected recovery copy.
|
|
1518
|
+
4. Confirm \`postgres\` is healthy, \`migrate\` exits successfully, and \`web\` and \`worker\` run.
|
|
1519
|
+
5. Open \`/install\`, unlock with \`AUTH_SECRET\`, and create the board and its first administrator. The installer seals itself and returns 404 after completion.
|
|
1622
1520
|
|
|
1623
|
-
|
|
1521
|
+
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.
|
|
1624
1522
|
|
|
1625
|
-
|
|
1626
|
-
npm install
|
|
1627
|
-
npm run dev
|
|
1628
|
-
\`\`\`
|
|
1523
|
+
### Advanced / prebuilt
|
|
1629
1524
|
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1525
|
+
1. Let \`.github/workflows/build.yml\` finish in GitHub Actions. It builds and publishes your board's image.
|
|
1526
|
+
2. Make that image accessible to Coolify and select \`/docker-compose.prebuilt.yaml\`.
|
|
1527
|
+
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.
|
|
1528
|
+
4. Deploy and complete \`/install\` as above.
|
|
1633
1529
|
|
|
1634
|
-
|
|
1635
|
-
\`DATABASE_URL\` and the two secrets in it, then:
|
|
1530
|
+
For a local image build, the build argument comes from the board's pinned package:
|
|
1636
1531
|
|
|
1637
1532
|
\`\`\`sh
|
|
1638
|
-
|
|
1639
|
-
echo "<password>" | npm run meith -- user:create --username <name> --email <address> --group administrators
|
|
1533
|
+
docker build -f Dockerfile.prebuilt --build-arg MEITH_VERSION=$(node -p "require('./package.json').dependencies['@meith/web']") -t ${name} .
|
|
1640
1534
|
\`\`\`
|
|
1641
1535
|
|
|
1642
|
-
|
|
1536
|
+
After changing the board, wait for its new image and update \`MEITH_IMAGE\` if pinned to a commit, then redeploy.
|
|
1643
1537
|
|
|
1644
|
-
|
|
1645
|
-
is named here so the bundler can see it; nothing is found by scanning a
|
|
1646
|
-
directory at runtime.
|
|
1647
|
-
- **\`/admin\`** \u2014 settings, forums, groups, members, themes, maintenance. An
|
|
1648
|
-
administrator re-enters their password to get in, and again for anything
|
|
1649
|
-
destructive.
|
|
1650
|
-
- **\`npm run meith -- --help\`** \u2014 the operator CLI. Everything the panel does
|
|
1651
|
-
and a few things it cannot, without a browser.
|
|
1538
|
+
## Run locally
|
|
1652
1539
|
|
|
1653
|
-
|
|
1540
|
+
\`\`\`sh
|
|
1541
|
+
npm install
|
|
1542
|
+
npm run dev
|
|
1543
|
+
\`\`\`
|
|
1654
1544
|
|
|
1655
|
-
|
|
1656
|
-
built into the image. In this repository:
|
|
1545
|
+
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).
|
|
1657
1546
|
|
|
1658
|
-
|
|
1659
|
-
registers it:
|
|
1547
|
+
## Configure the community
|
|
1660
1548
|
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1549
|
+
- \`meith.config.ts\` registers themes and board configuration.
|
|
1550
|
+
- \`board.plugins.json\` and \`meith.plugins.ts\` register installed plugins.
|
|
1551
|
+
- \`/admin\` manages forums, members, permissions and settings.
|
|
1552
|
+
- \`npm run meith -- --help\` lists operator commands.
|
|
1664
1553
|
|
|
1665
|
-
|
|
1666
|
-
(\`npm run meith -- plugin:remove <key>\` reverses it). A **theme** is
|
|
1667
|
-
\`npm install --save-exact @meith/theme-midnight\`, then an entry in
|
|
1668
|
-
\`meith.config.ts\`'s \`themes\` map following the shape of the \`default\` one
|
|
1669
|
-
already there \u2014 set \`defaultTheme\` to its key to make it the board's
|
|
1670
|
-
default.
|
|
1554
|
+
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\`.
|
|
1671
1555
|
|
|
1672
|
-
|
|
1673
|
-
not rebuild. Quick start builds the new image on that redeploy; advanced/prebuilt
|
|
1674
|
-
waits for \`.github/workflows/build.yml\` to finish first, and Redeploy is
|
|
1675
|
-
what actually pulls the result.
|
|
1556
|
+
## Install an extension
|
|
1676
1557
|
|
|
1677
|
-
|
|
1678
|
-
**Admin \u2192 System** (**Version & migrations**) in the browser, or:
|
|
1558
|
+
Add a plugin from this checkout:
|
|
1679
1559
|
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1560
|
+
\`\`\`sh
|
|
1561
|
+
npm run meith -- plugin:add @meith/plugin-dues
|
|
1562
|
+
\`\`\`
|
|
1683
1563
|
|
|
1684
|
-
|
|
1685
|
-
for the full guide.
|
|
1564
|
+
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.
|
|
1686
1565
|
|
|
1687
1566
|
## Upgrading
|
|
1688
1567
|
|
|
1689
|
-
\`.github/workflows/update.yml\`
|
|
1690
|
-
whenever you press **Run workflow** on the Actions tab \u2014 it checks for a new
|
|
1691
|
-
Meith release and opens a pull request that moves every \`@meith/*\` package
|
|
1692
|
-
and \`next\` together, and rewrites the deploy files this scaffold owns
|
|
1693
|
-
(\`Dockerfile\`, the compose files, the workflows) to the new release's
|
|
1694
|
-
shape. A file you have edited yourself is never rewritten; the run's log
|
|
1695
|
-
names any it left for you. One-time setup: under
|
|
1696
|
-
**Settings \u2192 Actions \u2192 General**, enable **Allow GitHub Actions to create
|
|
1697
|
-
and approve pull requests**, or the workflow cannot open one.
|
|
1568
|
+
\`.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 \u2192 Actions \u2192 General**.
|
|
1698
1569
|
|
|
1699
|
-
|
|
1700
|
-
release notes it links, take a backup first, and press **Redeploy** in
|
|
1701
|
-
Coolify after the merge \u2014 pushing alone does not rebuild. Once the new
|
|
1702
|
-
version serves, run \`npm run meith -- upgrade\` against it for the plugin
|
|
1703
|
-
migrations.
|
|
1570
|
+
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.
|
|
1704
1571
|
|
|
1705
|
-
|
|
1572
|
+
To prepare the update locally:
|
|
1706
1573
|
|
|
1707
1574
|
\`\`\`sh
|
|
1708
1575
|
npx create-meith@latest update
|
|
1709
|
-
git commit -am "Update Meith"
|
|
1710
|
-
git push
|
|
1711
1576
|
\`\`\`
|
|
1712
1577
|
|
|
1713
|
-
|
|
1714
|
-
rewrite neither of them can do:
|
|
1578
|
+
The updater moves package pins and supported deployment files together. Its package update includes these commands; running them alone does not update deployment files:
|
|
1715
1579
|
|
|
1716
1580
|
\`\`\`sh
|
|
1717
1581
|
npm install --save-exact @meith/web@latest @meith/cli@latest @meith/theme-default@latest
|
|
1718
1582
|
npm install --save-exact next@$(node -p "require('./node_modules/@meith/web/package.json').dependencies.next")
|
|
1719
1583
|
\`\`\`
|
|
1720
1584
|
|
|
1721
|
-
|
|
1722
|
-
the npm commands alone never bump it: upgrading only the \`@meith/*\` packages
|
|
1723
|
-
leaves the board's own pin on the old Next while \`@meith/web\` depends on the
|
|
1724
|
-
new one, which npm resolves by installing both \u2014 the build then runs on one
|
|
1725
|
-
version while everything reading \`package.json\` sees the other. Reading the
|
|
1726
|
-
version out of the freshly installed \`@meith/web\` is what keeps the two the
|
|
1727
|
-
same without anybody having to know the number.
|
|
1728
|
-
|
|
1729
|
-
\`next\` and \`@meith/web\` move together or not at all, which is why one
|
|
1730
|
-
updater owns the whole move and no dependency bot bumps either on its own.
|
|
1731
|
-
What Dependabot *does* keep current is this repository's own GitHub Actions \u2014
|
|
1732
|
-
\`.github/dependabot.yml\` opens a weekly pull request bumping the actions
|
|
1733
|
-
pinned under \`.github/workflows\`, a safe, independent update the updater
|
|
1734
|
-
leaves to it.
|
|
1735
|
-
|
|
1736
|
-
On the quick-start path there is no version to keep in sync by hand:
|
|
1737
|
-
\`Dockerfile\` runs \`npm install\` straight from this \`package.json\` on every
|
|
1738
|
-
build, so a rebuild always picks up whatever is pinned there. On the
|
|
1739
|
-
advanced/prebuilt path, that \`package.json\` change is the whole pin:
|
|
1740
|
-
\`Dockerfile.prebuilt\`'s own \`FROM\` line takes the version as a build argument,
|
|
1741
|
-
and \`.github/workflows/build.yml\` reads it straight out of \`package.json\`'s
|
|
1742
|
-
own \`@meith/web\` dependency when it rebuilds \u2014 nothing in
|
|
1743
|
-
\`Dockerfile.prebuilt\` itself to keep in sync by hand. \`--save-exact\` matters
|
|
1744
|
-
either way: npm's default \`save-prefix\` is \`^\`, and a caret range is not a
|
|
1745
|
-
legal Docker image tag for the advanced path \u2014 without it, this exact command
|
|
1746
|
-
would write \`"^0.18.0"\` and the next \`Dockerfile.prebuilt\` build would fail
|
|
1747
|
-
with \`invalid reference format\` instead of building. This
|
|
1748
|
-
project's own \`.npmrc\` sets \`save-exact=true\` for the same reason, so an
|
|
1749
|
-
\`npm install\` of anything else here \u2014 a plugin, say \u2014 stays pinned too; the
|
|
1750
|
-
build workflow also refuses to build from anything but an exact version, as
|
|
1751
|
-
a second line of defense. Once the rebuilt image is deployed, run
|
|
1752
|
-
\`npm run meith -- upgrade\` against it for the plugin migrations \u2014 see
|
|
1753
|
-
[the operator CLI](${repositoryUrl}/blob/main/docs/guides/operations/operating.md#the-operator-cli)
|
|
1754
|
-
for running it against this deployment.
|
|
1755
|
-
|
|
1756
|
-
Migrations are forward-only. Recovery is by restore, so take a backup first \u2014
|
|
1757
|
-
there is no down migration to undo a destructive one, and a button that pretended
|
|
1758
|
-
otherwise would be worse than its absence.
|
|
1585
|
+
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.
|
|
1759
1586
|
`
|
|
1760
1587
|
);
|
|
1761
1588
|
if (target === "vercel") {
|
|
@@ -1792,277 +1619,95 @@ ${AT_ROOT_IGNORES}
|
|
|
1792
1619
|
function vercelReadme({ name, repositoryUrl, templateRepositoryUrl }) {
|
|
1793
1620
|
return `# ${name}
|
|
1794
1621
|
|
|
1795
|
-
A
|
|
1622
|
+
A community board built on [Meith](${repositoryUrl}), deployed as Vercel functions.
|
|
1796
1623
|
|
|
1797
1624
|
[](${deployButtonUrl(templateRepositoryUrl)})
|
|
1798
1625
|
|
|
1799
|
-
##
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
pooled connection string as \`DATABASE_URL\` and the direct one as
|
|
1805
|
-
\`DATABASE_URL_UNPOOLED\`.
|
|
1806
|
-
- **An Upstash Redis store**, attached the same way, for the shared cache. It
|
|
1807
|
-
publishes \`KV_URL\`, which the board reads as \`REDIS_URL\` \u2014 \`KV_REST_API_URL\`
|
|
1808
|
-
beside it is an HTTPS endpoint and is not used for this.
|
|
1809
|
-
- **A Vercel Blob store** for uploads, which publishes \`BLOB_STORE_ID\` into the
|
|
1810
|
-
project by itself. That is the whole credential: the board hands the id to
|
|
1811
|
-
Vercel's SDK, which authenticates with the deployment's own OIDC identity, so
|
|
1812
|
-
there is no token to copy. This is what used to be four hand-typed \`S3_*\`
|
|
1813
|
-
secrets.
|
|
1814
|
-
- **A Resend mail account**, attached the same way, which publishes
|
|
1815
|
-
\`RESEND_API_KEY\` and \`RESEND_EMAIL_DOMAIN\`. The board reads both names
|
|
1816
|
-
directly: its mail driver already speaks Resend's request shape, so there is
|
|
1817
|
-
nothing to adapt, and the sending domain is what the sender is built from.
|
|
1818
|
-
- **A Vercel project** carrying \`vercel.json\` \u2014 the build command
|
|
1819
|
-
\`${VERCEL_BUILD_COMMAND}\`,
|
|
1820
|
-
which applies the schema before it builds, materializes the board's app at
|
|
1821
|
-
the project root so the artefact lands where Vercel reads it, and the cron
|
|
1822
|
-
entry that drives the tick.
|
|
1823
|
-
|
|
1824
|
-
**Mail needs no variables after the deploy.** Resend publishes both its key
|
|
1825
|
-
and its sending domain, and the board sends from \`${RESEND_SENDER_MAILBOX}@\`
|
|
1826
|
-
that domain \u2014 see *Mail* below to send from a different address, and for the
|
|
1827
|
-
one case that does need you: a domain Resend has not verified yet.
|
|
1828
|
-
|
|
1829
|
-
## What to type into the deploy form
|
|
1830
|
-
|
|
1831
|
-
**Two secrets**, generated rather than chosen. Thirty-two characters is a floor
|
|
1832
|
-
the board enforces at boot, not a suggestion:
|
|
1626
|
+
## 1. Connect services and set secrets
|
|
1627
|
+
|
|
1628
|
+
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.
|
|
1629
|
+
|
|
1630
|
+
Generate two independent secrets:
|
|
1833
1631
|
|
|
1834
1632
|
\`\`\`sh
|
|
1835
|
-
openssl rand -hex 32
|
|
1836
|
-
openssl rand -hex 32
|
|
1633
|
+
openssl rand -hex 32
|
|
1634
|
+
openssl rand -hex 32
|
|
1837
1635
|
\`\`\`
|
|
1838
1636
|
|
|
1839
|
-
\`
|
|
1840
|
-
cannot be told to send another \u2014 the caller is the platform, so this one has to
|
|
1841
|
-
be an environment variable both ends can read, and cannot be something the
|
|
1842
|
-
board makes up for itself. Note that this floor is stricter than the 16
|
|
1843
|
-
characters Vercel's own cron documentation suggests \u2014 a value generated by
|
|
1844
|
-
following those instructions is refused here, and the fix is a longer secret.
|
|
1637
|
+
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.
|
|
1845
1638
|
|
|
1846
|
-
|
|
1847
|
-
in outgoing mail. It stays in the environment deliberately: a copy of the
|
|
1848
|
-
database is then not enough to forge either.
|
|
1849
|
-
|
|
1850
|
-
**That is the whole form.** Everything else the board works out from the stores
|
|
1851
|
-
this button just linked to the project:
|
|
1639
|
+
The platform derives the following defaults when you have not supplied explicit overrides:
|
|
1852
1640
|
|
|
1853
1641
|
\`\`\`ini
|
|
1854
1642
|
${VERCEL_DERIVED_DRIVERS.join("\n")}
|
|
1855
1643
|
\`\`\`
|
|
1856
1644
|
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
\`
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
If a
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
If you would rather keep uploads somewhere you hold yourself \u2014 see *Leaving
|
|
1873
|
-
Vercel* below for why that matters \u2014 set \`FILESTORE_DRIVER=s3\` and add
|
|
1874
|
-
\`S3_BUCKET\`, \`S3_REGION\`, \`S3_ACCESS_KEY_ID\` and \`S3_SECRET_ACCESS_KEY\` in the
|
|
1875
|
-
project's environment settings, with \`S3_ENDPOINT\` for a bucket that is not AWS
|
|
1876
|
-
(\`S3_REGION=auto\` for R2). The same board runs either way.
|
|
1645
|
+
| Service value | Meith uses it for |
|
|
1646
|
+
|---|---|
|
|
1647
|
+
| \`DATABASE_URL\` | Runtime database connection |
|
|
1648
|
+
| \`DATABASE_URL_UNPOOLED\`, falling back to \`POSTGRES_URL_NON_POOLING\` | \`DIRECT_DATABASE_URL\` for migrations and installer locks |
|
|
1649
|
+
| \`KV_URL\` | \`REDIS_URL\`; the Redis protocol connection, not the HTTP REST endpoint |
|
|
1650
|
+
| \`BLOB_STORE_ID\` | Upload storage authenticated through the deployment identity |
|
|
1651
|
+
| \`RESEND_API_KEY\`, \`RESEND_EMAIL_DOMAIN\` | HTTP mail credentials and sender |
|
|
1652
|
+
|
|
1653
|
+
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.
|
|
1654
|
+
|
|
1655
|
+
## 2. Deploy and install
|
|
1656
|
+
|
|
1657
|
+
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.
|
|
1658
|
+
|
|
1659
|
+
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.
|
|
1877
1660
|
|
|
1878
1661
|
## Mail
|
|
1879
1662
|
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
A board with the key but no verified domain \u2014 which is what you get if you
|
|
1897
|
-
remove the integration's \`RESEND_EMAIL_DOMAIN\` without putting a
|
|
1898
|
-
\`MAIL_FROM\` in its place \u2014 does not guess a sender. It stays on the log
|
|
1899
|
-
driver and delivers nothing, which is the honest outcome: a guessed sender at
|
|
1900
|
-
an unverified domain would be refused by Resend anyway, one message at a
|
|
1901
|
-
time.
|
|
1902
|
-
|
|
1903
|
-
The board is not tied to Resend. Its mail driver is a plain JSON-over-HTTPS
|
|
1904
|
-
sender that posts \`{from, to, subject, text, html, reply_to}\` with a bearer
|
|
1905
|
-
token \u2014 Resend's \`POST /emails\` happens to be exactly that shape, which is why
|
|
1906
|
-
it needs no adapter. Any provider with the same shape works: set
|
|
1907
|
-
\`MAIL_HTTP_ENDPOINT\`, \`MAIL_HTTP_TOKEN\` and \`MAIL_DRIVER=http\` in the
|
|
1908
|
-
project's environment settings, and set the first two **together** \u2014 either
|
|
1909
|
-
one on its own stands the Resend bridge down, so a key issued for Resend is
|
|
1910
|
-
never presented to an endpoint you chose. Setting \`MAIL_DRIVER\` to anything
|
|
1911
|
-
but \`http\` stands the bridge down too, for the same reason: a board that
|
|
1912
|
-
moved to SMTP must not send through its new provider from Resend's domain.
|
|
1913
|
-
Delete \`RESEND_API_KEY\` once you have moved off Resend.
|
|
1914
|
-
|
|
1915
|
-
Check it worked: sign in as the administrator and use the test button on
|
|
1916
|
-
**/admin \u2192 Settings \u2192 Mail**.
|
|
1917
|
-
|
|
1918
|
-
## First run: \`/install\`
|
|
1919
|
-
|
|
1920
|
-
The build applies migrations, but an empty schema is not yet a board. Open
|
|
1921
|
-
\`https://<your-deployment>/install\` once the first deploy is green. It asks for
|
|
1922
|
-
the board's name and address and for the first administrator's username, email
|
|
1923
|
-
and password, creates the board and that account, and then **seals itself**:
|
|
1924
|
-
\`/install\` answers 404 from then on. Run it against the database you intend to
|
|
1925
|
-
keep \u2014 the screens are the ones
|
|
1926
|
-
[docs/getting-started/deployment/docker-compose.md](${repositoryUrl}/blob/main/docs/getting-started/deployment/docker-compose.md#6-install-it)
|
|
1927
|
-
walks through.
|
|
1928
|
-
|
|
1929
|
-
## The tick
|
|
1930
|
-
|
|
1931
|
-
\`vercel.json\` asks Vercel to call \`${TICK_PATH}\` on \`${TICK_SCHEDULE}\`. That
|
|
1932
|
-
route is how bans expire, digests send, mail leaves the outbox and the queue
|
|
1933
|
-
drains; nothing here runs it on its own, because there is no worker process on
|
|
1934
|
-
a function platform. Two things about it are worth knowing **before** you
|
|
1935
|
-
deploy rather than after:
|
|
1936
|
-
|
|
1937
|
-
- **This ships a daily schedule, because Hobby refuses anything faster.** A
|
|
1938
|
-
Hobby plan rejects a cron expression that would run more than once a day \u2014
|
|
1939
|
-
the deployment fails outright rather than being slowed down \u2014 so
|
|
1940
|
-
\`vercel.json\` carries \`${TICK_SCHEDULE}\` and deploys anywhere. On a
|
|
1941
|
-
paid plan, edit it to \`* * * * *\` and the board ticks every minute.
|
|
1942
|
-
|
|
1943
|
-
A daily tick loses nothing permanently: tasks are written so a missed run
|
|
1944
|
-
delays work rather than dropping it, and a password reset is sent as the
|
|
1945
|
-
request is handled rather than waiting for a tick. What it does delay is
|
|
1946
|
-
everything the tick drives \u2014 a new post is not findable in search, and a
|
|
1947
|
-
notification is not sent, until the next run.
|
|
1948
|
-
|
|
1949
|
-
**To keep a fast tick without paying**, drive \`${TICK_PATH}\` from anything
|
|
1950
|
-
that can call a URL on a schedule \u2014 a GitHub Actions workflow, a systemd
|
|
1951
|
-
timer, an uptime pinger \u2014 presenting \`TICK_SECRET\` instead of
|
|
1952
|
-
\`CRON_SECRET\`. The endpoint accepts either, so the Vercel cron and an
|
|
1953
|
-
outside scheduler can both drive it.
|
|
1954
|
-
- **\`maxDuration = 300\` is validated when the project builds, not when the
|
|
1955
|
-
function runs.** A plan that does not allow 300 seconds therefore **fails the
|
|
1956
|
-
deployment** rather than clamping the request. With Fluid Compute \u2014 the
|
|
1957
|
-
default for new projects \u2014 Hobby allows 300 and this builds as written. With
|
|
1958
|
-
Fluid Compute switched off, Hobby caps a function at 60 seconds and the build
|
|
1959
|
-
fails. Turn Fluid Compute back on.
|
|
1960
|
-
|
|
1961
|
-
A tick that reaches the tasks and runs them answers \`200\` even when one of them
|
|
1962
|
-
threw, with \`ok: false\` and the failure named in \`ran\`. That is deliberate:
|
|
1963
|
-
schedulers retry non-2xx answers, and a task that fails every time would turn
|
|
1964
|
-
each retry into another attempt against whatever it is failing against.
|
|
1663
|
+
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 \u2192 Settings \u2192 Mail**.
|
|
1664
|
+
|
|
1665
|
+
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.
|
|
1666
|
+
|
|
1667
|
+
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).
|
|
1668
|
+
|
|
1669
|
+
## 3. Verify scheduled work and hosting limits
|
|
1670
|
+
|
|
1671
|
+
\`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.
|
|
1672
|
+
|
|
1673
|
+
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).
|
|
1674
|
+
|
|
1675
|
+
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.
|
|
1676
|
+
|
|
1677
|
+
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.
|
|
1965
1678
|
|
|
1966
1679
|
## Upgrading
|
|
1967
1680
|
|
|
1968
|
-
\`.github/workflows/update.yml\`
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
and \`next\` together. A file you have edited yourself is never rewritten; the
|
|
1972
|
-
run's log names any it left for you. One-time setup: under
|
|
1973
|
-
**Settings \u2192 Actions \u2192 General**, enable **Allow GitHub Actions to create and
|
|
1974
|
-
approve pull requests**, or the workflow cannot open one. Read the release
|
|
1975
|
-
notes the pull request links and take a backup before merging; Vercel
|
|
1976
|
-
rebuilds on the merge, and the build command applies the new migrations
|
|
1977
|
-
before it builds.
|
|
1681
|
+
\`.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 \u2192 Actions \u2192 General**.
|
|
1682
|
+
|
|
1683
|
+
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.
|
|
1978
1684
|
|
|
1979
|
-
|
|
1685
|
+
To prepare the update locally:
|
|
1980
1686
|
|
|
1981
1687
|
\`\`\`sh
|
|
1982
1688
|
npx create-meith@latest update
|
|
1983
|
-
git commit -am "Update Meith"
|
|
1984
|
-
git push
|
|
1985
1689
|
\`\`\`
|
|
1986
1690
|
|
|
1987
|
-
|
|
1691
|
+
The updater also reconciles supported deployment files. Its package update keeps Meith and Next.js aligned:
|
|
1988
1692
|
|
|
1989
1693
|
\`\`\`sh
|
|
1990
1694
|
npm install --save-exact @meith/web@latest @meith/cli@latest @meith/theme-default@latest
|
|
1991
1695
|
npm install --save-exact next@$(node -p "require('./node_modules/@meith/web/package.json').dependencies.next")
|
|
1992
1696
|
\`\`\`
|
|
1993
1697
|
|
|
1994
|
-
|
|
1995
|
-
installed here.
|
|
1996
|
-
|
|
1997
|
-
The second command is not optional. This board pins \`next\` itself \u2014 Vercel
|
|
1998
|
-
reads that pin to pick its Next.js builder \u2014 and the npm commands alone never
|
|
1999
|
-
bump it. Upgrading only the \`@meith/*\` packages leaves two versions of Next
|
|
2000
|
-
installed, the board built with one and the platform configured for the
|
|
2001
|
-
other. Reading the version out of the freshly installed \`@meith/web\` keeps
|
|
2002
|
-
them the same without anybody having to know the number.
|
|
2003
|
-
|
|
2004
|
-
Migrations are forward-only. Recovery is by restore, so take a backup first \u2014
|
|
2005
|
-
there is no down migration to undo a destructive one.
|
|
1698
|
+
See [Upgrade Meith](${repositoryUrl}/blob/main/docs/operations/upgrading.md) for validation and recovery.
|
|
2006
1699
|
|
|
2007
1700
|
## Leaving Vercel
|
|
2008
1701
|
|
|
2009
|
-
|
|
2010
|
-
is not portable: Neon and Upstash hand out ordinary Postgres and Redis strings
|
|
2011
|
-
that any host accepts, but a Vercel Blob store is reachable only through Vercel's
|
|
2012
|
-
own API and there is no bucket to sync out of it. **The uploads are the thing you
|
|
2013
|
-
have to carry out deliberately, and \`meith backup\` is how.**
|
|
1702
|
+
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**.
|
|
2014
1703
|
|
|
2015
|
-
|
|
2016
|
-
default** \u2014 it walks the Blob store, pulls every object, and puts them in the
|
|
2017
|
-
bundle beside the database dump. This is the opposite of the \`s3\` default, which
|
|
2018
|
-
skips them, because a bucket has its own backup story you can drive yourself and
|
|
2019
|
-
a Blob store does not:
|
|
1704
|
+
With that environment selected and a writable output directory:
|
|
2020
1705
|
|
|
2021
1706
|
\`\`\`sh
|
|
2022
|
-
|
|
2023
|
-
DIRECT_DATABASE_URL=\u2026 # Neon's DATABASE_URL_UNPOOLED
|
|
2024
|
-
FILESTORE_DRIVER=blob
|
|
2025
|
-
BLOB_READ_WRITE_TOKEN=\u2026 # create one on the store; see below
|
|
2026
|
-
npm run meith -- backup
|
|
2027
|
-
\`\`\`
|
|
2028
|
-
|
|
2029
|
-
Run that from a checkout of this repository, with those four values in the
|
|
2030
|
-
environment \u2014 the CLI talks to Neon and to the Blob store over the network, so
|
|
2031
|
-
it does not have to run on Vercel.
|
|
2032
|
-
|
|
2033
|
-
That last one is the one value this route asks you to make by hand, and only
|
|
2034
|
-
here. On the deployment the board reaches the store with \`BLOB_STORE_ID\` and
|
|
2035
|
-
the deployment's OIDC identity, which a command on your own machine does not
|
|
2036
|
-
have. Open the store under **Storage**, create a read-write token, and use it
|
|
2037
|
-
for the backup; the board itself never needs it. The bundle it writes holds the dump *and*
|
|
2038
|
-
every object. Check the last line it prints: if it says *no uploads*, the
|
|
2039
|
-
uploads are not in the bundle and restoring it gives a board whose posts have
|
|
2040
|
-
broken images.
|
|
2041
|
-
|
|
2042
|
-
Restoring puts them wherever the *restoring* board's \`FILESTORE_DRIVER\` points,
|
|
2043
|
-
so the same bundle moves the board either onward or away:
|
|
2044
|
-
|
|
2045
|
-
\`\`\`sh
|
|
2046
|
-
# onto a self-hosted board with a bucket
|
|
2047
|
-
FILESTORE_DRIVER=s3 S3_BUCKET=\u2026 RESTORE_DATABASE_URL=\u2026 npm run meith -- restore bundle.tar.gz
|
|
2048
|
-
|
|
2049
|
-
# onto a board that keeps uploads on its own disk
|
|
2050
|
-
RESTORE_DATABASE_URL=\u2026 npm run meith -- restore bundle.tar.gz --uploads-dir ./uploads
|
|
1707
|
+
npm run meith -- backup --out ./board-backup.tar.gz --uploads include
|
|
2051
1708
|
\`\`\`
|
|
2052
1709
|
|
|
2053
|
-
|
|
2054
|
-
the attachments with it, and there is no second copy anywhere unless you made
|
|
2055
|
-
one.
|
|
2056
|
-
|
|
2057
|
-
## Somewhere other than Vercel
|
|
2058
|
-
|
|
2059
|
-
Everything above is one deployment shape.
|
|
2060
|
-
[docs/getting-started/deployment/docker-compose.md](${repositoryUrl}/blob/main/docs/getting-started/deployment/docker-compose.md) is the
|
|
2061
|
-
same board as containers you run yourself, and \`npx create-meith <name>\`
|
|
2062
|
-
scaffolds that shape instead \u2014 a Dockerfile, a compose file and a workflow that
|
|
2063
|
-
builds the image. [docs/guides/operations/scaling.md](${repositoryUrl}/blob/main/docs/guides/operations/scaling.md)
|
|
2064
|
-
explains why the drivers above are what they are, and why an S3-compatible
|
|
2065
|
-
bucket is the portable choice for uploads everywhere but here.
|
|
1710
|
+
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.
|
|
2066
1711
|
`;
|
|
2067
1712
|
}
|
|
2068
1713
|
function nextSteps(name) {
|
|
@@ -2367,7 +2012,7 @@ exercised once, each easy to delete.
|
|
|
2367
2012
|
npm test
|
|
2368
2013
|
|
|
2369
2014
|
\`src/plugin.tsx\` is the plugin. What a plugin may and may not do is
|
|
2370
|
-
documented in the meith repository under \`docs/
|
|
2015
|
+
documented in the meith repository under \`docs/extensions/plugins.md\`;
|
|
2371
2016
|
every hook and payload is listed in \`docs/reference/plugin-hooks.md\`.
|
|
2372
2017
|
|
|
2373
2018
|
## Run it inside a board
|
|
@@ -2381,8 +2026,7 @@ Scaffold a board next to this directory if you do not have one
|
|
|
2381
2026
|
npm installs a local directory as a symlink, so edits here are picked up by
|
|
2382
2027
|
the board's next build without reinstalling.
|
|
2383
2028
|
|
|
2384
|
-
Register the plugin in the board's \`meith.plugins.ts
|
|
2385
|
-
the top of that file shows the shape:
|
|
2029
|
+
Register the plugin in the board's \`meith.plugins.ts\`, keeping its existing entries:
|
|
2386
2030
|
|
|
2387
2031
|
import { messages as ${camel}Messages, plugin as ${camel}Plugin } from '${name}'
|
|
2388
2032
|
|
|
@@ -2390,12 +2034,12 @@ the top of that file shows the shape:
|
|
|
2390
2034
|
{ key: '${name}', enabled: true, plugin: ${camel}Plugin, messages: ${camel}Messages },
|
|
2391
2035
|
]
|
|
2392
2036
|
|
|
2393
|
-
and add the matching entry to \`board.plugins.json
|
|
2037
|
+
and add the matching entry to \`board.plugins.json\`, keeping its existing plugins:
|
|
2394
2038
|
|
|
2395
2039
|
{ "plugins": [{ "key": "${name}", "package": "${name}", "enabled": true }] }
|
|
2396
2040
|
|
|
2397
|
-
|
|
2398
|
-
migration,
|
|
2041
|
+
Run \`npm run meith -- upgrade\` against your development database to apply
|
|
2042
|
+
the plugin migration, then rebuild with \`npm run build\`. The plugin appears under
|
|
2399
2043
|
**Admin \u2192 Plugins**.
|
|
2400
2044
|
|
|
2401
2045
|
## Publish and list it
|
|
@@ -2405,7 +2049,7 @@ migration, run \`npx meith migrate\`. The plugin then appears under
|
|
|
2405
2049
|
finish \`listing.json\` (its \`repository\` field starts as a placeholder),
|
|
2406
2050
|
add the screenshot it names, and open a pull request against the meith
|
|
2407
2051
|
repository \u2014 the submission process and the review bar are documented there
|
|
2408
|
-
in \`docs/
|
|
2052
|
+
in \`docs/extensions/marketplace.md\`.
|
|
2409
2053
|
`;
|
|
2410
2054
|
}
|
|
2411
2055
|
function themeReadme(options, camel, title) {
|
|
@@ -2423,7 +2067,7 @@ tokens) plus a single slot override, the footer.
|
|
|
2423
2067
|
|
|
2424
2068
|
\`src/theme.ts\` declares the theme, \`src/tokens.ts\` carries the palette,
|
|
2425
2069
|
and slots live in \`src/slots/\`. What a theme may and may not do is
|
|
2426
|
-
documented in the meith repository under \`docs/
|
|
2070
|
+
documented in the meith repository under \`docs/extensions/themes.md\`;
|
|
2427
2071
|
every slot and view model is listed in \`docs/reference/theme-slots.md\`.
|
|
2428
2072
|
|
|
2429
2073
|
## Run it inside a board
|
|
@@ -2464,7 +2108,7 @@ on the appearance screen and to administrators under **Admin \u2192 Themes**.
|
|
|
2464
2108
|
finish \`listing.json\` (its \`repository\` field starts as a placeholder),
|
|
2465
2109
|
add the screenshot it names, and open a pull request against the meith
|
|
2466
2110
|
repository \u2014 the submission process and the review bar are documented there
|
|
2467
|
-
in \`docs/
|
|
2111
|
+
in \`docs/extensions/marketplace.md\`.
|
|
2468
2112
|
`;
|
|
2469
2113
|
}
|
|
2470
2114
|
function scaffoldPlugin(options) {
|
|
@@ -3034,7 +2678,7 @@ async function run(argv, version, updateOptions = {}) {
|
|
|
3034
2678
|
}
|
|
3035
2679
|
|
|
3036
2680
|
// src/bin.ts
|
|
3037
|
-
var result = await run(process.argv.slice(2), "0.37.
|
|
2681
|
+
var result = await run(process.argv.slice(2), "0.37.4");
|
|
3038
2682
|
for (const line of result.lines) {
|
|
3039
2683
|
if (result.code === 0) console.log(line);
|
|
3040
2684
|
else console.error(line);
|