@wtfalch/foundry 0.14.0 → 0.16.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/bootstrap/declare.d.ts +2 -2
- package/dist/bootstrap/declare.js +6 -6
- package/dist/index.d.ts +4 -4
- package/dist/index.js +3 -3
- package/dist/migrations/0034_owners.sql +42 -0
- package/dist/migrations/0035_chat_on_by_default.sql +10 -0
- package/dist/org.d.ts +80 -17
- package/dist/org.js +221 -18
- package/dist/plan/agora.d.ts +19 -1
- package/dist/plan/agora.js +37 -2
- package/dist/plan/chat-matrix.d.ts +99 -0
- package/dist/plan/chat-matrix.js +194 -0
- package/dist/plan/diff.d.ts +5 -3
- package/dist/plan/diff.js +143 -83
- package/dist/plan/entry-ai-key.d.ts +58 -0
- package/dist/plan/entry-ai-key.js +209 -0
- package/dist/plan/entry-files-key.d.ts +82 -0
- package/dist/plan/entry-files-key.js +327 -0
- package/dist/plan/executor.d.ts +244 -16
- package/dist/plan/executor.js +860 -78
- package/dist/plan/hostnames.d.ts +8 -1
- package/dist/plan/hostnames.js +18 -1
- package/dist/plan/index.d.ts +2 -1
- package/dist/plan/index.js +2 -1
- package/dist/plan/plan-apply.d.ts +2 -0
- package/dist/plan/plan-apply.js +42 -2
- package/dist/plan/platform-diff.d.ts +8 -0
- package/dist/plan/platform-diff.js +8 -0
- package/dist/plan/platform-org.d.ts +17 -5
- package/dist/plan/platform-org.js +15 -7
- package/dist/plan/platform-providers.d.ts +38 -12
- package/dist/plan/platform-providers.js +99 -16
- package/dist/plan/proxy-labels.d.ts +42 -0
- package/dist/plan/proxy-labels.js +73 -0
- package/dist/plan/zitadel-machine-user.d.ts +5 -1
- package/dist/plan/zitadel-machine-user.js +5 -1
- package/dist/plan/zitadel-project-roles.d.ts +25 -0
- package/dist/plan/zitadel-project-roles.js +104 -0
- package/dist/store/postgres.js +4 -17
- package/dist/store/schema.d.ts +13 -45
- package/dist/store/schema.js +5 -8
- package/package.json +2 -2
package/dist/plan/agora.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { chatUpstreams } from './chat-matrix.js';
|
|
1
2
|
/**
|
|
2
3
|
* #164: what both the planner (`diff.ts`) and the executor (`executor.ts`)
|
|
3
4
|
* need to agree on for an org's Agora -- the hostname, the values the host
|
|
@@ -11,6 +12,31 @@
|
|
|
11
12
|
* (recorded by `executor.ts`'s `zitadelApp`).
|
|
12
13
|
*/
|
|
13
14
|
export const ZITADEL_ORG_SCOPE_KEY = 'zitadel_org';
|
|
15
|
+
/**
|
|
16
|
+
* The org's ZITADEL messaging project: the audience chat-service requires in
|
|
17
|
+
* a token's `aud`, and Agora's `messagingProjectId`. `executor.ts`'s
|
|
18
|
+
* `coolifyChat` finds or creates it (`<slug>-messaging`) and records its id
|
|
19
|
+
* here; a host value for the same id wins when it supplies one.
|
|
20
|
+
*/
|
|
21
|
+
export const ZITADEL_MESSAGING_PROJECT_SCOPE_KEY = 'zitadel_messaging_project';
|
|
22
|
+
/**
|
|
23
|
+
* Recorded by `coolifyChat` last, once chat's three apps carry their fixed
|
|
24
|
+
* internal names: the chat service's address. `desiredAgoraEnv` waits for it
|
|
25
|
+
* (equal to the address the current slug gives), so Agora is never handed an
|
|
26
|
+
* address that does not resolve yet.
|
|
27
|
+
*/
|
|
28
|
+
export const CHAT_SERVICE_URL_SCOPE_KEY = 'coolify_chat_service_url';
|
|
29
|
+
/** Whether the recorded marker says chat's apps carry the names the org's current slug gives them. */
|
|
30
|
+
export function chatHasUpstreams(org, recorded) {
|
|
31
|
+
return recorded[CHAT_SERVICE_URL_SCOPE_KEY] === chatUpstreams(org.slug).chat;
|
|
32
|
+
}
|
|
33
|
+
/** `host` with the recorded messaging project filled in when the host supplies none. */
|
|
34
|
+
export function withRecordedMessagingProject(host, recorded) {
|
|
35
|
+
return {
|
|
36
|
+
...host,
|
|
37
|
+
messagingProjectId: host.messagingProjectId ?? recorded[ZITADEL_MESSAGING_PROJECT_SCOPE_KEY] ?? null,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
14
40
|
export const AGORA_CLIENT_ID_SCOPE_KEY = 'agora_client_id';
|
|
15
41
|
/**
|
|
16
42
|
* The recorded OIDC app's `clientId` (level-1 app-env campaign, gap P2) —
|
|
@@ -181,7 +207,8 @@ export function callsServiceHasTokenEnv(host, recorded) {
|
|
|
181
207
|
* not `host`: present only when calls are on and this org's calls service
|
|
182
208
|
* app carries its internal name ({@link callsServiceHasName}).
|
|
183
209
|
*/
|
|
184
|
-
export function desiredAgoraEnv(org, recorded,
|
|
210
|
+
export function desiredAgoraEnv(org, recorded, rawHost) {
|
|
211
|
+
const host = withRecordedMessagingProject(rawHost, recorded);
|
|
185
212
|
const env = { AGORA_CANONICAL_HOST: agoraHostnameFor(org) };
|
|
186
213
|
const zitadelOrgId = recorded[ZITADEL_ORG_SCOPE_KEY];
|
|
187
214
|
const clientId = recorded[AGORA_CLIENT_ID_SCOPE_KEY];
|
|
@@ -200,7 +227,7 @@ export function desiredAgoraEnv(org, recorded, host) {
|
|
|
200
227
|
organisationId: zitadelOrgId,
|
|
201
228
|
mailProjectId: host.mailProjectId,
|
|
202
229
|
messagingProjectId: host.messagingProjectId,
|
|
203
|
-
//
|
|
230
|
+
// Agora hides all chat UI unless this is true.
|
|
204
231
|
chat: { enabled: org.chat.enabled },
|
|
205
232
|
// Agora draws call buttons only when this is true.
|
|
206
233
|
calls: { enabled: org.calls.enabled },
|
|
@@ -214,6 +241,14 @@ export function desiredAgoraEnv(org, recorded, host) {
|
|
|
214
241
|
if (host.filesUpstream) {
|
|
215
242
|
env.AGORA_FILES_UPSTREAM = host.filesUpstream;
|
|
216
243
|
}
|
|
244
|
+
// Chat's three upstreams go together: the chat service alone cannot sign a
|
|
245
|
+
// person in (MAS) or load a room (Synapse).
|
|
246
|
+
if (org.chat.enabled && chatHasUpstreams(org, recorded)) {
|
|
247
|
+
const upstreams = chatUpstreams(org.slug);
|
|
248
|
+
env.AGORA_CHAT_UPSTREAM = upstreams.chat;
|
|
249
|
+
env.AGORA_MATRIX_UPSTREAM = upstreams.matrix;
|
|
250
|
+
env.AGORA_MAS_UPSTREAM = upstreams.mas;
|
|
251
|
+
}
|
|
217
252
|
if (org.calls.enabled && callsServiceHasName(org, recorded)) {
|
|
218
253
|
env.AGORA_CALLS_UPSTREAM = `http://${callsServiceInternalName(org)}:${CALLS_SERVICE_PORT}`;
|
|
219
254
|
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { type CallsDbConnection, type CallsMatrixSecrets, mintCallsMatrixSecrets } from './calls-matrix.js';
|
|
2
|
+
/**
|
|
3
|
+
* The org's chat stack: pure config generation for Synapse (with the
|
|
4
|
+
* `wtfalch_chat_gate` module), matrix-authentication-service (MAS) and the
|
|
5
|
+
* chat service, deployed by `executor.ts`'s `coolifyChat`. It follows
|
|
6
|
+
* `calls-matrix.ts`: three Coolify-managed Postgres databases (Synapse, MAS,
|
|
7
|
+
* chat), one config file per Matrix app, and internal Docker-network
|
|
8
|
+
* addresses only.
|
|
9
|
+
*
|
|
10
|
+
* NO PUBLIC HOSTNAME OF ITS OWN
|
|
11
|
+
* ------------------------------
|
|
12
|
+
* The org's Agora is the one public origin. Its Caddy proxies `/_chat/*`,
|
|
13
|
+
* the Matrix client routes and MAS's compat login to the three internal
|
|
14
|
+
* addresses below (`AGORA_CHAT_UPSTREAM`, `AGORA_MATRIX_UPSTREAM`,
|
|
15
|
+
* `AGORA_MAS_UPSTREAM`, `agora/apps/agora/Caddyfile`), so neither tunnel
|
|
16
|
+
* routes nor DNS are needed. The chat service mints Matrix sessions through
|
|
17
|
+
* MAS's admin API over the internal address and hands clients the Agora URL
|
|
18
|
+
* as their homeserver (`CHAT_HOMESERVER_URL`).
|
|
19
|
+
*
|
|
20
|
+
* Secrets are the same six values the calls stack mints
|
|
21
|
+
* ({@link mintCallsMatrixSecrets}); they live as envs on the chat MAS app,
|
|
22
|
+
* the only memory across a re-run (`CHAT_SECRET_ENV_KEYS` in `executor.ts`).
|
|
23
|
+
*/
|
|
24
|
+
export type ChatMatrixSecrets = CallsMatrixSecrets;
|
|
25
|
+
export declare const mintChatMatrixSecrets: typeof mintCallsMatrixSecrets;
|
|
26
|
+
/** The chat repo: the Synapse module and the chat service are both built from it. */
|
|
27
|
+
export declare const CHAT_REPO = "wtfalch/chat";
|
|
28
|
+
/** Synapse plus the `wtfalch_chat_gate` module; built from the repo root. */
|
|
29
|
+
export declare const CHAT_SYNAPSE_DOCKERFILE = "apps/chat-service/synapse-module/Dockerfile";
|
|
30
|
+
/** The chat service, built from the repo root (that Dockerfile's own top comment). */
|
|
31
|
+
export declare const CHAT_SERVICE_DOCKERFILE = "apps/chat-service/Dockerfile";
|
|
32
|
+
export declare const CHAT_SYNAPSE_PORT = "8008";
|
|
33
|
+
export declare const CHAT_MAS_PORT = "8080";
|
|
34
|
+
export declare const CHAT_SERVICE_PORT = "8080";
|
|
35
|
+
/** Fixed internal names, so the three apps and Agora address each other without guessing Coolify's own. */
|
|
36
|
+
export declare function chatInternalNames(orgSlug: string): {
|
|
37
|
+
synapse: string;
|
|
38
|
+
mas: string;
|
|
39
|
+
service: string;
|
|
40
|
+
synapseDb: string;
|
|
41
|
+
masDb: string;
|
|
42
|
+
serviceDb: string;
|
|
43
|
+
};
|
|
44
|
+
/** The three addresses Agora proxies to, as `http://<internal name>:<port>`. */
|
|
45
|
+
export declare function chatUpstreams(orgSlug: string): {
|
|
46
|
+
chat: string;
|
|
47
|
+
matrix: string;
|
|
48
|
+
mas: string;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Never resolved by DNS and never federates; the identifier Synapse, MAS and
|
|
52
|
+
* the chat service agree on. A level-1 org's chat lives on this name until a
|
|
53
|
+
* re-key onto its domain (not built). `.internal` never resolves, so it can
|
|
54
|
+
* not collide with a hostname some org owns.
|
|
55
|
+
*/
|
|
56
|
+
export declare function chatServerName(orgSlug: string): string;
|
|
57
|
+
/** The Matrix user the chat service acts as (`chat/apps/chat-service/src/workspace.ts`). */
|
|
58
|
+
export declare function chatAdminUser(orgSlug: string): string;
|
|
59
|
+
export interface ChatSynapseHomeserverInput {
|
|
60
|
+
orgSlug: string;
|
|
61
|
+
db: CallsDbConnection;
|
|
62
|
+
masHost: string;
|
|
63
|
+
masSharedSecret: string;
|
|
64
|
+
}
|
|
65
|
+
/** `homeserver.yaml`: federation and presence off, MAS for auth, the chat gate module on. */
|
|
66
|
+
export declare function chatSynapseHomeserverYaml(input: ChatSynapseHomeserverInput): string;
|
|
67
|
+
export interface ChatMasConfigInput {
|
|
68
|
+
orgSlug: string;
|
|
69
|
+
/** The org's Agora hostname: MAS's `public_base`, the origin clients reach it on. */
|
|
70
|
+
publicHost: string;
|
|
71
|
+
db: CallsDbConnection;
|
|
72
|
+
synapseHost: string;
|
|
73
|
+
sharedSecret: string;
|
|
74
|
+
clientId: string;
|
|
75
|
+
clientSecret: string;
|
|
76
|
+
encryptionKey: string;
|
|
77
|
+
rsaKeyPem: string;
|
|
78
|
+
ecKeyPem: string;
|
|
79
|
+
}
|
|
80
|
+
/** MAS `config.yaml`: password login and registration off; the chat service is the one client, with admin. */
|
|
81
|
+
export declare function chatMasConfigYaml(input: ChatMasConfigInput): string;
|
|
82
|
+
export interface ChatServiceEnvInput {
|
|
83
|
+
orgSlug: string;
|
|
84
|
+
/** The org's display name: the workspace space's name. */
|
|
85
|
+
companyName: string;
|
|
86
|
+
issuer: string;
|
|
87
|
+
/** The org's ZITADEL messaging project id, required in a token's `aud`. */
|
|
88
|
+
audience: string;
|
|
89
|
+
zitadelOrgId: string;
|
|
90
|
+
agoraHost: string;
|
|
91
|
+
masClientId: string;
|
|
92
|
+
masClientSecret: string;
|
|
93
|
+
databaseUrl: string;
|
|
94
|
+
}
|
|
95
|
+
/** Every variable `chat/apps/chat-service/src/config.ts` requires, plus the internal Synapse address and port. */
|
|
96
|
+
export declare function chatServiceEnvs(input: ChatServiceEnvInput): Array<{
|
|
97
|
+
key: string;
|
|
98
|
+
value: string;
|
|
99
|
+
}>;
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { mintCallsMatrixSecrets, } from './calls-matrix.js';
|
|
2
|
+
export const mintChatMatrixSecrets = mintCallsMatrixSecrets;
|
|
3
|
+
/** The chat repo: the Synapse module and the chat service are both built from it. */
|
|
4
|
+
export const CHAT_REPO = 'wtfalch/chat';
|
|
5
|
+
/** Synapse plus the `wtfalch_chat_gate` module; built from the repo root. */
|
|
6
|
+
export const CHAT_SYNAPSE_DOCKERFILE = 'apps/chat-service/synapse-module/Dockerfile';
|
|
7
|
+
/** The chat service, built from the repo root (that Dockerfile's own top comment). */
|
|
8
|
+
export const CHAT_SERVICE_DOCKERFILE = 'apps/chat-service/Dockerfile';
|
|
9
|
+
export const CHAT_SYNAPSE_PORT = '8008';
|
|
10
|
+
export const CHAT_MAS_PORT = '8080';
|
|
11
|
+
export const CHAT_SERVICE_PORT = '8080';
|
|
12
|
+
/** Fixed internal names, so the three apps and Agora address each other without guessing Coolify's own. */
|
|
13
|
+
export function chatInternalNames(orgSlug) {
|
|
14
|
+
return {
|
|
15
|
+
synapse: `${orgSlug}-chat-synapse`,
|
|
16
|
+
mas: `${orgSlug}-chat-mas`,
|
|
17
|
+
service: `${orgSlug}-chat-service`,
|
|
18
|
+
synapseDb: `${orgSlug}-chat-synapse-db`,
|
|
19
|
+
masDb: `${orgSlug}-chat-mas-db`,
|
|
20
|
+
serviceDb: `${orgSlug}-chat-service-db`,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/** The three addresses Agora proxies to, as `http://<internal name>:<port>`. */
|
|
24
|
+
export function chatUpstreams(orgSlug) {
|
|
25
|
+
const names = chatInternalNames(orgSlug);
|
|
26
|
+
return {
|
|
27
|
+
chat: `http://${names.service}:${CHAT_SERVICE_PORT}`,
|
|
28
|
+
matrix: `http://${names.synapse}:${CHAT_SYNAPSE_PORT}`,
|
|
29
|
+
mas: `http://${names.mas}:${CHAT_MAS_PORT}`,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Never resolved by DNS and never federates; the identifier Synapse, MAS and
|
|
34
|
+
* the chat service agree on. A level-1 org's chat lives on this name until a
|
|
35
|
+
* re-key onto its domain (not built). `.internal` never resolves, so it can
|
|
36
|
+
* not collide with a hostname some org owns.
|
|
37
|
+
*/
|
|
38
|
+
export function chatServerName(orgSlug) {
|
|
39
|
+
return `chat-${orgSlug}.internal`;
|
|
40
|
+
}
|
|
41
|
+
/** The Matrix user the chat service acts as (`chat/apps/chat-service/src/workspace.ts`). */
|
|
42
|
+
export function chatAdminUser(orgSlug) {
|
|
43
|
+
return `@chat-admin:${chatServerName(orgSlug)}`;
|
|
44
|
+
}
|
|
45
|
+
const q = (value) => JSON.stringify(value);
|
|
46
|
+
/** `homeserver.yaml`: federation and presence off, MAS for auth, the chat gate module on. */
|
|
47
|
+
export function chatSynapseHomeserverYaml(input) {
|
|
48
|
+
return [
|
|
49
|
+
'# Generated by @wtfalch/foundry (executor.ts, coolify:chat). Do not edit',
|
|
50
|
+
'# by hand; re-run apply instead -- it will not rotate secrets that exist.',
|
|
51
|
+
'',
|
|
52
|
+
`server_name: ${q(chatServerName(input.orgSlug))}`,
|
|
53
|
+
'pid_file: /data/homeserver.pid',
|
|
54
|
+
'report_stats: false',
|
|
55
|
+
'',
|
|
56
|
+
'listeners:',
|
|
57
|
+
` - port: ${CHAT_SYNAPSE_PORT}`,
|
|
58
|
+
' type: http',
|
|
59
|
+
' tls: false',
|
|
60
|
+
' bind_addresses: ["0.0.0.0"]',
|
|
61
|
+
' x_forwarded: true',
|
|
62
|
+
' resources:',
|
|
63
|
+
' - names: [client]',
|
|
64
|
+
' compress: false',
|
|
65
|
+
'',
|
|
66
|
+
'database:',
|
|
67
|
+
' name: psycopg2',
|
|
68
|
+
' args:',
|
|
69
|
+
` user: ${q(input.db.user)}`,
|
|
70
|
+
` password: ${q(input.db.password)}`,
|
|
71
|
+
` dbname: ${q(input.db.database)}`,
|
|
72
|
+
` host: ${q(input.db.host)}`,
|
|
73
|
+
` port: ${input.db.port}`,
|
|
74
|
+
' cp_min: 5',
|
|
75
|
+
' cp_max: 10',
|
|
76
|
+
'',
|
|
77
|
+
'# An empty list is the recommended way of disabling federation.',
|
|
78
|
+
'federation_domain_whitelist: []',
|
|
79
|
+
'',
|
|
80
|
+
'# Presence is out of scope for v1 (foundry#41).',
|
|
81
|
+
'presence:',
|
|
82
|
+
' enabled: false',
|
|
83
|
+
'',
|
|
84
|
+
'enable_authenticated_media: true',
|
|
85
|
+
'max_upload_size: "50M"',
|
|
86
|
+
'encryption_enabled_by_default_for_room_type: "off"',
|
|
87
|
+
'auto_join_rooms: []',
|
|
88
|
+
'',
|
|
89
|
+
'media_store_path: /data/media_store',
|
|
90
|
+
'signing_key_path: /data/signing.key',
|
|
91
|
+
'',
|
|
92
|
+
'matrix_authentication_service:',
|
|
93
|
+
' enabled: true',
|
|
94
|
+
` endpoint: ${q(`http://${input.masHost}:${CHAT_MAS_PORT}/`)}`,
|
|
95
|
+
` secret: ${q(input.masSharedSecret)}`,
|
|
96
|
+
'',
|
|
97
|
+
'# Gates channel creation, invites, joins and encryption (chat repo, MSG-007).',
|
|
98
|
+
'modules:',
|
|
99
|
+
' - module: wtfalch_chat_gate.ChatGate',
|
|
100
|
+
' config:',
|
|
101
|
+
` admin_user: ${q(chatAdminUser(input.orgSlug))}`,
|
|
102
|
+
' allow_encrypted_channels: false',
|
|
103
|
+
'',
|
|
104
|
+
].join('\n');
|
|
105
|
+
}
|
|
106
|
+
/** MAS `config.yaml`: password login and registration off; the chat service is the one client, with admin. */
|
|
107
|
+
export function chatMasConfigYaml(input) {
|
|
108
|
+
const indentKey = (pem, spaces) => pem
|
|
109
|
+
.trimEnd()
|
|
110
|
+
.split('\n')
|
|
111
|
+
.map((line) => `${' '.repeat(spaces)}${line}`)
|
|
112
|
+
.join('\n');
|
|
113
|
+
return [
|
|
114
|
+
'# Generated by @wtfalch/foundry (executor.ts, coolify:chat). Do not edit by hand.',
|
|
115
|
+
'',
|
|
116
|
+
'http:',
|
|
117
|
+
' listeners:',
|
|
118
|
+
' - name: web',
|
|
119
|
+
' resources:',
|
|
120
|
+
' - name: discovery',
|
|
121
|
+
' - name: human',
|
|
122
|
+
' - name: oauth',
|
|
123
|
+
' - name: compat',
|
|
124
|
+
' - name: graphql',
|
|
125
|
+
' - name: assets',
|
|
126
|
+
' - name: adminapi',
|
|
127
|
+
' - name: health',
|
|
128
|
+
' binds:',
|
|
129
|
+
` - address: "[::]:${CHAT_MAS_PORT}"`,
|
|
130
|
+
' proxy_protocol: false',
|
|
131
|
+
` public_base: ${q(`https://${input.publicHost}/`)}`,
|
|
132
|
+
' trusted_proxies:',
|
|
133
|
+
' - 0.0.0.0/0',
|
|
134
|
+
'',
|
|
135
|
+
'database:',
|
|
136
|
+
` host: ${q(input.db.host)}`,
|
|
137
|
+
` port: ${input.db.port}`,
|
|
138
|
+
` username: ${q(input.db.user)}`,
|
|
139
|
+
` password: ${q(input.db.password)}`,
|
|
140
|
+
` database: ${q(input.db.database)}`,
|
|
141
|
+
' ssl_mode: disable',
|
|
142
|
+
'',
|
|
143
|
+
'matrix:',
|
|
144
|
+
' kind: synapse',
|
|
145
|
+
` homeserver: ${q(chatServerName(input.orgSlug))}`,
|
|
146
|
+
` endpoint: ${q(`http://${input.synapseHost}:${CHAT_SYNAPSE_PORT}`)}`,
|
|
147
|
+
` secret: ${q(input.sharedSecret)}`,
|
|
148
|
+
'',
|
|
149
|
+
'# The chat service is the only way in.',
|
|
150
|
+
'passwords:',
|
|
151
|
+
' enabled: false',
|
|
152
|
+
'',
|
|
153
|
+
'account:',
|
|
154
|
+
' password_registration_enabled: false',
|
|
155
|
+
'',
|
|
156
|
+
'clients:',
|
|
157
|
+
` - client_id: ${q(input.clientId)}`,
|
|
158
|
+
' client_auth_method: client_secret_basic',
|
|
159
|
+
` client_secret: ${q(input.clientSecret)}`,
|
|
160
|
+
'',
|
|
161
|
+
'policy:',
|
|
162
|
+
' data:',
|
|
163
|
+
' admin_clients:',
|
|
164
|
+
` - ${q(input.clientId)}`,
|
|
165
|
+
'',
|
|
166
|
+
'secrets:',
|
|
167
|
+
` encryption: ${input.encryptionKey}`,
|
|
168
|
+
' keys:',
|
|
169
|
+
' - key: |',
|
|
170
|
+
indentKey(input.rsaKeyPem, 8),
|
|
171
|
+
' - key: |',
|
|
172
|
+
indentKey(input.ecKeyPem, 8),
|
|
173
|
+
'',
|
|
174
|
+
].join('\n');
|
|
175
|
+
}
|
|
176
|
+
/** Every variable `chat/apps/chat-service/src/config.ts` requires, plus the internal Synapse address and port. */
|
|
177
|
+
export function chatServiceEnvs(input) {
|
|
178
|
+
const upstreams = chatUpstreams(input.orgSlug);
|
|
179
|
+
return [
|
|
180
|
+
{ key: 'CHAT_ISSUER', value: input.issuer },
|
|
181
|
+
{ key: 'CHAT_AUDIENCE', value: input.audience },
|
|
182
|
+
{ key: 'CHAT_ORGANISATION_ID', value: input.zitadelOrgId },
|
|
183
|
+
{ key: 'CHAT_HOMESERVER_URL', value: `https://${input.agoraHost}` },
|
|
184
|
+
{ key: 'CHAT_INTERNAL_HOMESERVER_URL', value: upstreams.matrix },
|
|
185
|
+
{ key: 'CHAT_SERVER_NAME', value: chatServerName(input.orgSlug) },
|
|
186
|
+
{ key: 'CHAT_COMPANY_NAME', value: input.companyName },
|
|
187
|
+
{ key: 'MAS_URL', value: upstreams.mas },
|
|
188
|
+
{ key: 'MAS_CLIENT_ID', value: input.masClientId },
|
|
189
|
+
{ key: 'MAS_CLIENT_SECRET', value: input.masClientSecret },
|
|
190
|
+
{ key: 'DATABASE_URL', value: input.databaseUrl },
|
|
191
|
+
{ key: 'CHAT_ALLOWED_ORIGINS', value: `https://${input.agoraHost}` },
|
|
192
|
+
{ key: 'PORT', value: CHAT_SERVICE_PORT },
|
|
193
|
+
];
|
|
194
|
+
}
|
package/dist/plan/diff.d.ts
CHANGED
|
@@ -36,6 +36,8 @@ export interface PlanOrgOptions {
|
|
|
36
36
|
loginHostBindings?: boolean;
|
|
37
37
|
/** F5: the executor was built with a `broker` port. */
|
|
38
38
|
broker?: boolean;
|
|
39
|
+
/** F6a, F6b: the clock `planEntryAiKeys` and `planFilesKeys` read for the renewal window. Default `Date.now`. */
|
|
40
|
+
now?: () => number;
|
|
39
41
|
}
|
|
40
42
|
export interface PlanDetail {
|
|
41
43
|
orgId: OrgId;
|
|
@@ -67,7 +69,7 @@ export declare function toPlan(detail: PlanDetail): Plan;
|
|
|
67
69
|
* `planAiOrganisation`, each planned before its own system's limit step
|
|
68
70
|
* ({@link planStorage}, {@link planAiBudget}) so an org has somewhere for a
|
|
69
71
|
* limit to land, and AI use is switched on, before either limit is ever set.
|
|
70
|
-
* `
|
|
72
|
+
* `planOwners` below adds one more singleton, in a different shape from the
|
|
71
73
|
* rest: the org's owner, invited by ZITADEL email, idempotent on the
|
|
72
74
|
* *recorded* resource id (`recordedIds`, sourced from
|
|
73
75
|
* `AppliedState.serviceIds`) rather than a live resource, since a human user
|
|
@@ -84,9 +86,9 @@ export declare function toPlan(detail: PlanDetail): Plan;
|
|
|
84
86
|
* Agora). #173 adds one more, gated on `org.hosting.enabled` like
|
|
85
87
|
* {@link planApp} itself: {@link planJobsRegistration}, registering the org's
|
|
86
88
|
* app with the platform's jobs scheduler, idempotent on the *recorded*
|
|
87
|
-
* resource id like {@link
|
|
89
|
+
* resource id like {@link planOwners} rather than a live resource, since
|
|
88
90
|
* nothing in `live` can report a scheduler registration (no reader exists for
|
|
89
|
-
* it). #267 adds one more, right after {@link
|
|
91
|
+
* it). #267 adds one more, right after {@link planOwners} and before every
|
|
90
92
|
* app-creating step below: {@link planLoginHost}, the org's own company
|
|
91
93
|
* login host -- also idempotent on the *recorded* resource id, since
|
|
92
94
|
* `live` has no notion of a ZITADEL trusted domain or a login binding
|