openship 0.6.6 → 0.6.8
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/bare-FA66N7GC.js +29 -0
- package/dist/{chunk-77QDF6UD.js → chunk-3MP6ACKM.js} +587 -156
- package/dist/{chunk-YIMOCZBZ.js → chunk-5DJOB7IX.js} +3 -3
- package/dist/{chunk-WVAML4H6.js → chunk-5I3COAFH.js} +274 -65
- package/dist/{chunk-5DMWSTOM.js → chunk-5UXHT6ED.js} +940 -224
- package/dist/{chunk-LUKYPVJ4.js → chunk-6GU6JSK6.js} +329 -466
- package/dist/{chunk-RSR7PFIQ.js → chunk-7PEXM2UI.js} +213 -45
- package/dist/{chunk-VTGPEC6B.js → chunk-7YBWXEM6.js} +6 -4
- package/dist/chunk-AHJYP7DG.js +938 -0
- package/dist/chunk-BO6CY4V2.js +265 -0
- package/dist/{chunk-FE7ZZQSE.js → chunk-D3OEMXYP.js} +3 -3
- package/dist/chunk-D6HU4VUZ.js +249 -0
- package/dist/{chunk-LFE5KQ7Y.js → chunk-ENKJ7XIF.js} +152 -57
- package/dist/{chunk-YMMQRE5J.js → chunk-GNOMEMNI.js} +1 -1
- package/dist/{chunk-QVW76WDL.js → chunk-IAMOUDHY.js} +190 -64
- package/dist/{chunk-OBPKYXQL.js → chunk-MUWVC5R4.js} +219 -58
- package/dist/chunk-N5YZMO33.js +111 -0
- package/dist/{chunk-A4GXYE4L.js → chunk-NIYD7MD2.js} +206 -52
- package/dist/{chunk-UVNMQM7W.js → chunk-NPSRGIHV.js} +8 -6
- package/dist/{chunk-2GWYYHOR.js → chunk-NRU2EBYZ.js} +2 -2
- package/dist/{chunk-CBLD2HS5.js → chunk-NUT6HKK6.js} +8 -5
- package/dist/{chunk-ZFJXPCQP.js → chunk-P7RCYELG.js} +4 -3
- package/dist/chunk-R4VDPFLS.js +106 -0
- package/dist/{chunk-HKRQXPTI.js → chunk-R5DC5PEF.js} +1 -1
- package/dist/{chunk-NK5N5LXN.js → chunk-RCMERVBL.js} +24 -9
- package/dist/{chunk-RXQBD4ZY.js → chunk-RHLNVZMS.js} +2 -2
- package/dist/chunk-SK4FFCFR.js +70 -0
- package/dist/{chunk-4I2BM2DT.js → chunk-T46BQOLY.js} +3 -3
- package/dist/{chunk-6FH4AXVY.js → chunk-T6DVUX4F.js} +1 -1
- package/dist/{chunk-PCPVTH6M.js → chunk-TAZZZKYO.js} +6 -4
- package/dist/{chunk-MLYSEZQB.js → chunk-UAWY6X7V.js} +43 -72
- package/dist/{chunk-Q7WGTQQ5.js → chunk-VAI5VOOI.js} +1 -1
- package/dist/{chunk-UJ6JZM65.js → chunk-WEZFTL4V.js} +1325 -513
- package/dist/{chunk-2YM3NI2E.js → chunk-X53JXRS4.js} +38 -44
- package/dist/{cloud-KRTZ2IL5.js → cloud-2CRDKOWQ.js} +1 -1
- package/dist/cloud-REV3DPJH.js +42 -0
- package/dist/{detect-OIE5V3VZ.js → detect-BBFT5GEU.js} +9 -6
- package/dist/{docker-VTR2GK5Y.js → docker-EG3QSE6I.js} +11 -8
- package/dist/{docker-edge-executor-22IBXNYJ.js → docker-edge-executor-E5TOTLMR.js} +8 -6
- package/dist/edge-import-YJVJUGYC.js +56 -0
- package/dist/{edge-preflight-GBNCPNVU.js → edge-preflight-QUW4BM7N.js} +15 -12
- package/dist/{ensure-container-edge-KPI2CVXJ.js → ensure-container-edge-FOEFQ3PI.js} +15 -10
- package/dist/{executor-7LXXZPBR.js → executor-NTWDJ3DB.js} +8 -6
- package/dist/host-channel-preflight-OMCZMMKR.js +54 -0
- package/dist/index.js +198 -76
- package/dist/{local-executor-WOFZD72X.js → local-executor-OQ2GRN7K.js} +3 -2
- package/dist/{nginx-KUFMLMAC.js → nginx-4U4C2XUS.js} +11 -8
- package/dist/{noop-NJ72F46T.js → noop-CLLJHCPL.js} +1 -1
- package/dist/{openresty-lua-ATTH35BE.js → openresty-lua-LLWT5XQW.js} +8 -5
- package/dist/{privilege-I4W3ZNZK.js → privilege-MNDVMDST.js} +6 -5
- package/dist/server/index.js +40049 -30378
- package/dist/server/migrations/0109_service_build_args.sql +9 -0
- package/dist/server/migrations/0110_service_deployment_host_ports.sql +18 -0
- package/dist/server/migrations/0111_host_port_claim.sql +381 -0
- package/dist/server/migrations/0112_backup_run_execution_lease.sql +12 -0
- package/dist/server/migrations/0113_docker_migration_execution_lease.sql +11 -0
- package/dist/server/migrations/0114_build_session_execution_lease_index.sql +6 -0
- package/dist/server/migrations/0115_orphan_target_key.sql +3 -0
- package/dist/server/migrations/0116_domain_ssl_challenge.sql +2 -0
- package/dist/server/migrations/meta/_journal.json +56 -0
- package/dist/setup-IFIMPQZJ.js +25 -0
- package/dist/{ssh-client-JBMA7SN4.js → ssh-client-2XWZCMGI.js} +3 -2
- package/dist/up-HQS7IR7H.js +61 -0
- package/package.json +1 -1
- package/dist/bare-OXJVW3AK.js +0 -25
- package/dist/chunk-LH2PLRQO.js +0 -434
- package/dist/chunk-SM26HDS2.js +0 -185
- package/dist/cloud-75VIT4KV.js +0 -37
- package/dist/edge-import-WJUB4M7F.js +0 -52
- package/dist/host-channel-preflight-KE6OONYK.js +0 -50
- package/dist/setup-XGLGUUFW.js +0 -22
- package/dist/up-K4BV7466.js +0 -57
|
@@ -6,6 +6,57 @@ import {
|
|
|
6
6
|
__export
|
|
7
7
|
} from "./chunk-PXKCZB2I.js";
|
|
8
8
|
|
|
9
|
+
// ../../packages/core/src/shell-split.ts
|
|
10
|
+
function shellQuote(value) {
|
|
11
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
12
|
+
}
|
|
13
|
+
function shellSplitWords(value) {
|
|
14
|
+
const words = [];
|
|
15
|
+
let current = "";
|
|
16
|
+
let quoted = false;
|
|
17
|
+
let inSingle = false;
|
|
18
|
+
let inDouble = false;
|
|
19
|
+
let escaped = false;
|
|
20
|
+
for (const char of value.trim()) {
|
|
21
|
+
if (escaped) {
|
|
22
|
+
current += char;
|
|
23
|
+
escaped = false;
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (char === "\\" && !inSingle) {
|
|
27
|
+
escaped = true;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (char === "'" && !inDouble) {
|
|
31
|
+
inSingle = !inSingle;
|
|
32
|
+
quoted = true;
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (char === '"' && !inSingle) {
|
|
36
|
+
inDouble = !inDouble;
|
|
37
|
+
quoted = true;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (/\s/.test(char) && !inSingle && !inDouble) {
|
|
41
|
+
if (current || quoted) {
|
|
42
|
+
words.push(current);
|
|
43
|
+
current = "";
|
|
44
|
+
quoted = false;
|
|
45
|
+
}
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
current += char;
|
|
49
|
+
}
|
|
50
|
+
if (escaped) current += "\\";
|
|
51
|
+
if (current || quoted) words.push(current);
|
|
52
|
+
return words;
|
|
53
|
+
}
|
|
54
|
+
function commandToArgv(command) {
|
|
55
|
+
if (command == null) return null;
|
|
56
|
+
if (Array.isArray(command)) return command.map((part) => String(part));
|
|
57
|
+
return shellSplitWords(command);
|
|
58
|
+
}
|
|
59
|
+
|
|
9
60
|
// ../../packages/core/src/stacks.ts
|
|
10
61
|
var LANGUAGES = {
|
|
11
62
|
javascript: {
|
|
@@ -831,6 +882,21 @@ function packageManagerEnsureCommand(packageManager) {
|
|
|
831
882
|
if (packageManager !== "pnpm" && packageManager !== "yarn") return "";
|
|
832
883
|
return `(corepack enable ${packageManager} || corepack enable || npm i -g ${packageManager}) >/dev/null 2>&1 || true`;
|
|
833
884
|
}
|
|
885
|
+
var NODE_BIN_PACKAGE_MANAGERS = /* @__PURE__ */ new Set(["npm", "yarn", "pnpm", "bun"]);
|
|
886
|
+
function nodeBinDirs(packageManager, roots) {
|
|
887
|
+
if (!packageManager || !NODE_BIN_PACKAGE_MANAGERS.has(packageManager)) return [];
|
|
888
|
+
const dirs = [];
|
|
889
|
+
for (const root of roots) {
|
|
890
|
+
const dir = `${root.replace(/\/+$/, "")}/node_modules/.bin`;
|
|
891
|
+
if (!dirs.includes(dir)) dirs.push(dir);
|
|
892
|
+
}
|
|
893
|
+
return dirs;
|
|
894
|
+
}
|
|
895
|
+
function nodeBinPathExport(packageManager, roots) {
|
|
896
|
+
const dirs = nodeBinDirs(packageManager, roots);
|
|
897
|
+
if (dirs.length === 0) return "";
|
|
898
|
+
return `export PATH=${dirs.map(shellQuote).join(":")}:"$PATH"`;
|
|
899
|
+
}
|
|
834
900
|
var DI = "https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons";
|
|
835
901
|
var STACK_ICONS = {
|
|
836
902
|
// JS/TS - Frontend & Fullstack
|
|
@@ -951,6 +1017,65 @@ function ownsNetworkEndpoint(resolvedNetworkMode) {
|
|
|
951
1017
|
return !resolvedNetworkMode;
|
|
952
1018
|
}
|
|
953
1019
|
|
|
1020
|
+
// ../../packages/core/src/utils.ts
|
|
1021
|
+
var REDIRECT_STATUSES = [301, 302, 307, 308];
|
|
1022
|
+
var DEFAULT_REDIRECT_STATUS = 301;
|
|
1023
|
+
function resolveRedirectStatus(status) {
|
|
1024
|
+
return REDIRECT_STATUSES.includes(status) ? status : DEFAULT_REDIRECT_STATUS;
|
|
1025
|
+
}
|
|
1026
|
+
function isValidEmail(value) {
|
|
1027
|
+
const email3 = value?.trim() ?? "";
|
|
1028
|
+
if (!email3 || email3.length > 254) return false;
|
|
1029
|
+
return /^[A-Za-z0-9._%+-]+@[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/.test(email3);
|
|
1030
|
+
}
|
|
1031
|
+
function formatBytes(bytes, decimals = 2) {
|
|
1032
|
+
if (bytes <= 0) return "0 B";
|
|
1033
|
+
const k = 1024;
|
|
1034
|
+
const sizes = ["B", "KB", "MB", "GB", "TB"];
|
|
1035
|
+
const i = Math.min(Math.max(Math.floor(Math.log(bytes) / Math.log(k)), 0), sizes.length - 1);
|
|
1036
|
+
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(decimals))} ${sizes[i]}`;
|
|
1037
|
+
}
|
|
1038
|
+
function withTimeout(promise2, ms, message) {
|
|
1039
|
+
if (!ms) return promise2;
|
|
1040
|
+
return new Promise((resolve, reject) => {
|
|
1041
|
+
const timer = setTimeout(() => reject(new Error(message)), ms);
|
|
1042
|
+
promise2.then(
|
|
1043
|
+
(v) => {
|
|
1044
|
+
clearTimeout(timer);
|
|
1045
|
+
resolve(v);
|
|
1046
|
+
},
|
|
1047
|
+
(e) => {
|
|
1048
|
+
clearTimeout(timer);
|
|
1049
|
+
reject(e);
|
|
1050
|
+
}
|
|
1051
|
+
);
|
|
1052
|
+
});
|
|
1053
|
+
}
|
|
1054
|
+
function isValidEnvKey(key) {
|
|
1055
|
+
return /^[A-Za-z_][A-Za-z0-9_]*$/.test(key);
|
|
1056
|
+
}
|
|
1057
|
+
function wrapText(text, width = 88) {
|
|
1058
|
+
const out = [];
|
|
1059
|
+
for (const paragraph of text.split("\n")) {
|
|
1060
|
+
const words = paragraph.split(/\s+/).filter(Boolean);
|
|
1061
|
+
if (words.length === 0) {
|
|
1062
|
+
out.push("");
|
|
1063
|
+
continue;
|
|
1064
|
+
}
|
|
1065
|
+
let line = "";
|
|
1066
|
+
for (const word of words) {
|
|
1067
|
+
if (line && line.length + 1 + word.length > width) {
|
|
1068
|
+
out.push(line);
|
|
1069
|
+
line = word;
|
|
1070
|
+
} else {
|
|
1071
|
+
line = line ? `${line} ${word}` : word;
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
if (line) out.push(line);
|
|
1075
|
+
}
|
|
1076
|
+
return out;
|
|
1077
|
+
}
|
|
1078
|
+
|
|
954
1079
|
// ../../packages/core/src/constants.ts
|
|
955
1080
|
var DEFAULT_IMAGE_REGISTRY = "ghcr.io/oblien";
|
|
956
1081
|
|
|
@@ -1133,62 +1258,6 @@ var SYSTEM = {
|
|
|
1133
1258
|
}
|
|
1134
1259
|
};
|
|
1135
1260
|
|
|
1136
|
-
// ../../packages/core/src/utils.ts
|
|
1137
|
-
var REDIRECT_STATUSES = [301, 302, 307, 308];
|
|
1138
|
-
var DEFAULT_REDIRECT_STATUS = 301;
|
|
1139
|
-
function resolveRedirectStatus(status) {
|
|
1140
|
-
return REDIRECT_STATUSES.includes(status) ? status : DEFAULT_REDIRECT_STATUS;
|
|
1141
|
-
}
|
|
1142
|
-
function isValidEmail(value) {
|
|
1143
|
-
const email3 = value?.trim() ?? "";
|
|
1144
|
-
if (!email3 || email3.length > 254) return false;
|
|
1145
|
-
return /^[A-Za-z0-9._%+-]+@[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/.test(email3);
|
|
1146
|
-
}
|
|
1147
|
-
function formatBytes(bytes, decimals = 2) {
|
|
1148
|
-
if (bytes <= 0) return "0 B";
|
|
1149
|
-
const k = 1024;
|
|
1150
|
-
const sizes = ["B", "KB", "MB", "GB", "TB"];
|
|
1151
|
-
const i = Math.min(Math.max(Math.floor(Math.log(bytes) / Math.log(k)), 0), sizes.length - 1);
|
|
1152
|
-
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(decimals))} ${sizes[i]}`;
|
|
1153
|
-
}
|
|
1154
|
-
function withTimeout(promise2, ms, message) {
|
|
1155
|
-
if (!ms) return promise2;
|
|
1156
|
-
return new Promise((resolve, reject) => {
|
|
1157
|
-
const timer = setTimeout(() => reject(new Error(message)), ms);
|
|
1158
|
-
promise2.then(
|
|
1159
|
-
(v) => {
|
|
1160
|
-
clearTimeout(timer);
|
|
1161
|
-
resolve(v);
|
|
1162
|
-
},
|
|
1163
|
-
(e) => {
|
|
1164
|
-
clearTimeout(timer);
|
|
1165
|
-
reject(e);
|
|
1166
|
-
}
|
|
1167
|
-
);
|
|
1168
|
-
});
|
|
1169
|
-
}
|
|
1170
|
-
function wrapText(text, width = 88) {
|
|
1171
|
-
const out = [];
|
|
1172
|
-
for (const paragraph of text.split("\n")) {
|
|
1173
|
-
const words = paragraph.split(/\s+/).filter(Boolean);
|
|
1174
|
-
if (words.length === 0) {
|
|
1175
|
-
out.push("");
|
|
1176
|
-
continue;
|
|
1177
|
-
}
|
|
1178
|
-
let line = "";
|
|
1179
|
-
for (const word of words) {
|
|
1180
|
-
if (line && line.length + 1 + word.length > width) {
|
|
1181
|
-
out.push(line);
|
|
1182
|
-
line = word;
|
|
1183
|
-
} else {
|
|
1184
|
-
line = line ? `${line} ${word}` : word;
|
|
1185
|
-
}
|
|
1186
|
-
}
|
|
1187
|
-
if (line) out.push(line);
|
|
1188
|
-
}
|
|
1189
|
-
return out;
|
|
1190
|
-
}
|
|
1191
|
-
|
|
1192
1261
|
// ../../packages/core/src/errors.ts
|
|
1193
1262
|
var AppError = class extends Error {
|
|
1194
1263
|
constructor(message, statusCode = 500, code) {
|
|
@@ -2482,6 +2551,104 @@ function hostFirewallRuleRemoval(firewall, cidrs, port) {
|
|
|
2482
2551
|
|
|
2483
2552
|
// ../../packages/core/src/compose-spec.ts
|
|
2484
2553
|
var asIs = (s) => s;
|
|
2554
|
+
var REMOTE_BUILD_CONTEXT_RE = /^(?:[A-Za-z][A-Za-z0-9+.-]*:\/\/|[^@\s]+@[^:\s]+:)/;
|
|
2555
|
+
var ABSOLUTE_BUILD_CONTEXT_RE = /^(?:[\\/]|~(?:[\\/]|$)|[A-Za-z]:[\\/]|\\\\)/;
|
|
2556
|
+
function composeOptionRequestsSomething(value) {
|
|
2557
|
+
if (value === void 0 || value === null || value === false || value === "" || value === 0) {
|
|
2558
|
+
return false;
|
|
2559
|
+
}
|
|
2560
|
+
if (Array.isArray(value)) return value.length > 0;
|
|
2561
|
+
if (typeof value === "object") return Object.keys(value).length > 0;
|
|
2562
|
+
return true;
|
|
2563
|
+
}
|
|
2564
|
+
function composeBuildIssues(build, options = {}) {
|
|
2565
|
+
if (build === void 0 || build === null) return [];
|
|
2566
|
+
const issues = [];
|
|
2567
|
+
const block = (field, reason) => issues.push({ field, reason, blocking: true });
|
|
2568
|
+
const validateContext = (context) => {
|
|
2569
|
+
const resolved = (options.interpolate?.(context) ?? context).trim();
|
|
2570
|
+
if (!resolved) {
|
|
2571
|
+
block("build.context", "build.context must not be empty.");
|
|
2572
|
+
} else if (REMOTE_BUILD_CONTEXT_RE.test(resolved)) {
|
|
2573
|
+
block(
|
|
2574
|
+
"build.context",
|
|
2575
|
+
"build.context uses a remote URL, which Openship cannot build as a repository-relative context."
|
|
2576
|
+
);
|
|
2577
|
+
} else if (!options.allowAbsoluteContext && ABSOLUTE_BUILD_CONTEXT_RE.test(resolved)) {
|
|
2578
|
+
block(
|
|
2579
|
+
"build.context",
|
|
2580
|
+
"build.context is absolute or home-relative; Openship requires a path inside the linked repository."
|
|
2581
|
+
);
|
|
2582
|
+
}
|
|
2583
|
+
};
|
|
2584
|
+
if (typeof build === "string") {
|
|
2585
|
+
validateContext(build);
|
|
2586
|
+
return issues;
|
|
2587
|
+
}
|
|
2588
|
+
if (typeof build !== "object" || Array.isArray(build)) {
|
|
2589
|
+
block("build", "build must be a context string or an object.");
|
|
2590
|
+
return issues;
|
|
2591
|
+
}
|
|
2592
|
+
const value = build;
|
|
2593
|
+
if (value.context !== void 0) {
|
|
2594
|
+
if (typeof value.context !== "string") {
|
|
2595
|
+
block("build.context", "build.context must be a string.");
|
|
2596
|
+
} else {
|
|
2597
|
+
validateContext(value.context);
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
if (value.dockerfile !== void 0) {
|
|
2601
|
+
if (typeof value.dockerfile !== "string") {
|
|
2602
|
+
block("build.dockerfile", "build.dockerfile must be a string.");
|
|
2603
|
+
} else {
|
|
2604
|
+
const dockerfile = (options.interpolate?.(value.dockerfile) ?? value.dockerfile).trim();
|
|
2605
|
+
if (!dockerfile) {
|
|
2606
|
+
block("build.dockerfile", "build.dockerfile must not be empty.");
|
|
2607
|
+
} else if (REMOTE_BUILD_CONTEXT_RE.test(dockerfile) || ABSOLUTE_BUILD_CONTEXT_RE.test(dockerfile) || dockerfile.split(/[\\/]/).includes("..")) {
|
|
2608
|
+
block("build.dockerfile", "build.dockerfile must be a path inside its build context.");
|
|
2609
|
+
}
|
|
2610
|
+
}
|
|
2611
|
+
}
|
|
2612
|
+
const args = value.args;
|
|
2613
|
+
if (args !== void 0) {
|
|
2614
|
+
if (Array.isArray(args)) {
|
|
2615
|
+
args.forEach((entry, index) => {
|
|
2616
|
+
if (typeof entry !== "string") {
|
|
2617
|
+
block(`build.args[${index}]`, "build.args list entries must be strings.");
|
|
2618
|
+
return;
|
|
2619
|
+
}
|
|
2620
|
+
const resolvedEntry = options.interpolate?.(entry) ?? entry;
|
|
2621
|
+
const equals = resolvedEntry.indexOf("=");
|
|
2622
|
+
const key = (equals >= 0 ? resolvedEntry.slice(0, equals) : resolvedEntry).trim();
|
|
2623
|
+
if (!isValidEnvKey(key)) {
|
|
2624
|
+
block(`build.args[${index}]`, "build.args contains an invalid argument name.");
|
|
2625
|
+
}
|
|
2626
|
+
});
|
|
2627
|
+
} else if (args && typeof args === "object") {
|
|
2628
|
+
for (const [key, argValue] of Object.entries(args)) {
|
|
2629
|
+
if (!isValidEnvKey(key)) {
|
|
2630
|
+
block("build.args", "build.args contains an invalid argument name.");
|
|
2631
|
+
}
|
|
2632
|
+
if (argValue !== null && argValue !== void 0 && !["string", "number", "boolean"].includes(typeof argValue)) {
|
|
2633
|
+
block(`build.args.${key}`, "build.args values must be scalar or null.");
|
|
2634
|
+
}
|
|
2635
|
+
}
|
|
2636
|
+
} else {
|
|
2637
|
+
block("build.args", "build.args must be a list of strings or a scalar/null map.");
|
|
2638
|
+
}
|
|
2639
|
+
}
|
|
2640
|
+
const modeled = /* @__PURE__ */ new Set(["context", "dockerfile", "args"]);
|
|
2641
|
+
for (const [key, optionValue] of Object.entries(value)) {
|
|
2642
|
+
if (modeled.has(key) || /^x-/i.test(key) || !composeOptionRequestsSomething(optionValue)) {
|
|
2643
|
+
continue;
|
|
2644
|
+
}
|
|
2645
|
+
block(
|
|
2646
|
+
`build.${key}`,
|
|
2647
|
+
`build.${key} is not supported; ignoring it could change the produced image or omit requested build behavior.`
|
|
2648
|
+
);
|
|
2649
|
+
}
|
|
2650
|
+
return issues;
|
|
2651
|
+
}
|
|
2485
2652
|
function composeMountToSpec(mount, interpolate = asIs) {
|
|
2486
2653
|
const source = mount.source ?? mount.name;
|
|
2487
2654
|
const target = mount.target;
|
|
@@ -2561,57 +2728,6 @@ function isAwsS3Endpoint(endpoint2) {
|
|
|
2561
2728
|
return !endpoint2 || endpoint2.includes(".amazonaws.com");
|
|
2562
2729
|
}
|
|
2563
2730
|
|
|
2564
|
-
// ../../packages/core/src/shell-split.ts
|
|
2565
|
-
function shellQuote(value) {
|
|
2566
|
-
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
2567
|
-
}
|
|
2568
|
-
function shellSplitWords(value) {
|
|
2569
|
-
const words = [];
|
|
2570
|
-
let current = "";
|
|
2571
|
-
let quoted = false;
|
|
2572
|
-
let inSingle = false;
|
|
2573
|
-
let inDouble = false;
|
|
2574
|
-
let escaped = false;
|
|
2575
|
-
for (const char of value.trim()) {
|
|
2576
|
-
if (escaped) {
|
|
2577
|
-
current += char;
|
|
2578
|
-
escaped = false;
|
|
2579
|
-
continue;
|
|
2580
|
-
}
|
|
2581
|
-
if (char === "\\" && !inSingle) {
|
|
2582
|
-
escaped = true;
|
|
2583
|
-
continue;
|
|
2584
|
-
}
|
|
2585
|
-
if (char === "'" && !inDouble) {
|
|
2586
|
-
inSingle = !inSingle;
|
|
2587
|
-
quoted = true;
|
|
2588
|
-
continue;
|
|
2589
|
-
}
|
|
2590
|
-
if (char === '"' && !inSingle) {
|
|
2591
|
-
inDouble = !inDouble;
|
|
2592
|
-
quoted = true;
|
|
2593
|
-
continue;
|
|
2594
|
-
}
|
|
2595
|
-
if (/\s/.test(char) && !inSingle && !inDouble) {
|
|
2596
|
-
if (current || quoted) {
|
|
2597
|
-
words.push(current);
|
|
2598
|
-
current = "";
|
|
2599
|
-
quoted = false;
|
|
2600
|
-
}
|
|
2601
|
-
continue;
|
|
2602
|
-
}
|
|
2603
|
-
current += char;
|
|
2604
|
-
}
|
|
2605
|
-
if (escaped) current += "\\";
|
|
2606
|
-
if (current || quoted) words.push(current);
|
|
2607
|
-
return words;
|
|
2608
|
-
}
|
|
2609
|
-
function commandToArgv(command) {
|
|
2610
|
-
if (command == null) return null;
|
|
2611
|
-
if (Array.isArray(command)) return command.map((part) => String(part));
|
|
2612
|
-
return shellSplitWords(command);
|
|
2613
|
-
}
|
|
2614
|
-
|
|
2615
2731
|
// ../../packages/core/src/mail-image-ref.ts
|
|
2616
2732
|
function buildMailImageRef(opts) {
|
|
2617
2733
|
return buildImageRef(opts, {
|
|
@@ -2621,6 +2737,284 @@ function buildMailImageRef(opts) {
|
|
|
2621
2737
|
});
|
|
2622
2738
|
}
|
|
2623
2739
|
|
|
2740
|
+
// ../../packages/core/src/backup-catalog.ts
|
|
2741
|
+
var PAYLOAD_KINDS = [
|
|
2742
|
+
"pg_dump",
|
|
2743
|
+
"mysql_dump",
|
|
2744
|
+
"redis_rdb",
|
|
2745
|
+
"mongo_dump",
|
|
2746
|
+
"custom_command",
|
|
2747
|
+
"path",
|
|
2748
|
+
"volume"
|
|
2749
|
+
];
|
|
2750
|
+
var PAYLOAD_CONFIG_KEYS = [
|
|
2751
|
+
"sourceIds",
|
|
2752
|
+
"exclude",
|
|
2753
|
+
"quiesce",
|
|
2754
|
+
"compression",
|
|
2755
|
+
"paths",
|
|
2756
|
+
"clearPath",
|
|
2757
|
+
"produceCommand",
|
|
2758
|
+
"command",
|
|
2759
|
+
"restoreCommand",
|
|
2760
|
+
"artifactName"
|
|
2761
|
+
];
|
|
2762
|
+
var UNIVERSAL_PAYLOAD_CONFIG_KEYS = ["verifyOnPrepare", "mail"];
|
|
2763
|
+
var ALL_PAYLOAD_CONFIG_KEYS = [
|
|
2764
|
+
...PAYLOAD_CONFIG_KEYS,
|
|
2765
|
+
...UNIVERSAL_PAYLOAD_CONFIG_KEYS
|
|
2766
|
+
];
|
|
2767
|
+
var HOUR_MS = 60 * 60 * 1e3;
|
|
2768
|
+
var BACKUP_PAYLOADS = {
|
|
2769
|
+
pg_dump: {
|
|
2770
|
+
kind: "pg_dump",
|
|
2771
|
+
label: "PostgreSQL",
|
|
2772
|
+
method: "pg_dump",
|
|
2773
|
+
shape: "database",
|
|
2774
|
+
images: { names: ["postgres", "postgis/postgis"] },
|
|
2775
|
+
restoreNeedsLiveContainer: true,
|
|
2776
|
+
restoreAppliesAfterBounce: false,
|
|
2777
|
+
restoreTimeoutMs: HOUR_MS,
|
|
2778
|
+
// Inert: `pg_restore --clean --if-exists` owns replacement.
|
|
2779
|
+
restoreClearsTargetByDefault: false,
|
|
2780
|
+
// The only kind that can claim this, and only since `producers/pg-dump.ts` restores
|
|
2781
|
+
// under `--single-transaction`. Before that flag, a DROP blocked by a dependent view
|
|
2782
|
+
// was skipped and the archive's rows were loaded ON TOP of the live ones — one table
|
|
2783
|
+
// holding a merge of two points in time, reported as "errors ignored on restore".
|
|
2784
|
+
restoreFailureLeavesTargetIntact: true,
|
|
2785
|
+
requiresRestoreCommand: false,
|
|
2786
|
+
operatorSelectable: true,
|
|
2787
|
+
configKeys: []
|
|
2788
|
+
},
|
|
2789
|
+
mysql_dump: {
|
|
2790
|
+
kind: "mysql_dump",
|
|
2791
|
+
label: "MySQL/MariaDB",
|
|
2792
|
+
method: "mysqldump",
|
|
2793
|
+
shape: "database",
|
|
2794
|
+
// `percona/percona-server` and `percona/percona-server-mongodb` are kept apart by
|
|
2795
|
+
// the `(:|$)` anchor: what follows `percona-server` in the MongoDB image is `-`,
|
|
2796
|
+
// which is neither a tag separator nor the end of the string.
|
|
2797
|
+
images: { names: ["mysql", "mariadb", "percona/percona-server"] },
|
|
2798
|
+
restoreNeedsLiveContainer: true,
|
|
2799
|
+
restoreAppliesAfterBounce: false,
|
|
2800
|
+
restoreTimeoutMs: HOUR_MS,
|
|
2801
|
+
restoreClearsTargetByDefault: false,
|
|
2802
|
+
// MySQL has no transactional DDL: replaying a dump commits each statement as it
|
|
2803
|
+
// goes, so a failure halfway leaves half the schema and part of the data.
|
|
2804
|
+
restoreFailureLeavesTargetIntact: false,
|
|
2805
|
+
requiresRestoreCommand: false,
|
|
2806
|
+
operatorSelectable: true,
|
|
2807
|
+
configKeys: []
|
|
2808
|
+
},
|
|
2809
|
+
redis_rdb: {
|
|
2810
|
+
kind: "redis_rdb",
|
|
2811
|
+
label: "Redis",
|
|
2812
|
+
method: "RDB snapshot",
|
|
2813
|
+
shape: "database",
|
|
2814
|
+
images: { names: ["redis"], namespaces: ["redis"] },
|
|
2815
|
+
restoreNeedsLiveContainer: true,
|
|
2816
|
+
// The one kind whose write is inert until the service restarts. See the field doc.
|
|
2817
|
+
restoreAppliesAfterBounce: true,
|
|
2818
|
+
restoreTimeoutMs: 30 * 60 * 1e3,
|
|
2819
|
+
restoreClearsTargetByDefault: false,
|
|
2820
|
+
// The write is a stream into `dump.rdb`. A failure halfway leaves a TRUNCATED
|
|
2821
|
+
// snapshot — which the running server has not read (see restoreAppliesAfterBounce)
|
|
2822
|
+
// but the next start will, and refuse.
|
|
2823
|
+
restoreFailureLeavesTargetIntact: false,
|
|
2824
|
+
requiresRestoreCommand: false,
|
|
2825
|
+
operatorSelectable: true,
|
|
2826
|
+
configKeys: []
|
|
2827
|
+
},
|
|
2828
|
+
mongo_dump: {
|
|
2829
|
+
kind: "mongo_dump",
|
|
2830
|
+
label: "MongoDB",
|
|
2831
|
+
method: "mongodump",
|
|
2832
|
+
shape: "database",
|
|
2833
|
+
images: { names: ["mongo", "percona/percona-server-mongodb"] },
|
|
2834
|
+
restoreNeedsLiveContainer: true,
|
|
2835
|
+
restoreAppliesAfterBounce: false,
|
|
2836
|
+
restoreTimeoutMs: HOUR_MS,
|
|
2837
|
+
restoreClearsTargetByDefault: false,
|
|
2838
|
+
// `mongorestore --drop` drops and reloads per collection, so a failure between two
|
|
2839
|
+
// collections leaves one restored and the next dropped.
|
|
2840
|
+
restoreFailureLeavesTargetIntact: false,
|
|
2841
|
+
requiresRestoreCommand: false,
|
|
2842
|
+
operatorSelectable: true,
|
|
2843
|
+
configKeys: []
|
|
2844
|
+
},
|
|
2845
|
+
custom_command: {
|
|
2846
|
+
kind: "custom_command",
|
|
2847
|
+
label: "Custom command",
|
|
2848
|
+
method: "your command",
|
|
2849
|
+
shape: "opaque",
|
|
2850
|
+
restoreNeedsLiveContainer: true,
|
|
2851
|
+
restoreAppliesAfterBounce: false,
|
|
2852
|
+
restoreTimeoutMs: HOUR_MS,
|
|
2853
|
+
// Inert: the operator's own inverse command decides what it replaces.
|
|
2854
|
+
restoreClearsTargetByDefault: false,
|
|
2855
|
+
// We cannot reason about someone else's command. `opaque` in, opaque out.
|
|
2856
|
+
restoreFailureLeavesTargetIntact: false,
|
|
2857
|
+
requiresRestoreCommand: true,
|
|
2858
|
+
operatorSelectable: true,
|
|
2859
|
+
configKeys: ["produceCommand", "command", "restoreCommand", "artifactName"]
|
|
2860
|
+
},
|
|
2861
|
+
path: {
|
|
2862
|
+
kind: "path",
|
|
2863
|
+
label: "Files and folders",
|
|
2864
|
+
method: "tar",
|
|
2865
|
+
shape: "filesystem",
|
|
2866
|
+
// Explicit-only: a path is something the operator names. Nothing about an image
|
|
2867
|
+
// says "back up /var/www", and guessing would either miss the data or archive an
|
|
2868
|
+
// OS.
|
|
2869
|
+
restoreNeedsLiveContainer: true,
|
|
2870
|
+
restoreAppliesAfterBounce: false,
|
|
2871
|
+
// Matches the docker helper's own absolute ceiling for the volume directions, so
|
|
2872
|
+
// one number covers all three: a tree big enough to need hours to capture needs
|
|
2873
|
+
// hours to restore.
|
|
2874
|
+
restoreTimeoutMs: 6 * HOUR_MS,
|
|
2875
|
+
// Merge, unless the policy sets `clearPath`. A folder is not necessarily this
|
|
2876
|
+
// artifact's alone.
|
|
2877
|
+
restoreClearsTargetByDefault: false,
|
|
2878
|
+
// `tar -x` writes file by file: a failure halfway leaves the tree part-updated,
|
|
2879
|
+
// and with `clearPath` set it leaves it part-updated on top of an emptied folder.
|
|
2880
|
+
restoreFailureLeavesTargetIntact: false,
|
|
2881
|
+
requiresRestoreCommand: false,
|
|
2882
|
+
operatorSelectable: true,
|
|
2883
|
+
configKeys: ["paths", "exclude", "compression", "clearPath"]
|
|
2884
|
+
},
|
|
2885
|
+
volume: {
|
|
2886
|
+
kind: "volume",
|
|
2887
|
+
label: "Volume snapshot",
|
|
2888
|
+
method: "tar",
|
|
2889
|
+
shape: "filesystem",
|
|
2890
|
+
// No `images`: this is the fallback the registry uses when nothing else detects,
|
|
2891
|
+
// not a match on anything.
|
|
2892
|
+
//
|
|
2893
|
+
// A volume restore extracts through a SEPARATE helper container mounted on the
|
|
2894
|
+
// volume, so it is the only kind that can restore into a service whose container
|
|
2895
|
+
// is gone — or that never deployed.
|
|
2896
|
+
restoreNeedsLiveContainer: false,
|
|
2897
|
+
restoreAppliesAfterBounce: false,
|
|
2898
|
+
restoreTimeoutMs: 6 * HOUR_MS,
|
|
2899
|
+
// Replace: a volume is a single-purpose store, so a merge would leave files this
|
|
2900
|
+
// artifact never held.
|
|
2901
|
+
restoreClearsTargetByDefault: true,
|
|
2902
|
+
// Clear-then-extract, and the clear happens in the helper's PRELUDE — so by the
|
|
2903
|
+
// time any error escapes, the volume is already empty. This is the case the
|
|
2904
|
+
// orchestrator's old inference was built around, and it is still exactly right.
|
|
2905
|
+
restoreFailureLeavesTargetIntact: false,
|
|
2906
|
+
requiresRestoreCommand: false,
|
|
2907
|
+
operatorSelectable: true,
|
|
2908
|
+
configKeys: ["sourceIds", "exclude", "quiesce", "compression"]
|
|
2909
|
+
}
|
|
2910
|
+
};
|
|
2911
|
+
function payloadSpec(kind) {
|
|
2912
|
+
const spec = BACKUP_PAYLOADS[kind];
|
|
2913
|
+
if (!spec) {
|
|
2914
|
+
throw new Error(
|
|
2915
|
+
`Unknown backup payload kind "${kind}". Known kinds: ${PAYLOAD_KINDS.join(", ")}.`
|
|
2916
|
+
);
|
|
2917
|
+
}
|
|
2918
|
+
return spec;
|
|
2919
|
+
}
|
|
2920
|
+
function payloadSpecs() {
|
|
2921
|
+
return PAYLOAD_KINDS.map((k) => BACKUP_PAYLOADS[k]);
|
|
2922
|
+
}
|
|
2923
|
+
function imageDetectableSpecs() {
|
|
2924
|
+
return payloadSpecs().filter(
|
|
2925
|
+
(s) => !!s.images
|
|
2926
|
+
);
|
|
2927
|
+
}
|
|
2928
|
+
var SYSTEM_ROOTS = [
|
|
2929
|
+
"/bin",
|
|
2930
|
+
"/boot",
|
|
2931
|
+
"/dev",
|
|
2932
|
+
"/etc",
|
|
2933
|
+
"/lib",
|
|
2934
|
+
"/lib32",
|
|
2935
|
+
"/lib64",
|
|
2936
|
+
"/libx32",
|
|
2937
|
+
"/proc",
|
|
2938
|
+
"/root",
|
|
2939
|
+
"/run",
|
|
2940
|
+
"/sbin",
|
|
2941
|
+
"/sys",
|
|
2942
|
+
"/usr",
|
|
2943
|
+
"/var"
|
|
2944
|
+
];
|
|
2945
|
+
function normalizeBackupPath(input) {
|
|
2946
|
+
const collapsed = input.trim().replace(/\/{2,}/g, "/");
|
|
2947
|
+
if (collapsed === "/") return "/";
|
|
2948
|
+
return collapsed.replace(/\/+$/, "");
|
|
2949
|
+
}
|
|
2950
|
+
function validateBackupPath(input) {
|
|
2951
|
+
const path = normalizeBackupPath(input);
|
|
2952
|
+
if (path === "") return "A backup path cannot be empty.";
|
|
2953
|
+
if (!path.startsWith("/")) {
|
|
2954
|
+
return `Backup path "${input}" must be absolute \u2014 it is resolved inside the service, where there is no meaningful working directory.`;
|
|
2955
|
+
}
|
|
2956
|
+
if (path.includes("\0")) return `Backup path "${input}" contains a NUL byte.`;
|
|
2957
|
+
if (path.split("/").includes("..")) {
|
|
2958
|
+
return `Backup path "${input}" contains "..". Name the directory you mean directly; a traversal segment would archive somewhere other than what is shown here.`;
|
|
2959
|
+
}
|
|
2960
|
+
if (path === "/") {
|
|
2961
|
+
return "Refusing to archive `/` \u2014 that is the whole filesystem, including /proc and /sys. Name the data directory instead.";
|
|
2962
|
+
}
|
|
2963
|
+
return null;
|
|
2964
|
+
}
|
|
2965
|
+
function validateClearPath(input) {
|
|
2966
|
+
const path = normalizeBackupPath(input);
|
|
2967
|
+
const invalid = validateBackupPath(path);
|
|
2968
|
+
if (invalid) return invalid;
|
|
2969
|
+
const segments = path.split("/").filter(Boolean);
|
|
2970
|
+
if (segments.length < 2) {
|
|
2971
|
+
return `Refusing to clear "${path}" before restoring: it is a top-level directory. Name a subdirectory, or leave clearing off so the restore extracts over the existing files.`;
|
|
2972
|
+
}
|
|
2973
|
+
const top = `/${segments[0]}`;
|
|
2974
|
+
if (SYSTEM_ROOTS.includes(top)) {
|
|
2975
|
+
return `Refusing to clear "${path}" before restoring: it is inside ${top}, which holds the operating system rather than your data. Leave clearing off, or restore into a path you own.`;
|
|
2976
|
+
}
|
|
2977
|
+
return null;
|
|
2978
|
+
}
|
|
2979
|
+
|
|
2980
|
+
// ../../packages/core/src/backup-image-detect.ts
|
|
2981
|
+
var escapeRe = (n) => n.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2982
|
+
function matcherFor(names, namespaces = []) {
|
|
2983
|
+
const exact = `^(?:${names.map(escapeRe).join("|")})(?::|$)`;
|
|
2984
|
+
if (namespaces.length === 0) return new RegExp(exact, "i");
|
|
2985
|
+
return new RegExp(`${exact}|^(?:${namespaces.map(escapeRe).join("|")})/`, "i");
|
|
2986
|
+
}
|
|
2987
|
+
var MATCHERS = imageDetectableSpecs().map(
|
|
2988
|
+
(spec) => ({
|
|
2989
|
+
re: matcherFor(spec.images.names, spec.images.namespaces),
|
|
2990
|
+
match: { payloadKind: spec.kind, label: spec.label, method: spec.method }
|
|
2991
|
+
})
|
|
2992
|
+
);
|
|
2993
|
+
function normalizeImageRef(image) {
|
|
2994
|
+
let ref = image.trim();
|
|
2995
|
+
const at = ref.indexOf("@");
|
|
2996
|
+
if (at > 0) ref = ref.slice(0, at);
|
|
2997
|
+
const parts = ref.split("/");
|
|
2998
|
+
const first = parts[0] ?? "";
|
|
2999
|
+
if (parts.length > 1 && (first.includes(".") || first.includes(":") || first === "localhost")) {
|
|
3000
|
+
parts.shift();
|
|
3001
|
+
}
|
|
3002
|
+
const library = parts.lastIndexOf("library");
|
|
3003
|
+
if (library >= 0 && library < parts.length - 1) parts.splice(0, library + 1);
|
|
3004
|
+
return parts.join("/");
|
|
3005
|
+
}
|
|
3006
|
+
function detectDbImage(image) {
|
|
3007
|
+
const img = normalizeImageRef(image ?? "");
|
|
3008
|
+
if (!img) return null;
|
|
3009
|
+
for (const { re, match } of MATCHERS) {
|
|
3010
|
+
if (re.test(img)) return match;
|
|
3011
|
+
}
|
|
3012
|
+
return null;
|
|
3013
|
+
}
|
|
3014
|
+
function isDbImage(image, kind) {
|
|
3015
|
+
return detectDbImage(image)?.payloadKind === kind;
|
|
3016
|
+
}
|
|
3017
|
+
|
|
2624
3018
|
// ../../packages/core/src/runtime-config.ts
|
|
2625
3019
|
var DEFAULT_PORT = {
|
|
2626
3020
|
web: 3e3,
|
|
@@ -5809,6 +6203,7 @@ COMMIT;
|
|
|
5809
6203
|
{
|
|
5810
6204
|
available: true,
|
|
5811
6205
|
verified: true,
|
|
6206
|
+
minEngine: "0.6.6",
|
|
5812
6207
|
id: "minio",
|
|
5813
6208
|
name: "MinIO",
|
|
5814
6209
|
description: "S3-compatible object storage with a web console \u2014 point any S3 client (aws-cli, SDKs) at it, or bind a bucket straight into another project's uploads.",
|
|
@@ -6303,6 +6698,7 @@ COMMIT;
|
|
|
6303
6698
|
{
|
|
6304
6699
|
available: true,
|
|
6305
6700
|
verified: false,
|
|
6701
|
+
minEngine: "0.6.6",
|
|
6306
6702
|
minResources: {
|
|
6307
6703
|
memoryMb: 4096,
|
|
6308
6704
|
cpuCores: 2
|
|
@@ -6484,6 +6880,7 @@ Two things worth knowing, both upstream behaviour: if the console shows a branch
|
|
|
6484
6880
|
{
|
|
6485
6881
|
available: true,
|
|
6486
6882
|
verified: false,
|
|
6883
|
+
minEngine: "0.6.6",
|
|
6487
6884
|
minResources: {
|
|
6488
6885
|
memoryMb: 16384,
|
|
6489
6886
|
cpuCores: 4
|
|
@@ -7306,6 +7703,7 @@ Two things worth knowing, both upstream behaviour: if the console shows a branch
|
|
|
7306
7703
|
{
|
|
7307
7704
|
available: true,
|
|
7308
7705
|
verified: false,
|
|
7706
|
+
minEngine: "0.6.6",
|
|
7309
7707
|
id: "redis",
|
|
7310
7708
|
name: "Valkey (Redis)",
|
|
7311
7709
|
description: "In-memory data store for caching, sessions, rate limits, and queues \u2014 Valkey, the open-source Redis fork. Drop-in Redis-compatible: point any redis client at it. Ships with RedisInsight, a browser UI that arrives already connected to this instance.",
|
|
@@ -7784,6 +8182,7 @@ Two things worth knowing, both upstream behaviour: if the console shows a branch
|
|
|
7784
8182
|
{
|
|
7785
8183
|
available: true,
|
|
7786
8184
|
verified: false,
|
|
8185
|
+
minEngine: "0.6.6",
|
|
7787
8186
|
id: "clickhouse",
|
|
7788
8187
|
name: "ClickHouse",
|
|
7789
8188
|
description: "The columnar SQL database for analytics \u2014 billions of rows scanned per second. Ships with the CH-UI console, so you get a SQL editor, schema browser and dashboards the moment it installs: sign in with the ClickHouse user and password below, no extra setup.",
|
|
@@ -7958,96 +8357,268 @@ Two things worth knowing, both upstream behaviour: if the console shows a branch
|
|
|
7958
8357
|
help: "ClickHouse's HTTP interface (port 8123) \u2014 what clickhouse-connect, clickhouse-js, the JDBC/ODBC drivers and plain curl talk to. Send the user and password as basic auth. Switch to Internal for apps on the same project network; Public only resolves if you gave this port a domain."
|
|
7959
8358
|
},
|
|
7960
8359
|
{
|
|
7961
|
-
id: "httpDsn",
|
|
7962
|
-
label: "HTTP DSN",
|
|
7963
|
-
source: "template:http://{{env:clickhouse:CLICKHOUSE_USER}}:{{env:clickhouse:CLICKHOUSE_PASSWORD}}@clickhouse:8123/{{env:clickhouse:CLICKHOUSE_DB}}",
|
|
7964
|
-
sourceLabel: "Internal",
|
|
7965
|
-
service: "clickhouse",
|
|
7966
|
-
secret: true,
|
|
7967
|
-
envKey: "CLICKHOUSE_HTTP_DSN",
|
|
8360
|
+
id: "httpDsn",
|
|
8361
|
+
label: "HTTP DSN",
|
|
8362
|
+
source: "template:http://{{env:clickhouse:CLICKHOUSE_USER}}:{{env:clickhouse:CLICKHOUSE_PASSWORD}}@clickhouse:8123/{{env:clickhouse:CLICKHOUSE_DB}}",
|
|
8363
|
+
sourceLabel: "Internal",
|
|
8364
|
+
service: "clickhouse",
|
|
8365
|
+
secret: true,
|
|
8366
|
+
envKey: "CLICKHOUSE_HTTP_DSN",
|
|
8367
|
+
recommended: true,
|
|
8368
|
+
help: "One string with the credentials baked in, over the private project network. For a client outside this server, use the public HTTP endpoint above plus the user and password."
|
|
8369
|
+
},
|
|
8370
|
+
{
|
|
8371
|
+
id: "nativeDsn",
|
|
8372
|
+
label: "Native TCP DSN",
|
|
8373
|
+
source: "template:clickhouse://{{env:clickhouse:CLICKHOUSE_USER}}:{{env:clickhouse:CLICKHOUSE_PASSWORD}}@clickhouse:9000/{{env:clickhouse:CLICKHOUSE_DB}}",
|
|
8374
|
+
sourceLabel: "Internal",
|
|
8375
|
+
service: "clickhouse",
|
|
8376
|
+
secret: true,
|
|
8377
|
+
envKey: "CLICKHOUSE_DSN",
|
|
8378
|
+
help: "ClickHouse's native protocol (port 9000) \u2014 faster and what clickhouse-client, clickhouse-go and clickhouse-driver prefer. Reachable from inside the project only; the port is never published to the host."
|
|
8379
|
+
},
|
|
8380
|
+
{
|
|
8381
|
+
id: "user",
|
|
8382
|
+
label: "User",
|
|
8383
|
+
source: "env:clickhouse:CLICKHOUSE_USER",
|
|
8384
|
+
service: "clickhouse",
|
|
8385
|
+
envKey: "CLICKHOUSE_USER",
|
|
8386
|
+
recommended: true,
|
|
8387
|
+
width: "half"
|
|
8388
|
+
},
|
|
8389
|
+
{
|
|
8390
|
+
id: "password",
|
|
8391
|
+
label: "Password",
|
|
8392
|
+
source: "env:clickhouse:CLICKHOUSE_PASSWORD",
|
|
8393
|
+
service: "clickhouse",
|
|
8394
|
+
envKey: "CLICKHOUSE_PASSWORD",
|
|
8395
|
+
secret: true,
|
|
8396
|
+
recommended: true,
|
|
8397
|
+
width: "half"
|
|
8398
|
+
},
|
|
8399
|
+
{
|
|
8400
|
+
id: "database",
|
|
8401
|
+
label: "Database",
|
|
8402
|
+
source: "env:clickhouse:CLICKHOUSE_DB",
|
|
8403
|
+
service: "clickhouse",
|
|
8404
|
+
envKey: "CLICKHOUSE_DATABASE",
|
|
8405
|
+
width: "half"
|
|
8406
|
+
}
|
|
8407
|
+
]
|
|
8408
|
+
},
|
|
8409
|
+
provides: [
|
|
8410
|
+
{
|
|
8411
|
+
id: "clickhouse",
|
|
8412
|
+
outputRefs: [
|
|
8413
|
+
"httpUrl",
|
|
8414
|
+
"httpDsn",
|
|
8415
|
+
"user",
|
|
8416
|
+
"password",
|
|
8417
|
+
"database"
|
|
8418
|
+
],
|
|
8419
|
+
category: "database"
|
|
8420
|
+
}
|
|
8421
|
+
],
|
|
8422
|
+
endpoints: [
|
|
8423
|
+
{
|
|
8424
|
+
service: "ch-ui",
|
|
8425
|
+
port: 3488,
|
|
8426
|
+
label: "Console",
|
|
8427
|
+
kind: "http",
|
|
8428
|
+
required: true,
|
|
8429
|
+
scope: "public",
|
|
8430
|
+
defaultMode: "domain"
|
|
8431
|
+
},
|
|
8432
|
+
{
|
|
8433
|
+
service: "clickhouse",
|
|
8434
|
+
port: 8123,
|
|
8435
|
+
label: "HTTP API",
|
|
8436
|
+
kind: "http",
|
|
8437
|
+
scope: "public",
|
|
8438
|
+
defaultMode: "port"
|
|
8439
|
+
},
|
|
8440
|
+
{
|
|
8441
|
+
service: "clickhouse",
|
|
8442
|
+
port: 9e3,
|
|
8443
|
+
label: "Native protocol",
|
|
8444
|
+
kind: "tcp",
|
|
8445
|
+
scope: "internal",
|
|
8446
|
+
defaultMode: "internal",
|
|
8447
|
+
allowedModes: [
|
|
8448
|
+
"internal"
|
|
8449
|
+
]
|
|
8450
|
+
}
|
|
8451
|
+
]
|
|
8452
|
+
},
|
|
8453
|
+
{
|
|
8454
|
+
available: true,
|
|
8455
|
+
verified: true,
|
|
8456
|
+
id: "mindwire",
|
|
8457
|
+
name: "MindWire",
|
|
8458
|
+
description: "Run coding-agent runtimes behind a self-hosted control plane. MindWire keeps the runtime private and gives you one console to manage agents, credentials, and sessions.",
|
|
8459
|
+
repository: "https://github.com/oblien/mindwire",
|
|
8460
|
+
kind: "template",
|
|
8461
|
+
logo: "mindwire",
|
|
8462
|
+
category: "automation",
|
|
8463
|
+
tags: [
|
|
8464
|
+
"ai",
|
|
8465
|
+
"agents",
|
|
8466
|
+
"coding",
|
|
8467
|
+
"control-plane"
|
|
8468
|
+
],
|
|
8469
|
+
framework: "docker-compose",
|
|
8470
|
+
services: [
|
|
8471
|
+
{
|
|
8472
|
+
name: "runtime",
|
|
8473
|
+
image: "ghcr.io/oblien/mindwire-runtime:0.1.3",
|
|
8474
|
+
environment: {
|
|
8475
|
+
ADDR: "0.0.0.0:8790",
|
|
8476
|
+
AGENT_TYPE: "claude-code",
|
|
8477
|
+
DAEMON_TOKEN: "{{config:MINDWIRE_RUNTIME_TOKEN}}"
|
|
8478
|
+
},
|
|
8479
|
+
secretEnv: [
|
|
8480
|
+
"DAEMON_TOKEN"
|
|
8481
|
+
],
|
|
8482
|
+
volumes: [
|
|
8483
|
+
"mindwire_runtime:/home/node"
|
|
8484
|
+
],
|
|
8485
|
+
restart: "unless-stopped"
|
|
8486
|
+
},
|
|
8487
|
+
{
|
|
8488
|
+
name: "console",
|
|
8489
|
+
image: "ghcr.io/oblien/mindwire-console:0.1.3",
|
|
8490
|
+
exposedPort: 8787,
|
|
8491
|
+
exposed: true,
|
|
8492
|
+
routes: [
|
|
8493
|
+
{
|
|
8494
|
+
port: 8787
|
|
8495
|
+
}
|
|
8496
|
+
],
|
|
8497
|
+
dependsOn: [
|
|
8498
|
+
"runtime"
|
|
8499
|
+
],
|
|
8500
|
+
environment: {
|
|
8501
|
+
NODE_ENV: "production",
|
|
8502
|
+
PORT: "8787",
|
|
8503
|
+
BASE_URL: "{{publicUrl:console}}",
|
|
8504
|
+
AUTH_DB_PATH: "/data/auth.db",
|
|
8505
|
+
CONSOLE_MODE: "self-hosted",
|
|
8506
|
+
CONSOLE_USERNAME: "{{config:CONSOLE_USERNAME}}",
|
|
8507
|
+
CONSOLE_PASSWORD: "{{config:CONSOLE_PASSWORD}}",
|
|
8508
|
+
AUTH_SECRET: "{{config:AUTH_SECRET}}",
|
|
8509
|
+
SECRETS_ENCRYPTION_KEY: "{{config:SECRETS_ENCRYPTION_KEY}}",
|
|
8510
|
+
ALLOW_LOCAL_RUNTIME: "true",
|
|
8511
|
+
ALLOW_SSH_RUNTIME: "true",
|
|
8512
|
+
ALLOW_DOCKER_RUNTIME: "true",
|
|
8513
|
+
ALLOW_REMOTE_RUNTIME: "true",
|
|
8514
|
+
DAEMON_URL: "http://runtime:8790",
|
|
8515
|
+
MINDWIRE_RUNTIME_TOKEN: "{{config:MINDWIRE_RUNTIME_TOKEN}}",
|
|
8516
|
+
SEED_DEFAULT_DAEMON: "true"
|
|
8517
|
+
},
|
|
8518
|
+
secretEnv: [
|
|
8519
|
+
"CONSOLE_PASSWORD",
|
|
8520
|
+
"AUTH_SECRET",
|
|
8521
|
+
"SECRETS_ENCRYPTION_KEY",
|
|
8522
|
+
"MINDWIRE_RUNTIME_TOKEN"
|
|
8523
|
+
],
|
|
8524
|
+
volumes: [
|
|
8525
|
+
"mindwire_console:/data"
|
|
8526
|
+
],
|
|
8527
|
+
healthcheck: {
|
|
8528
|
+
test: [
|
|
8529
|
+
"CMD-SHELL",
|
|
8530
|
+
`node -e "fetch('http://127.0.0.1:8787/api/ping').then(r => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))"`
|
|
8531
|
+
],
|
|
8532
|
+
interval: "10s",
|
|
8533
|
+
timeout: "5s",
|
|
8534
|
+
retries: 12,
|
|
8535
|
+
startPeriod: "20s"
|
|
8536
|
+
},
|
|
8537
|
+
restart: "unless-stopped"
|
|
8538
|
+
}
|
|
8539
|
+
],
|
|
8540
|
+
configFields: [
|
|
8541
|
+
{
|
|
8542
|
+
key: "CONSOLE_USERNAME",
|
|
8543
|
+
service: "console",
|
|
8544
|
+
label: "Console username",
|
|
8545
|
+
help: "The single owner account for this self-hosted console.",
|
|
8546
|
+
default: "admin",
|
|
8547
|
+
required: true
|
|
8548
|
+
},
|
|
8549
|
+
{
|
|
8550
|
+
key: "CONSOLE_PASSWORD",
|
|
8551
|
+
service: "console",
|
|
8552
|
+
label: "Console password",
|
|
8553
|
+
help: "Auto-generated. Use this with the owner username to sign in.",
|
|
8554
|
+
type: "password",
|
|
8555
|
+
generate: "secret",
|
|
8556
|
+
secret: true
|
|
8557
|
+
},
|
|
8558
|
+
{
|
|
8559
|
+
key: "AUTH_SECRET",
|
|
8560
|
+
service: "console",
|
|
8561
|
+
label: "Session signing secret",
|
|
8562
|
+
help: "Auto-generated. Keep it stable so existing sign-in sessions remain valid after upgrades.",
|
|
8563
|
+
generate: "secret",
|
|
8564
|
+
secret: true
|
|
8565
|
+
},
|
|
8566
|
+
{
|
|
8567
|
+
key: "SECRETS_ENCRYPTION_KEY",
|
|
8568
|
+
service: "console",
|
|
8569
|
+
label: "Credential vault key",
|
|
8570
|
+
help: "Auto-generated 32-byte key used to encrypt provider and runtime credentials at rest.",
|
|
8571
|
+
generate: "secret",
|
|
8572
|
+
secret: true
|
|
8573
|
+
},
|
|
8574
|
+
{
|
|
8575
|
+
key: "MINDWIRE_RUNTIME_TOKEN",
|
|
8576
|
+
service: "runtime",
|
|
8577
|
+
label: "Runtime bearer token",
|
|
8578
|
+
help: "Auto-generated. Authenticates the Console to the private MindWire runtime.",
|
|
8579
|
+
generate: "secret",
|
|
8580
|
+
secret: true
|
|
8581
|
+
}
|
|
8582
|
+
],
|
|
8583
|
+
management: {
|
|
8584
|
+
kind: "schema"
|
|
8585
|
+
},
|
|
8586
|
+
connection: {
|
|
8587
|
+
title: "Sign in to MindWire",
|
|
8588
|
+
description: "Open the Console and sign in with the generated owner credentials. The runtime stays private to this deployment.",
|
|
8589
|
+
guide: {
|
|
8590
|
+
intro: "MindWire gives you a production control plane for coding-agent runtimes without exposing the runtime API to the public internet.",
|
|
8591
|
+
defaultMode: "public"
|
|
8592
|
+
},
|
|
8593
|
+
outputs: [
|
|
8594
|
+
{
|
|
8595
|
+
id: "console",
|
|
8596
|
+
label: "MindWire Console",
|
|
8597
|
+
source: "publicUrl:console",
|
|
8598
|
+
kind: "url",
|
|
7968
8599
|
recommended: true,
|
|
7969
|
-
help: "
|
|
7970
|
-
},
|
|
7971
|
-
{
|
|
7972
|
-
id: "nativeDsn",
|
|
7973
|
-
label: "Native TCP DSN",
|
|
7974
|
-
source: "template:clickhouse://{{env:clickhouse:CLICKHOUSE_USER}}:{{env:clickhouse:CLICKHOUSE_PASSWORD}}@clickhouse:9000/{{env:clickhouse:CLICKHOUSE_DB}}",
|
|
7975
|
-
sourceLabel: "Internal",
|
|
7976
|
-
service: "clickhouse",
|
|
7977
|
-
secret: true,
|
|
7978
|
-
envKey: "CLICKHOUSE_DSN",
|
|
7979
|
-
help: "ClickHouse's native protocol (port 9000) \u2014 faster and what clickhouse-client, clickhouse-go and clickhouse-driver prefer. Reachable from inside the project only; the port is never published to the host."
|
|
8600
|
+
help: "Open the control plane in your browser."
|
|
7980
8601
|
},
|
|
7981
8602
|
{
|
|
7982
|
-
id: "
|
|
7983
|
-
label: "
|
|
7984
|
-
source: "env:
|
|
7985
|
-
service: "clickhouse",
|
|
7986
|
-
envKey: "CLICKHOUSE_USER",
|
|
7987
|
-
recommended: true,
|
|
7988
|
-
width: "half"
|
|
8603
|
+
id: "username",
|
|
8604
|
+
label: "Owner username",
|
|
8605
|
+
source: "env:console:CONSOLE_USERNAME"
|
|
7989
8606
|
},
|
|
7990
8607
|
{
|
|
7991
8608
|
id: "password",
|
|
7992
|
-
label: "
|
|
7993
|
-
source: "env:
|
|
7994
|
-
|
|
7995
|
-
envKey: "CLICKHOUSE_PASSWORD",
|
|
7996
|
-
secret: true,
|
|
7997
|
-
recommended: true,
|
|
7998
|
-
width: "half"
|
|
7999
|
-
},
|
|
8000
|
-
{
|
|
8001
|
-
id: "database",
|
|
8002
|
-
label: "Database",
|
|
8003
|
-
source: "env:clickhouse:CLICKHOUSE_DB",
|
|
8004
|
-
service: "clickhouse",
|
|
8005
|
-
envKey: "CLICKHOUSE_DATABASE",
|
|
8006
|
-
width: "half"
|
|
8609
|
+
label: "Owner password",
|
|
8610
|
+
source: "env:console:CONSOLE_PASSWORD",
|
|
8611
|
+
secret: true
|
|
8007
8612
|
}
|
|
8008
8613
|
]
|
|
8009
8614
|
},
|
|
8010
|
-
provides: [
|
|
8011
|
-
{
|
|
8012
|
-
id: "clickhouse",
|
|
8013
|
-
outputRefs: [
|
|
8014
|
-
"httpUrl",
|
|
8015
|
-
"httpDsn",
|
|
8016
|
-
"user",
|
|
8017
|
-
"password",
|
|
8018
|
-
"database"
|
|
8019
|
-
],
|
|
8020
|
-
category: "database"
|
|
8021
|
-
}
|
|
8022
|
-
],
|
|
8023
8615
|
endpoints: [
|
|
8024
8616
|
{
|
|
8025
|
-
service: "
|
|
8026
|
-
port:
|
|
8027
|
-
label: "Console",
|
|
8617
|
+
service: "console",
|
|
8618
|
+
port: 8787,
|
|
8619
|
+
label: "MindWire Console",
|
|
8028
8620
|
kind: "http",
|
|
8029
|
-
required: true,
|
|
8030
|
-
scope: "public",
|
|
8031
8621
|
defaultMode: "domain"
|
|
8032
|
-
},
|
|
8033
|
-
{
|
|
8034
|
-
service: "clickhouse",
|
|
8035
|
-
port: 8123,
|
|
8036
|
-
label: "HTTP API",
|
|
8037
|
-
kind: "http",
|
|
8038
|
-
scope: "public",
|
|
8039
|
-
defaultMode: "port"
|
|
8040
|
-
},
|
|
8041
|
-
{
|
|
8042
|
-
service: "clickhouse",
|
|
8043
|
-
port: 9e3,
|
|
8044
|
-
label: "Native protocol",
|
|
8045
|
-
kind: "tcp",
|
|
8046
|
-
scope: "internal",
|
|
8047
|
-
defaultMode: "internal",
|
|
8048
|
-
allowedModes: [
|
|
8049
|
-
"internal"
|
|
8050
|
-
]
|
|
8051
8622
|
}
|
|
8052
8623
|
]
|
|
8053
8624
|
}
|
|
@@ -22928,6 +23499,25 @@ var planLimitsSchema = external_exports.object({
|
|
|
22928
23499
|
* `null` = uncapped (enterprise).
|
|
22929
23500
|
*/
|
|
22930
23501
|
maxResourceTier: external_exports.enum(RESOURCE_TIER_ORDER).nullable(),
|
|
23502
|
+
/**
|
|
23503
|
+
* App runtime included per month, in COMPUTE MINUTES — one minute of a `low`
|
|
23504
|
+
* machine. Bigger machines burn proportionally more (`computeUnitsPerMinute`:
|
|
23505
|
+
* medium 2×, high 4×, xlarge 8×), so one published number covers every size
|
|
23506
|
+
* without quoting a separate allowance per tier of machine.
|
|
23507
|
+
*
|
|
23508
|
+
* This replaced an authored `credits` blob whose numbers meant nothing: 60,000
|
|
23509
|
+
* credits against a 43,200-minute month, on a tier advertising 50 running
|
|
23510
|
+
* services — i.e. not enough to run ONE app around the clock. The unit now has
|
|
23511
|
+
* a definition, and `planMonthlyCredits()` derives the Oblien grant from it.
|
|
23512
|
+
*
|
|
23513
|
+
* `runningServices` is a CONCURRENCY cap, this is the METER. A tier may allow
|
|
23514
|
+
* more always-on apps than its minutes cover; that is what "up to" means and is
|
|
23515
|
+
* how metered compute works everywhere.
|
|
23516
|
+
*
|
|
23517
|
+
* 0 is meaningful and correct for a static-only tier: the edge serves static
|
|
23518
|
+
* sites, `runningServices` is already 0, so no app compute is consumed.
|
|
23519
|
+
*/
|
|
23520
|
+
computeMinutesPerMonth: limitNumber,
|
|
22931
23521
|
buildMinutesPerMonth: limitNumber,
|
|
22932
23522
|
freeSubdomains: limitNumber,
|
|
22933
23523
|
customDomains: limitNumber,
|
|
@@ -22949,10 +23539,6 @@ var planSchema = external_exports.object({
|
|
|
22949
23539
|
support: external_exports.enum(["community", "email", "priority", "dedicated"]),
|
|
22950
23540
|
/** Tier whose features this one builds on — drives the "Everything in X" line. */
|
|
22951
23541
|
inherits: external_exports.string().min(1).optional(),
|
|
22952
|
-
credits: external_exports.object({
|
|
22953
|
-
/** Milli-credits granted per period; null = granted by hand (enterprise). */
|
|
22954
|
-
monthlyMilli: external_exports.number().int().positive().nullable()
|
|
22955
|
-
}),
|
|
22956
23542
|
limits: planLimitsSchema,
|
|
22957
23543
|
/** Copy keys, in display order. Each must exist in `locales/en.json#features`. */
|
|
22958
23544
|
features: external_exports.array(external_exports.string().min(1)).min(1)
|
|
@@ -23025,6 +23611,20 @@ var pricingCatalogSchema = external_exports.object({
|
|
|
23025
23611
|
campaigns: external_exports.array(campaignSchema),
|
|
23026
23612
|
plans: external_exports.array(planSchema).min(1),
|
|
23027
23613
|
creditPacks: external_exports.array(creditPackSchema),
|
|
23614
|
+
/**
|
|
23615
|
+
* Copy for what EVERY cloud tier includes, stated once instead of repeated
|
|
23616
|
+
* down each column.
|
|
23617
|
+
*
|
|
23618
|
+
* This exists because the per-tier lists had become the differentiator, and
|
|
23619
|
+
* they were differentiating on nothing: the audit log, live logs and metrics
|
|
23620
|
+
* are ungated for everyone, so listing them only under Team implied a paywall
|
|
23621
|
+
* that no code enforces. A tier's bullets are now its NUMBERS; anything true
|
|
23622
|
+
* everywhere belongs here.
|
|
23623
|
+
*
|
|
23624
|
+
* Only put a key here that is actually shipped and reachable on cloud — the
|
|
23625
|
+
* defect this block replaces was advertising capability, not describing it.
|
|
23626
|
+
*/
|
|
23627
|
+
standard: external_exports.object({ features: external_exports.array(external_exports.string().min(1)).min(1) }),
|
|
23028
23628
|
selfHosted: external_exports.object({ features: external_exports.array(external_exports.string().min(1)).min(1) })
|
|
23029
23629
|
}).superRefine((data, ctx) => {
|
|
23030
23630
|
if (data.schemaVersion > MAX_SUPPORTED_PRICING_SCHEMA) {
|
|
@@ -23123,7 +23723,13 @@ var pricingCopySchema = external_exports.object({
|
|
|
23123
23723
|
billedMonthly: external_exports.string(),
|
|
23124
23724
|
campaignBadge: external_exports.string(),
|
|
23125
23725
|
campaignEnds: external_exports.string(),
|
|
23126
|
-
wasPrice: external_exports.string()
|
|
23726
|
+
wasPrice: external_exports.string(),
|
|
23727
|
+
/** Heading over the `standard.features` block. Lives here, not in an app
|
|
23728
|
+
* dictionary, because the marketing site has no i18n framework. */
|
|
23729
|
+
standardTitle: external_exports.string(),
|
|
23730
|
+
/** What a top-up pack buys, in recognisable units. Interpolates
|
|
23731
|
+
* `{appHours}` and `{buildMinutes}`, both derived from the catalog. */
|
|
23732
|
+
creditPackNote: external_exports.string()
|
|
23127
23733
|
}).partial().optional()
|
|
23128
23734
|
});
|
|
23129
23735
|
|
|
@@ -23159,9 +23765,6 @@ var pricing_default = {
|
|
|
23159
23765
|
popular: false,
|
|
23160
23766
|
contactSales: null,
|
|
23161
23767
|
support: "community",
|
|
23162
|
-
credits: {
|
|
23163
|
-
monthlyMilli: 5e5
|
|
23164
|
-
},
|
|
23165
23768
|
limits: {
|
|
23166
23769
|
workloads: [
|
|
23167
23770
|
"static"
|
|
@@ -23170,7 +23773,8 @@ var pricing_default = {
|
|
|
23170
23773
|
runningServices: 0,
|
|
23171
23774
|
maxProjects: 3,
|
|
23172
23775
|
maxResourceTier: "low",
|
|
23173
|
-
|
|
23776
|
+
computeMinutesPerMonth: 0,
|
|
23777
|
+
buildMinutesPerMonth: 500,
|
|
23174
23778
|
freeSubdomains: 10,
|
|
23175
23779
|
customDomains: null,
|
|
23176
23780
|
seats: null
|
|
@@ -23178,10 +23782,8 @@ var pricing_default = {
|
|
|
23178
23782
|
features: [
|
|
23179
23783
|
"staticOnly",
|
|
23180
23784
|
"buildMinutes",
|
|
23181
|
-
"freeSubdomains",
|
|
23182
|
-
"customDomains",
|
|
23183
23785
|
"projects",
|
|
23184
|
-
"
|
|
23786
|
+
"freeSubdomains",
|
|
23185
23787
|
"communitySupport"
|
|
23186
23788
|
]
|
|
23187
23789
|
},
|
|
@@ -23199,9 +23801,6 @@ var pricing_default = {
|
|
|
23199
23801
|
contactSales: null,
|
|
23200
23802
|
support: "email",
|
|
23201
23803
|
inherits: "free",
|
|
23202
|
-
credits: {
|
|
23203
|
-
monthlyMilli: 2e6
|
|
23204
|
-
},
|
|
23205
23804
|
limits: {
|
|
23206
23805
|
workloads: [
|
|
23207
23806
|
"static",
|
|
@@ -23210,9 +23809,10 @@ var pricing_default = {
|
|
|
23210
23809
|
],
|
|
23211
23810
|
services: true,
|
|
23212
23811
|
runningServices: 3,
|
|
23213
|
-
maxProjects:
|
|
23812
|
+
maxProjects: 10,
|
|
23214
23813
|
maxResourceTier: "medium",
|
|
23215
|
-
|
|
23814
|
+
computeMinutesPerMonth: 15e4,
|
|
23815
|
+
buildMinutesPerMonth: 3e3,
|
|
23216
23816
|
freeSubdomains: 25,
|
|
23217
23817
|
customDomains: null,
|
|
23218
23818
|
seats: null
|
|
@@ -23220,15 +23820,11 @@ var pricing_default = {
|
|
|
23220
23820
|
features: [
|
|
23221
23821
|
"everythingIn",
|
|
23222
23822
|
"anyWorkload",
|
|
23223
|
-
"managedServices",
|
|
23224
23823
|
"runningServices",
|
|
23824
|
+
"computeMinutes",
|
|
23825
|
+
"buildMinutes",
|
|
23225
23826
|
"power",
|
|
23226
23827
|
"projects",
|
|
23227
|
-
"buildMinutes",
|
|
23228
|
-
"credits",
|
|
23229
|
-
"meteredCredits",
|
|
23230
|
-
"freeSubdomains",
|
|
23231
|
-
"previewDeploys",
|
|
23232
23828
|
"emailSupport"
|
|
23233
23829
|
]
|
|
23234
23830
|
},
|
|
@@ -23246,9 +23842,6 @@ var pricing_default = {
|
|
|
23246
23842
|
contactSales: null,
|
|
23247
23843
|
support: "priority",
|
|
23248
23844
|
inherits: "starter",
|
|
23249
|
-
credits: {
|
|
23250
|
-
monthlyMilli: 1e7
|
|
23251
|
-
},
|
|
23252
23845
|
limits: {
|
|
23253
23846
|
workloads: [
|
|
23254
23847
|
"static",
|
|
@@ -23257,9 +23850,10 @@ var pricing_default = {
|
|
|
23257
23850
|
],
|
|
23258
23851
|
services: true,
|
|
23259
23852
|
runningServices: 10,
|
|
23260
|
-
maxProjects:
|
|
23853
|
+
maxProjects: 50,
|
|
23261
23854
|
maxResourceTier: "high",
|
|
23262
|
-
|
|
23855
|
+
computeMinutesPerMonth: 6e5,
|
|
23856
|
+
buildMinutesPerMonth: 12e3,
|
|
23263
23857
|
freeSubdomains: 100,
|
|
23264
23858
|
customDomains: null,
|
|
23265
23859
|
seats: null
|
|
@@ -23267,15 +23861,10 @@ var pricing_default = {
|
|
|
23267
23861
|
features: [
|
|
23268
23862
|
"everythingIn",
|
|
23269
23863
|
"runningServices",
|
|
23864
|
+
"computeMinutes",
|
|
23865
|
+
"buildMinutes",
|
|
23270
23866
|
"power",
|
|
23271
23867
|
"projects",
|
|
23272
|
-
"buildMinutes",
|
|
23273
|
-
"credits",
|
|
23274
|
-
"meteredCredits",
|
|
23275
|
-
"freeSubdomains",
|
|
23276
|
-
"backups",
|
|
23277
|
-
"observability",
|
|
23278
|
-
"mailServer",
|
|
23279
23868
|
"prioritySupport"
|
|
23280
23869
|
]
|
|
23281
23870
|
},
|
|
@@ -23293,9 +23882,6 @@ var pricing_default = {
|
|
|
23293
23882
|
contactSales: null,
|
|
23294
23883
|
support: "priority",
|
|
23295
23884
|
inherits: "pro",
|
|
23296
|
-
credits: {
|
|
23297
|
-
monthlyMilli: 6e7
|
|
23298
|
-
},
|
|
23299
23885
|
limits: {
|
|
23300
23886
|
workloads: [
|
|
23301
23887
|
"static",
|
|
@@ -23306,7 +23892,8 @@ var pricing_default = {
|
|
|
23306
23892
|
runningServices: 50,
|
|
23307
23893
|
maxProjects: null,
|
|
23308
23894
|
maxResourceTier: "xlarge",
|
|
23309
|
-
|
|
23895
|
+
computeMinutesPerMonth: 22e5,
|
|
23896
|
+
buildMinutesPerMonth: 36e3,
|
|
23310
23897
|
freeSubdomains: null,
|
|
23311
23898
|
customDomains: null,
|
|
23312
23899
|
seats: null
|
|
@@ -23314,14 +23901,11 @@ var pricing_default = {
|
|
|
23314
23901
|
features: [
|
|
23315
23902
|
"everythingIn",
|
|
23316
23903
|
"runningServices",
|
|
23904
|
+
"computeMinutes",
|
|
23905
|
+
"buildMinutes",
|
|
23317
23906
|
"power",
|
|
23318
23907
|
"projectsUnlimited",
|
|
23319
|
-
"buildMinutes",
|
|
23320
|
-
"credits",
|
|
23321
|
-
"meteredCredits",
|
|
23322
23908
|
"freeSubdomainsUnlimited",
|
|
23323
|
-
"auditLog",
|
|
23324
|
-
"sharedChannel",
|
|
23325
23909
|
"prioritySupport"
|
|
23326
23910
|
]
|
|
23327
23911
|
},
|
|
@@ -23339,9 +23923,6 @@ var pricing_default = {
|
|
|
23339
23923
|
contactSales: "mailto:sales@openship.io",
|
|
23340
23924
|
support: "dedicated",
|
|
23341
23925
|
inherits: "team",
|
|
23342
|
-
credits: {
|
|
23343
|
-
monthlyMilli: null
|
|
23344
|
-
},
|
|
23345
23926
|
limits: {
|
|
23346
23927
|
workloads: [
|
|
23347
23928
|
"static",
|
|
@@ -23352,6 +23933,7 @@ var pricing_default = {
|
|
|
23352
23933
|
runningServices: null,
|
|
23353
23934
|
maxProjects: null,
|
|
23354
23935
|
maxResourceTier: null,
|
|
23936
|
+
computeMinutesPerMonth: null,
|
|
23355
23937
|
buildMinutesPerMonth: null,
|
|
23356
23938
|
freeSubdomains: null,
|
|
23357
23939
|
customDomains: null,
|
|
@@ -23390,6 +23972,15 @@ var pricing_default = {
|
|
|
23390
23972
|
sortOrder: 30
|
|
23391
23973
|
}
|
|
23392
23974
|
],
|
|
23975
|
+
standard: {
|
|
23976
|
+
features: [
|
|
23977
|
+
"gitDeploys",
|
|
23978
|
+
"customDomains",
|
|
23979
|
+
"deployLogs",
|
|
23980
|
+
"auditLog",
|
|
23981
|
+
"unlimitedSeats"
|
|
23982
|
+
]
|
|
23983
|
+
},
|
|
23393
23984
|
selfHosted: {
|
|
23394
23985
|
features: [
|
|
23395
23986
|
"selfHostedOpenSource",
|
|
@@ -23429,20 +24020,20 @@ var en_default2 = {
|
|
|
23429
24020
|
features: {
|
|
23430
24021
|
everythingIn: "Everything in {inherited}, plus:",
|
|
23431
24022
|
staticOnly: "Static sites only \u2014 SSG, SPA, and plain HTML",
|
|
23432
|
-
anyWorkload: "Run anything \u2014 containers, Compose
|
|
23433
|
-
|
|
24023
|
+
anyWorkload: "Run anything \u2014 containers, Compose, workers, Postgres, Redis",
|
|
24024
|
+
runningServices: "{runningServices} running apps, databases or workers",
|
|
24025
|
+
computeMinutes: "{computeMinutes} compute minutes per month",
|
|
23434
24026
|
buildMinutes: "{buildMinutes} build minutes per month",
|
|
23435
|
-
|
|
24027
|
+
power: "Up to {powerCpu} vCPU and {powerRamGb} GB RAM per app",
|
|
24028
|
+
projects: "{maxProjects} projects",
|
|
24029
|
+
projectsUnlimited: "Unlimited projects",
|
|
23436
24030
|
freeSubdomains: "{freeSubdomains} free {freeDomainSuffix} subdomains",
|
|
23437
24031
|
freeSubdomainsUnlimited: "Unlimited free {freeDomainSuffix} subdomains",
|
|
24032
|
+
gitDeploys: "Deploy from GitHub, GitLab, or any Git remote",
|
|
23438
24033
|
customDomains: "Unlimited custom domains, TLS included",
|
|
24034
|
+
deployLogs: "Live build and deploy logs",
|
|
24035
|
+
auditLog: "Audit log of every change",
|
|
23439
24036
|
unlimitedSeats: "Unlimited team members \u2014 never pay per seat",
|
|
23440
|
-
previewDeploys: "Preview deploys on every push",
|
|
23441
|
-
backups: "Daily backups with point-in-time recovery",
|
|
23442
|
-
observability: "Live logs, metrics, and health alerts",
|
|
23443
|
-
mailServer: "Built-in mail server \u2014 SMTP, IMAP, and webmail",
|
|
23444
|
-
auditLog: "Audit log with extended retention",
|
|
23445
|
-
sharedChannel: "Shared Slack or Discord channel",
|
|
23446
24037
|
sso: "SSO and SAML",
|
|
23447
24038
|
customLimits: "Custom limits, negotiated per contract",
|
|
23448
24039
|
dedicatedInfra: "Dedicated infrastructure and regions",
|
|
@@ -23455,12 +24046,7 @@ var en_default2 = {
|
|
|
23455
24046
|
selfHostedUnlimited: "Unlimited deploys, projects, servers, and domains",
|
|
23456
24047
|
selfHostedServices: "Every managed service \u2014 Postgres, Redis, mail",
|
|
23457
24048
|
selfHostedClients: "CLI, web, and desktop \u2014 one backend",
|
|
23458
|
-
selfHostedFreeSubdomains: "{freeSubdomains} free {freeDomainSuffix} subdomains when you connect to Cloud"
|
|
23459
|
-
runningServices: "{runningServices} running services \u2014 apps, databases, workers",
|
|
23460
|
-
power: "Up to {powerCpu} vCPU and {powerRamGb} GB RAM per service",
|
|
23461
|
-
projects: "{maxProjects} projects",
|
|
23462
|
-
projectsUnlimited: "Unlimited projects",
|
|
23463
|
-
meteredCredits: "Compute, bandwidth and storage all draw from your monthly credits"
|
|
24049
|
+
selfHostedFreeSubdomains: "{freeSubdomains} free {freeDomainSuffix} subdomains when you connect to Cloud"
|
|
23464
24050
|
},
|
|
23465
24051
|
selfHosted: {
|
|
23466
24052
|
name: "Self-hosted",
|
|
@@ -23470,9 +24056,9 @@ var en_default2 = {
|
|
|
23470
24056
|
cta: "Start self-hosting"
|
|
23471
24057
|
},
|
|
23472
24058
|
creditPacks: {
|
|
23473
|
-
pack_5k: "{
|
|
23474
|
-
pack_25k: "{
|
|
23475
|
-
pack_100k: "{
|
|
24059
|
+
pack_5k: "{computeMinutes} compute minutes",
|
|
24060
|
+
pack_25k: "{computeMinutes} compute minutes",
|
|
24061
|
+
pack_100k: "{computeMinutes} compute minutes"
|
|
23476
24062
|
},
|
|
23477
24063
|
ui: {
|
|
23478
24064
|
perMonth: "/mo",
|
|
@@ -23488,7 +24074,9 @@ var en_default2 = {
|
|
|
23488
24074
|
billedMonthly: "billed monthly, cancel anytime",
|
|
23489
24075
|
campaignBadge: "{percentOff}% off",
|
|
23490
24076
|
campaignEnds: "Offer ends {date}",
|
|
23491
|
-
wasPrice: "was {price}"
|
|
24077
|
+
wasPrice: "was {price}",
|
|
24078
|
+
standardTitle: "In every plan",
|
|
24079
|
+
creditPackNote: "\u2248 {appHours} hours of a small app, or {buildMinutes} build minutes"
|
|
23492
24080
|
}
|
|
23493
24081
|
};
|
|
23494
24082
|
|
|
@@ -23519,20 +24107,20 @@ var ar_default2 = {
|
|
|
23519
24107
|
features: {
|
|
23520
24108
|
everythingIn: "\u0643\u0644 \u0645\u0627 \u0641\u064A \u062E\u0637\u0629 \xAB{inherited}\xBB\u060C \u0628\u0627\u0644\u0625\u0636\u0627\u0641\u0629 \u0625\u0644\u0649:",
|
|
23521
24109
|
staticOnly: "\u0627\u0644\u0645\u0648\u0627\u0642\u0639 \u0627\u0644\u062B\u0627\u0628\u062A\u0629 \u0641\u0642\u0637 \u2014 SSG \u0648SPA \u0648HTML \u0639\u0627\u062F\u064A",
|
|
23522
|
-
anyWorkload: "\u0634\u063A\u0651\u0644 \u0623\u064A \u0634\u064A\u0621 \u2014 \u062D\u0627\u0648\u064A\u0627\u062A \
|
|
23523
|
-
|
|
24110
|
+
anyWorkload: "\u0634\u063A\u0651\u0644 \u0623\u064A \u0634\u064A\u0621 \u2014 \u062D\u0627\u0648\u064A\u0627\u062A \u0648Compose \u0648\u0639\u0645\u0644\u064A\u0627\u062A \u062E\u0644\u0641\u064A\u0629 \u0648Postgres \u0648Redis",
|
|
24111
|
+
runningServices: "{runningServices} \u062A\u0637\u0628\u064A\u0642 \u0623\u0648 \u0642\u0627\u0639\u062F\u0629 \u0628\u064A\u0627\u0646\u0627\u062A \u0623\u0648 \u0639\u0645\u0644\u064A\u0629 \u062E\u0644\u0641\u064A\u0629 \u0642\u064A\u062F \u0627\u0644\u062A\u0634\u063A\u064A\u0644",
|
|
24112
|
+
computeMinutes: "{computeMinutes} \u062F\u0642\u064A\u0642\u0629 \u062D\u0648\u0633\u0628\u0629 \u0634\u0647\u0631\u064A\u064B\u0627",
|
|
23524
24113
|
buildMinutes: "{buildMinutes} \u062F\u0642\u064A\u0642\u0629 \u0628\u0646\u0627\u0621 \u0634\u0647\u0631\u064A\u064B\u0627",
|
|
23525
|
-
|
|
24114
|
+
power: "\u062D\u062A\u0649 {powerCpu} vCPU \u0648{powerRamGb} GB RAM \u0644\u0643\u0644 \u062A\u0637\u0628\u064A\u0642",
|
|
24115
|
+
projects: "{maxProjects} \u0645\u0634\u0631\u0648\u0639",
|
|
24116
|
+
projectsUnlimited: "\u0645\u0634\u0627\u0631\u064A\u0639 \u063A\u064A\u0631 \u0645\u062D\u062F\u0648\u062F\u0629",
|
|
23526
24117
|
freeSubdomains: "{freeSubdomains} \u0646\u0637\u0627\u0642 \u0641\u0631\u0639\u064A \u0645\u062C\u0627\u0646\u064A \u0639\u0644\u0649 {freeDomainSuffix}",
|
|
23527
24118
|
freeSubdomainsUnlimited: "\u0646\u0637\u0627\u0642\u0627\u062A \u0641\u0631\u0639\u064A\u0629 \u0645\u062C\u0627\u0646\u064A\u0629 \u063A\u064A\u0631 \u0645\u062D\u062F\u0648\u062F\u0629 \u0639\u0644\u0649 {freeDomainSuffix}",
|
|
24119
|
+
gitDeploys: "\u0627\u0644\u0646\u0634\u0631 \u0645\u0646 GitHub \u0623\u0648 GitLab \u0623\u0648 \u0623\u064A \u0645\u0633\u062A\u0648\u062F\u0639 Git",
|
|
23528
24120
|
customDomains: "\u0646\u0637\u0627\u0642\u0627\u062A \u0645\u062E\u0635\u0651\u0635\u0629 \u063A\u064A\u0631 \u0645\u062D\u062F\u0648\u062F\u0629\u060C \u0645\u0639 TLS",
|
|
24121
|
+
deployLogs: "\u0633\u062C\u0644\u0627\u062A \u062D\u064A\u0651\u0629 \u0644\u0644\u0628\u0646\u0627\u0621 \u0648\u0627\u0644\u0646\u0634\u0631",
|
|
24122
|
+
auditLog: "\u0633\u062C\u0644 \u062A\u062F\u0642\u064A\u0642 \u0644\u0643\u0644 \u062A\u063A\u064A\u064A\u0631",
|
|
23529
24123
|
unlimitedSeats: "\u0623\u0639\u0636\u0627\u0621 \u0641\u0631\u064A\u0642 \u063A\u064A\u0631 \u0645\u062D\u062F\u0648\u062F\u064A\u0646 \u2014 \u0628\u0644\u0627 \u0631\u0633\u0648\u0645 \u0639\u0644\u0649 \u0643\u0644 \u0645\u0633\u062A\u062E\u062F\u0645",
|
|
23530
|
-
previewDeploys: "\u0646\u0634\u0631 \u0645\u0639\u0627\u064A\u0646\u0629 \u0639\u0646\u062F \u0643\u0644 \u062F\u0641\u0639 \u0644\u0644\u0643\u0648\u062F",
|
|
23531
|
-
backups: "\u0646\u0633\u062E \u0627\u062D\u062A\u064A\u0627\u0637\u064A \u064A\u0648\u0645\u064A \u0645\u0639 \u0627\u0633\u062A\u0639\u0627\u062F\u0629 \u0625\u0644\u0649 \u0644\u062D\u0638\u0629 \u0632\u0645\u0646\u064A\u0629 \u0645\u062D\u062F\u0651\u062F\u0629",
|
|
23532
|
-
observability: "\u0633\u062C\u0644\u0627\u062A \u062D\u064A\u0651\u0629 \u0648\u0645\u0642\u0627\u064A\u064A\u0633 \u0648\u062A\u0646\u0628\u064A\u0647\u0627\u062A \u0644\u0635\u062D\u0629 \u0627\u0644\u062E\u062F\u0645\u0629",
|
|
23533
|
-
mailServer: "\u062E\u0627\u062F\u0645 \u0628\u0631\u064A\u062F \u0645\u062F\u0645\u062C \u2014 SMTP \u0648IMAP \u0648\u0628\u0631\u064A\u062F \u0639\u0644\u0649 \u0627\u0644\u0648\u064A\u0628",
|
|
23534
|
-
auditLog: "\u0633\u062C\u0644 \u062A\u062F\u0642\u064A\u0642 \u0628\u0645\u062F\u0629 \u062D\u0641\u0638 \u0645\u0645\u062A\u062F\u0629",
|
|
23535
|
-
sharedChannel: "\u0642\u0646\u0627\u0629 \u0645\u0634\u062A\u0631\u0643\u0629 \u0639\u0644\u0649 Slack \u0623\u0648 Discord",
|
|
23536
24124
|
sso: "SSO \u0648SAML",
|
|
23537
24125
|
customLimits: "\u062D\u062F\u0648\u062F \u0645\u062E\u0635\u0651\u0635\u0629 \u064A\u064F\u062A\u0641\u0642 \u0639\u0644\u064A\u0647\u0627 \u0641\u064A \u0627\u0644\u0639\u0642\u062F",
|
|
23538
24126
|
dedicatedInfra: "\u0628\u0646\u064A\u0629 \u062A\u062D\u062A\u064A\u0629 \u0648\u0645\u0646\u0627\u0637\u0642 \u0645\u062E\u0635\u0651\u0635\u0629",
|
|
@@ -23545,12 +24133,7 @@ var ar_default2 = {
|
|
|
23545
24133
|
selfHostedUnlimited: "\u0639\u0645\u0644\u064A\u0627\u062A \u0646\u0634\u0631 \u0648\u0645\u0634\u0627\u0631\u064A\u0639 \u0648\u062E\u0648\u0627\u062F\u0645 \u0648\u0646\u0637\u0627\u0642\u0627\u062A \u063A\u064A\u0631 \u0645\u062D\u062F\u0648\u062F\u0629",
|
|
23546
24134
|
selfHostedServices: "\u0643\u0644 \u0627\u0644\u062E\u062F\u0645\u0627\u062A \u0627\u0644\u0645\u064F\u062F\u0627\u0631\u0629 \u2014 Postgres \u0648Redis \u0648\u0627\u0644\u0628\u0631\u064A\u062F",
|
|
23547
24135
|
selfHostedClients: "CLI \u0648\u0648\u0627\u062C\u0647\u0629 \u0648\u064A\u0628 \u0648\u062A\u0637\u0628\u064A\u0642 \u0633\u0637\u062D \u0645\u0643\u062A\u0628 \u2014 \u062E\u062F\u0645\u0629 \u062E\u0644\u0641\u064A\u0629 \u0648\u0627\u062D\u062F\u0629",
|
|
23548
|
-
selfHostedFreeSubdomains: "{freeSubdomains} \u0646\u0637\u0627\u0642 \u0641\u0631\u0639\u064A \u0645\u062C\u0627\u0646\u064A \u0639\u0644\u0649 {freeDomainSuffix} \u0639\u0646\u062F \u0627\u0644\u0631\u0628\u0637 \u0645\u0639 Openship Cloud"
|
|
23549
|
-
runningServices: "{runningServices} \u062E\u062F\u0645\u0629 \u0642\u064A\u062F \u0627\u0644\u062A\u0634\u063A\u064A\u0644 \u2014 \u062A\u0637\u0628\u064A\u0642\u0627\u062A \u0648\u0642\u0648\u0627\u0639\u062F \u0628\u064A\u0627\u0646\u0627\u062A \u0648\u0639\u0645\u0644\u064A\u0627\u062A \u062E\u0644\u0641\u064A\u0629",
|
|
23550
|
-
power: "\u062D\u062A\u0649 {powerCpu} vCPU \u0648{powerRamGb} GB RAM \u0644\u0643\u0644 \u062E\u062F\u0645\u0629",
|
|
23551
|
-
projects: "{maxProjects} \u0645\u0634\u0631\u0648\u0639",
|
|
23552
|
-
projectsUnlimited: "\u0645\u0634\u0627\u0631\u064A\u0639 \u063A\u064A\u0631 \u0645\u062D\u062F\u0648\u062F\u0629",
|
|
23553
|
-
meteredCredits: "\u0627\u0644\u062D\u0648\u0633\u0628\u0629 \u0648\u0627\u0644\u0646\u0637\u0627\u0642 \u0627\u0644\u062A\u0631\u062F\u062F\u064A \u0648\u0627\u0644\u062A\u062E\u0632\u064A\u0646 \u062A\u064F\u062E\u0635\u0645 \u0643\u0644\u0647\u0627 \u0645\u0646 \u0631\u0635\u064A\u062F\u0643 \u0627\u0644\u0634\u0647\u0631\u064A"
|
|
24136
|
+
selfHostedFreeSubdomains: "{freeSubdomains} \u0646\u0637\u0627\u0642 \u0641\u0631\u0639\u064A \u0645\u062C\u0627\u0646\u064A \u0639\u0644\u0649 {freeDomainSuffix} \u0639\u0646\u062F \u0627\u0644\u0631\u0628\u0637 \u0645\u0639 Openship Cloud"
|
|
23554
24137
|
},
|
|
23555
24138
|
selfHosted: {
|
|
23556
24139
|
name: "\u0627\u0633\u062A\u0636\u0627\u0641\u0629 \u0630\u0627\u062A\u064A\u0629",
|
|
@@ -23560,9 +24143,9 @@ var ar_default2 = {
|
|
|
23560
24143
|
cta: "\u0627\u0628\u062F\u0623 \u0627\u0644\u0627\u0633\u062A\u0636\u0627\u0641\u0629 \u0627\u0644\u0630\u0627\u062A\u064A\u0629"
|
|
23561
24144
|
},
|
|
23562
24145
|
creditPacks: {
|
|
23563
|
-
pack_5k: "{
|
|
23564
|
-
pack_25k: "{
|
|
23565
|
-
pack_100k: "{
|
|
24146
|
+
pack_5k: "{computeMinutes} \u062F\u0642\u064A\u0642\u0629 \u062D\u0648\u0633\u0628\u0629",
|
|
24147
|
+
pack_25k: "{computeMinutes} \u062F\u0642\u064A\u0642\u0629 \u062D\u0648\u0633\u0628\u0629",
|
|
24148
|
+
pack_100k: "{computeMinutes} \u062F\u0642\u064A\u0642\u0629 \u062D\u0648\u0633\u0628\u0629"
|
|
23566
24149
|
},
|
|
23567
24150
|
ui: {
|
|
23568
24151
|
perMonth: "/\u0634\u0647\u0631",
|
|
@@ -23578,7 +24161,9 @@ var ar_default2 = {
|
|
|
23578
24161
|
billedMonthly: "\u0641\u0627\u062A\u0648\u0631\u0629 \u0634\u0647\u0631\u064A\u0629\u060C \u0648\u0627\u0644\u0625\u0644\u063A\u0627\u0621 \u0645\u062A\u0627\u062D \u0641\u064A \u0623\u064A \u0648\u0642\u062A",
|
|
23579
24162
|
campaignBadge: "\u062E\u0635\u0645 {percentOff}%",
|
|
23580
24163
|
campaignEnds: "\u064A\u0646\u062A\u0647\u064A \u0627\u0644\u0639\u0631\u0636 \u0641\u064A {date}",
|
|
23581
|
-
wasPrice: "\u0633\u0627\u0628\u0642\u064B\u0627 {price}"
|
|
24164
|
+
wasPrice: "\u0633\u0627\u0628\u0642\u064B\u0627 {price}",
|
|
24165
|
+
standardTitle: "\u0641\u064A \u0643\u0644 \u0627\u0644\u062E\u0637\u0637",
|
|
24166
|
+
creditPackNote: "\u2248 {appHours} \u0633\u0627\u0639\u0629 \u0644\u062A\u0637\u0628\u064A\u0642 \u0635\u063A\u064A\u0631\u060C \u0623\u0648 {buildMinutes} \u062F\u0642\u064A\u0642\u0629 \u0628\u0646\u0627\u0621"
|
|
23582
24167
|
}
|
|
23583
24168
|
};
|
|
23584
24169
|
|
|
@@ -23609,20 +24194,20 @@ var de_default2 = {
|
|
|
23609
24194
|
features: {
|
|
23610
24195
|
everythingIn: "Alles aus {inherited}, plus:",
|
|
23611
24196
|
staticOnly: "Nur statische Websites \u2013 SSG, SPA und reines HTML",
|
|
23612
|
-
anyWorkload: "Beliebige Workloads \u2013 Container, Compose
|
|
23613
|
-
|
|
24197
|
+
anyWorkload: "Beliebige Workloads \u2013 Container, Compose, Worker, Postgres, Redis",
|
|
24198
|
+
runningServices: "{runningServices} laufende Apps, Datenbanken oder Worker",
|
|
24199
|
+
computeMinutes: "{computeMinutes} Compute-Minuten pro Monat",
|
|
23614
24200
|
buildMinutes: "{buildMinutes} Build-Minuten pro Monat",
|
|
23615
|
-
|
|
24201
|
+
power: "Bis zu {powerCpu} vCPU und {powerRamGb} GB RAM pro App",
|
|
24202
|
+
projects: "{maxProjects} Projekte",
|
|
24203
|
+
projectsUnlimited: "Unbegrenzt viele Projekte",
|
|
23616
24204
|
freeSubdomains: "{freeSubdomains} kostenlose Subdomains unter {freeDomainSuffix}",
|
|
23617
24205
|
freeSubdomainsUnlimited: "Unbegrenzt viele kostenlose Subdomains unter {freeDomainSuffix}",
|
|
24206
|
+
gitDeploys: "Deployen von GitHub, GitLab oder jedem Git-Remote",
|
|
23618
24207
|
customDomains: "Unbegrenzt viele eigene Domains, TLS inklusive",
|
|
24208
|
+
deployLogs: "Live-Logs f\xFCr Build und Deployment",
|
|
24209
|
+
auditLog: "Audit-Log f\xFCr jede \xC4nderung",
|
|
23619
24210
|
unlimitedSeats: "Unbegrenzt viele Teammitglieder \u2013 nie Kosten pro Nutzer",
|
|
23620
|
-
previewDeploys: "Preview-Deploys bei jedem Push",
|
|
23621
|
-
backups: "T\xE4gliche Backups mit Point-in-Time-Recovery",
|
|
23622
|
-
observability: "Live-Logs, Metriken und Health-Alerts",
|
|
23623
|
-
mailServer: "Integrierter Mailserver \u2013 SMTP, IMAP und Webmail",
|
|
23624
|
-
auditLog: "Audit-Log mit verl\xE4ngerter Aufbewahrungsdauer",
|
|
23625
|
-
sharedChannel: "Gemeinsamer Slack- oder Discord-Channel",
|
|
23626
24211
|
sso: "SSO und SAML",
|
|
23627
24212
|
customLimits: "Individuelle Limits, vertraglich vereinbart",
|
|
23628
24213
|
dedicatedInfra: "Dedizierte Infrastruktur und Regionen",
|
|
@@ -23635,12 +24220,7 @@ var de_default2 = {
|
|
|
23635
24220
|
selfHostedUnlimited: "Unbegrenzt viele Deploys, Projekte, Server und Domains",
|
|
23636
24221
|
selfHostedServices: "Alle Managed Services \u2013 Postgres, Redis, Mail",
|
|
23637
24222
|
selfHostedClients: "CLI, Web und Desktop \u2013 ein Backend",
|
|
23638
|
-
selfHostedFreeSubdomains: "{freeSubdomains} kostenlose Subdomains unter {freeDomainSuffix} bei Verbindung mit Openship Cloud"
|
|
23639
|
-
runningServices: "{runningServices} laufende Services \u2013 Apps, Datenbanken, Worker",
|
|
23640
|
-
power: "Bis zu {powerCpu} vCPU und {powerRamGb} GB RAM pro Service",
|
|
23641
|
-
projects: "{maxProjects} Projekte",
|
|
23642
|
-
projectsUnlimited: "Unbegrenzt viele Projekte",
|
|
23643
|
-
meteredCredits: "Compute, Daten\xFCbertragung und Speicher werden alle \xFCber die monatlichen Credits abgerechnet"
|
|
24223
|
+
selfHostedFreeSubdomains: "{freeSubdomains} kostenlose Subdomains unter {freeDomainSuffix} bei Verbindung mit Openship Cloud"
|
|
23644
24224
|
},
|
|
23645
24225
|
selfHosted: {
|
|
23646
24226
|
name: "Selbst gehostet",
|
|
@@ -23650,9 +24230,9 @@ var de_default2 = {
|
|
|
23650
24230
|
cta: "Self-Hosting starten"
|
|
23651
24231
|
},
|
|
23652
24232
|
creditPacks: {
|
|
23653
|
-
pack_5k: "{
|
|
23654
|
-
pack_25k: "{
|
|
23655
|
-
pack_100k: "{
|
|
24233
|
+
pack_5k: "{computeMinutes} Compute-Minuten",
|
|
24234
|
+
pack_25k: "{computeMinutes} Compute-Minuten",
|
|
24235
|
+
pack_100k: "{computeMinutes} Compute-Minuten"
|
|
23656
24236
|
},
|
|
23657
24237
|
ui: {
|
|
23658
24238
|
perMonth: "/Mon.",
|
|
@@ -23668,7 +24248,9 @@ var de_default2 = {
|
|
|
23668
24248
|
billedMonthly: "monatliche Abrechnung, jederzeit k\xFCndbar",
|
|
23669
24249
|
campaignBadge: "{percentOff} % Rabatt",
|
|
23670
24250
|
campaignEnds: "Angebot endet am {date}",
|
|
23671
|
-
wasPrice: "vorher {price}"
|
|
24251
|
+
wasPrice: "vorher {price}",
|
|
24252
|
+
standardTitle: "In jedem Plan enthalten",
|
|
24253
|
+
creditPackNote: "\u2248 {appHours} Stunden einer kleinen App oder {buildMinutes} Build-Minuten"
|
|
23672
24254
|
}
|
|
23673
24255
|
};
|
|
23674
24256
|
|
|
@@ -23699,20 +24281,20 @@ var es_default2 = {
|
|
|
23699
24281
|
features: {
|
|
23700
24282
|
everythingIn: "Todo lo de {inherited}, y adem\xE1s:",
|
|
23701
24283
|
staticOnly: "Solo sitios est\xE1ticos: SSG, SPA y HTML puro",
|
|
23702
|
-
anyWorkload: "Ejecuta lo que sea: contenedores,
|
|
23703
|
-
|
|
24284
|
+
anyWorkload: "Ejecuta lo que sea: contenedores, Compose, workers, Postgres, Redis",
|
|
24285
|
+
runningServices: "{runningServices} apps, bases de datos o workers en ejecuci\xF3n",
|
|
24286
|
+
computeMinutes: "{computeMinutes} minutos de c\xF3mputo al mes",
|
|
23704
24287
|
buildMinutes: "{buildMinutes} minutos de build al mes",
|
|
23705
|
-
|
|
24288
|
+
power: "Hasta {powerCpu} vCPU y {powerRamGb} GB de RAM por app",
|
|
24289
|
+
projects: "{maxProjects} proyectos",
|
|
24290
|
+
projectsUnlimited: "Proyectos ilimitados",
|
|
23706
24291
|
freeSubdomains: "{freeSubdomains} subdominios {freeDomainSuffix} gratis",
|
|
23707
24292
|
freeSubdomainsUnlimited: "Subdominios {freeDomainSuffix} gratis e ilimitados",
|
|
24293
|
+
gitDeploys: "Despliega desde GitHub, GitLab o cualquier remoto Git",
|
|
23708
24294
|
customDomains: "Dominios propios ilimitados, con TLS incluido",
|
|
24295
|
+
deployLogs: "Logs en vivo de build y despliegue",
|
|
24296
|
+
auditLog: "Registro de auditor\xEDa de cada cambio",
|
|
23709
24297
|
unlimitedSeats: "Miembros de equipo ilimitados: nunca pagas por usuario",
|
|
23710
|
-
previewDeploys: "Despliegues de vista previa en cada push",
|
|
23711
|
-
backups: "Copias de seguridad diarias con recuperaci\xF3n a un punto en el tiempo",
|
|
23712
|
-
observability: "Logs en vivo, m\xE9tricas y alertas de estado",
|
|
23713
|
-
mailServer: "Servidor de correo integrado: SMTP, IMAP y webmail",
|
|
23714
|
-
auditLog: "Registro de auditor\xEDa con retenci\xF3n ampliada",
|
|
23715
|
-
sharedChannel: "Canal compartido en Slack o Discord",
|
|
23716
24298
|
sso: "SSO y SAML",
|
|
23717
24299
|
customLimits: "L\xEDmites a medida, negociados por contrato",
|
|
23718
24300
|
dedicatedInfra: "Infraestructura y regiones dedicadas",
|
|
@@ -23725,12 +24307,7 @@ var es_default2 = {
|
|
|
23725
24307
|
selfHostedUnlimited: "Despliegues, proyectos, servidores y dominios ilimitados",
|
|
23726
24308
|
selfHostedServices: "Todos los servicios gestionados: Postgres, Redis, correo",
|
|
23727
24309
|
selfHostedClients: "CLI, web y escritorio: un solo backend",
|
|
23728
|
-
selfHostedFreeSubdomains: "{freeSubdomains} subdominios {freeDomainSuffix} gratis al conectarte a Cloud"
|
|
23729
|
-
runningServices: "{runningServices} servicios en ejecuci\xF3n: apps, bases de datos, workers",
|
|
23730
|
-
power: "Hasta {powerCpu} vCPU y {powerRamGb} GB de RAM por servicio",
|
|
23731
|
-
projects: "{maxProjects} proyectos",
|
|
23732
|
-
projectsUnlimited: "Proyectos ilimitados",
|
|
23733
|
-
meteredCredits: "El c\xF3mputo, el ancho de banda y el almacenamiento se descuentan de tus cr\xE9ditos mensuales"
|
|
24310
|
+
selfHostedFreeSubdomains: "{freeSubdomains} subdominios {freeDomainSuffix} gratis al conectarte a Cloud"
|
|
23734
24311
|
},
|
|
23735
24312
|
selfHosted: {
|
|
23736
24313
|
name: "Autoalojado",
|
|
@@ -23740,9 +24317,9 @@ var es_default2 = {
|
|
|
23740
24317
|
cta: "Empezar a autoalojar"
|
|
23741
24318
|
},
|
|
23742
24319
|
creditPacks: {
|
|
23743
|
-
pack_5k: "{
|
|
23744
|
-
pack_25k: "{
|
|
23745
|
-
pack_100k: "{
|
|
24320
|
+
pack_5k: "{computeMinutes} minutos de c\xF3mputo",
|
|
24321
|
+
pack_25k: "{computeMinutes} minutos de c\xF3mputo",
|
|
24322
|
+
pack_100k: "{computeMinutes} minutos de c\xF3mputo"
|
|
23746
24323
|
},
|
|
23747
24324
|
ui: {
|
|
23748
24325
|
perMonth: "/mes",
|
|
@@ -23758,7 +24335,9 @@ var es_default2 = {
|
|
|
23758
24335
|
billedMonthly: "facturaci\xF3n mensual, cancela cuando quieras",
|
|
23759
24336
|
campaignBadge: "{percentOff}% de descuento",
|
|
23760
24337
|
campaignEnds: "La oferta termina el {date}",
|
|
23761
|
-
wasPrice: "antes {price}"
|
|
24338
|
+
wasPrice: "antes {price}",
|
|
24339
|
+
standardTitle: "En todos los planes",
|
|
24340
|
+
creditPackNote: "\u2248 {appHours} horas de una app peque\xF1a, o {buildMinutes} minutos de build"
|
|
23762
24341
|
}
|
|
23763
24342
|
};
|
|
23764
24343
|
|
|
@@ -23789,20 +24368,20 @@ var fr_default2 = {
|
|
|
23789
24368
|
features: {
|
|
23790
24369
|
everythingIn: "Tout ce que contient {inherited}, plus :",
|
|
23791
24370
|
staticOnly: "Sites statiques uniquement \u2014 SSG, SPA et HTML brut",
|
|
23792
|
-
anyWorkload: "Ex\xE9cutez
|
|
23793
|
-
|
|
24371
|
+
anyWorkload: "Ex\xE9cutez tout \u2014 conteneurs, Compose, workers, Postgres, Redis",
|
|
24372
|
+
runningServices: "{runningServices} applications, bases de donn\xE9es ou workers actifs",
|
|
24373
|
+
computeMinutes: "{computeMinutes} minutes de calcul par mois",
|
|
23794
24374
|
buildMinutes: "{buildMinutes} minutes de build par mois",
|
|
23795
|
-
|
|
24375
|
+
power: "Jusqu'\xE0 {powerCpu} vCPU et {powerRamGb} GB de RAM par application",
|
|
24376
|
+
projects: "{maxProjects} projets",
|
|
24377
|
+
projectsUnlimited: "Projets illimit\xE9s",
|
|
23796
24378
|
freeSubdomains: "{freeSubdomains} sous-domaines {freeDomainSuffix} gratuits",
|
|
23797
24379
|
freeSubdomainsUnlimited: "Sous-domaines {freeDomainSuffix} gratuits en illimit\xE9",
|
|
24380
|
+
gitDeploys: "D\xE9ployez depuis GitHub, GitLab ou n'importe quel d\xE9p\xF4t Git",
|
|
23798
24381
|
customDomains: "Domaines personnalis\xE9s illimit\xE9s, TLS inclus",
|
|
24382
|
+
deployLogs: "Logs de build et de d\xE9ploiement en direct",
|
|
24383
|
+
auditLog: "Journal d'audit de chaque modification",
|
|
23799
24384
|
unlimitedSeats: "Membres d'\xE9quipe illimit\xE9s \u2014 jamais de facturation par utilisateur",
|
|
23800
|
-
previewDeploys: "D\xE9ploiements de pr\xE9visualisation \xE0 chaque push",
|
|
23801
|
-
backups: "Sauvegardes quotidiennes avec restauration \xE0 un instant pr\xE9cis",
|
|
23802
|
-
observability: "Logs en direct, m\xE9triques et alertes de sant\xE9",
|
|
23803
|
-
mailServer: "Serveur mail int\xE9gr\xE9 \u2014 SMTP, IMAP et webmail",
|
|
23804
|
-
auditLog: "Journal d'audit avec r\xE9tention \xE9tendue",
|
|
23805
|
-
sharedChannel: "Canal Slack ou Discord partag\xE9",
|
|
23806
24385
|
sso: "SSO et SAML",
|
|
23807
24386
|
customLimits: "Limites sur mesure, n\xE9goci\xE9es par contrat",
|
|
23808
24387
|
dedicatedInfra: "Infrastructure et r\xE9gions d\xE9di\xE9es",
|
|
@@ -23815,12 +24394,7 @@ var fr_default2 = {
|
|
|
23815
24394
|
selfHostedUnlimited: "D\xE9ploiements, projets, serveurs et domaines illimit\xE9s",
|
|
23816
24395
|
selfHostedServices: "Tous les services manag\xE9s \u2014 Postgres, Redis, mail",
|
|
23817
24396
|
selfHostedClients: "CLI, web et desktop \u2014 un seul backend",
|
|
23818
|
-
selfHostedFreeSubdomains: "{freeSubdomains} sous-domaines {freeDomainSuffix} gratuits en vous connectant au Cloud"
|
|
23819
|
-
runningServices: "{runningServices} services actifs \u2014 applications, bases de donn\xE9es, workers",
|
|
23820
|
-
power: "Jusqu'\xE0 {powerCpu} vCPU et {powerRamGb} GB de RAM par service",
|
|
23821
|
-
projects: "{maxProjects} projets",
|
|
23822
|
-
projectsUnlimited: "Projets illimit\xE9s",
|
|
23823
|
-
meteredCredits: "Le calcul, la bande passante et le stockage sont tous d\xE9duits de vos cr\xE9dits mensuels"
|
|
24397
|
+
selfHostedFreeSubdomains: "{freeSubdomains} sous-domaines {freeDomainSuffix} gratuits en vous connectant au Cloud"
|
|
23824
24398
|
},
|
|
23825
24399
|
selfHosted: {
|
|
23826
24400
|
name: "Auto-h\xE9berg\xE9",
|
|
@@ -23830,9 +24404,9 @@ var fr_default2 = {
|
|
|
23830
24404
|
cta: "Commencer l'auto-h\xE9bergement"
|
|
23831
24405
|
},
|
|
23832
24406
|
creditPacks: {
|
|
23833
|
-
pack_5k: "{
|
|
23834
|
-
pack_25k: "{
|
|
23835
|
-
pack_100k: "{
|
|
24407
|
+
pack_5k: "{computeMinutes} minutes de calcul",
|
|
24408
|
+
pack_25k: "{computeMinutes} minutes de calcul",
|
|
24409
|
+
pack_100k: "{computeMinutes} minutes de calcul"
|
|
23836
24410
|
},
|
|
23837
24411
|
ui: {
|
|
23838
24412
|
perMonth: "/mois",
|
|
@@ -23848,7 +24422,9 @@ var fr_default2 = {
|
|
|
23848
24422
|
billedMonthly: "factur\xE9 mensuellement, r\xE9siliable \xE0 tout moment",
|
|
23849
24423
|
campaignBadge: "{percentOff} % de remise",
|
|
23850
24424
|
campaignEnds: "L'offre se termine le {date}",
|
|
23851
|
-
wasPrice: "avant {price}"
|
|
24425
|
+
wasPrice: "avant {price}",
|
|
24426
|
+
standardTitle: "Dans tous les forfaits",
|
|
24427
|
+
creditPackNote: "\u2248 {appHours} heures d'une petite application, ou {buildMinutes} minutes de build"
|
|
23852
24428
|
}
|
|
23853
24429
|
};
|
|
23854
24430
|
|
|
@@ -23879,20 +24455,20 @@ var ja_default2 = {
|
|
|
23879
24455
|
features: {
|
|
23880
24456
|
everythingIn: "{inherited} \u306E\u5168\u6A5F\u80FD\u306B\u52A0\u3048\u3066:",
|
|
23881
24457
|
staticOnly: "\u9759\u7684\u30B5\u30A4\u30C8\u306E\u307F \u2014 SSG\u3001SPA\u3001\u7D20\u306E HTML",
|
|
23882
|
-
anyWorkload: "\u4F55\u3067\u3082\u52D5\u304B\u305B\u308B \u2014 \u30B3\u30F3\u30C6\u30CA\u3001Compose
|
|
23883
|
-
|
|
24458
|
+
anyWorkload: "\u4F55\u3067\u3082\u52D5\u304B\u305B\u308B \u2014 \u30B3\u30F3\u30C6\u30CA\u3001Compose\u3001\u30EF\u30FC\u30AB\u30FC\u3001Postgres\u3001Redis",
|
|
24459
|
+
runningServices: "\u540C\u6642\u306B\u7A3C\u50CD\u3059\u308B\u30A2\u30D7\u30EA\u30FB\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u30FB\u30EF\u30FC\u30AB\u30FC {runningServices} \u500B",
|
|
24460
|
+
computeMinutes: "\u6BCE\u6708 {computeMinutes} \u30B3\u30F3\u30D4\u30E5\u30FC\u30C8\u5206",
|
|
23884
24461
|
buildMinutes: "\u6BCE\u6708 {buildMinutes} \u5206\u306E\u30D3\u30EB\u30C9\u6642\u9593",
|
|
23885
|
-
|
|
24462
|
+
power: "\u30A2\u30D7\u30EA 1 \u3064\u3042\u305F\u308A\u6700\u5927 {powerCpu} vCPU \u3068 {powerRamGb} GB RAM",
|
|
24463
|
+
projects: "\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8 {maxProjects} \u500B",
|
|
24464
|
+
projectsUnlimited: "\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u306F\u7121\u5236\u9650",
|
|
23886
24465
|
freeSubdomains: "\u7121\u6599\u306E {freeDomainSuffix} \u30B5\u30D6\u30C9\u30E1\u30A4\u30F3 {freeSubdomains} \u500B",
|
|
23887
24466
|
freeSubdomainsUnlimited: "\u7121\u6599\u306E {freeDomainSuffix} \u30B5\u30D6\u30C9\u30E1\u30A4\u30F3\u304C\u7121\u5236\u9650",
|
|
24467
|
+
gitDeploys: "GitHub\u3001GitLab\u3001\u4EFB\u610F\u306E Git \u30EA\u30E2\u30FC\u30C8\u304B\u3089\u30C7\u30D7\u30ED\u30A4",
|
|
23888
24468
|
customDomains: "\u30AB\u30B9\u30BF\u30E0\u30C9\u30E1\u30A4\u30F3\u306F\u7121\u5236\u9650\u3001TLS \u8FBC\u307F",
|
|
24469
|
+
deployLogs: "\u30D3\u30EB\u30C9\u3068\u30C7\u30D7\u30ED\u30A4\u306E\u30E9\u30A4\u30D6\u30ED\u30B0",
|
|
24470
|
+
auditLog: "\u3059\u3079\u3066\u306E\u5909\u66F4\u3092\u8A18\u9332\u3059\u308B\u76E3\u67FB\u30ED\u30B0",
|
|
23889
24471
|
unlimitedSeats: "\u30C1\u30FC\u30E0\u30E1\u30F3\u30D0\u30FC\u306F\u7121\u5236\u9650 \u2014 \u4EBA\u6570\u8AB2\u91D1\u306F\u4E00\u5207\u306A\u3057",
|
|
23890
|
-
previewDeploys: "\u30D7\u30C3\u30B7\u30E5\u3054\u3068\u306B\u30D7\u30EC\u30D3\u30E5\u30FC\u30C7\u30D7\u30ED\u30A4",
|
|
23891
|
-
backups: "\u65E5\u6B21\u30D0\u30C3\u30AF\u30A2\u30C3\u30D7\u3068\u30DD\u30A4\u30F3\u30C8\u30A4\u30F3\u30BF\u30A4\u30E0\u30EA\u30AB\u30D0\u30EA",
|
|
23892
|
-
observability: "\u30E9\u30A4\u30D6\u30ED\u30B0\u3001\u30E1\u30C8\u30EA\u30AF\u30B9\u3001\u30D8\u30EB\u30B9\u30A2\u30E9\u30FC\u30C8",
|
|
23893
|
-
mailServer: "\u30E1\u30FC\u30EB\u30B5\u30FC\u30D0\u30FC\u5185\u8535 \u2014 SMTP\u3001IMAP\u3001\u30A6\u30A7\u30D6\u30E1\u30FC\u30EB",
|
|
23894
|
-
auditLog: "\u4FDD\u6301\u671F\u9593\u3092\u5EF6\u9577\u3057\u305F\u76E3\u67FB\u30ED\u30B0",
|
|
23895
|
-
sharedChannel: "Slack \u307E\u305F\u306F Discord \u306E\u5171\u6709\u30C1\u30E3\u30F3\u30CD\u30EB",
|
|
23896
24472
|
sso: "SSO \u3068 SAML",
|
|
23897
24473
|
customLimits: "\u5951\u7D04\u3054\u3068\u306B\u8ABF\u6574\u3059\u308B\u30AB\u30B9\u30BF\u30E0\u306E\u4E0A\u9650",
|
|
23898
24474
|
dedicatedInfra: "\u5C02\u7528\u30A4\u30F3\u30D5\u30E9\u3068\u5C02\u7528\u30EA\u30FC\u30B8\u30E7\u30F3",
|
|
@@ -23905,12 +24481,7 @@ var ja_default2 = {
|
|
|
23905
24481
|
selfHostedUnlimited: "\u30C7\u30D7\u30ED\u30A4\u3001\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3001\u30B5\u30FC\u30D0\u30FC\u3001\u30C9\u30E1\u30A4\u30F3\u3059\u3079\u3066\u7121\u5236\u9650",
|
|
23906
24482
|
selfHostedServices: "\u30DE\u30CD\u30FC\u30B8\u30C9\u30B5\u30FC\u30D3\u30B9\u306F\u3059\u3079\u3066\u5229\u7528\u53EF\u80FD \u2014 Postgres\u3001Redis\u3001\u30E1\u30FC\u30EB",
|
|
23907
24483
|
selfHostedClients: "CLI\u3001Web\u3001\u30C7\u30B9\u30AF\u30C8\u30C3\u30D7 \u2014 \u30D0\u30C3\u30AF\u30A8\u30F3\u30C9\u306F\u4E00\u3064",
|
|
23908
|
-
selfHostedFreeSubdomains: "Openship Cloud \u306B\u63A5\u7D9A\u3059\u308B\u3068\u3001\u7121\u6599\u306E {freeDomainSuffix} \u30B5\u30D6\u30C9\u30E1\u30A4\u30F3\u304C {freeSubdomains} \u500B"
|
|
23909
|
-
runningServices: "\u540C\u6642\u306B\u7A3C\u50CD\u3059\u308B\u30B5\u30FC\u30D3\u30B9 {runningServices} \u500B \u2014 \u30A2\u30D7\u30EA\u3001\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u3001\u30EF\u30FC\u30AB\u30FC",
|
|
23910
|
-
power: "\u30B5\u30FC\u30D3\u30B9 1 \u3064\u3042\u305F\u308A\u6700\u5927 {powerCpu} vCPU \u3068 {powerRamGb} GB RAM",
|
|
23911
|
-
projects: "\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8 {maxProjects} \u500B",
|
|
23912
|
-
projectsUnlimited: "\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u306F\u7121\u5236\u9650",
|
|
23913
|
-
meteredCredits: "\u30B3\u30F3\u30D4\u30E5\u30FC\u30C8\u3001\u5E2F\u57DF\u5E45\u3001\u30B9\u30C8\u30EC\u30FC\u30B8\u306F\u3059\u3079\u3066\u6BCE\u6708\u306E\u30AF\u30EC\u30B8\u30C3\u30C8\u304B\u3089\u6D88\u8CBB\u3055\u308C\u307E\u3059"
|
|
24484
|
+
selfHostedFreeSubdomains: "Openship Cloud \u306B\u63A5\u7D9A\u3059\u308B\u3068\u3001\u7121\u6599\u306E {freeDomainSuffix} \u30B5\u30D6\u30C9\u30E1\u30A4\u30F3\u304C {freeSubdomains} \u500B"
|
|
23914
24485
|
},
|
|
23915
24486
|
selfHosted: {
|
|
23916
24487
|
name: "\u30BB\u30EB\u30D5\u30DB\u30B9\u30C8",
|
|
@@ -23920,9 +24491,9 @@ var ja_default2 = {
|
|
|
23920
24491
|
cta: "\u30BB\u30EB\u30D5\u30DB\u30B9\u30C8\u3092\u59CB\u3081\u308B"
|
|
23921
24492
|
},
|
|
23922
24493
|
creditPacks: {
|
|
23923
|
-
pack_5k: "{
|
|
23924
|
-
pack_25k: "{
|
|
23925
|
-
pack_100k: "{
|
|
24494
|
+
pack_5k: "{computeMinutes} \u30B3\u30F3\u30D4\u30E5\u30FC\u30C8\u5206",
|
|
24495
|
+
pack_25k: "{computeMinutes} \u30B3\u30F3\u30D4\u30E5\u30FC\u30C8\u5206",
|
|
24496
|
+
pack_100k: "{computeMinutes} \u30B3\u30F3\u30D4\u30E5\u30FC\u30C8\u5206"
|
|
23926
24497
|
},
|
|
23927
24498
|
ui: {
|
|
23928
24499
|
perMonth: "/\u6708",
|
|
@@ -23938,7 +24509,9 @@ var ja_default2 = {
|
|
|
23938
24509
|
billedMonthly: "\u6708\u984D\u8ACB\u6C42\u3001\u3044\u3064\u3067\u3082\u30AD\u30E3\u30F3\u30BB\u30EB\u53EF\u80FD",
|
|
23939
24510
|
campaignBadge: "{percentOff}% \u30AA\u30D5",
|
|
23940
24511
|
campaignEnds: "\u30AD\u30E3\u30F3\u30DA\u30FC\u30F3\u306F {date} \u307E\u3067",
|
|
23941
|
-
wasPrice: "\u901A\u5E38 {price}"
|
|
24512
|
+
wasPrice: "\u901A\u5E38 {price}",
|
|
24513
|
+
standardTitle: "\u3059\u3079\u3066\u306E\u30D7\u30E9\u30F3\u306B\u542B\u307E\u308C\u308B\u3082\u306E",
|
|
24514
|
+
creditPackNote: "\u5C0F\u898F\u6A21\u30A2\u30D7\u30EA\u7D04 {appHours} \u6642\u9593\u3001\u307E\u305F\u306F\u30D3\u30EB\u30C9 {buildMinutes} \u5206"
|
|
23942
24515
|
}
|
|
23943
24516
|
};
|
|
23944
24517
|
|
|
@@ -23969,20 +24542,20 @@ var pt_default2 = {
|
|
|
23969
24542
|
features: {
|
|
23970
24543
|
everythingIn: "Tudo do {inherited}, e mais:",
|
|
23971
24544
|
staticOnly: "Somente sites est\xE1ticos: SSG, SPA e HTML puro",
|
|
23972
|
-
anyWorkload: "Execute qualquer coisa: cont\xEAineres,
|
|
23973
|
-
|
|
24545
|
+
anyWorkload: "Execute qualquer coisa: cont\xEAineres, Compose, workers, Postgres, Redis",
|
|
24546
|
+
runningServices: "{runningServices} apps, bancos de dados ou workers em execu\xE7\xE3o",
|
|
24547
|
+
computeMinutes: "{computeMinutes} minutos de computa\xE7\xE3o por m\xEAs",
|
|
23974
24548
|
buildMinutes: "{buildMinutes} minutos de build por m\xEAs",
|
|
23975
|
-
|
|
24549
|
+
power: "At\xE9 {powerCpu} vCPU e {powerRamGb} GB de RAM por app",
|
|
24550
|
+
projects: "{maxProjects} projetos",
|
|
24551
|
+
projectsUnlimited: "Projetos ilimitados",
|
|
23976
24552
|
freeSubdomains: "{freeSubdomains} subdom\xEDnios {freeDomainSuffix} gratuitos",
|
|
23977
24553
|
freeSubdomainsUnlimited: "Subdom\xEDnios {freeDomainSuffix} gratuitos e ilimitados",
|
|
24554
|
+
gitDeploys: "Fa\xE7a deploy do GitHub, GitLab ou qualquer remoto Git",
|
|
23978
24555
|
customDomains: "Dom\xEDnios pr\xF3prios ilimitados, com TLS inclu\xEDdo",
|
|
24556
|
+
deployLogs: "Logs de build e deploy em tempo real",
|
|
24557
|
+
auditLog: "Log de auditoria de cada altera\xE7\xE3o",
|
|
23979
24558
|
unlimitedSeats: "Membros de equipe ilimitados: nunca pague por usu\xE1rio",
|
|
23980
|
-
previewDeploys: "Deploys de pr\xE9-visualiza\xE7\xE3o a cada push",
|
|
23981
|
-
backups: "Backups di\xE1rios com recupera\xE7\xE3o para um ponto no tempo",
|
|
23982
|
-
observability: "Logs em tempo real, m\xE9tricas e alertas de sa\xFAde",
|
|
23983
|
-
mailServer: "Servidor de e-mail integrado: SMTP, IMAP e webmail",
|
|
23984
|
-
auditLog: "Log de auditoria com reten\xE7\xE3o estendida",
|
|
23985
|
-
sharedChannel: "Canal compartilhado no Slack ou Discord",
|
|
23986
24559
|
sso: "SSO e SAML",
|
|
23987
24560
|
customLimits: "Limites sob medida, negociados por contrato",
|
|
23988
24561
|
dedicatedInfra: "Infraestrutura e regi\xF5es dedicadas",
|
|
@@ -23995,12 +24568,7 @@ var pt_default2 = {
|
|
|
23995
24568
|
selfHostedUnlimited: "Deploys, projetos, servidores e dom\xEDnios ilimitados",
|
|
23996
24569
|
selfHostedServices: "Todos os servi\xE7os gerenciados: Postgres, Redis, e-mail",
|
|
23997
24570
|
selfHostedClients: "CLI, web e desktop: um \xFAnico backend",
|
|
23998
|
-
selfHostedFreeSubdomains: "{freeSubdomains} subdom\xEDnios {freeDomainSuffix} gratuitos ao conectar \xE0 Cloud"
|
|
23999
|
-
runningServices: "{runningServices} servi\xE7os em execu\xE7\xE3o: apps, bancos de dados, workers",
|
|
24000
|
-
power: "At\xE9 {powerCpu} vCPU e {powerRamGb} GB de RAM por servi\xE7o",
|
|
24001
|
-
projects: "{maxProjects} projetos",
|
|
24002
|
-
projectsUnlimited: "Projetos ilimitados",
|
|
24003
|
-
meteredCredits: "Computa\xE7\xE3o, largura de banda e armazenamento consomem os seus cr\xE9ditos mensais"
|
|
24571
|
+
selfHostedFreeSubdomains: "{freeSubdomains} subdom\xEDnios {freeDomainSuffix} gratuitos ao conectar \xE0 Cloud"
|
|
24004
24572
|
},
|
|
24005
24573
|
selfHosted: {
|
|
24006
24574
|
name: "Auto-hospedado",
|
|
@@ -24010,9 +24578,9 @@ var pt_default2 = {
|
|
|
24010
24578
|
cta: "Come\xE7ar a auto-hospedar"
|
|
24011
24579
|
},
|
|
24012
24580
|
creditPacks: {
|
|
24013
|
-
pack_5k: "{
|
|
24014
|
-
pack_25k: "{
|
|
24015
|
-
pack_100k: "{
|
|
24581
|
+
pack_5k: "{computeMinutes} minutos de computa\xE7\xE3o",
|
|
24582
|
+
pack_25k: "{computeMinutes} minutos de computa\xE7\xE3o",
|
|
24583
|
+
pack_100k: "{computeMinutes} minutos de computa\xE7\xE3o"
|
|
24016
24584
|
},
|
|
24017
24585
|
ui: {
|
|
24018
24586
|
perMonth: "/m\xEAs",
|
|
@@ -24028,7 +24596,9 @@ var pt_default2 = {
|
|
|
24028
24596
|
billedMonthly: "cobran\xE7a mensal, cancele quando quiser",
|
|
24029
24597
|
campaignBadge: "{percentOff}% de desconto",
|
|
24030
24598
|
campaignEnds: "Oferta termina em {date}",
|
|
24031
|
-
wasPrice: "antes {price}"
|
|
24599
|
+
wasPrice: "antes {price}",
|
|
24600
|
+
standardTitle: "Em todos os planos",
|
|
24601
|
+
creditPackNote: "\u2248 {appHours} horas de um app pequeno, ou {buildMinutes} minutos de build"
|
|
24032
24602
|
}
|
|
24033
24603
|
};
|
|
24034
24604
|
|
|
@@ -24059,20 +24629,20 @@ var tr_default2 = {
|
|
|
24059
24629
|
features: {
|
|
24060
24630
|
everythingIn: "{inherited} plan\u0131ndaki her \u015Fey, ayr\u0131ca:",
|
|
24061
24631
|
staticOnly: "Yaln\u0131zca statik siteler \u2014 SSG, SPA ve d\xFCz HTML",
|
|
24062
|
-
anyWorkload: "Her \u015Feyi \xE7al\u0131\u015Ft\u0131r\u0131n \u2014 container
|
|
24063
|
-
|
|
24632
|
+
anyWorkload: "Her \u015Feyi \xE7al\u0131\u015Ft\u0131r\u0131n \u2014 container, Compose, worker, Postgres, Redis",
|
|
24633
|
+
runningServices: "{runningServices} \xE7al\u0131\u015Fan uygulama, veritaban\u0131 veya worker",
|
|
24634
|
+
computeMinutes: "Ayda {computeMinutes} hesaplama dakikas\u0131",
|
|
24064
24635
|
buildMinutes: "Ayda {buildMinutes} build dakikas\u0131",
|
|
24065
|
-
|
|
24636
|
+
power: "Uygulama ba\u015F\u0131na en fazla {powerCpu} vCPU ve {powerRamGb} GB RAM",
|
|
24637
|
+
projects: "{maxProjects} proje",
|
|
24638
|
+
projectsUnlimited: "S\u0131n\u0131rs\u0131z proje",
|
|
24066
24639
|
freeSubdomains: "{freeSubdomains} \xFCcretsiz {freeDomainSuffix} alt alan ad\u0131",
|
|
24067
24640
|
freeSubdomainsUnlimited: "S\u0131n\u0131rs\u0131z \xFCcretsiz {freeDomainSuffix} alt alan ad\u0131",
|
|
24641
|
+
gitDeploys: "GitHub, GitLab veya herhangi bir Git uzak deposundan da\u011F\u0131t\u0131n",
|
|
24068
24642
|
customDomains: "S\u0131n\u0131rs\u0131z \xF6zel alan ad\u0131, TLS dahil",
|
|
24643
|
+
deployLogs: "Canl\u0131 derleme ve da\u011F\u0131t\u0131m loglar\u0131",
|
|
24644
|
+
auditLog: "Her de\u011Fi\u015Fikli\u011Fin denetim kayd\u0131",
|
|
24069
24645
|
unlimitedSeats: "S\u0131n\u0131rs\u0131z ekip \xFCyesi \u2014 kullan\u0131c\u0131 ba\u015F\u0131na \xF6deme yok",
|
|
24070
|
-
previewDeploys: "Her push'ta \xF6nizleme da\u011F\u0131t\u0131m\u0131",
|
|
24071
|
-
backups: "G\xFCnl\xFCk yedekler ve belirli bir ana geri d\xF6n\xFC\u015F",
|
|
24072
|
-
observability: "Canl\u0131 loglar, metrikler ve sa\u011Fl\u0131k uyar\u0131lar\u0131",
|
|
24073
|
-
mailServer: "Dahili posta sunucusu \u2014 SMTP, IMAP ve webmail",
|
|
24074
|
-
auditLog: "Denetim kayd\u0131, uzat\u0131lm\u0131\u015F saklama s\xFCresiyle",
|
|
24075
|
-
sharedChannel: "Payla\u015F\u0131ml\u0131 Slack veya Discord kanal\u0131",
|
|
24076
24646
|
sso: "SSO ve SAML",
|
|
24077
24647
|
customLimits: "S\xF6zle\u015Fmeye g\xF6re belirlenen \xF6zel limitler",
|
|
24078
24648
|
dedicatedInfra: "Size ayr\u0131lm\u0131\u015F altyap\u0131 ve b\xF6lgeler",
|
|
@@ -24085,12 +24655,7 @@ var tr_default2 = {
|
|
|
24085
24655
|
selfHostedUnlimited: "S\u0131n\u0131rs\u0131z da\u011F\u0131t\u0131m, proje, sunucu ve alan ad\u0131",
|
|
24086
24656
|
selfHostedServices: "T\xFCm y\xF6netilen servisler \u2014 Postgres, Redis, posta",
|
|
24087
24657
|
selfHostedClients: "CLI, web ve masa\xFCst\xFC \u2014 tek backend",
|
|
24088
|
-
selfHostedFreeSubdomains: "Cloud'a ba\u011Fland\u0131\u011F\u0131n\u0131zda {freeSubdomains} \xFCcretsiz {freeDomainSuffix} alt alan ad\u0131"
|
|
24089
|
-
runningServices: "{runningServices} \xE7al\u0131\u015Fan servis \u2014 uygulamalar, veritabanlar\u0131, worker'lar",
|
|
24090
|
-
power: "Servis ba\u015F\u0131na en fazla {powerCpu} vCPU ve {powerRamGb} GB RAM",
|
|
24091
|
-
projects: "{maxProjects} proje",
|
|
24092
|
-
projectsUnlimited: "S\u0131n\u0131rs\u0131z proje",
|
|
24093
|
-
meteredCredits: "Hesaplama, bant geni\u015Fli\u011Fi ve depolaman\u0131n tamam\u0131 ayl\u0131k kredilerinizden d\xFC\u015Fer"
|
|
24658
|
+
selfHostedFreeSubdomains: "Cloud'a ba\u011Fland\u0131\u011F\u0131n\u0131zda {freeSubdomains} \xFCcretsiz {freeDomainSuffix} alt alan ad\u0131"
|
|
24094
24659
|
},
|
|
24095
24660
|
selfHosted: {
|
|
24096
24661
|
name: "Kendi sunucunuzda",
|
|
@@ -24100,9 +24665,9 @@ var tr_default2 = {
|
|
|
24100
24665
|
cta: "Kendi sunucunuzda ba\u015Flay\u0131n"
|
|
24101
24666
|
},
|
|
24102
24667
|
creditPacks: {
|
|
24103
|
-
pack_5k: "{
|
|
24104
|
-
pack_25k: "{
|
|
24105
|
-
pack_100k: "{
|
|
24668
|
+
pack_5k: "{computeMinutes} hesaplama dakikas\u0131",
|
|
24669
|
+
pack_25k: "{computeMinutes} hesaplama dakikas\u0131",
|
|
24670
|
+
pack_100k: "{computeMinutes} hesaplama dakikas\u0131"
|
|
24106
24671
|
},
|
|
24107
24672
|
ui: {
|
|
24108
24673
|
perMonth: "/ay",
|
|
@@ -24118,7 +24683,9 @@ var tr_default2 = {
|
|
|
24118
24683
|
billedMonthly: "ayl\u0131k faturalan\u0131r, istedi\u011Finiz zaman iptal edin",
|
|
24119
24684
|
campaignBadge: "%{percentOff} indirim",
|
|
24120
24685
|
campaignEnds: "Kampanya {date} tarihinde sona eriyor",
|
|
24121
|
-
wasPrice: "eski fiyat {price}"
|
|
24686
|
+
wasPrice: "eski fiyat {price}",
|
|
24687
|
+
standardTitle: "T\xFCm planlarda",
|
|
24688
|
+
creditPackNote: "\u2248 K\xFC\xE7\xFCk bir uygulama i\xE7in {appHours} saat veya {buildMinutes} derleme dakikas\u0131"
|
|
24122
24689
|
}
|
|
24123
24690
|
};
|
|
24124
24691
|
|
|
@@ -24149,20 +24716,20 @@ var zh_default = {
|
|
|
24149
24716
|
features: {
|
|
24150
24717
|
everythingIn: "\u5305\u542B {inherited} \u7684\u5168\u90E8\u529F\u80FD,\u53E6\u6709:",
|
|
24151
24718
|
staticOnly: "\u4EC5\u652F\u6301\u9759\u6001\u7AD9\u70B9:SSG\u3001SPA \u548C\u7EAF HTML",
|
|
24152
|
-
anyWorkload: "\u4EFB\u610F\u5DE5\u4F5C\u8D1F\u8F7D
|
|
24153
|
-
|
|
24719
|
+
anyWorkload: "\u4EFB\u610F\u5DE5\u4F5C\u8D1F\u8F7D:\u5BB9\u5668\u3001Compose\u3001\u540E\u53F0\u4EFB\u52A1\u3001Postgres\u3001Redis",
|
|
24720
|
+
runningServices: "{runningServices} \u4E2A\u540C\u65F6\u8FD0\u884C\u7684\u5E94\u7528\u3001\u6570\u636E\u5E93\u6216\u540E\u53F0\u4EFB\u52A1",
|
|
24721
|
+
computeMinutes: "\u6BCF\u6708 {computeMinutes} \u8BA1\u7B97\u5206\u949F",
|
|
24154
24722
|
buildMinutes: "\u6BCF\u6708 {buildMinutes} \u5206\u949F\u6784\u5EFA\u65F6\u957F",
|
|
24155
|
-
|
|
24723
|
+
power: "\u6BCF\u4E2A\u5E94\u7528\u6700\u9AD8\u53EF\u8FBE {powerCpu} vCPU \u548C {powerRamGb} GB RAM",
|
|
24724
|
+
projects: "{maxProjects} \u4E2A\u9879\u76EE",
|
|
24725
|
+
projectsUnlimited: "\u9879\u76EE\u4E0D\u9650\u6570\u91CF",
|
|
24156
24726
|
freeSubdomains: "{freeSubdomains} \u4E2A\u514D\u8D39 {freeDomainSuffix} \u5B50\u57DF\u540D",
|
|
24157
24727
|
freeSubdomainsUnlimited: "\u514D\u8D39 {freeDomainSuffix} \u5B50\u57DF\u540D\u4E0D\u9650\u6570\u91CF",
|
|
24728
|
+
gitDeploys: "\u4ECE GitHub\u3001GitLab \u6216\u4EFB\u610F Git \u8FDC\u7AEF\u90E8\u7F72",
|
|
24158
24729
|
customDomains: "\u81EA\u5B9A\u4E49\u57DF\u540D\u4E0D\u9650\u6570\u91CF,\u542B TLS",
|
|
24730
|
+
deployLogs: "\u5B9E\u65F6\u6784\u5EFA\u4E0E\u90E8\u7F72\u65E5\u5FD7",
|
|
24731
|
+
auditLog: "\u8BB0\u5F55\u6BCF\u4E00\u6B21\u53D8\u66F4\u7684\u5BA1\u8BA1\u65E5\u5FD7",
|
|
24159
24732
|
unlimitedSeats: "\u56E2\u961F\u6210\u5458\u4E0D\u9650\u4EBA\u6570,\u6C38\u4E0D\u6309\u5E2D\u4F4D\u8BA1\u8D39",
|
|
24160
|
-
previewDeploys: "\u6BCF\u6B21\u63A8\u9001\u90FD\u751F\u6210\u9884\u89C8\u90E8\u7F72",
|
|
24161
|
-
backups: "\u6BCF\u65E5\u5907\u4EFD,\u652F\u6301\u6309\u65F6\u95F4\u70B9\u6062\u590D",
|
|
24162
|
-
observability: "\u5B9E\u65F6\u65E5\u5FD7\u3001\u6307\u6807\u548C\u5065\u5EB7\u544A\u8B66",
|
|
24163
|
-
mailServer: "\u5185\u7F6E\u90AE\u4EF6\u670D\u52A1\u5668:SMTP\u3001IMAP \u548C\u7F51\u9875\u90AE\u7BB1",
|
|
24164
|
-
auditLog: "\u5BA1\u8BA1\u65E5\u5FD7,\u4FDD\u7559\u671F\u66F4\u957F",
|
|
24165
|
-
sharedChannel: "\u5171\u4EAB\u7684 Slack \u6216 Discord \u9891\u9053",
|
|
24166
24733
|
sso: "SSO \u4E0E SAML",
|
|
24167
24734
|
customLimits: "\u81EA\u5B9A\u4E49\u914D\u989D,\u6309\u5408\u540C\u5546\u5B9A",
|
|
24168
24735
|
dedicatedInfra: "\u4E13\u5C5E\u57FA\u7840\u8BBE\u65BD\u4E0E\u4E13\u5C5E\u533A\u57DF",
|
|
@@ -24175,12 +24742,7 @@ var zh_default = {
|
|
|
24175
24742
|
selfHostedUnlimited: "\u90E8\u7F72\u3001\u9879\u76EE\u3001\u670D\u52A1\u5668\u548C\u57DF\u540D\u5747\u4E0D\u9650\u6570\u91CF",
|
|
24176
24743
|
selfHostedServices: "\u5168\u90E8\u6258\u7BA1\u670D\u52A1:Postgres\u3001Redis\u3001\u90AE\u4EF6\u670D\u52A1",
|
|
24177
24744
|
selfHostedClients: "CLI\u3001\u7F51\u9875\u7AEF\u548C\u684C\u9762\u7AEF,\u5171\u7528\u4E00\u4E2A\u540E\u7AEF",
|
|
24178
|
-
selfHostedFreeSubdomains: "\u8FDE\u63A5 Cloud \u540E\u53EF\u83B7\u5F97 {freeSubdomains} \u4E2A\u514D\u8D39 {freeDomainSuffix} \u5B50\u57DF\u540D"
|
|
24179
|
-
runningServices: "{runningServices} \u4E2A\u540C\u65F6\u8FD0\u884C\u7684\u670D\u52A1:\u5E94\u7528\u3001\u6570\u636E\u5E93\u3001\u540E\u53F0\u4EFB\u52A1",
|
|
24180
|
-
power: "\u6BCF\u4E2A\u670D\u52A1\u6700\u9AD8\u53EF\u8FBE {powerCpu} vCPU \u548C {powerRamGb} GB RAM",
|
|
24181
|
-
projects: "{maxProjects} \u4E2A\u9879\u76EE",
|
|
24182
|
-
projectsUnlimited: "\u9879\u76EE\u4E0D\u9650\u6570\u91CF",
|
|
24183
|
-
meteredCredits: "\u8BA1\u7B97\u3001\u6D41\u91CF\u548C\u5B58\u50A8\u90FD\u4ECE\u4F60\u7684\u6BCF\u6708\u79EF\u5206\u4E2D\u6263\u9664"
|
|
24745
|
+
selfHostedFreeSubdomains: "\u8FDE\u63A5 Cloud \u540E\u53EF\u83B7\u5F97 {freeSubdomains} \u4E2A\u514D\u8D39 {freeDomainSuffix} \u5B50\u57DF\u540D"
|
|
24184
24746
|
},
|
|
24185
24747
|
selfHosted: {
|
|
24186
24748
|
name: "\u81EA\u6258\u7BA1",
|
|
@@ -24190,9 +24752,9 @@ var zh_default = {
|
|
|
24190
24752
|
cta: "\u5F00\u59CB\u81EA\u6258\u7BA1"
|
|
24191
24753
|
},
|
|
24192
24754
|
creditPacks: {
|
|
24193
|
-
pack_5k: "{
|
|
24194
|
-
pack_25k: "{
|
|
24195
|
-
pack_100k: "{
|
|
24755
|
+
pack_5k: "{computeMinutes} \u8BA1\u7B97\u5206\u949F",
|
|
24756
|
+
pack_25k: "{computeMinutes} \u8BA1\u7B97\u5206\u949F",
|
|
24757
|
+
pack_100k: "{computeMinutes} \u8BA1\u7B97\u5206\u949F"
|
|
24196
24758
|
},
|
|
24197
24759
|
ui: {
|
|
24198
24760
|
perMonth: "/\u6708",
|
|
@@ -24208,7 +24770,9 @@ var zh_default = {
|
|
|
24208
24770
|
billedMonthly: "\u6309\u6708\u8BA1\u8D39,\u53EF\u968F\u65F6\u53D6\u6D88",
|
|
24209
24771
|
campaignBadge: "\u7ACB\u51CF {percentOff}%",
|
|
24210
24772
|
campaignEnds: "\u4F18\u60E0\u622A\u6B62 {date}",
|
|
24211
|
-
wasPrice: "\u539F\u4EF7 {price}"
|
|
24773
|
+
wasPrice: "\u539F\u4EF7 {price}",
|
|
24774
|
+
standardTitle: "\u6240\u6709\u5957\u9910\u5747\u5305\u542B",
|
|
24775
|
+
creditPackNote: "\u2248 \u5C0F\u578B\u5E94\u7528\u8FD0\u884C {appHours} \u5C0F\u65F6,\u6216 {buildMinutes} \u6784\u5EFA\u5206\u949F"
|
|
24212
24776
|
}
|
|
24213
24777
|
};
|
|
24214
24778
|
|
|
@@ -24258,6 +24822,17 @@ function featureTemplate(key, locale) {
|
|
|
24258
24822
|
return copyFor(locale).features?.[key] ?? en_default2.features[key] ?? null;
|
|
24259
24823
|
}
|
|
24260
24824
|
var PLAN_BY_ID = new Map(PRICING.plans.map((p) => [p.id, p]));
|
|
24825
|
+
var MILLI_PER_COMPUTE_MINUTE = 1e3;
|
|
24826
|
+
function buildUnitsPerMinute() {
|
|
24827
|
+
return PRICING.oblien.buildResources.cpuCores / RESOURCE_TIER_SPECS.low.cpuCores;
|
|
24828
|
+
}
|
|
24829
|
+
function planMonthlyCredits(planId) {
|
|
24830
|
+
const plan = PLAN_BY_ID.get(planId ?? "") ?? PLAN_BY_ID.get(DEFAULT_PLAN_TIER);
|
|
24831
|
+
const { computeMinutesPerMonth, buildMinutesPerMonth } = plan.limits;
|
|
24832
|
+
if (computeMinutesPerMonth === null || buildMinutesPerMonth === null) return null;
|
|
24833
|
+
const units = computeMinutesPerMonth + buildMinutesPerMonth * buildUnitsPerMinute();
|
|
24834
|
+
return Math.round(units * MILLI_PER_COMPUTE_MINUTE);
|
|
24835
|
+
}
|
|
24261
24836
|
function toOblienLimits(limits) {
|
|
24262
24837
|
const { runningServices, maxResourceTier } = limits;
|
|
24263
24838
|
if (runningServices === null && maxResourceTier === null) return null;
|
|
@@ -24287,11 +24862,12 @@ function placeholders(plan, locale) {
|
|
|
24287
24862
|
powerCpu: svc ? formatDecimal(svc.cpuCores, locale) : unlimited,
|
|
24288
24863
|
powerRamGb: svc ? formatDecimal(svc.memoryMb / 1024, locale) : unlimited,
|
|
24289
24864
|
powerDiskGb: svc ? formatCount(Math.round(svc.diskMb / 1024), locale) : unlimited,
|
|
24290
|
-
|
|
24865
|
+
computeMinutes: n(limits.computeMinutesPerMonth),
|
|
24291
24866
|
inherited: plan.inherits ? planName(plan.inherits, locale) : "",
|
|
24292
24867
|
freeDomainSuffix: PRICING.freeDomainSuffix
|
|
24293
24868
|
};
|
|
24294
24869
|
}
|
|
24870
|
+
var EVERYTHING_IN_KEY = "everythingIn";
|
|
24295
24871
|
function resolvePlan(planId, locale = "en") {
|
|
24296
24872
|
const plan = PLAN_BY_ID.get(planId) ?? PLAN_BY_ID.get(DEFAULT_PLAN_TIER);
|
|
24297
24873
|
const values = placeholders(plan, locale);
|
|
@@ -24300,10 +24876,14 @@ function resolvePlan(planId, locale = "en") {
|
|
|
24300
24876
|
name: planName(plan.id, locale),
|
|
24301
24877
|
description: planTagline(plan.id, locale),
|
|
24302
24878
|
price: plan.price,
|
|
24303
|
-
monthlyCredits: plan.
|
|
24879
|
+
monthlyCredits: planMonthlyCredits(plan.id),
|
|
24304
24880
|
oblienLimits: toOblienLimits(plan.limits),
|
|
24305
24881
|
limits: plan.limits,
|
|
24306
|
-
|
|
24882
|
+
...(() => {
|
|
24883
|
+
const lead = plan.features.includes(EVERYTHING_IN_KEY) ? featureTemplate(EVERYTHING_IN_KEY, locale) : null;
|
|
24884
|
+
return lead === null ? {} : { inheritedFrom: fill(lead, values) };
|
|
24885
|
+
})(),
|
|
24886
|
+
features: plan.features.filter((key) => key !== EVERYTHING_IN_KEY).map((key) => {
|
|
24307
24887
|
const template = featureTemplate(key, locale);
|
|
24308
24888
|
return template === null ? null : fill(template, values);
|
|
24309
24889
|
}).filter((s) => s !== null),
|
|
@@ -24317,10 +24897,17 @@ var PLANS = Object.fromEntries(
|
|
|
24317
24897
|
);
|
|
24318
24898
|
function resolveCreditPacks(locale = "en") {
|
|
24319
24899
|
return [...PRICING.creditPacks].sort((a, b) => a.sortOrder - b.sortOrder).map((pack) => {
|
|
24320
|
-
const template = copyFor(locale).creditPacks?.[pack.id] ?? en_default2.creditPacks[pack.id] ?? "{
|
|
24900
|
+
const template = copyFor(locale).creditPacks?.[pack.id] ?? en_default2.creditPacks[pack.id] ?? "{computeMinutes}";
|
|
24901
|
+
const minutes = pack.creditsMilli / MILLI_PER_COMPUTE_MINUTE;
|
|
24902
|
+
const amount = formatCount(minutes, locale);
|
|
24903
|
+
const noteTemplate = copyFor(locale).ui?.creditPackNote ?? en_default2.ui.creditPackNote;
|
|
24321
24904
|
return {
|
|
24322
24905
|
id: pack.id,
|
|
24323
|
-
name: fill(template, {
|
|
24906
|
+
name: fill(template, { computeMinutes: amount, credits: amount }),
|
|
24907
|
+
explains: fill(noteTemplate, {
|
|
24908
|
+
appHours: formatCount(Math.round(minutes / 60), locale),
|
|
24909
|
+
buildMinutes: formatCount(Math.round(minutes / buildUnitsPerMinute()), locale)
|
|
24910
|
+
}),
|
|
24324
24911
|
credits_milli: pack.creditsMilli,
|
|
24325
24912
|
price_cents: pack.priceCents,
|
|
24326
24913
|
sortOrder: pack.sortOrder
|
|
@@ -24332,6 +24919,240 @@ var ANNUAL_ENABLED = PRICING.annual.enabled;
|
|
|
24332
24919
|
var ANNUAL_MONTHS_FREE = PRICING.annual.monthsFree;
|
|
24333
24920
|
var FREE_DOMAIN_SUFFIX = PRICING.freeDomainSuffix;
|
|
24334
24921
|
|
|
24922
|
+
// ../../packages/core/src/project-source.ts
|
|
24923
|
+
var MAX_IMAGE_NAME_LENGTH = 255;
|
|
24924
|
+
var MAX_IMAGE_TAG_LENGTH = 128;
|
|
24925
|
+
var IMAGE_PATH_COMPONENT_RE = /^[a-z0-9]+(?:(?:[._]|__|-+)[a-z0-9]+)*$/;
|
|
24926
|
+
var IMAGE_TAG_RE = /^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$/;
|
|
24927
|
+
var IMAGE_DIGEST_RE = /^([A-Za-z][A-Za-z0-9]*(?:[+._-][A-Za-z][A-Za-z0-9]*)*):([A-Za-z0-9=_-]+)$/;
|
|
24928
|
+
var IMAGE_DOMAIN_LABEL_RE = /^[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?$/;
|
|
24929
|
+
function validateImageReference(ref) {
|
|
24930
|
+
if (typeof ref !== "string") return "Container image reference must be a string.";
|
|
24931
|
+
if (!ref) return "Container image reference cannot be empty.";
|
|
24932
|
+
if (ref !== ref.trim()) return "Container image reference cannot have surrounding whitespace.";
|
|
24933
|
+
if (/\s/.test(ref))
|
|
24934
|
+
return `Container image reference ${JSON.stringify(ref)} cannot contain whitespace.`;
|
|
24935
|
+
if (ref.includes("\0"))
|
|
24936
|
+
return `Container image reference ${JSON.stringify(ref)} contains a NUL byte.`;
|
|
24937
|
+
if (ref.includes("://")) {
|
|
24938
|
+
return `Container image reference ${JSON.stringify(ref)} must not include a URL scheme.`;
|
|
24939
|
+
}
|
|
24940
|
+
const at = ref.indexOf("@");
|
|
24941
|
+
if (at !== -1 && at !== ref.lastIndexOf("@")) {
|
|
24942
|
+
return `Container image reference ${JSON.stringify(ref)} contains more than one digest separator (@).`;
|
|
24943
|
+
}
|
|
24944
|
+
const nameAndTag = at === -1 ? ref : ref.slice(0, at);
|
|
24945
|
+
const digest = at === -1 ? null : ref.slice(at + 1);
|
|
24946
|
+
if (!nameAndTag)
|
|
24947
|
+
return `Container image reference ${JSON.stringify(ref)} is missing a repository name.`;
|
|
24948
|
+
if (digest !== null) {
|
|
24949
|
+
const parsed = IMAGE_DIGEST_RE.exec(digest);
|
|
24950
|
+
if (!parsed) {
|
|
24951
|
+
return `Container image reference ${JSON.stringify(ref)} has an invalid OCI digest.`;
|
|
24952
|
+
}
|
|
24953
|
+
if (parsed[1].toLowerCase() === "sha256" && !/^[a-fA-F0-9]{64}$/.test(parsed[2])) {
|
|
24954
|
+
return `Container image reference ${JSON.stringify(ref)} has an invalid sha256 digest (expected 64 hexadecimal characters).`;
|
|
24955
|
+
}
|
|
24956
|
+
}
|
|
24957
|
+
const lastSlash = nameAndTag.lastIndexOf("/");
|
|
24958
|
+
const lastColon = nameAndTag.lastIndexOf(":");
|
|
24959
|
+
const hasTag = lastColon > lastSlash;
|
|
24960
|
+
const name = hasTag ? nameAndTag.slice(0, lastColon) : nameAndTag;
|
|
24961
|
+
const tag = hasTag ? nameAndTag.slice(lastColon + 1) : null;
|
|
24962
|
+
if (!name)
|
|
24963
|
+
return `Container image reference ${JSON.stringify(ref)} is missing a repository name.`;
|
|
24964
|
+
if (name.length > MAX_IMAGE_NAME_LENGTH) {
|
|
24965
|
+
return `Container image repository name is too long (${name.length}; maximum ${MAX_IMAGE_NAME_LENGTH}).`;
|
|
24966
|
+
}
|
|
24967
|
+
if (tag !== null) {
|
|
24968
|
+
if (!tag) return `Container image reference ${JSON.stringify(ref)} has an empty tag.`;
|
|
24969
|
+
if (tag.length > MAX_IMAGE_TAG_LENGTH || !IMAGE_TAG_RE.test(tag)) {
|
|
24970
|
+
return `Container image reference ${JSON.stringify(ref)} has an invalid tag; tags must be 1-${MAX_IMAGE_TAG_LENGTH} ASCII letters, digits, underscores, periods, or hyphens and cannot start with a period or hyphen.`;
|
|
24971
|
+
}
|
|
24972
|
+
}
|
|
24973
|
+
const parts = name.split("/");
|
|
24974
|
+
if (parts.some((part) => !part)) {
|
|
24975
|
+
return `Container image reference ${JSON.stringify(ref)} contains an empty repository path component.`;
|
|
24976
|
+
}
|
|
24977
|
+
const first = parts[0];
|
|
24978
|
+
const hasRegistry = parts.length > 1 && (first.includes(".") || first.includes(":") || first === "localhost" || first.startsWith("["));
|
|
24979
|
+
const path = hasRegistry ? parts.slice(1) : parts;
|
|
24980
|
+
if (path.length === 0 || path.some((part) => !IMAGE_PATH_COMPONENT_RE.test(part))) {
|
|
24981
|
+
return `Container image reference ${JSON.stringify(ref)} has an invalid repository path; repository names must be lowercase OCI path components.`;
|
|
24982
|
+
}
|
|
24983
|
+
if (hasRegistry) {
|
|
24984
|
+
const registryError = validateImageRegistry(first);
|
|
24985
|
+
if (registryError) return `Container image reference ${JSON.stringify(ref)} ${registryError}`;
|
|
24986
|
+
}
|
|
24987
|
+
return null;
|
|
24988
|
+
}
|
|
24989
|
+
function validateReleaseVersionUrl(value) {
|
|
24990
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
24991
|
+
return "Release version URL cannot be empty.";
|
|
24992
|
+
}
|
|
24993
|
+
if (value !== value.trim()) {
|
|
24994
|
+
return "Release version URL cannot have surrounding whitespace.";
|
|
24995
|
+
}
|
|
24996
|
+
let url2;
|
|
24997
|
+
try {
|
|
24998
|
+
url2 = new URL(value);
|
|
24999
|
+
} catch {
|
|
25000
|
+
return "Release version URL must be a valid HTTPS URL.";
|
|
25001
|
+
}
|
|
25002
|
+
if (url2.protocol !== "https:") {
|
|
25003
|
+
return "Release version URL must use HTTPS.";
|
|
25004
|
+
}
|
|
25005
|
+
if (url2.username || url2.password) {
|
|
25006
|
+
return "Release version URL must not include embedded credentials.";
|
|
25007
|
+
}
|
|
25008
|
+
return null;
|
|
25009
|
+
}
|
|
25010
|
+
function validateReleaseRepository(value) {
|
|
25011
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
25012
|
+
return 'GitHub release repository must use the "owner/repository" format.';
|
|
25013
|
+
}
|
|
25014
|
+
if (value !== value.trim()) {
|
|
25015
|
+
return "GitHub release repository cannot have surrounding whitespace.";
|
|
25016
|
+
}
|
|
25017
|
+
const parts = value.split("/");
|
|
25018
|
+
if (parts.length !== 2) {
|
|
25019
|
+
return 'GitHub release repository must use the "owner/repository" format.';
|
|
25020
|
+
}
|
|
25021
|
+
const [owner, repository] = parts;
|
|
25022
|
+
const ownerValid = owner.length <= 39 && /^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/.test(owner);
|
|
25023
|
+
const repositoryValid = repository.length <= 100 && repository !== "." && repository !== ".." && /^[A-Za-z0-9_.-]+$/.test(repository);
|
|
25024
|
+
if (!ownerValid || !repositoryValid) {
|
|
25025
|
+
return "GitHub release repository contains an invalid owner or repository name.";
|
|
25026
|
+
}
|
|
25027
|
+
return null;
|
|
25028
|
+
}
|
|
25029
|
+
function isValidBracketedIpv6Host(host) {
|
|
25030
|
+
if (!/^\[[0-9a-fA-F:]+\]$/.test(host)) return false;
|
|
25031
|
+
try {
|
|
25032
|
+
const parsed = new URL(`http://${host}/`);
|
|
25033
|
+
return parsed.hostname.startsWith("[") && parsed.hostname.endsWith("]");
|
|
25034
|
+
} catch {
|
|
25035
|
+
return false;
|
|
25036
|
+
}
|
|
25037
|
+
}
|
|
25038
|
+
function validateImageRegistry(registry2) {
|
|
25039
|
+
let host = registry2;
|
|
25040
|
+
let port = null;
|
|
25041
|
+
if (registry2.startsWith("[")) {
|
|
25042
|
+
const close = registry2.indexOf("]");
|
|
25043
|
+
if (close <= 1) return "has an invalid bracketed IPv6 registry host.";
|
|
25044
|
+
host = registry2.slice(0, close + 1);
|
|
25045
|
+
const rest = registry2.slice(close + 1);
|
|
25046
|
+
if (rest) {
|
|
25047
|
+
if (!rest.startsWith(":")) return "has invalid characters after its registry host.";
|
|
25048
|
+
port = rest.slice(1);
|
|
25049
|
+
}
|
|
25050
|
+
if (!isValidBracketedIpv6Host(host)) {
|
|
25051
|
+
return "has an invalid bracketed IPv6 registry host.";
|
|
25052
|
+
}
|
|
25053
|
+
} else {
|
|
25054
|
+
const colon = registry2.lastIndexOf(":");
|
|
25055
|
+
if (colon !== -1) {
|
|
25056
|
+
host = registry2.slice(0, colon);
|
|
25057
|
+
port = registry2.slice(colon + 1);
|
|
25058
|
+
}
|
|
25059
|
+
if (!host) return "is missing its registry host.";
|
|
25060
|
+
const labels = host.split(".");
|
|
25061
|
+
if (labels.some((label) => !IMAGE_DOMAIN_LABEL_RE.test(label))) {
|
|
25062
|
+
return "has an invalid registry hostname.";
|
|
25063
|
+
}
|
|
25064
|
+
}
|
|
25065
|
+
if (port !== null) {
|
|
25066
|
+
if (!/^\d+$/.test(port)) return "has a non-numeric registry port.";
|
|
25067
|
+
const value = Number(port);
|
|
25068
|
+
if (value < 1 || value > 65535) return "has a registry port outside 1-65535.";
|
|
25069
|
+
}
|
|
25070
|
+
return null;
|
|
25071
|
+
}
|
|
25072
|
+
var RELEASE_IMAGE_PLACEHOLDERS = ["version", "tag"];
|
|
25073
|
+
function renderReleaseImage(template, opts) {
|
|
25074
|
+
if (typeof template !== "string" || !template.trim()) {
|
|
25075
|
+
throw new Error("Release image template cannot be empty.");
|
|
25076
|
+
}
|
|
25077
|
+
if (template !== template.trim()) {
|
|
25078
|
+
throw new Error("Release image template cannot have surrounding whitespace.");
|
|
25079
|
+
}
|
|
25080
|
+
let insidePlaceholder = false;
|
|
25081
|
+
for (const char of template) {
|
|
25082
|
+
if (char === "{") {
|
|
25083
|
+
if (insidePlaceholder) {
|
|
25084
|
+
throw new Error(
|
|
25085
|
+
`Release image template ${JSON.stringify(template)} contains malformed placeholder braces.`
|
|
25086
|
+
);
|
|
25087
|
+
}
|
|
25088
|
+
insidePlaceholder = true;
|
|
25089
|
+
} else if (char === "}") {
|
|
25090
|
+
if (!insidePlaceholder) {
|
|
25091
|
+
throw new Error(
|
|
25092
|
+
`Release image template ${JSON.stringify(template)} contains malformed placeholder braces.`
|
|
25093
|
+
);
|
|
25094
|
+
}
|
|
25095
|
+
insidePlaceholder = false;
|
|
25096
|
+
}
|
|
25097
|
+
}
|
|
25098
|
+
if (insidePlaceholder) {
|
|
25099
|
+
throw new Error(
|
|
25100
|
+
`Release image template ${JSON.stringify(template)} contains malformed placeholder braces.`
|
|
25101
|
+
);
|
|
25102
|
+
}
|
|
25103
|
+
const placeholders2 = template.match(/\{[^{}]*\}/g) ?? [];
|
|
25104
|
+
const unknown2 = placeholders2.filter(
|
|
25105
|
+
(placeholder) => !RELEASE_IMAGE_PLACEHOLDERS.includes(placeholder.slice(1, -1))
|
|
25106
|
+
);
|
|
25107
|
+
if (unknown2.length > 0) {
|
|
25108
|
+
throw new Error(
|
|
25109
|
+
`Release image template ${JSON.stringify(template)} uses unknown placeholder(s) ${unknown2.join(", ")}. Supported: ${RELEASE_IMAGE_PLACEHOLDERS.map((p) => `{${p}}`).join(" ")}.`
|
|
25110
|
+
);
|
|
25111
|
+
}
|
|
25112
|
+
if (!template.includes("{version}") && !template.includes("{tag}")) {
|
|
25113
|
+
throw new Error(
|
|
25114
|
+
`Release image template ${JSON.stringify(template)} must contain {version} or {tag}.`
|
|
25115
|
+
);
|
|
25116
|
+
}
|
|
25117
|
+
if (template.includes("@")) {
|
|
25118
|
+
throw new Error(
|
|
25119
|
+
`Release image template ${JSON.stringify(template)} must use a tag, not a digest.`
|
|
25120
|
+
);
|
|
25121
|
+
}
|
|
25122
|
+
const lastSlash = template.lastIndexOf("/");
|
|
25123
|
+
const tagColon = template.lastIndexOf(":");
|
|
25124
|
+
if (tagColon <= lastSlash) {
|
|
25125
|
+
throw new Error(
|
|
25126
|
+
`Release image template ${JSON.stringify(template)} must put {version} or {tag} in an explicit image tag.`
|
|
25127
|
+
);
|
|
25128
|
+
}
|
|
25129
|
+
const tagStart = tagColon + 1;
|
|
25130
|
+
for (const placeholder of ["{version}", "{tag}"]) {
|
|
25131
|
+
let at = template.indexOf(placeholder);
|
|
25132
|
+
while (at !== -1) {
|
|
25133
|
+
if (at < tagStart) {
|
|
25134
|
+
throw new Error(
|
|
25135
|
+
`Release image template ${JSON.stringify(template)} may use ${placeholder} only in the image tag.`
|
|
25136
|
+
);
|
|
25137
|
+
}
|
|
25138
|
+
at = template.indexOf(placeholder, at + placeholder.length);
|
|
25139
|
+
}
|
|
25140
|
+
}
|
|
25141
|
+
const rawVersion = opts.version?.trim();
|
|
25142
|
+
const rawTag = opts.tag?.trim();
|
|
25143
|
+
if (!rawVersion) throw new Error("Release image version cannot be empty.");
|
|
25144
|
+
if (!rawTag) throw new Error("Release image tag cannot be empty.");
|
|
25145
|
+
const version2 = rawVersion.replace(/^v/i, "");
|
|
25146
|
+
if (!version2) throw new Error("Release image version cannot contain only a leading v.");
|
|
25147
|
+
const rendered = template.replace(
|
|
25148
|
+
/\{(?:version|tag)\}/g,
|
|
25149
|
+
(placeholder) => placeholder === "{version}" ? version2 : rawTag
|
|
25150
|
+
);
|
|
25151
|
+
const invalid = validateImageReference(rendered);
|
|
25152
|
+
if (invalid) throw new Error(invalid);
|
|
25153
|
+
return rendered;
|
|
25154
|
+
}
|
|
25155
|
+
|
|
24335
25156
|
// ../../packages/core/src/updates/types.ts
|
|
24336
25157
|
var GITHUB_REPO = "oblien/openship";
|
|
24337
25158
|
var RELEASES_LATEST_API = `https://api.github.com/repos/${GITHUB_REPO}/releases/latest`;
|
|
@@ -24463,6 +25284,13 @@ var AUDIT_EVENTS = {
|
|
|
24463
25284
|
tone: "warning",
|
|
24464
25285
|
description: "A delete was refused \u2014 something still depends on this app."
|
|
24465
25286
|
},
|
|
25287
|
+
"project.deletion.failed": {
|
|
25288
|
+
category: "apps",
|
|
25289
|
+
action: "failed to delete",
|
|
25290
|
+
label: "App deletion failed",
|
|
25291
|
+
tone: "warning",
|
|
25292
|
+
description: "Cleanup did not complete, so the app record was kept for a safe retry."
|
|
25293
|
+
},
|
|
24466
25294
|
"project:write": {
|
|
24467
25295
|
category: "apps",
|
|
24468
25296
|
action: "changed",
|
|
@@ -24688,6 +25516,13 @@ var AUDIT_EVENTS = {
|
|
|
24688
25516
|
label: "Server removed",
|
|
24689
25517
|
tone: "danger"
|
|
24690
25518
|
},
|
|
25519
|
+
"server.removal.rejected": {
|
|
25520
|
+
category: "servers",
|
|
25521
|
+
action: "tried to remove the server",
|
|
25522
|
+
label: "Server removal refused",
|
|
25523
|
+
tone: "warning",
|
|
25524
|
+
description: "A removal was refused because a workload could not be torn down, or was destroyed but left a resource behind for cleanup. The server row was kept so those resources can still be reclaimed."
|
|
25525
|
+
},
|
|
24691
25526
|
"server.exec": {
|
|
24692
25527
|
category: "servers",
|
|
24693
25528
|
action: "ran a command on the server",
|
|
@@ -24943,6 +25778,20 @@ var AUDIT_EVENTS = {
|
|
|
24943
25778
|
tone: "warning",
|
|
24944
25779
|
description: "A full database dump left this instance."
|
|
24945
25780
|
},
|
|
25781
|
+
"instance.data.receive_code_created": {
|
|
25782
|
+
category: "security",
|
|
25783
|
+
action: "created a direct data-transfer receive code",
|
|
25784
|
+
label: "Data-transfer receive code created",
|
|
25785
|
+
tone: "warning",
|
|
25786
|
+
description: "A short-lived code was created to receive instance data directly."
|
|
25787
|
+
},
|
|
25788
|
+
"instance.data.sent": {
|
|
25789
|
+
category: "security",
|
|
25790
|
+
action: "sent instance data to another instance",
|
|
25791
|
+
label: "Instance data sent",
|
|
25792
|
+
tone: "warning",
|
|
25793
|
+
description: "Selected instance data was transferred directly to another instance."
|
|
25794
|
+
},
|
|
24946
25795
|
"instance.data.imported": {
|
|
24947
25796
|
category: "security",
|
|
24948
25797
|
action: "imported instance data",
|
|
@@ -25451,132 +26300,100 @@ function idSuffix(distroId) {
|
|
|
25451
26300
|
return distroId ? ` (os-release ID=${JSON.stringify(distroId)})` : "";
|
|
25452
26301
|
}
|
|
25453
26302
|
|
|
25454
|
-
// ../../packages/
|
|
25455
|
-
|
|
25456
|
-
|
|
25457
|
-
|
|
25458
|
-
|
|
25459
|
-
|
|
25460
|
-
|
|
25461
|
-
"ETIMEDOUT",
|
|
25462
|
-
"EHOSTUNREACH",
|
|
25463
|
-
"ENETUNREACH",
|
|
25464
|
-
"Timed out",
|
|
25465
|
-
"Not connected",
|
|
25466
|
-
"Connection lost",
|
|
25467
|
-
"read ECONNRESET",
|
|
25468
|
-
"Handshake failed",
|
|
25469
|
-
"keepalive timeout",
|
|
25470
|
-
"SSH connection closed before ready",
|
|
25471
|
-
// ssh2 emits "Channel open failure: open failed" / "Channel open failure: …"
|
|
25472
|
-
// when the SSH SERVER refuses a new channel - typically because the cached
|
|
25473
|
-
// connection has gone half-dead (peer-side LOGOUT, network blip,
|
|
25474
|
-
// remote sshd MaxSessions). The inner SshExecutor retries `exec`/
|
|
25475
|
-
// `streamExec` on this, but `pipeLocal`, `transferIn`, and any other
|
|
25476
|
-
// channel-bearing op bubble it up. Listing it here lets `withExecutor`
|
|
25477
|
-
// drop the dead connection and retry transparently - fixes the
|
|
25478
|
-
// "first redeploy fails, second click works" pattern.
|
|
25479
|
-
"Channel open failure",
|
|
25480
|
-
"open failed"
|
|
25481
|
-
// NOT "socket hang up": it is what an HTTP client reports for ANY far end that vanished
|
|
25482
|
-
// mid-request, including a permission-denied Docker socket and a wedged channel, neither
|
|
25483
|
-
// of which a fresh connection fixes. A match here makes `withExecutor` replay its whole
|
|
25484
|
-
// callback — and those callbacks install mail, create mailboxes and ensure the edge, so a
|
|
25485
|
-
// non-retryable cause becomes a repeated mutation. `core/connectivity.ts` labels it
|
|
25486
|
-
// without retrying, `api/lib/remote-state.ts` still maps it to a 503, and the bridge's
|
|
25487
|
-
// 502 carries the real reason — so leaving it out here costs no diagnosis.
|
|
25488
|
-
];
|
|
25489
|
-
var SshDisconnectedError = class extends Error {
|
|
25490
|
-
constructor(message = "SSH connection lost") {
|
|
25491
|
-
super(message);
|
|
25492
|
-
this.name = "SshDisconnectedError";
|
|
25493
|
-
}
|
|
25494
|
-
};
|
|
25495
|
-
function isSshDisconnectedError(err) {
|
|
25496
|
-
return err instanceof SshDisconnectedError || err instanceof Error && err.name === "SshDisconnectedError";
|
|
25497
|
-
}
|
|
25498
|
-
function isSshAuthError(err) {
|
|
25499
|
-
if (!(err instanceof Error)) return false;
|
|
25500
|
-
return SSH_AUTH_ERROR_PATTERNS.some((pattern) => err.message.includes(pattern));
|
|
25501
|
-
}
|
|
25502
|
-
var HostChannelUnavailableError = class extends Error {
|
|
25503
|
-
code;
|
|
25504
|
-
constructor(code, message) {
|
|
25505
|
-
super(message);
|
|
25506
|
-
this.name = "HostChannelUnavailableError";
|
|
25507
|
-
this.code = code;
|
|
26303
|
+
// ../../packages/core/src/network.ts
|
|
26304
|
+
function ipv4Octets(host) {
|
|
26305
|
+
const parts = host.split(".");
|
|
26306
|
+
if (parts.length !== 4) return null;
|
|
26307
|
+
const octets = parts.map((part) => /^(?:0|[1-9]\d{0,2})$/.test(part) ? Number(part) : NaN);
|
|
26308
|
+
if (octets.some((octet) => !Number.isInteger(octet) || octet < 0 || octet > 255)) {
|
|
26309
|
+
return null;
|
|
25508
26310
|
}
|
|
25509
|
-
|
|
25510
|
-
|
|
25511
|
-
|
|
25512
|
-
|
|
25513
|
-
|
|
25514
|
-
|
|
26311
|
+
return octets;
|
|
26312
|
+
}
|
|
26313
|
+
function ipv6Words(host) {
|
|
26314
|
+
let value = host;
|
|
26315
|
+
const zone = value.indexOf("%");
|
|
26316
|
+
if (zone >= 0) value = value.slice(0, zone);
|
|
26317
|
+
const dottedTail = /(?:^|:)(\d+\.\d+\.\d+\.\d+)$/.exec(value)?.[1];
|
|
26318
|
+
if (dottedTail) {
|
|
26319
|
+
const octets = ipv4Octets(dottedTail);
|
|
26320
|
+
if (!octets) return null;
|
|
26321
|
+
const replacement = `${(octets[0] << 8 | octets[1]).toString(16)}:${(octets[2] << 8 | octets[3]).toString(16)}`;
|
|
26322
|
+
value = value.slice(0, -dottedTail.length) + replacement;
|
|
26323
|
+
}
|
|
26324
|
+
if ((value.match(/::/g) ?? []).length > 1) return null;
|
|
26325
|
+
const [leftRaw, rightRaw] = value.split("::");
|
|
26326
|
+
const left = leftRaw ? leftRaw.split(":") : [];
|
|
26327
|
+
const right = rightRaw ? rightRaw.split(":") : [];
|
|
26328
|
+
if ([...left, ...right].some((word) => !/^[0-9a-f]{1,4}$/i.test(word))) return null;
|
|
26329
|
+
const compressed = value.includes("::");
|
|
26330
|
+
const missing = 8 - left.length - right.length;
|
|
26331
|
+
if (!compressed && missing !== 0 || compressed && missing < 1) return null;
|
|
26332
|
+
return [...left, ...Array.from({ length: missing }, () => "0"), ...right].map(
|
|
26333
|
+
(word) => Number.parseInt(word, 16)
|
|
25515
26334
|
);
|
|
25516
26335
|
}
|
|
25517
|
-
function
|
|
25518
|
-
|
|
25519
|
-
|
|
25520
|
-
|
|
25521
|
-
|
|
25522
|
-
const oneLine = command.trim().replace(/\s+/g, " ");
|
|
25523
|
-
return oneLine.length > MAX_COMMAND_CHARS ? `${oneLine.slice(0, MAX_COMMAND_CHARS)}\u2026 (${command.length} chars)` : oneLine;
|
|
25524
|
-
}
|
|
25525
|
-
function isRuntimeNotFoundError(err) {
|
|
25526
|
-
if (!err || typeof err !== "object") return false;
|
|
25527
|
-
const e = err;
|
|
25528
|
-
if (e.statusCode === 404) return true;
|
|
25529
|
-
return typeof e.message === "string" && /no such (container|image|volume|network)/i.test(e.message);
|
|
25530
|
-
}
|
|
25531
|
-
|
|
25532
|
-
// ../../packages/adapters/src/system/debug.ts
|
|
25533
|
-
function isTruthy(value) {
|
|
26336
|
+
function isLoopbackHost(host) {
|
|
26337
|
+
if (!host) return false;
|
|
26338
|
+
let value = host.trim().toLowerCase();
|
|
26339
|
+
if (value.startsWith("[") && value.endsWith("]")) value = value.slice(1, -1);
|
|
26340
|
+
value = value.replace(/\.+$/, "");
|
|
25534
26341
|
if (!value) return false;
|
|
25535
|
-
|
|
25536
|
-
|
|
25537
|
-
|
|
25538
|
-
|
|
25539
|
-
|
|
25540
|
-
|
|
25541
|
-
if (!
|
|
25542
|
-
|
|
25543
|
-
|
|
25544
|
-
|
|
25545
|
-
|
|
25546
|
-
|
|
26342
|
+
if (value === "localhost" || value.endsWith(".localhost") || value === "localhost.localdomain" || value === "ip6-localhost" || value === "ip6-loopback") {
|
|
26343
|
+
return true;
|
|
26344
|
+
}
|
|
26345
|
+
const ipv43 = ipv4Octets(value);
|
|
26346
|
+
if (ipv43) return ipv43[0] === 127 || ipv43.every((octet) => octet === 0);
|
|
26347
|
+
const words = ipv6Words(value);
|
|
26348
|
+
if (!words) return false;
|
|
26349
|
+
if (words.every((word) => word === 0)) return true;
|
|
26350
|
+
if (words.slice(0, 7).every((word) => word === 0) && words[7] === 1) return true;
|
|
26351
|
+
const ipv4PrefixIsLocal = words[6] >> 8 === 127;
|
|
26352
|
+
const mapped = words.slice(0, 5).every((word) => word === 0) && words[5] === 65535;
|
|
26353
|
+
const compatible = words.slice(0, 6).every((word) => word === 0);
|
|
26354
|
+
return ipv4PrefixIsLocal && (mapped || compatible);
|
|
25547
26355
|
}
|
|
25548
26356
|
|
|
25549
26357
|
export {
|
|
26358
|
+
shellQuote,
|
|
26359
|
+
shellSplitWords,
|
|
26360
|
+
commandToArgv,
|
|
25550
26361
|
LANGUAGES,
|
|
25551
26362
|
STACKS,
|
|
25552
26363
|
TRANSFER_EXCLUDES,
|
|
25553
26364
|
buildOutputTransferExcludes,
|
|
25554
26365
|
PACKAGE_ROOT_ONLY_EXCLUDES,
|
|
25555
26366
|
packageManagerEnsureCommand,
|
|
26367
|
+
nodeBinDirs,
|
|
26368
|
+
nodeBinPathExport,
|
|
25556
26369
|
appVolumeTargets,
|
|
25557
26370
|
parseComposeNamespace,
|
|
25558
26371
|
ownsNetworkEndpoint,
|
|
26372
|
+
resolveRedirectStatus,
|
|
26373
|
+
isValidEmail,
|
|
26374
|
+
formatBytes,
|
|
26375
|
+
withTimeout,
|
|
26376
|
+
isValidEnvKey,
|
|
26377
|
+
wrapText,
|
|
26378
|
+
composeBuildIssues,
|
|
25559
26379
|
composeMountToSpec,
|
|
25560
26380
|
composeMountIssues,
|
|
25561
26381
|
composePortToSpec,
|
|
25562
26382
|
isAwsS3Endpoint,
|
|
25563
26383
|
DEFAULT_IMAGE_REGISTRY,
|
|
25564
|
-
shellQuote,
|
|
25565
|
-
shellSplitWords,
|
|
25566
|
-
commandToArgv,
|
|
25567
26384
|
registryForImage,
|
|
25568
26385
|
registryConfigKeys,
|
|
25569
26386
|
buildEdgeImageRef,
|
|
25570
26387
|
buildMailImageRef,
|
|
25571
26388
|
SYSTEM,
|
|
25572
|
-
resolveRedirectStatus,
|
|
25573
|
-
isValidEmail,
|
|
25574
|
-
formatBytes,
|
|
25575
|
-
withTimeout,
|
|
25576
|
-
wrapText,
|
|
25577
26389
|
AppError,
|
|
25578
26390
|
DeployError,
|
|
25579
26391
|
safeErrorMessage,
|
|
26392
|
+
payloadSpec,
|
|
26393
|
+
normalizeBackupPath,
|
|
26394
|
+
validateBackupPath,
|
|
26395
|
+
validateClearPath,
|
|
26396
|
+
isDbImage,
|
|
25580
26397
|
LOCAL_DASHBOARD_URL,
|
|
25581
26398
|
LOCAL_API_URL,
|
|
25582
26399
|
CLOUD_DASHBOARD_URL,
|
|
@@ -25588,6 +26405,9 @@ export {
|
|
|
25588
26405
|
missingOutputDirectoryMessage,
|
|
25589
26406
|
compareSemver,
|
|
25590
26407
|
PRICING,
|
|
26408
|
+
validateReleaseVersionUrl,
|
|
26409
|
+
validateReleaseRepository,
|
|
26410
|
+
renderReleaseImage,
|
|
25591
26411
|
resolveCliUpdatePlan,
|
|
25592
26412
|
cliInstallCommand,
|
|
25593
26413
|
PROXY_DIRECTIVES,
|
|
@@ -25623,13 +26443,5 @@ export {
|
|
|
25623
26443
|
hostChannelRuleScope,
|
|
25624
26444
|
hostFirewallRule,
|
|
25625
26445
|
hostFirewallRuleRemoval,
|
|
25626
|
-
|
|
25627
|
-
isSshAuthError,
|
|
25628
|
-
HostChannelUnavailableError,
|
|
25629
|
-
isRetryableRemoteConnectionError,
|
|
25630
|
-
isRemoteConnectionError,
|
|
25631
|
-
commandForError,
|
|
25632
|
-
isRuntimeNotFoundError,
|
|
25633
|
-
systemDebug,
|
|
25634
|
-
formatDuration
|
|
26446
|
+
isLoopbackHost
|
|
25635
26447
|
};
|