create-ortha-app 0.5.0 → 0.5.2
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/README.md +2 -1
- package/dist/lib/features.d.ts +6 -4
- package/dist/lib/features.d.ts.map +1 -1
- package/dist/lib/features.js +6 -4
- package/dist/lib/run.d.ts.map +1 -1
- package/dist/lib/run.js +4 -3
- package/package.json +2 -2
- package/templates/default/apps/server/config/graphql.ts +25 -0
- package/templates/default/apps/server/config/segments.ts +23 -0
- package/templates/default/apps/server/config/transfer.ts +28 -0
- package/templates/default/apps/server/config/webhooks.ts +34 -0
- package/templates/default/apps/server/ortha.config.ts +28 -0
- package/templates/default/apps/server/src/plugins.ts +15 -7
- package/templates/default/env.tmpl +15 -0
package/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# create-ortha-app
|
|
2
2
|
|
|
3
|
-
Part of [Ortha CMS](https://
|
|
3
|
+
Part of [Ortha CMS](https://orthacms.com) — the source is on
|
|
4
|
+
[GitHub](https://github.com/ortha-source/ortha-cms/tree/main/packages/create-ortha-app).
|
|
4
5
|
|
|
5
6
|
```sh
|
|
6
7
|
npm install create-ortha-app
|
package/dist/lib/features.d.ts
CHANGED
|
@@ -41,7 +41,7 @@ export interface Feature {
|
|
|
41
41
|
*
|
|
42
42
|
* The **copilot** is here — plugin and admin panel — even though it is a large
|
|
43
43
|
* feature nobody may want, because its server half arrives anyway: five core
|
|
44
|
-
* plugins (`content`, `
|
|
44
|
+
* plugins (`content`, `i18n`, `media`, `segments`, `users`) depend on
|
|
45
45
|
* `copilot-server` to contribute their tools, so the code is on disk whatever
|
|
46
46
|
* the manifest says, and leaving it undeclared bought nothing but a missing chat
|
|
47
47
|
* panel. No **model backend** comes with it: those are the opt-in
|
|
@@ -53,7 +53,8 @@ export interface Feature {
|
|
|
53
53
|
* answer.
|
|
54
54
|
*
|
|
55
55
|
* The **extension points** are here for the same reason — `content-domain`,
|
|
56
|
-
* `copilot-domain`, `
|
|
56
|
+
* `copilot-domain`, `segments-domain`, `transfer-domain`, `webhooks-domain`,
|
|
57
|
+
* `tools-server`, `query-builder-admin`. Every one of them
|
|
57
58
|
* already arrives transitively, so an import would resolve on npm's flat
|
|
58
59
|
* `node_modules` today; declaring them is what makes that resolution something
|
|
59
60
|
* the app owns rather than something it borrows. An undeclared import breaks
|
|
@@ -109,8 +110,9 @@ export declare const TRANSITIVE_PACKAGES: readonly string[];
|
|
|
109
110
|
* Where uploads are written.
|
|
110
111
|
*
|
|
111
112
|
* A single-choice group: media always runs, the question is only which adapter
|
|
112
|
-
* backs it.
|
|
113
|
-
*
|
|
113
|
+
* backs it. All five are published and selectable; `available` stays on the
|
|
114
|
+
* type for the next adapter that lands in the codebase before it lands on npm,
|
|
115
|
+
* which is shown greyed out rather than hidden.
|
|
114
116
|
*/
|
|
115
117
|
export declare const MEDIA_PROVIDERS: readonly Feature[];
|
|
116
118
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"features.d.ts","sourceRoot":"","sources":["../../src/lib/features.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,qDAAqD;AACrD,MAAM,WAAW,OAAO;IACpB,2EAA2E;IAC3E,EAAE,EAAE,MAAM,CAAC;IACX,6BAA6B;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,oEAAoE;IACpE,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,gCAAgC;IAChC,gBAAgB,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,SAAS,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CACpB;AAED
|
|
1
|
+
{"version":3,"file":"features.d.ts","sourceRoot":"","sources":["../../src/lib/features.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,qDAAqD;AACrD,MAAM,WAAW,OAAO;IACpB,2EAA2E;IAC3E,EAAE,EAAE,MAAM,CAAC;IACX,6BAA6B;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,oEAAoE;IACpE,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,gCAAgC;IAChC,gBAAgB,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,SAAS,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,eAAO,MAAM,aAAa,EAAE,SAAS,MAAM,EA6C1C,CAAC;AAEF,0EAA0E;AAC1E,eAAO,MAAM,iBAAiB,EAAE,SAAS,MAAM,EAAsB,CAAC;AAEtE;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,mBAAmB,EAAE,SAAS,MAAM,EAGhD,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,EAAE,SAAS,OAAO,EAyC7C,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,iBAAiB,EAAE,SAAS,OAAO,EAiB/C,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,aAAa,EAAE,SAAS,OAAO,EAyB3C,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,SAAS,EAAE,SAAS,OAAO,EA0BvC,CAAC;AAEF,uEAAuE;AACvE,eAAO,MAAM,YAAY,EAAE,SAAS,OAAO,EAK1C,CAAC;AAEF,8CAA8C;AAC9C,MAAM,WAAW,gBAAgB;IAC7B,gFAAgF;IAChF,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CAChC;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,gBAAgB,GAAG,MAAM,EAAE,CASrE;AAED,uDAAuD;AACvD,wBAAgB,kBAAkB,IAAI,MAAM,EAAE,CAE7C;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,gBAAgB,GAAG,GAAG,CAAC,MAAM,CAAC,CAQrE"}
|
package/dist/lib/features.js
CHANGED
|
@@ -23,7 +23,7 @@ exports.resolveFlags = resolveFlags;
|
|
|
23
23
|
*
|
|
24
24
|
* The **copilot** is here — plugin and admin panel — even though it is a large
|
|
25
25
|
* feature nobody may want, because its server half arrives anyway: five core
|
|
26
|
-
* plugins (`content`, `
|
|
26
|
+
* plugins (`content`, `i18n`, `media`, `segments`, `users`) depend on
|
|
27
27
|
* `copilot-server` to contribute their tools, so the code is on disk whatever
|
|
28
28
|
* the manifest says, and leaving it undeclared bought nothing but a missing chat
|
|
29
29
|
* panel. No **model backend** comes with it: those are the opt-in
|
|
@@ -35,7 +35,8 @@ exports.resolveFlags = resolveFlags;
|
|
|
35
35
|
* answer.
|
|
36
36
|
*
|
|
37
37
|
* The **extension points** are here for the same reason — `content-domain`,
|
|
38
|
-
* `copilot-domain`, `
|
|
38
|
+
* `copilot-domain`, `segments-domain`, `transfer-domain`, `webhooks-domain`,
|
|
39
|
+
* `tools-server`, `query-builder-admin`. Every one of them
|
|
39
40
|
* already arrives transitively, so an import would resolve on npm's flat
|
|
40
41
|
* `node_modules` today; declaring them is what makes that resolution something
|
|
41
42
|
* the app owns rather than something it borrows. An undeclared import breaks
|
|
@@ -139,8 +140,9 @@ exports.TRANSITIVE_PACKAGES = [
|
|
|
139
140
|
* Where uploads are written.
|
|
140
141
|
*
|
|
141
142
|
* A single-choice group: media always runs, the question is only which adapter
|
|
142
|
-
* backs it.
|
|
143
|
-
*
|
|
143
|
+
* backs it. All five are published and selectable; `available` stays on the
|
|
144
|
+
* type for the next adapter that lands in the codebase before it lands on npm,
|
|
145
|
+
* which is shown greyed out rather than hidden.
|
|
144
146
|
*/
|
|
145
147
|
exports.MEDIA_PROVIDERS = [
|
|
146
148
|
{
|
package/dist/lib/run.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/lib/run.ts"],"names":[],"mappings":"AAeA,OAAO,EAMH,KAAK,OAAO,EACZ,KAAK,gBAAgB,EACxB,MAAM,YAAY,CAAC;AASpB,eAAO,MAAM,KAAK,8oBAcjB,CAAC;AA6HF,kEAAkE;AAClE,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,GAAG,MAAM,EAAE,CAI/D;AA6BD,sCAAsC;AACtC,MAAM,WAAW,OAAO;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,gBAAgB,CAAC;CAC/B;AAED;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAChC,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,WAAW,EAAE,MAAM,GACpB,OAAO,CAAC,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/lib/run.ts"],"names":[],"mappings":"AAeA,OAAO,EAMH,KAAK,OAAO,EACZ,KAAK,gBAAgB,EACxB,MAAM,YAAY,CAAC;AASpB,eAAO,MAAM,KAAK,8oBAcjB,CAAC;AA6HF,kEAAkE;AAClE,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,GAAG,MAAM,EAAE,CAI/D;AA6BD,sCAAsC;AACtC,MAAM,WAAW,OAAO;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,gBAAgB,CAAC;CAC/B;AAED;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAChC,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,WAAW,EAAE,MAAM,GACpB,OAAO,CAAC,OAAO,CAAC,CAwFlB;AA+BD,wBAAsB,IAAI,CACtB,IAAI,GAAE,SAAS,MAAM,EAA0B,GAChD,OAAO,CAAC,IAAI,CAAC,CAsFf"}
|
package/dist/lib/run.js
CHANGED
|
@@ -202,9 +202,10 @@ async function resolveAnswers(argv, defaultName) {
|
|
|
202
202
|
ui.section('Features');
|
|
203
203
|
ui.note('Everything else is installed for you. These are the choices.');
|
|
204
204
|
console.log('');
|
|
205
|
-
// Only ask when there is more than one answer available
|
|
206
|
-
//
|
|
207
|
-
//
|
|
205
|
+
// Only ask when there is more than one answer available — a question with a
|
|
206
|
+
// single possible reply is noise pretending to be a choice. Every adapter
|
|
207
|
+
// is published today, so the question is asked; it stops being asked by
|
|
208
|
+
// itself if all but one are ever marked unavailable.
|
|
208
209
|
const selectable = features_1.MEDIA_PROVIDERS.filter((provider) => provider.available);
|
|
209
210
|
const chosenMedia = selectable.length > 1
|
|
210
211
|
? [
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-ortha-app",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"description": "Create an Ortha CMS app: npx create-ortha-app my-cms",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"homepage": "https://
|
|
6
|
+
"homepage": "https://orthacms.com",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
9
|
"url": "git+https://github.com/ortha-source/ortha-cms.git",
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// ortha:if graphql
|
|
2
|
+
import type { ContentGraphqlPluginConfig } from '@orthacms/content-graphql';
|
|
3
|
+
import { readPositiveInt } from '@orthacms/utils-server';
|
|
4
|
+
|
|
5
|
+
/** The public GraphQL endpoint's cost budget. */
|
|
6
|
+
export function contentGraphqlConfig(): ContentGraphqlPluginConfig {
|
|
7
|
+
return {
|
|
8
|
+
// The cost budget one GraphQL operation may spend. REST bounded a
|
|
9
|
+
// request structurally — one route, one page — and a GraphQL document
|
|
10
|
+
// does not, so these are the replacement bound. Stable tuning, hence
|
|
11
|
+
// literals, with env overrides for an operator who needs to loosen or
|
|
12
|
+
// tighten them without a redeploy.
|
|
13
|
+
limits: {
|
|
14
|
+
maxDepth: readPositiveInt('GRAPHQL_MAX_DEPTH', 8),
|
|
15
|
+
maxComplexity: readPositiveInt('GRAPHQL_MAX_COMPLEXITY', 1000),
|
|
16
|
+
maxFields: readPositiveInt('GRAPHQL_MAX_FIELDS', 500),
|
|
17
|
+
maxQueryLength: readPositiveInt('GRAPHQL_MAX_QUERY_LENGTH', 16_384)
|
|
18
|
+
},
|
|
19
|
+
// How long a built schema is reused before it is derived again from the
|
|
20
|
+
// workspace's content grants. Freshness only — every read is authorized
|
|
21
|
+
// against the live grants regardless.
|
|
22
|
+
schemaCacheTtlMs: readPositiveInt('GRAPHQL_SCHEMA_CACHE_TTL_MS', 60_000)
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
// ortha:end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { SegmentsPluginConfig } from '@orthacms/segments-server';
|
|
2
|
+
|
|
3
|
+
/** Reader entitlements — where a reader's tags come from. */
|
|
4
|
+
export function segmentsConfig(): SegmentsPluginConfig {
|
|
5
|
+
return {
|
|
6
|
+
// Where a reader's tags come from — the one line this feature needs per
|
|
7
|
+
// install. A resolver receives the request, so a JWT claim, a header the
|
|
8
|
+
// CDN sets, or a lookup against a billing system are all equally
|
|
9
|
+
// reachable:
|
|
10
|
+
//
|
|
11
|
+
// resolver: {
|
|
12
|
+
// resolve: async (request) => readTagsFrom(request)
|
|
13
|
+
// }
|
|
14
|
+
//
|
|
15
|
+
// Left out — as it is here — every reader is anonymous, so unrestricted
|
|
16
|
+
// content serves and restricted content does not. That is a working
|
|
17
|
+
// configuration, and it fails in the safe direction: an audience nobody
|
|
18
|
+
// can be resolved into cannot accidentally be admitted.
|
|
19
|
+
//
|
|
20
|
+
// It is a function rather than an environment value, which is why this
|
|
21
|
+
// module reads no env: what it needs is code only you can write.
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { TransferPluginConfig } from '@orthacms/transfer-server';
|
|
2
|
+
|
|
3
|
+
/** Export/import — the per-type identity fields and the transfer ceilings. */
|
|
4
|
+
export function transferConfig(): TransferPluginConfig {
|
|
5
|
+
return {
|
|
6
|
+
// Which field identifies a record of each type, per content type.
|
|
7
|
+
//
|
|
8
|
+
// This is the setting that decides whether importing the same file
|
|
9
|
+
// twice updates the records or duplicates them. Left out, a type falls
|
|
10
|
+
// back to a derived guess — a field *named* like an identifier
|
|
11
|
+
// (`slug`, `sku`, `email`), then the first required text field — which
|
|
12
|
+
// is usually right and is reported in every export's manifest, but is
|
|
13
|
+
// still a guess. Name the fields for any type where being wrong would
|
|
14
|
+
// be expensive:
|
|
15
|
+
//
|
|
16
|
+
// identity: { product: ['sku'], author: ['email'] }
|
|
17
|
+
//
|
|
18
|
+
// This app registers no content types yet, so there is nothing to key
|
|
19
|
+
// here until you add some.
|
|
20
|
+
identity: {},
|
|
21
|
+
// Ceilings on one transfer. The defaults (see `DEFAULT_TRANSFER_LIMITS`)
|
|
22
|
+
// sit comfortably above real editorial work and far below "the whole
|
|
23
|
+
// library"; the import-side archive limits are a safety boundary rather
|
|
24
|
+
// than a capacity setting, so lowering them costs nothing and raising
|
|
25
|
+
// them should be deliberate.
|
|
26
|
+
limits: {}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { WebhooksPluginConfig } from '@orthacms/webhooks-server';
|
|
2
|
+
import { readFlag, readPositiveInt } from '@orthacms/utils-server';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Outgoing webhooks — how hard the sender pushes, and where it may reach.
|
|
6
|
+
*
|
|
7
|
+
* Endpoints themselves live in the database; an administrator adds them on the
|
|
8
|
+
* admin's Webhooks page. Only the five values a deployment genuinely varies are
|
|
9
|
+
* env-sourced, and the rest are left to the plugin's own defaults, where the
|
|
10
|
+
* reasoning behind them is written down.
|
|
11
|
+
*
|
|
12
|
+
* The two `allow*` flags are the ones worth reading twice. Both default to
|
|
13
|
+
* **off**, and both widen what a URL an operator types can reach:
|
|
14
|
+
* `WEBHOOKS_ALLOW_INSECURE_URLS` permits plain HTTP, and
|
|
15
|
+
* `WEBHOOKS_ALLOW_PRIVATE_NETWORKS` permits loopback and RFC 1918 — which a
|
|
16
|
+
* self-hosted install with an in-cluster receiver genuinely needs, and an
|
|
17
|
+
* internet-facing one must not have. A webhook is this server making a request
|
|
18
|
+
* to an address a user typed, which is the shape of every SSRF.
|
|
19
|
+
*/
|
|
20
|
+
export function webhooksConfig(): WebhooksPluginConfig {
|
|
21
|
+
return {
|
|
22
|
+
// 0 turns the sender off in this process. Rows still queue, so a
|
|
23
|
+
// deployment can run web nodes that only enqueue and one that sends.
|
|
24
|
+
deliveryIntervalMs: readPositiveInt(
|
|
25
|
+
'WEBHOOKS_DELIVERY_INTERVAL',
|
|
26
|
+
2_000
|
|
27
|
+
),
|
|
28
|
+
timeoutMs: readPositiveInt('WEBHOOKS_TIMEOUT', 10_000),
|
|
29
|
+
// Nothing else prunes the delivery log.
|
|
30
|
+
retentionDays: readPositiveInt('WEBHOOKS_RETENTION_DAYS', 30),
|
|
31
|
+
allowInsecureUrls: readFlag('WEBHOOKS_ALLOW_INSECURE_URLS', false),
|
|
32
|
+
allowPrivateNetworks: readFlag('WEBHOOKS_ALLOW_PRIVATE_NETWORKS', false)
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -34,6 +34,12 @@ import type {
|
|
|
34
34
|
TrustProxySetting
|
|
35
35
|
} from '@orthacms/bootstrap-server';
|
|
36
36
|
import type { I18nPluginConfig } from '@orthacms/i18n-server';
|
|
37
|
+
import type { SegmentsPluginConfig } from '@orthacms/segments-server';
|
|
38
|
+
import type { TransferPluginConfig } from '@orthacms/transfer-server';
|
|
39
|
+
import type { WebhooksPluginConfig } from '@orthacms/webhooks-server';
|
|
40
|
+
// ortha:if graphql
|
|
41
|
+
import type { ContentGraphqlPluginConfig } from '@orthacms/content-graphql';
|
|
42
|
+
// ortha:end
|
|
37
43
|
// ortha:if mcp
|
|
38
44
|
import type { McpPluginConfig } from '@orthacms/mcp-server';
|
|
39
45
|
// ortha:end
|
|
@@ -49,6 +55,12 @@ import { identityConfig, type AppIdentityConfig } from './config/identity';
|
|
|
49
55
|
import { i18nConfig } from './config/i18n';
|
|
50
56
|
import { mediaConfig, type AppMediaConfig } from './config/media';
|
|
51
57
|
import { copilotConfig, type AppCopilotConfig } from './config/copilot';
|
|
58
|
+
import { segmentsConfig } from './config/segments';
|
|
59
|
+
import { transferConfig } from './config/transfer';
|
|
60
|
+
import { webhooksConfig } from './config/webhooks';
|
|
61
|
+
// ortha:if graphql
|
|
62
|
+
import { contentGraphqlConfig } from './config/graphql';
|
|
63
|
+
// ortha:end
|
|
52
64
|
// ortha:if mcp
|
|
53
65
|
import { mcpConfig } from './config/mcp';
|
|
54
66
|
// ortha:end
|
|
@@ -74,6 +86,16 @@ export interface OrthaConfig {
|
|
|
74
86
|
i18n: I18nPluginConfig;
|
|
75
87
|
media: AppMediaConfig;
|
|
76
88
|
copilot: AppCopilotConfig;
|
|
89
|
+
/** Export/import — per-type identity fields and transfer ceilings. */
|
|
90
|
+
transfer: TransferPluginConfig;
|
|
91
|
+
/** Reader entitlements — where a reader's tags come from. */
|
|
92
|
+
segments: SegmentsPluginConfig;
|
|
93
|
+
/** Outgoing webhooks — delivery pacing and the URL policy. */
|
|
94
|
+
webhooks: WebhooksPluginConfig;
|
|
95
|
+
// ortha:if graphql
|
|
96
|
+
/** Public GraphQL endpoint — the per-operation cost budget. */
|
|
97
|
+
contentGraphql: ContentGraphqlPluginConfig;
|
|
98
|
+
// ortha:end
|
|
77
99
|
// ortha:if mcp
|
|
78
100
|
mcp: McpPluginConfig;
|
|
79
101
|
// ortha:end
|
|
@@ -107,6 +129,12 @@ const config: OrthaConfig = {
|
|
|
107
129
|
i18n: i18nConfig(),
|
|
108
130
|
media: mediaConfig(),
|
|
109
131
|
copilot: copilotConfig(),
|
|
132
|
+
transfer: transferConfig(),
|
|
133
|
+
segments: segmentsConfig(),
|
|
134
|
+
webhooks: webhooksConfig(),
|
|
135
|
+
// ortha:if graphql
|
|
136
|
+
contentGraphql: contentGraphqlConfig(),
|
|
137
|
+
// ortha:end
|
|
110
138
|
// ortha:if mcp
|
|
111
139
|
mcp: mcpConfig()
|
|
112
140
|
// ortha:end
|
|
@@ -212,6 +212,10 @@ export function buildPlugins(config: OrthaConfig): ServerPlugin[] {
|
|
|
212
212
|
// first request from a workspace granted both.
|
|
213
213
|
ContentGraphqlPlugin({
|
|
214
214
|
content,
|
|
215
|
+
...config.plugins.contentGraphql,
|
|
216
|
+
// GraphiQL rides the same switch as the Scalar reference: both are
|
|
217
|
+
// developer tooling, and neither should be reachable in production
|
|
218
|
+
// unless the operator asks (`API_DOCS=true`).
|
|
215
219
|
playground: config.docs.enabled === true
|
|
216
220
|
}),
|
|
217
221
|
// ortha:end
|
|
@@ -227,8 +231,9 @@ export function buildPlugins(config: OrthaConfig): ServerPlugin[] {
|
|
|
227
231
|
AlarmsPlugin(),
|
|
228
232
|
// Outgoing webhooks. Inert until someone adds an endpoint in the admin,
|
|
229
233
|
// and it only subscribes to the outbox, so nothing depends on it being
|
|
230
|
-
// registered any earlier than this.
|
|
231
|
-
|
|
234
|
+
// registered any earlier than this. The settings worth knowing about
|
|
235
|
+
// are the two `WEBHOOKS_ALLOW_*` flags — see `config/webhooks.ts`.
|
|
236
|
+
WebhooksPlugin(config.plugins.webhooks),
|
|
232
237
|
MediaServerPlugin({
|
|
233
238
|
// ortha:if media-local
|
|
234
239
|
provider: createLocalStorageProvider(config.plugins.media.storage),
|
|
@@ -259,9 +264,10 @@ export function buildPlugins(config: OrthaConfig): ServerPlugin[] {
|
|
|
259
264
|
// The setting worth filling in per install is `identity`: it says
|
|
260
265
|
// which field identifies a record of each type, which is what lets an
|
|
261
266
|
// import recognise "this is that record" instead of adding a
|
|
262
|
-
// duplicate. Without it the natural key is a heuristic
|
|
263
|
-
//
|
|
264
|
-
|
|
267
|
+
// duplicate. Without it the natural key is a heuristic. It is a map of
|
|
268
|
+
// your own content types rather than an environment value, so it lives
|
|
269
|
+
// in `config/transfer.ts`.
|
|
270
|
+
TransferPlugin(config.plugins.transfer),
|
|
265
271
|
// Reader entitlements — who may *read* published content, as against
|
|
266
272
|
// who may touch it. After content, whose read-scope port it binds, so
|
|
267
273
|
// one decision covers REST, GraphQL and MCP at once.
|
|
@@ -270,8 +276,10 @@ export function buildPlugins(config: OrthaConfig): ServerPlugin[] {
|
|
|
270
276
|
// in the admin no predicate is emitted and every read costs what it
|
|
271
277
|
// did before. The line to fill in per install is `resolver` — it says
|
|
272
278
|
// where a reader's tags come from, and its absence means every reader
|
|
273
|
-
// is anonymous, which serves unrestricted content and nothing else.
|
|
274
|
-
|
|
279
|
+
// is anonymous, which serves unrestricted content and nothing else. It
|
|
280
|
+
// is a function you write, not an environment value, so it lives in
|
|
281
|
+
// `config/segments.ts`.
|
|
282
|
+
SegmentsPlugin(config.plugins.segments),
|
|
275
283
|
// Registered after workspaces (runs are workspace-scoped) and identity
|
|
276
284
|
// (runs execute as the calling user, gated on `copilot:use`). The
|
|
277
285
|
// composition root is the single place that selects a backend: the
|
|
@@ -103,6 +103,21 @@ COPILOT_OPENAI_MODELS=llama3.1
|
|
|
103
103
|
# Leave empty for a local runtime that wants no auth.
|
|
104
104
|
COPILOT_OPENAI_API_KEY=
|
|
105
105
|
# ortha:end
|
|
106
|
+
# ortha:if graphql
|
|
107
|
+
# --- graphql ---
|
|
108
|
+
# The cost budget one GraphQL operation may spend. REST bounds a request
|
|
109
|
+
# structurally — one route, one page — and a document does not, so these are the
|
|
110
|
+
# replacement bound. The defaults suit an ordinary content API; loosen them only
|
|
111
|
+
# for a client you control.
|
|
112
|
+
GRAPHQL_MAX_DEPTH=8
|
|
113
|
+
GRAPHQL_MAX_COMPLEXITY=1000
|
|
114
|
+
GRAPHQL_MAX_FIELDS=500
|
|
115
|
+
GRAPHQL_MAX_QUERY_LENGTH=16384
|
|
116
|
+
# How long a built schema is reused before it is derived again from the
|
|
117
|
+
# workspace's content grants. Freshness only — every read is authorized against
|
|
118
|
+
# the live grants regardless.
|
|
119
|
+
GRAPHQL_SCHEMA_CACHE_TTL_MS=60000
|
|
120
|
+
# ortha:end
|
|
106
121
|
# ortha:if mcp
|
|
107
122
|
# --- mcp ---
|
|
108
123
|
# Global kill switch. OFF by default: once on, any holder of a `full`-scope API
|