create-kelpie 0.5.2 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/scaffold.d.ts.map +1 -1
- package/dist/scaffold.js +1 -0
- package/dist/scaffold.js.map +1 -1
- package/package.json +1 -1
- package/src/scaffold.ts +1 -0
- package/templates/README.md +17 -11
- package/templates/env +16 -11
- package/templates/kelpie.config.ts +112 -15
- package/templates/package.json +1 -0
- package/templates/src/migrate.ts +59 -0
- package/templates/src/reseal.ts +15 -83
- package/templates/src/server.ts +14 -39
package/dist/scaffold.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AASH,MAAM,WAAW,eAAe;IAC9B,wEAAwE;IACxE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,0FAA0F;IAC1F,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;IACxB,qEAAqE;IACrE,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAA;IACpC,qEAAqE;IACrE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAC7B;AAED,wEAAwE;AACxE,qBAAa,aAAc,SAAQ,KAAK;IACtC,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF;
|
|
1
|
+
{"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AASH,MAAM,WAAW,eAAe;IAC9B,wEAAwE;IACxE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,0FAA0F;IAC1F,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;IACxB,qEAAqE;IACrE,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAA;IACpC,qEAAqE;IACrE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAC7B;AAED,wEAAwE;AACxE,qBAAa,aAAc,SAAQ,KAAK;IACtC,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF;AA6DD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAkBtD;AAED,gGAAgG;AAChG,wBAAgB,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,SAAS,MAAM,EAAE,CAsBpE"}
|
package/dist/scaffold.js
CHANGED
|
@@ -29,6 +29,7 @@ const TEMPLATE_FILES = new Map([
|
|
|
29
29
|
['kelpie.config.ts', 'kelpie.config.ts'],
|
|
30
30
|
['kelpie.ui.config.ts', 'kelpie.ui.config.ts'],
|
|
31
31
|
['src/server.ts', 'src/server.ts'],
|
|
32
|
+
['src/migrate.ts', 'src/migrate.ts'],
|
|
32
33
|
['src/reseal.ts', 'src/reseal.ts'],
|
|
33
34
|
['web/index.html', 'web/index.html'],
|
|
34
35
|
['web/main.tsx', 'web/main.tsx'],
|
package/dist/scaffold.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/scaffold.ts
CHANGED
|
@@ -50,6 +50,7 @@ const TEMPLATE_FILES: ReadonlyMap<string, string> = new Map([
|
|
|
50
50
|
['kelpie.config.ts', 'kelpie.config.ts'],
|
|
51
51
|
['kelpie.ui.config.ts', 'kelpie.ui.config.ts'],
|
|
52
52
|
['src/server.ts', 'src/server.ts'],
|
|
53
|
+
['src/migrate.ts', 'src/migrate.ts'],
|
|
53
54
|
['src/reseal.ts', 'src/reseal.ts'],
|
|
54
55
|
['web/index.html', 'web/index.html'],
|
|
55
56
|
['web/main.tsx', 'web/main.tsx'],
|
package/templates/README.md
CHANGED
|
@@ -40,10 +40,15 @@ Then open <http://localhost:__WEB_PORT__/signup> and create an account.
|
|
|
40
40
|
Passwords need at least 12 characters. Signup names your workspace, invites your
|
|
41
41
|
team, and leaves you on People with a starter handbook in place.
|
|
42
42
|
|
|
43
|
-
This
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
This install picks its transactional-mail sender through `EMAIL_PROVIDER` in
|
|
44
|
+
`.env`. Out of the box it is `log`, which writes invitation and password-reset
|
|
45
|
+
messages to the API's log instead of sending them; copy the link from there
|
|
46
|
+
to follow either flow locally. Set `EMAIL_PROVIDER=smtp` and fill in the
|
|
47
|
+
`SMTP_*` variables below to send them over SMTP through the built-in
|
|
48
|
+
`smtp-email` core module (no install needed). Other provider modules
|
|
49
|
+
(Resend, Postmark, …) register their own name; install one, add it to
|
|
50
|
+
`kelpie.config.ts`'s `modules:`, and set `EMAIL_PROVIDER` to that name to
|
|
51
|
+
use it instead.
|
|
47
52
|
|
|
48
53
|
## Adding a module
|
|
49
54
|
|
|
@@ -66,13 +71,13 @@ lists every problem at once.
|
|
|
66
71
|
| `DATABASE_URL` | A `postgres://` or `postgresql://` connection string |
|
|
67
72
|
| `APP_BASE_URL` | The address people reach this Kelpie on. Emailed links (verification, password reset, invitations) are built from it. In development that is the Vite dev server; a deployment sets its real origin |
|
|
68
73
|
| `LOG_LEVEL` | `debug`, `info`, `warn`, or `error` |
|
|
69
|
-
| `EMAIL_PROVIDER` |
|
|
74
|
+
| `EMAIL_PROVIDER` | Name of the transactional-mail sender to use. `log` (built in) writes to the log instead of sending; `smtp` sends over SMTP through the built-in `smtp-email` core module; other names come from third-party provider modules |
|
|
70
75
|
| `EMAIL_FROM` | The address transactional mail comes from |
|
|
71
|
-
| `SMTP_HOST` |
|
|
72
|
-
| `SMTP_PORT` |
|
|
73
|
-
| `SMTP_SECURE` |
|
|
74
|
-
| `SMTP_USER` |
|
|
75
|
-
| `SMTP_PASSWORD` |
|
|
76
|
+
| `SMTP_HOST` | Read by the built-in `smtp-email` module when `EMAIL_PROVIDER=smtp`. The mail server to connect to |
|
|
77
|
+
| `SMTP_PORT` | Read by the built-in `smtp-email` module when `EMAIL_PROVIDER=smtp`. The mail server's port |
|
|
78
|
+
| `SMTP_SECURE` | Read by the built-in `smtp-email` module when `EMAIL_PROVIDER=smtp`. `true` or `false`. Whether to connect over TLS from the start (typically port 465) rather than upgrading with STARTTLS (typically port 587 or 25) |
|
|
79
|
+
| `SMTP_USER` | Read by the built-in `smtp-email` module when `EMAIL_PROVIDER=smtp`. The SMTP username |
|
|
80
|
+
| `SMTP_PASSWORD` | Read by the built-in `smtp-email` module when `EMAIL_PROVIDER=smtp`. The SMTP password |
|
|
76
81
|
| `SECRET_ENCRYPTION_KEY` | 32 bytes of base64, generated for this project |
|
|
77
82
|
| `SECRET_ENCRYPTION_KEY_PREVIOUS` | Optional. Set only while rotating the key above |
|
|
78
83
|
| `WEBHOOK_DELIVERY_RETENTION_DAYS` | Optional, default 30 |
|
|
@@ -112,7 +117,8 @@ One process now serves the pages and the API on `PORT`. Boot fails if
|
|
|
112
117
|
stops rather than answering every browser with a blank page.
|
|
113
118
|
|
|
114
119
|
Migrations apply at boot. Running more than one instance makes that a race, so
|
|
115
|
-
migrate once in a release step
|
|
120
|
+
migrate once with `npm run migrate` in a release step, then start the instances
|
|
121
|
+
with `--no-migrate`. That command is forward-only and safe to re-run.
|
|
116
122
|
|
|
117
123
|
## Upgrading
|
|
118
124
|
|
package/templates/env
CHANGED
|
@@ -26,17 +26,22 @@ APP_BASE_URL=http://localhost:__WEB_PORT__
|
|
|
26
26
|
# changing it needs the rotation procedure in README.md.
|
|
27
27
|
SECRET_ENCRYPTION_KEY=__SECRET_ENCRYPTION_KEY__
|
|
28
28
|
|
|
29
|
-
#
|
|
30
|
-
#
|
|
29
|
+
# EMAIL_PROVIDER picks a named sender the runtime resolves. `log` is a
|
|
30
|
+
# built-in that writes messages to the log instead of sending them; `smtp`
|
|
31
|
+
# comes from `@kelpie/module-smtp-email`, which reads the SMTP variables
|
|
32
|
+
# below. A different provider module (Resend, Postmark, …) registers its
|
|
33
|
+
# own name — set EMAIL_PROVIDER to that name to use it.
|
|
31
34
|
EMAIL_PROVIDER=log
|
|
32
35
|
EMAIL_FROM=__EMAIL_FROM__
|
|
33
36
|
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
37
|
+
# Read by `@kelpie/module-smtp-email` when EMAIL_PROVIDER=smtp. Ignored
|
|
38
|
+
# otherwise.
|
|
39
|
+
#
|
|
40
|
+
# SMTP_SECURE is "true" for a server that expects TLS from the first byte
|
|
41
|
+
# (usually port 465), "false" for one you connect to in plain text and that
|
|
42
|
+
# upgrades with STARTTLS (usually port 587 or 25).
|
|
43
|
+
SMTP_HOST=smtp.example.com
|
|
44
|
+
SMTP_PORT=587
|
|
45
|
+
SMTP_SECURE=false
|
|
46
|
+
SMTP_USER=
|
|
47
|
+
SMTP_PASSWORD=
|
|
@@ -1,21 +1,118 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
appUrlConfigSchema,
|
|
3
|
+
coreModules,
|
|
4
|
+
defineKelpieConfig,
|
|
5
|
+
fromEnv,
|
|
6
|
+
secretEncryptionConfigSchema,
|
|
7
|
+
} from '@kelpie/server'
|
|
8
|
+
import { z } from 'zod'
|
|
3
9
|
|
|
4
10
|
/**
|
|
5
|
-
*
|
|
11
|
+
* Your Kelpie service's configuration, and the only place it is declared.
|
|
6
12
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
13
|
+
* Every leaf is either a literal, committed to git and locked in for this
|
|
14
|
+
* deployment, or `fromEnv(...)`, marking a leaf the environment fills in. Edit
|
|
15
|
+
* this file for anything that should not change per deploy, and set environment
|
|
16
|
+
* variables (in `.env`, `.env.local`, or your process manager) for anything
|
|
17
|
+
* that does (secrets, per-tier limits, per-environment URLs).
|
|
10
18
|
*
|
|
11
|
-
*
|
|
19
|
+
* `resolveKelpieConfig(config, process.env)` at boot walks this object,
|
|
20
|
+
* resolves markers, validates, and produces the typed `KelpieConfig` the app
|
|
21
|
+
* runs on. Nothing in the app reads `process.env` for these fields after boot.
|
|
12
22
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* Removing one from `coreModules` is possible too, but core modules depend on
|
|
18
|
-
* each other, so boot will tell you if you have taken out something another
|
|
19
|
-
* module needs.
|
|
23
|
+
* Add a module by installing it and putting it in the `modules` array below.
|
|
24
|
+
* `coreModules` already includes the built-in `smtp-email` module; set
|
|
25
|
+
* `EMAIL_PROVIDER=log` to fall back to the log sender.
|
|
20
26
|
*/
|
|
21
|
-
|
|
27
|
+
|
|
28
|
+
const runtimeMode = z.enum(['development', 'test', 'production'])
|
|
29
|
+
const logLevel = z.enum(['debug', 'info', 'warn', 'error'])
|
|
30
|
+
const positiveInt = z.coerce.number().int().positive()
|
|
31
|
+
const nonNegativeInt = z.coerce.number().int().nonnegative()
|
|
32
|
+
const port = positiveInt.max(65535)
|
|
33
|
+
const postgresUrl = z.string().refine(
|
|
34
|
+
(value) => {
|
|
35
|
+
try {
|
|
36
|
+
const { protocol } = new URL(value)
|
|
37
|
+
return protocol === 'postgres:' || protocol === 'postgresql:'
|
|
38
|
+
} catch {
|
|
39
|
+
return false
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
{ message: 'must be a postgres:// or postgresql:// connection string' },
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
export default defineKelpieConfig({
|
|
46
|
+
runtimeMode: fromEnv('NODE_ENV', runtimeMode),
|
|
47
|
+
port: fromEnv('PORT', port),
|
|
48
|
+
databaseUrl: fromEnv('DATABASE_URL', postgresUrl),
|
|
49
|
+
|
|
50
|
+
// Every log line writes to each entry in `destinations`. Stdout is the
|
|
51
|
+
// default. Add a destination by extending the `LoggingDestination` union in
|
|
52
|
+
// `@kelpie/server` and placing an entry here.
|
|
53
|
+
logging: {
|
|
54
|
+
level: fromEnv('LOG_LEVEL', logLevel),
|
|
55
|
+
destinations: [{ kind: 'stdout' }],
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
webBundleDirectory: fromEnv<string | undefined>('WEB_BUNDLE_DIR', z.string().min(1).optional(), undefined),
|
|
59
|
+
moduleConfigPath: fromEnv<string | undefined>('KELPIE_MODULE_CONFIG_PATH', z.string().min(1).optional(), undefined),
|
|
60
|
+
// A human-readable name for this install ("dev", "demo", "cloud"). Read by
|
|
61
|
+
// the browser through `GET /v1/public/config` and shown at the top of the
|
|
62
|
+
// UI on non-production runs.
|
|
63
|
+
siteName: fromEnv<string | undefined>('KELPIE_SITE_NAME', z.string().min(1).optional(), undefined),
|
|
64
|
+
trustedProxyHopCount: fromEnv('TRUSTED_PROXY_HOP_COUNT', nonNegativeInt, 0),
|
|
65
|
+
|
|
66
|
+
// The deployment's base URL. Every emailed link (invite, password reset,
|
|
67
|
+
// email verification) is built from it.
|
|
68
|
+
appBaseUrl: fromEnv('APP_BASE_URL', appUrlConfigSchema.shape.APP_BASE_URL),
|
|
69
|
+
|
|
70
|
+
// Keys that seal stored secrets. Generate one with:
|
|
71
|
+
// node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
|
|
72
|
+
// To rotate, set the current key as `previousKey`, put the new key here,
|
|
73
|
+
// deploy, then run `npm run reseal`.
|
|
74
|
+
secretEncryption: {
|
|
75
|
+
key: fromEnv('SECRET_ENCRYPTION_KEY', secretEncryptionConfigSchema.shape.SECRET_ENCRYPTION_KEY),
|
|
76
|
+
previousKey: fromEnv<string | undefined>(
|
|
77
|
+
'SECRET_ENCRYPTION_KEY_PREVIOUS',
|
|
78
|
+
secretEncryptionConfigSchema.shape.SECRET_ENCRYPTION_KEY_PREVIOUS,
|
|
79
|
+
undefined,
|
|
80
|
+
),
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
// `provider` picks a named sender from the runtime's registry: `'log'` is
|
|
84
|
+
// built in and writes the message to the log instead of sending it. `'smtp'`
|
|
85
|
+
// is registered by the built-in `smtp-email` core module and reads the
|
|
86
|
+
// `SMTP_*` variables at boot when it is picked. Other names come from
|
|
87
|
+
// third-party provider modules (Resend, Postmark) added to `modules` below.
|
|
88
|
+
// `from` is the address on every outgoing message.
|
|
89
|
+
email: {
|
|
90
|
+
provider: fromEnv('EMAIL_PROVIDER', z.string().min(1)),
|
|
91
|
+
from: fromEnv('EMAIL_FROM', z.string().min(1)),
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
rateLimit: {
|
|
95
|
+
forms: {
|
|
96
|
+
limit: fromEnv('RATE_LIMIT_FORMS_LIMIT', positiveInt, 20),
|
|
97
|
+
windowSeconds: fromEnv('RATE_LIMIT_FORMS_WINDOW_SECONDS', positiveInt, 60),
|
|
98
|
+
},
|
|
99
|
+
auth: {
|
|
100
|
+
limit: fromEnv('RATE_LIMIT_AUTH_LIMIT', positiveInt, 10),
|
|
101
|
+
windowSeconds: fromEnv('RATE_LIMIT_AUTH_WINDOW_SECONDS', positiveInt, 60),
|
|
102
|
+
},
|
|
103
|
+
loginAccount: {
|
|
104
|
+
limit: fromEnv('RATE_LIMIT_LOGIN_ACCOUNT_LIMIT', positiveInt, 10),
|
|
105
|
+
windowSeconds: fromEnv('RATE_LIMIT_LOGIN_ACCOUNT_WINDOW_SECONDS', positiveInt, 900),
|
|
106
|
+
},
|
|
107
|
+
api: {
|
|
108
|
+
limit: fromEnv('RATE_LIMIT_API_LIMIT', positiveInt, 600),
|
|
109
|
+
windowSeconds: fromEnv('RATE_LIMIT_API_WINDOW_SECONDS', positiveInt, 60),
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
// `coreModules` already includes the built-in `smtp-email` module, which
|
|
114
|
+
// registers a `'smtp'` provider. Set `EMAIL_PROVIDER=log` to fall back to
|
|
115
|
+
// the built-in log sender; the `smtp-email` module only reads the SMTP
|
|
116
|
+
// environment when `email.provider` picks its name.
|
|
117
|
+
modules: [...coreModules],
|
|
118
|
+
})
|
package/templates/package.json
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"dev:api": "node --watch --env-file-if-exists=.env src/server.ts",
|
|
12
12
|
"dev:web": "vite",
|
|
13
13
|
"start": "node --env-file-if-exists=.env src/server.ts",
|
|
14
|
+
"migrate": "node --env-file-if-exists=.env src/migrate.ts",
|
|
14
15
|
"reseal": "node --env-file-if-exists=.env src/reseal.ts",
|
|
15
16
|
"build": "vite build",
|
|
16
17
|
"typecheck": "tsc --project tsconfig.server.json && tsc --project tsconfig.web.json"
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ConfigurationError,
|
|
3
|
+
ModuleBootError,
|
|
4
|
+
ModuleConfigFileError,
|
|
5
|
+
bootAssembly,
|
|
6
|
+
runMigrations,
|
|
7
|
+
} from '@kelpie/server'
|
|
8
|
+
|
|
9
|
+
import kelpieConfig from '../kelpie.config.ts'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Applies every pending migration, then exits.
|
|
13
|
+
*
|
|
14
|
+
* This is the release step `--no-migrate` expects: run it once before starting
|
|
15
|
+
* instances that skip boot-time migration, so applying migrations is not a race
|
|
16
|
+
* between them. Registration runs first, because modules declare their
|
|
17
|
+
* migrations directory while registering and there is nothing to migrate until
|
|
18
|
+
* that pass has finished. Migrations are forward-only and safe to re-run; a
|
|
19
|
+
* second run is a no-op.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
function reportFatal(message: string): void {
|
|
23
|
+
process.stderr.write(`${message}\n`)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function migrate(): Promise<void> {
|
|
27
|
+
const { database, contributions, logger } = await bootAssembly(kelpieConfig, process.env)
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
await runMigrations(database.db, contributions.schemas, logger)
|
|
31
|
+
} finally {
|
|
32
|
+
await database.close()
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
await migrate()
|
|
38
|
+
process.exit(0)
|
|
39
|
+
} catch (error: unknown) {
|
|
40
|
+
if (error instanceof ConfigurationError) {
|
|
41
|
+
reportFatal(error.message)
|
|
42
|
+
reportFatal('Check .env against the table in README.md.')
|
|
43
|
+
process.exit(1)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (error instanceof ModuleConfigFileError) {
|
|
47
|
+
reportFatal(error.message)
|
|
48
|
+
reportFatal('Fix the file at KELPIE_MODULE_CONFIG_PATH, or unset it to let workspaces decide for themselves.')
|
|
49
|
+
process.exit(1)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (error instanceof ModuleBootError) {
|
|
53
|
+
reportFatal(error.message)
|
|
54
|
+
reportFatal('Fix the module list in kelpie.config.ts, or the configuration it needs.')
|
|
55
|
+
process.exit(1)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
throw error
|
|
59
|
+
}
|
package/templates/src/reseal.ts
CHANGED
|
@@ -1,101 +1,33 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ConfigurationError,
|
|
3
|
-
connectDatabase,
|
|
4
3
|
createLogger,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
secretEncryptionConfigSchema,
|
|
4
|
+
createTransportForDestination,
|
|
5
|
+
resolveKelpieConfig,
|
|
6
|
+
runReseal,
|
|
9
7
|
} from '@kelpie/server'
|
|
10
8
|
|
|
9
|
+
import kelpieConfig from '../kelpie.config.ts'
|
|
10
|
+
|
|
11
11
|
/**
|
|
12
12
|
* Re-seals stored secrets after `SECRET_ENCRYPTION_KEY` changes.
|
|
13
13
|
*
|
|
14
|
-
* The rotation procedure
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* 2. Deploy. Deliveries keep signing: new secrets seal under the new key, and
|
|
19
|
-
* existing ones still open under the previous one.
|
|
20
|
-
* 3. Run `npm run reseal`. It rewrites every row still sealed under the old key.
|
|
21
|
-
* 4. Remove `SECRET_ENCRYPTION_KEY_PREVIOUS` and deploy again.
|
|
22
|
-
*
|
|
23
|
-
* Safe to run at any point, including with no previous key set, where it reports
|
|
24
|
-
* that everything is already current and writes nothing.
|
|
25
|
-
*
|
|
26
|
-
* A script rather than a subcommand because the repository has no CLI surface
|
|
27
|
-
* yet. When self-host packaging adds one, this becomes a subcommand of it and
|
|
28
|
-
* the body moves across unchanged.
|
|
14
|
+
* The rotation procedure lives on `runReseal`'s JSDoc; this file only wires
|
|
15
|
+
* the assembly's config in and hands `ConfigurationError` a fix hint on the
|
|
16
|
+
* way out. A future `kelpie` CLI turns this into a subcommand and the body
|
|
17
|
+
* stays the same size.
|
|
29
18
|
*/
|
|
30
19
|
|
|
31
|
-
function report(message: string): void {
|
|
32
|
-
process.stdout.write(`${message}\n`)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
20
|
function reportFatal(message: string): void {
|
|
36
21
|
process.stderr.write(`${message}\n`)
|
|
37
22
|
}
|
|
38
23
|
|
|
39
|
-
async function reseal(): Promise<number> {
|
|
40
|
-
const config = loadConfig(process.env)
|
|
41
|
-
const logger = createLogger(config.logLevel)
|
|
42
|
-
const database = connectDatabase(config.databaseUrl, logger)
|
|
43
|
-
|
|
44
|
-
// Validated here rather than trusted, so a mistyped key fails before the pass
|
|
45
|
-
// opens a single row instead of reporting every row unreadable.
|
|
46
|
-
const secretConfig = secretEncryptionConfigSchema.safeParse(process.env)
|
|
47
|
-
|
|
48
|
-
if (!secretConfig.success) {
|
|
49
|
-
await database.close()
|
|
50
|
-
|
|
51
|
-
throw new ConfigurationError(
|
|
52
|
-
secretConfig.error.issues.map((issue) => `${issue.path.join('.')}: ${issue.message}`),
|
|
53
|
-
)
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const hasPrevious =
|
|
57
|
-
secretConfig.data.SECRET_ENCRYPTION_KEY_PREVIOUS !== undefined &&
|
|
58
|
-
secretConfig.data.SECRET_ENCRYPTION_KEY_PREVIOUS.trim().length > 0
|
|
59
|
-
|
|
60
|
-
if (!hasPrevious) {
|
|
61
|
-
report('SECRET_ENCRYPTION_KEY_PREVIOUS is not set. Nothing sealed under an older key can be read.')
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
try {
|
|
65
|
-
const outcome = await resealStoredSecrets(database.db, createSecretCipher(secretConfig.data))
|
|
66
|
-
|
|
67
|
-
for (const column of outcome.columns) {
|
|
68
|
-
report(
|
|
69
|
-
`${column.label}: ${String(column.examined)} examined, ${String(column.resealed)} re-sealed, ${String(column.unreadable.length)} unreadable`,
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
-
for (const id of column.unreadable) {
|
|
73
|
-
reportFatal(` unreadable: ${id}`)
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (outcome.unreadable > 0) {
|
|
78
|
-
reportFatal(
|
|
79
|
-
`\n${String(outcome.unreadable)} value(s) opened under neither key. They were sealed under a key that is not configured, or the rows have been altered. Nothing here can recover them: set SECRET_ENCRYPTION_KEY_PREVIOUS to the key they were sealed with and run this again, or have those records re-created.`,
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
return 1
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
report(
|
|
86
|
-
outcome.resealed === 0
|
|
87
|
-
? `\nNothing to do: all ${String(outcome.examined)} value(s) are already sealed under the current key.`
|
|
88
|
-
: `\nRe-sealed ${String(outcome.resealed)} of ${String(outcome.examined)} value(s). You can now remove SECRET_ENCRYPTION_KEY_PREVIOUS.`,
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
return 0
|
|
92
|
-
} finally {
|
|
93
|
-
await database.close()
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
24
|
try {
|
|
98
|
-
process.
|
|
25
|
+
const config = resolveKelpieConfig(kelpieConfig, process.env)
|
|
26
|
+
const logger = createLogger({
|
|
27
|
+
level: config.logging.level,
|
|
28
|
+
transports: config.logging.destinations.map(createTransportForDestination),
|
|
29
|
+
})
|
|
30
|
+
process.exit(await runReseal({ config, logger }))
|
|
99
31
|
} catch (error: unknown) {
|
|
100
32
|
if (error instanceof ConfigurationError) {
|
|
101
33
|
reportFatal(error.message)
|
package/templates/src/server.ts
CHANGED
|
@@ -5,35 +5,26 @@ import {
|
|
|
5
5
|
ModuleBootError,
|
|
6
6
|
ModuleConfigFileError,
|
|
7
7
|
WebBundleError,
|
|
8
|
-
|
|
8
|
+
bootAssembly,
|
|
9
9
|
createApp,
|
|
10
|
-
createEmailSender,
|
|
11
|
-
createEventBus,
|
|
12
|
-
createIdFactory,
|
|
13
|
-
createLogger,
|
|
14
|
-
createTransactionScope,
|
|
15
|
-
loadConfig,
|
|
16
|
-
readModuleConfigFile,
|
|
17
|
-
registerModules,
|
|
18
|
-
resolveActorFrom,
|
|
19
10
|
resolveClientIpFrom,
|
|
20
11
|
runMigrations,
|
|
21
12
|
serveWebBundle,
|
|
22
13
|
} from '@kelpie/server'
|
|
23
|
-
import type { CredentialDependencies } from '@kelpie/server'
|
|
24
14
|
|
|
25
|
-
import
|
|
15
|
+
import kelpieConfig from '../kelpie.config.ts'
|
|
26
16
|
|
|
27
17
|
/**
|
|
28
18
|
* The entry point. It reads the environment, registers the configured modules,
|
|
29
19
|
* applies migrations, wires the dependencies, and serves.
|
|
30
20
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
21
|
+
* The config, database, and registration pass come from `bootAssembly`, shared
|
|
22
|
+
* with the standalone `migrate` command. Registration runs before migrations:
|
|
23
|
+
* modules declare their migrations directory while registering, so there is
|
|
24
|
+
* nothing to migrate until that pass has finished.
|
|
34
25
|
*
|
|
35
|
-
* `--no-migrate` skips the migration step, for deployments where
|
|
36
|
-
* step
|
|
26
|
+
* `--no-migrate` skips the migration step, for deployments where `npm run migrate`
|
|
27
|
+
* migrates once in a release step and many instances then start.
|
|
37
28
|
*/
|
|
38
29
|
|
|
39
30
|
function reportFatal(message: string): void {
|
|
@@ -41,28 +32,10 @@ function reportFatal(message: string): void {
|
|
|
41
32
|
}
|
|
42
33
|
|
|
43
34
|
async function start(): Promise<void> {
|
|
44
|
-
const config =
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const createId = createIdFactory()
|
|
49
|
-
const credentials: CredentialDependencies = { db: database.db, now: () => new Date() }
|
|
50
|
-
const moduleConfig = readModuleConfigFile(config.moduleConfigPath)
|
|
51
|
-
const contributions = await registerModules({
|
|
52
|
-
modules,
|
|
53
|
-
environment: process.env,
|
|
54
|
-
logger,
|
|
55
|
-
events,
|
|
56
|
-
moduleConfig,
|
|
57
|
-
resolveActor: (context) => resolveActorFrom(credentials, context),
|
|
58
|
-
services: {
|
|
59
|
-
db: database.db,
|
|
60
|
-
transaction: createTransactionScope({ db: database.db, bus: events, logger }),
|
|
61
|
-
email: createEmailSender(config.email, logger),
|
|
62
|
-
createId,
|
|
63
|
-
now: () => new Date(),
|
|
64
|
-
},
|
|
65
|
-
})
|
|
35
|
+
const { config, logger, database, createId, credentials, contributions } = await bootAssembly(
|
|
36
|
+
kelpieConfig,
|
|
37
|
+
process.env,
|
|
38
|
+
)
|
|
66
39
|
|
|
67
40
|
if (process.argv.includes('--no-migrate')) {
|
|
68
41
|
logger.info('skipping migrations', { reason: '--no-migrate' })
|
|
@@ -77,6 +50,8 @@ async function start(): Promise<void> {
|
|
|
77
50
|
credentials,
|
|
78
51
|
createId,
|
|
79
52
|
rateLimit: config.rateLimit,
|
|
53
|
+
runtimeMode: config.runtimeMode,
|
|
54
|
+
siteName: config.siteName,
|
|
80
55
|
// The socket address, then `X-Forwarded-For` only as far as the configured
|
|
81
56
|
// number of trusted proxies allows. Resolved from the connection itself
|
|
82
57
|
// rather than defaulted inside `createApp`, and trusting the header any
|