- migrate, migrate:rollback and deferred:purge gain --pretend and --force; they and queue:clear confirm only in production on an interactive terminal, so deploys over SSH proceed as before - result lines: Applied N migrations, Rolled back <id> of <plugin>, Cleared N jobs from <queue> - --json for migrate:status, key:generate, route:list, admin:token:create, admin:token:list, admin:token:scopes and websockets:health; -q keeps the key and the token - hints for an empty DSN, an unreachable database, an invalid app.key, a missing uploads bucket and an application binary run below its root - flare and websockets:health return bonfire.Reported after printing - long help and examples for every application command, greeter:hello and blog:publish; cmd/summer TestCommandsDocumented fails on any gap
178 lines
12 KiB
Markdown
178 lines
12 KiB
Markdown
---
|
|
title: Setup and maintenance
|
|
description: Every runtime command of an application binary, with its flags and purpose, from migrations and the server to workers, admin accounts and realtime checks.
|
|
section: console
|
|
order: 20
|
|
---
|
|
# Setup and maintenance
|
|
|
|
Every application binary carries the framework's runtime commands. The examples use a binary named `acme`; yours is named by `binary:` in `summer.yaml`.
|
|
|
|
## Migrations
|
|
|
|
| Command | Flags | Purpose |
|
|
|---------|-------|---------|
|
|
| `migrate` | `--pretend`, `--force` | Runs the framework migrations, then every plugin's migrations in dependency order, printing how many each set applied and `Applied N migrations` (or `Nothing to migrate`). `--pretend` lists the pending migrations as `<plugin>: <id>` and applies nothing. |
|
|
| `migrate:rollback` | `--plugin <id>`, `--pretend`, `--force` | Rolls back the last migration of the plugin; without the flag, of the last activated plugin that has migrations. Prints `Rolled back <id> of <plugin>`; `--pretend` only names it. |
|
|
| `migrate:status` | `--json` | Prints a table of plugin, history table and applied migration IDs. `--json` prints every declared migration of those plugins with `plugin`, `table`, `id` and `applied`. |
|
|
|
|
```sh
|
|
./bin/acme migrate --pretend
|
|
./bin/acme migrate
|
|
./bin/acme migrate:status --json
|
|
./bin/acme migrate:rollback --plugin acme.blog
|
|
```
|
|
|
|
Each plugin keeps its own migration history table, so rolling back one plugin never touches another. The migrations are listed in [lagoon](../../modules/lagoon/README.md).
|
|
|
|
### Confirmation in production
|
|
|
|
`migrate`, `migrate:rollback`, `deferred:purge` and `queue:clear` change or delete data. When the environment is `production` and the command runs on an interactive terminal, they ask before doing it; answering no prints a cancellation and exits 1. `--force` skips the question. Off a terminal (a deploy over SSH, a container entrypoint, the scheduler) and with `-n` they never ask and proceed as before, so deploy scripts need no change.
|
|
|
|
```sh
|
|
./bin/acme migrate --force
|
|
```
|
|
|
|
When the database cannot be reached, or `database.dsn` is empty, the error names the fix (`docker compose up -d --wait`, `cp .env.example .env`). Run from a directory below the application root, an application binary finds no `config/` and adds a hint to its error with the command to run instead, for example `cd ../..; bin/acme migrate`.
|
|
|
|
## Pending uploads and related records
|
|
|
|
An admin form holds the files uploaded and the related records created for a record that is not saved yet as deferred bindings. Saving the record commits them; abandoned ones are removed by `deferred:purge`:
|
|
|
|
| Command | Flags | Purpose |
|
|
|---------|-------|---------|
|
|
| `deferred:purge` | `--days <n>`, `--pretend`, `--force` | Deletes deferred bindings older than `n` days, the unattached uploads they hold and the records created under deferral, and prints the counts. Records that were only linked are kept. Without the flag, `database.deferred_bindings.purge_days` applies, else 5 days. `--pretend` counts the bindings that would go and deletes nothing. |
|
|
|
|
```sh
|
|
./bin/acme deferred:purge --pretend
|
|
./bin/acme deferred:purge --days=1
|
|
```
|
|
|
|
The scheduler already runs it every day at 03:00; see [Task scheduling](../plugins/scheduling.md#the-frameworks-own-schedule).
|
|
|
|
## Application key
|
|
|
|
| Command | Flags | Purpose |
|
|
|---------|-------|---------|
|
|
| `key:generate` | `--json` | Prints a fresh base64 32-byte key for `app.key`. It writes nothing and needs no database. With `-q` it prints only the key; `--json` prints `{"key": "..."}`. |
|
|
|
|
```sh
|
|
./bin/acme key:generate
|
|
KEY=$(./bin/acme key:generate -q)
|
|
```
|
|
|
|
Copy the printed value into `SUMMER_APP__KEY`. When you rotate the key, keep the old one in `app.previous_keys` so existing encrypted columns can still be read.
|
|
|
|
## The HTTP server
|
|
|
|
| Command | Flags | Purpose |
|
|
|---------|-------|---------|
|
|
| `serve` | `--addr` (default `:8080`) | Opens the database and uploads bucket, builds the router, binds the address, starts the in-process job worker and serves HTTP until SIGINT or SIGTERM. Once it listens it prints a startup summary; on a signal it drains requests and the worker within 10 seconds and prints a shutdown report. |
|
|
| `route:list` | `--json` | Builds the router the way `serve` does, without opening the database or listening, and prints every route with its method, pattern, plugin, middleware and raw flag, followed by the admin status line. `--json` prints the routes as a JSON array. |
|
|
|
|
```sh
|
|
./bin/acme route:list
|
|
./bin/acme serve --addr 127.0.0.1:8080
|
|
```
|
|
|
|
The default `--addr` listens on every interface. Pass a loopback address during development, and put a reverse proxy in front of the binary in production. The routing options are in [surf](../../modules/surf/README.md).
|
|
|
|
From the application directory, `summer serve --addr 127.0.0.1:8123` passes the flag through to the built binary.
|
|
|
|
### Startup summary
|
|
|
|
On a terminal `serve` prints a block once the port is bound:
|
|
|
|
```text
|
|
SummerCMS acme v1.4.0 (framework v0.4.0)
|
|
Environment production
|
|
URL http://127.0.0.1:8080/
|
|
Admin http://127.0.0.1:8080/backend
|
|
Database db.internal:5432/acme
|
|
Migrations up to date
|
|
Plugins 3 (acme.user, acme.blog, acme.shop)
|
|
Queue default, mail, scheduled (worker in serve)
|
|
Scheduler in serve
|
|
Mail smtp
|
|
Cache redis
|
|
Realtime null
|
|
Search null
|
|
```
|
|
|
|
The database line shows the host, port and database name, never the user or password. Pending migrations read `3 pending — run bin/acme migrate`. The admin line shows the admin URL, or `off` with the steps to add an admin controller; a warning naming `summer admin:create` follows when the `backend_users` table is empty. `-v` adds one line per plugin with the plugins it requires and its pending migrations, and lets every component's info logs through; `-q` prints no summary, only warnings and errors.
|
|
|
|
Off a terminal (systemd, Docker, a redirected log) nothing is printed on stdout: the same facts are logged as one `startup summary` record and one record whose message is exactly `listening on <addr>`, both from the `serve` component, so journald and the daily files keep them and scripts can wait for the second one. On a terminal the two records still reach the daily files, but the console does not repeat them.
|
|
|
|
### Shutting down
|
|
|
|
Ctrl+C or SIGTERM drains the requests in flight for up to 10 seconds, stops the in-process worker and prints a report (on a terminal) and a `shutdown report` record:
|
|
|
|
```text
|
|
Shutdown report
|
|
Requests 1520 served, 2 drained, 0 aborted
|
|
Worker 84 done, 1 failed, 0 handed back to the queue
|
|
Uptime 3h12m40s
|
|
Errors none
|
|
```
|
|
|
|
Jobs still running at the deadline are cancelled; River records the cancelled attempt and retries the job later, which the report counts as handed back to the queue. `serve` exits 0 after a clean stop and 1 when draining fails (requests still running at the deadline, or the worker failing to stop).
|
|
|
|
## Admin accounts
|
|
|
|
| Command | Arguments and flags | Purpose |
|
|
|---------|---------------------|---------|
|
|
| `admin:create` | `--email` (required), `--login`, `--role <code>`, `--superuser`, `--password` (deprecated) | Creates an activated backend administrator. `--login` defaults to the lower-cased email. The password is read from a hidden prompt, or from stdin when the input is not a terminal. |
|
|
| `admin:reset-password` | `<identifier>` (login or email), `--password` (deprecated) | Sets a new password, read like the one of `admin:create`, and revokes every JWT issued before the reset. Personal API tokens are unchanged. |
|
|
| `admin:token:create` | `<identifier>`, `--name`, `--scope` (repeatable), `--expires` (`30d`, `90d`, `365d`, `never`, or `YYYY-MM-DD`), `--json` | Creates a personal API token for the named administrator and prints exactly the plaintext and a newline on stdout, also under `-q`. `--json` prints `id`, `name`, `token`, `scopes` and `expires_at`. |
|
|
| `admin:token:list` | `[<identifier>]`, `--revoked`, `--json` | Lists tokens (hint only, never the hash or plaintext); `--json` as a JSON array. |
|
|
| `admin:token:revoke` | `<id>` | Soft-revokes any user's active token by id. |
|
|
| `admin:token:scopes` | `--json` | Lists framework and plugin scopes available for tokens; `--json` as a JSON array. |
|
|
|
|
```sh
|
|
./bin/acme admin:create --email admin@example.com --superuser
|
|
./bin/acme admin:reset-password admin@example.com
|
|
TOKEN=$(./bin/acme admin:token:create admin@example.com --name agent --scope blog)
|
|
```
|
|
|
|
`admin:create` and `admin:reset-password` prompt for the password without echoing it. In a script, pipe it on stdin, for example `printf '%s\n' "$ADMIN_PASSWORD" | ./bin/acme admin:create --email admin@example.com --superuser`. The `--password` flag still works but is deprecated and prints a warning, because the value stays in your shell history and is visible in the process list. The admin and personal API tokens are described in [cabana](../../modules/cabana/README.md) and [Users and permissions](../backend/users-and-permissions.md#api-tokens).
|
|
|
|
From the application directory, `summer admin:create --email admin@example.com --superuser` runs the same command on the built binary, prompt included. The four `admin:token:*` commands have matching `summer` delegates.
|
|
|
|
When the admin is mounted and the `backend_users` table is empty, `serve` prints a warning after its startup summary naming `summer admin:create`.
|
|
|
|
## Queues and the scheduler
|
|
|
|
| Command | Arguments and flags | Purpose |
|
|
|---------|---------------------|---------|
|
|
| `queue:work` | `--queue <name>`, repeatable | Runs a job worker in the foreground on the named queues (default: every known queue) until SIGINT or SIGTERM, with a startup summary and a shutdown report. An unknown queue is an error that lists the known ones. |
|
|
| `queue:clear` | `[queue]` (default `default`), `--force` | Deletes the waiting, scheduled and retryable jobs of one queue and prints `Cleared N jobs from <queue>`. Running jobs are never touched. |
|
|
| `schedule:run` | `--once` | Without `--once`, runs a scheduler-only worker until stopped, with a startup summary and a shutdown report. With `--once`, runs the entries due in the current minute, prints `Ran N due entries (M failed)` and exits, for system cron. |
|
|
|
|
```sh
|
|
./bin/acme queue:work --queue default --queue imports
|
|
./bin/acme queue:clear imports
|
|
./bin/acme schedule:run --once
|
|
```
|
|
|
|
`queue:work` prints a startup summary on a terminal: the environment, each queue with its concurrency, the database host and whether the `scheduled` queue runs in this worker. `schedule:run` prints the number of schedule entries and the next one due. Off a terminal they log the same facts as a `startup summary` record (components `queue` and `schedule`). On Ctrl+C or SIGTERM both stop within 10 seconds and print a shutdown report: jobs done, failed and handed back to the queue (jobs still running at the deadline are cancelled and River retries them), the uptime and any stop error. They exit 0 after a clean stop and 1 when the worker fails to stop.
|
|
|
|
`serve` runs a job worker in the same process unless `queue.work_in_serve` is `false`; set it to `false` when you run `queue:work` separately. [Task scheduling](../plugins/scheduling.md) explains the scheduler, and [conga](../../modules/conga/README.md) the queue settings.
|
|
|
|
## Realtime and push
|
|
|
|
These commands are not added by the generated `main`. An application that uses Centrifugo or Web Push appends them to the list one of its plugins returns from `pact.HasCommands`: `centrifugo.Commands` returns `websockets:health`, and `flare.Commands` returns the two push commands.
|
|
|
|
| Command | Arguments and flags | Purpose |
|
|
|---------|---------------------|---------|
|
|
| `websockets:health` | `--json` | Calls the Centrifugo `info` API and prints the configuration. Exits 1 when the API key is missing or the call fails. The key itself is never printed. `--json` prints `healthy`, `api_url`, `enabled`, `api_key_set` and `error` for monitoring. |
|
|
| `websockets:generate-vapid-keys` | `--update`, `--show-current` | Shows the configured VAPID keys, truncated, then generates a new pair. With `--update` it saves them to the environment's `overrides.yaml`; without it, it prints the variables to set by hand. |
|
|
| `websockets:test-push` | `<user_id>`, `--show-config` | Lists a user's push subscriptions and, after confirmation (yes by default, so `-n` sends without asking), sends one encrypted test notification to each. |
|
|
|
|
```sh
|
|
./bin/acme websockets:health
|
|
./bin/acme websockets:generate-vapid-keys --show-current
|
|
./bin/acme websockets:test-push 1
|
|
```
|
|
|
|
The settings are in [lighthouse](../../modules/lighthouse/README.md) and [flare](../../modules/flare/README.md).
|