@wtfalch/foundry 0.36.0 → 0.37.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/versions.d.ts +9 -3
- package/dist/bootstrap/versions.js +13 -4
- package/dist/plan/executor.d.ts +8 -0
- package/dist/plan/executor.js +8 -4
- package/dist/plan/own-services.d.ts +5 -1
- package/dist/plan/own-services.js +3 -1
- package/dist/plan/own-sfu.js +3 -4
- package/dist/plan/platform-providers.d.ts +32 -0
- package/dist/plan/platform-providers.js +34 -7
- package/package.json +1 -1
|
@@ -34,10 +34,16 @@ export declare const DIGESTS: PinnedVersions;
|
|
|
34
34
|
*/
|
|
35
35
|
export declare function pinnedRef(key: keyof PinnedVersions): string;
|
|
36
36
|
/**
|
|
37
|
-
*
|
|
38
|
-
*
|
|
37
|
+
* The two image fields Coolify accepts. Its tag rule has no `@` or `:`, so a
|
|
38
|
+
* digest goes in the name as `<image>@sha256` and the tag holds the bare
|
|
39
|
+
* 64-hex digest (Coolify pulls `<name>:<tag>`). A declared tag that equals the
|
|
40
|
+
* pinned one gets its digest; any other declared tag (an override) is passed
|
|
41
|
+
* through unchanged.
|
|
39
42
|
*/
|
|
40
|
-
export declare function
|
|
43
|
+
export declare function coolifyImageFor(key: keyof PinnedVersions, image: string, tag: string): {
|
|
44
|
+
docker_registry_image_name: string;
|
|
45
|
+
docker_registry_image_tag: string;
|
|
46
|
+
};
|
|
41
47
|
/**
|
|
42
48
|
* Why an image tag cannot be used, or `null`. #248: generalised out of what
|
|
43
49
|
* was `platform-providers.ts`'s `mailImageTagBlocked`, the one guard
|
|
@@ -41,11 +41,20 @@ export function pinnedRef(key) {
|
|
|
41
41
|
return `${VERSIONS[key]}@${DIGESTS[key]}`;
|
|
42
42
|
}
|
|
43
43
|
/**
|
|
44
|
-
*
|
|
45
|
-
*
|
|
44
|
+
* The two image fields Coolify accepts. Its tag rule has no `@` or `:`, so a
|
|
45
|
+
* digest goes in the name as `<image>@sha256` and the tag holds the bare
|
|
46
|
+
* 64-hex digest (Coolify pulls `<name>:<tag>`). A declared tag that equals the
|
|
47
|
+
* pinned one gets its digest; any other declared tag (an override) is passed
|
|
48
|
+
* through unchanged.
|
|
46
49
|
*/
|
|
47
|
-
export function
|
|
48
|
-
|
|
50
|
+
export function coolifyImageFor(key, image, tag) {
|
|
51
|
+
if (tag !== VERSIONS[key]) {
|
|
52
|
+
return { docker_registry_image_name: image, docker_registry_image_tag: tag };
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
docker_registry_image_name: `${image}@sha256`,
|
|
56
|
+
docker_registry_image_tag: DIGESTS[key].replace(/^sha256:/, ''),
|
|
57
|
+
};
|
|
49
58
|
}
|
|
50
59
|
/**
|
|
51
60
|
* Tag spellings that resolve to whatever the registry currently has, not a
|
package/dist/plan/executor.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ import { type MailCopyPort } from './mail-copy.js';
|
|
|
15
15
|
import { type OwnServicesPort } from './own-services.js';
|
|
16
16
|
import type { DestroyTarget, StepDestroyer, StepExecutionContext, StepExecutor } from './plan-apply.js';
|
|
17
17
|
import type { EdgePortName } from './platform-org.js';
|
|
18
|
+
import type { RequestHeaderPort } from './platform-providers.js';
|
|
18
19
|
export declare function coolifyProjectMark(org: Pick<DeclaredOrg, 'id'>): string;
|
|
19
20
|
/**
|
|
20
21
|
* The Coolify calls this executor makes, shaped to match
|
|
@@ -1595,6 +1596,13 @@ export interface FoundryStepExecutorPorts {
|
|
|
1595
1596
|
* `edge.tunnels` (see {@link EdgePort.tunnels}). Kept for a host or a test that supplies its own.
|
|
1596
1597
|
*/
|
|
1597
1598
|
tunnels?: TunnelPort;
|
|
1599
|
+
/**
|
|
1600
|
+
* #745: the Cloudflare request header rule call, edge's `platform.requestHeaders`
|
|
1601
|
+
* (`@wtfalch/edge` 0.14.0). A company's sign-in hostname gets its header strip rule
|
|
1602
|
+
* through it before its DNS record; without it the step logs one fixed line and
|
|
1603
|
+
* writes no rule. Mail and SFU hostnames never use it.
|
|
1604
|
+
*/
|
|
1605
|
+
requestHeaders?: RequestHeaderPort;
|
|
1598
1606
|
/**
|
|
1599
1607
|
* F4.access: overrides the Access calls, which otherwise go through
|
|
1600
1608
|
* `edge.access` (see {@link EdgePort.access}). Kept for a host or a test that supplies its own.
|
package/dist/plan/executor.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
2
|
import { randomBytes, randomUUID } from 'node:crypto';
|
|
3
3
|
import { URL } from 'node:url';
|
|
4
|
-
import {
|
|
4
|
+
import { coolifyImageFor } from '../bootstrap/versions.js';
|
|
5
5
|
import { redactSecrets } from '../calls-sfu-deploy-support.js';
|
|
6
6
|
import { resolveCallsSfuUrls } from '../calls-sfu.js';
|
|
7
7
|
import { APP_ENTRY_KEY, appEntry, filesCollectionEnvName, ownerMailboxRecorded, ownerMailboxes, ownsServices, sharedSecretRef, } from '../org.js';
|
|
@@ -3870,8 +3870,7 @@ export class FoundryStepExecutor {
|
|
|
3870
3870
|
server_uuid: server.uuid,
|
|
3871
3871
|
environment_name: environment.name,
|
|
3872
3872
|
environment_uuid: environment.uuid,
|
|
3873
|
-
|
|
3874
|
-
docker_registry_image_tag: pinnedTagFor('synapse', synapseTag),
|
|
3873
|
+
...coolifyImageFor('synapse', SYNAPSE_IMAGE, synapseTag),
|
|
3875
3874
|
ports_exposes: '8008',
|
|
3876
3875
|
name: names.synapse,
|
|
3877
3876
|
description: `Synapse for ${org.slug}'s calls stack (${SYNAPSE_IMAGE}:${synapseTag})`,
|
|
@@ -5750,7 +5749,12 @@ export class FoundryStepExecutor {
|
|
|
5750
5749
|
apex: hn.zone,
|
|
5751
5750
|
gitRef,
|
|
5752
5751
|
project: { ensure: () => this.ensureCoolifyProject(org, context) },
|
|
5753
|
-
network: orgNetwork({
|
|
5752
|
+
network: orgNetwork({
|
|
5753
|
+
dns: this.dnsPort(),
|
|
5754
|
+
tunnels: this.tunnelPort(),
|
|
5755
|
+
requestHeaders: this.ports.requestHeaders,
|
|
5756
|
+
zones: this.zonePort(),
|
|
5757
|
+
}, scope, `${org.slug}:auth`),
|
|
5754
5758
|
smtpSender: `noreply@${hn.mailDomain}`,
|
|
5755
5759
|
defaultRedirectUri: `https://${agoraHostnameFor(org)}`,
|
|
5756
5760
|
ownerCompany: null,
|
|
@@ -6,7 +6,7 @@ import type { DnsPort } from './edge-dns.js';
|
|
|
6
6
|
import type { ZonePort } from './edge-zones.js';
|
|
7
7
|
import type { CallsSfuPort, CloudflarePort, CoolifyPort, FilesOrganisationPort, FoundryStepExecutorPorts, JobsSchedulerPort, MailPort, ZitadelPort } from './executor.js';
|
|
8
8
|
import type { PlatformStepExecutionContext, StepExecutionContext } from './plan-apply.js';
|
|
9
|
-
import type { FoundryPlatformStepExecutorOptions, PlatformAuthDeps, PlatformDnsFactory, PlatformFilesDeps, PlatformMailDeps, PlatformTunnelPort, PlatformTunnelsFactory } from './platform-providers.js';
|
|
9
|
+
import type { FoundryPlatformStepExecutorOptions, PlatformAuthDeps, PlatformDnsFactory, PlatformFilesDeps, PlatformMailDeps, PlatformRequestHeadersFactory, PlatformTunnelPort, PlatformTunnelsFactory, RequestHeaderPort } from './platform-providers.js';
|
|
10
10
|
/** Applied-state key under which an own org's provisioned-services marker is recorded. */
|
|
11
11
|
export declare const OWN_SERVICES_APPLIED_SCOPE_KEY = "own_services_applied";
|
|
12
12
|
/**
|
|
@@ -82,10 +82,13 @@ export interface ServiceNetwork {
|
|
|
82
82
|
dns(): Promise<DnsPort>;
|
|
83
83
|
tunnels(): Promise<PlatformTunnelPort>;
|
|
84
84
|
zoneId(apex: string): Promise<string | undefined>;
|
|
85
|
+
/** Undefined when the host supplies no way to write Cloudflare request header rules. */
|
|
86
|
+
requestHeaders(): Promise<RequestHeaderPort | undefined>;
|
|
85
87
|
}
|
|
86
88
|
export declare function platformNetwork(deps: {
|
|
87
89
|
dns: PlatformDnsFactory;
|
|
88
90
|
tunnels: PlatformTunnelsFactory;
|
|
91
|
+
requestHeaders?: PlatformRequestHeadersFactory;
|
|
89
92
|
cloudflare: Pick<CloudflarePort, 'findZoneByApex'>;
|
|
90
93
|
}, scope: StepScope, step: string): ServiceNetwork;
|
|
91
94
|
/**
|
|
@@ -96,6 +99,7 @@ export declare function platformNetwork(deps: {
|
|
|
96
99
|
export declare function orgNetwork(ports: {
|
|
97
100
|
dns?: DnsPort;
|
|
98
101
|
tunnels?: PlatformTunnelPort;
|
|
102
|
+
requestHeaders?: RequestHeaderPort;
|
|
99
103
|
zones?: Pick<ZonePort, 'findZoneByApex'>;
|
|
100
104
|
}, scope: StepScope, step: string): ServiceNetwork;
|
|
101
105
|
/** Where the project comes from: the platform's 'platform' project, or the org's own. */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NO_DOMAIN_ZONE } from './own-hostnames.js';
|
|
2
|
-
import { apexZoneKey, exactZone, platformDns, platformTunnels } from './platform-providers.js';
|
|
2
|
+
import { apexZoneKey, exactZone, platformDns, platformRequestHeaders, platformTunnels, } from './platform-providers.js';
|
|
3
3
|
/** The key the org's zone step records its Cloudflare zone id under. */
|
|
4
4
|
const ORG_ZONE_SCOPE_KEY = 'cloudflare_zone';
|
|
5
5
|
/** Applied-state key under which an own org's provisioned-services marker is recorded. */
|
|
@@ -123,6 +123,7 @@ export function platformNetwork(deps, scope, step) {
|
|
|
123
123
|
return {
|
|
124
124
|
dns: () => platformDns(deps, scope.recordedIds(), step),
|
|
125
125
|
tunnels: () => platformTunnels(deps, scope.recordedIds(), step),
|
|
126
|
+
requestHeaders: () => platformRequestHeaders(deps, scope.recordedIds()),
|
|
126
127
|
zoneId: async (apex) => scope.recordedIds()[apexZoneKey(apex)] ?? (await exactZone(deps.cloudflare, apex))?.id,
|
|
127
128
|
};
|
|
128
129
|
}
|
|
@@ -143,6 +144,7 @@ export function orgNetwork(ports, scope, step) {
|
|
|
143
144
|
throw new Error(`${step}: the host supplies no tunnel port for this org`);
|
|
144
145
|
return ports.tunnels;
|
|
145
146
|
},
|
|
147
|
+
requestHeaders: async () => ports.requestHeaders,
|
|
146
148
|
zoneId: async (apex) => {
|
|
147
149
|
// The recorded zone is the company's own; `oikos.host` is never it.
|
|
148
150
|
const recorded = apex === NO_DOMAIN_ZONE ? undefined : scope.recordedIds()[ORG_ZONE_SCOPE_KEY];
|
package/dist/plan/own-sfu.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { coolifyImageFor } from '../bootstrap/versions.js';
|
|
2
2
|
import { mintLivekitCredentials } from '../calls-sfu-keys.js';
|
|
3
3
|
import { LIVEKIT_SIGNALLING_PORT, sfuSlotPorts } from '../calls-sfu.js';
|
|
4
4
|
import { ensureCloudflaredConnector } from './cloudflared-connector.js';
|
|
@@ -105,8 +105,7 @@ export async function provisionSfuApp(scope, deps, target, input) {
|
|
|
105
105
|
server_uuid: server.uuid,
|
|
106
106
|
environment_name: environment.name,
|
|
107
107
|
environment_uuid: environment.uuid,
|
|
108
|
-
|
|
109
|
-
docker_registry_image_tag: pinnedTagFor('livekit', input.version),
|
|
108
|
+
...coolifyImageFor('livekit', OWN_SFU_IMAGE, input.version),
|
|
110
109
|
ports_exposes: String(LIVEKIT_SIGNALLING_PORT),
|
|
111
110
|
ports_mappings: sfuPortsMappings(input.slot),
|
|
112
111
|
name: target.names.sfuApp,
|
|
@@ -122,7 +121,7 @@ export async function provisionSfuApp(scope, deps, target, input) {
|
|
|
122
121
|
// Set on every run: a declared version or slot reaches an existing app here.
|
|
123
122
|
await coolify.updateApplication(appId, {
|
|
124
123
|
custom_internal_name: target.internal.sfu,
|
|
125
|
-
|
|
124
|
+
...coolifyImageFor('livekit', OWN_SFU_IMAGE, input.version),
|
|
126
125
|
ports_exposes: String(LIVEKIT_SIGNALLING_PORT),
|
|
127
126
|
ports_mappings: sfuPortsMappings(input.slot),
|
|
128
127
|
...healthCheckSettings('/', LIVEKIT_SIGNALLING_PORT),
|
|
@@ -59,6 +59,33 @@ export type PlatformTunnelPort = Pick<TunnelPort, 'findTunnelByName' | 'createTu
|
|
|
59
59
|
export declare function platformTunnels(deps: {
|
|
60
60
|
tunnels: PlatformTunnelsFactory;
|
|
61
61
|
}, recorded: Readonly<Record<string, string>>, step: string): Promise<PlatformTunnelPort>;
|
|
62
|
+
/**
|
|
63
|
+
* Request headers a client can set that ZITADEL reads to pick or name an
|
|
64
|
+
* instance (auth#241, auth#252). Cloudflare removes them from every inbound
|
|
65
|
+
* request on a sign-in hostname, because the tunnel routes ZITADEL's paths
|
|
66
|
+
* straight to ZITADEL and no app code sees them first.
|
|
67
|
+
*/
|
|
68
|
+
export declare const SIGNIN_STRIPPED_REQUEST_HEADERS: readonly ["X-Zitadel-Instance-Host", "X-Zitadel-Public-Host", "X-Zitadel-Forward-Host", "X-Forwarded-Host"];
|
|
69
|
+
/** One Cloudflare request-header Transform Rule (phase `http_request_late_transform`) that removes headers. */
|
|
70
|
+
export interface RequestHeaderStripRule {
|
|
71
|
+
/** Names the rule in the zone: the key that makes a repeat safe. */
|
|
72
|
+
description: string;
|
|
73
|
+
expression: string;
|
|
74
|
+
headers: readonly string[];
|
|
75
|
+
}
|
|
76
|
+
/** The Cloudflare request header rule calls a sign-in step makes. */
|
|
77
|
+
export interface RequestHeaderPort {
|
|
78
|
+
/** Makes the rule, or returns the one already made for that zone and description. */
|
|
79
|
+
ensureRequestHeaderStrip(zoneId: string, rule: RequestHeaderStripRule): Promise<{
|
|
80
|
+
id: string;
|
|
81
|
+
}>;
|
|
82
|
+
}
|
|
83
|
+
/** Like {@link PlatformTunnelsFactory}: builds the {@link RequestHeaderPort} at edge's recorded address. */
|
|
84
|
+
export type PlatformRequestHeadersFactory = (edgeUrl: string) => RequestHeaderPort | Promise<RequestHeaderPort>;
|
|
85
|
+
/** The request header port for a platform step; undefined when edge is not recorded or the host supplied no factory. */
|
|
86
|
+
export declare function platformRequestHeaders(deps: {
|
|
87
|
+
requestHeaders?: PlatformRequestHeadersFactory;
|
|
88
|
+
}, recorded: Readonly<Record<string, string>>): Promise<RequestHeaderPort | undefined>;
|
|
62
89
|
/**
|
|
63
90
|
* F4.access: builds the {@link AccessPort} a platform step finds, creates and
|
|
64
91
|
* deletes its Access app through, given the same address as
|
|
@@ -110,6 +137,11 @@ export interface PlatformAuthDeps {
|
|
|
110
137
|
dns: PlatformDnsFactory;
|
|
111
138
|
/** F4.tunnels: every tunnel call, through edge at its recorded address (see {@link PlatformTunnelsFactory}). */
|
|
112
139
|
tunnels: PlatformTunnelsFactory;
|
|
140
|
+
/**
|
|
141
|
+
* Request header rules through edge. Edge has no such call yet (@wtfalch/edge 0.13.0 offers
|
|
142
|
+
* rate limit rules only), so a host that leaves this out gets no rule and a logged line per run.
|
|
143
|
+
*/
|
|
144
|
+
requestHeaders?: PlatformRequestHeadersFactory;
|
|
113
145
|
cloudflare: Pick<CloudflarePort, 'findZoneByApex'>;
|
|
114
146
|
/** Must target the platform's own new instance (its issuer), or `zitadelForInstance` must be set. */
|
|
115
147
|
zitadel: Pick<ZitadelPort, 'findOrCreateOrg' | 'mintAdminMachineUser' | 'mintLoginClientMachineUser' | 'listLoginClientPats' | 'reserveUserName' | 'closeSelfRegistration' | 'enforceSecondFactor' | 'inviteOrgOwner' | 'ensureDomainPolicy' | 'addOrgDomain' | 'removeOrgDomain' | 'setPrimaryOrgDomain' | 'setUserMetadata' | 'getUserMetadata'> & PlatformZitadelSmtp;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash, createPrivateKey, generateKeyPairSync, randomBytes, randomUUID, sign, } from 'node:crypto';
|
|
2
2
|
import { isIPv4 } from 'node:net';
|
|
3
3
|
import { applyObjects, openInstance, platformMailSettingsObjects } from '@wtfalch/email/postmaster';
|
|
4
|
-
import { VERSIONS, floatingTagBlocked, pinnedRef
|
|
4
|
+
import { VERSIONS, coolifyImageFor, floatingTagBlocked, pinnedRef } from '../bootstrap/versions.js';
|
|
5
5
|
import { redactSecrets } from '../calls-sfu-deploy-support.js';
|
|
6
6
|
import { ownerMailboxes } from '../org.js';
|
|
7
7
|
import { BOULE_BRIDGE_SECRET_ENV, BOULE_PEER_PASSWORD_ENV, BOULE_PEER_ROLE, bouleHostnameFor, } from './boule.js';
|
|
@@ -53,6 +53,23 @@ export async function platformTunnels(deps, recorded, step) {
|
|
|
53
53
|
}
|
|
54
54
|
return deps.tunnels(edgeUrl);
|
|
55
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Request headers a client can set that ZITADEL reads to pick or name an
|
|
58
|
+
* instance (auth#241, auth#252). Cloudflare removes them from every inbound
|
|
59
|
+
* request on a sign-in hostname, because the tunnel routes ZITADEL's paths
|
|
60
|
+
* straight to ZITADEL and no app code sees them first.
|
|
61
|
+
*/
|
|
62
|
+
export const SIGNIN_STRIPPED_REQUEST_HEADERS = [
|
|
63
|
+
'X-Zitadel-Instance-Host',
|
|
64
|
+
'X-Zitadel-Public-Host',
|
|
65
|
+
'X-Zitadel-Forward-Host',
|
|
66
|
+
'X-Forwarded-Host',
|
|
67
|
+
];
|
|
68
|
+
/** The request header port for a platform step; undefined when edge is not recorded or the host supplied no factory. */
|
|
69
|
+
export async function platformRequestHeaders(deps, recorded) {
|
|
70
|
+
const edgeUrl = recorded[PLATFORM_EDGE_INTERNAL_URL_SCOPE_KEY];
|
|
71
|
+
return edgeUrl && deps.requestHeaders ? deps.requestHeaders(edgeUrl) : undefined;
|
|
72
|
+
}
|
|
56
73
|
/** The Access port for a platform step; same address and same reason as {@link platformDns}. */
|
|
57
74
|
async function platformAccess(deps, recorded, step) {
|
|
58
75
|
const edgeUrl = recorded[PLATFORM_EDGE_INTERNAL_URL_SCOPE_KEY];
|
|
@@ -1701,6 +1718,18 @@ export async function provisionAuthStack(scope, deps, target, input) {
|
|
|
1701
1718
|
if (!zoneId) {
|
|
1702
1719
|
throw new Error(`${label}: no Cloudflare zone for ${apex} to put ${hostname} in`);
|
|
1703
1720
|
}
|
|
1721
|
+
// Before the DNS record, so the hostname is never published without the rule.
|
|
1722
|
+
const requestHeaders = await network.requestHeaders();
|
|
1723
|
+
if (requestHeaders) {
|
|
1724
|
+
await requestHeaders.ensureRequestHeaderStrip(zoneId, {
|
|
1725
|
+
description: `strip spoofable sign-in headers: ${hostname}`,
|
|
1726
|
+
expression: `(http.host eq "${hostname}")`,
|
|
1727
|
+
headers: SIGNIN_STRIPPED_REQUEST_HEADERS,
|
|
1728
|
+
});
|
|
1729
|
+
}
|
|
1730
|
+
else {
|
|
1731
|
+
console.error(`${label}: no request header port, so the sign-in hostname keeps X-Zitadel-* and X-Forwarded-Host from clients`);
|
|
1732
|
+
}
|
|
1704
1733
|
const dnsTarget = `${tunnel.id}.cfargotunnel.com`;
|
|
1705
1734
|
const dns = await network.dns();
|
|
1706
1735
|
const [dnsRecord] = await dns.findDnsRecord(apex, hostname, 'CNAME');
|
|
@@ -2539,8 +2568,7 @@ export async function provisionMailServer(scope, deps, target, input) {
|
|
|
2539
2568
|
server_uuid: server.uuid,
|
|
2540
2569
|
environment_name: environment.name,
|
|
2541
2570
|
environment_uuid: environment.uuid,
|
|
2542
|
-
|
|
2543
|
-
docker_registry_image_tag: pinnedTagFor('stalwart', imageTag),
|
|
2571
|
+
...coolifyImageFor('stalwart', STALWART_IMAGE, imageTag),
|
|
2544
2572
|
ports_exposes: STALWART_PORTS_EXPOSES,
|
|
2545
2573
|
...(hostPorts ? { ports_mappings: hostPorts } : {}),
|
|
2546
2574
|
name: target.names.mailApp,
|
|
@@ -2560,7 +2588,7 @@ export async function provisionMailServer(scope, deps, target, input) {
|
|
|
2560
2588
|
// `'ready'` probe below. Stalwart's own readiness route and HTTP port.
|
|
2561
2589
|
await deps.coolify.updateApplication(appId, {
|
|
2562
2590
|
custom_internal_name: target.internal.mail,
|
|
2563
|
-
|
|
2591
|
+
...coolifyImageFor('stalwart', STALWART_IMAGE, imageTag),
|
|
2564
2592
|
...healthCheckSettings(STALWART_READY_PATH, Number(STALWART_HTTP_PORT)),
|
|
2565
2593
|
// Only a platform that uses the shared relay, or one that did, changes a published port.
|
|
2566
2594
|
...(portsChanged ? { ports_mappings: hostPorts } : {}),
|
|
@@ -7263,8 +7291,7 @@ async function provisionPlatformHc(context, deps) {
|
|
|
7263
7291
|
server_uuid: server.uuid,
|
|
7264
7292
|
environment_name: environment.name,
|
|
7265
7293
|
environment_uuid: environment.uuid,
|
|
7266
|
-
|
|
7267
|
-
docker_registry_image_tag: pinnedTagFor('hc', gitRef),
|
|
7294
|
+
...coolifyImageFor('hc', HC_IMAGE, gitRef),
|
|
7268
7295
|
ports_exposes: HC_PORT,
|
|
7269
7296
|
name: PLATFORM_HC_APP_NAME,
|
|
7270
7297
|
description: `Healthchecks for the platform's own heartbeats (#540, ${HC_IMAGE}:${gitRef})`,
|
|
@@ -7277,7 +7304,7 @@ async function provisionPlatformHc(context, deps) {
|
|
|
7277
7304
|
// own rule. The image tag too, so a declared release change reaches the app.
|
|
7278
7305
|
await deps.coolify.updateApplication(appId, {
|
|
7279
7306
|
custom_internal_name: HC_INTERNAL_NAME,
|
|
7280
|
-
|
|
7307
|
+
...coolifyImageFor('hc', HC_IMAGE, gitRef),
|
|
7281
7308
|
...healthCheckSettings(HC_HEALTH_CHECK_PATH, Number(HC_PORT)),
|
|
7282
7309
|
});
|
|
7283
7310
|
// ---- env, parsed off the database's own owner credentials -----------------
|