@insforge/cli 0.1.85 → 0.1.87
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -0
- package/dist/index.js +309 -12
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -654,6 +654,58 @@ Running `npx @insforge/cli link` creates a `.insforge/` directory in your projec
|
|
|
654
654
|
|
|
655
655
|
Add `.insforge/` to your `.gitignore` — it contains your project API key.
|
|
656
656
|
|
|
657
|
+
### Declarative project config — `insforge.toml`
|
|
658
|
+
|
|
659
|
+
Use `config export`, `config plan`, and `config apply` to manage project settings through `insforge.toml`:
|
|
660
|
+
|
|
661
|
+
```bash
|
|
662
|
+
npx @insforge/cli config export --out insforge.toml
|
|
663
|
+
npx @insforge/cli config plan --file insforge.toml
|
|
664
|
+
npx @insforge/cli config apply --file insforge.toml --auto-approve
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
Supported TOML sections include auth redirects and verification flags, password policy, SMTP, storage upload size, realtime/schedule retention, and cloud deployment subdomain:
|
|
668
|
+
|
|
669
|
+
```toml
|
|
670
|
+
[auth]
|
|
671
|
+
allowed_redirect_urls = ["https://app.example.com"]
|
|
672
|
+
require_email_verification = true
|
|
673
|
+
verify_email_method = "link"
|
|
674
|
+
reset_password_method = "code"
|
|
675
|
+
disable_signup = false
|
|
676
|
+
|
|
677
|
+
[auth.password]
|
|
678
|
+
min_length = 12
|
|
679
|
+
require_number = true
|
|
680
|
+
require_lowercase = true
|
|
681
|
+
require_uppercase = false
|
|
682
|
+
require_special_char = true
|
|
683
|
+
|
|
684
|
+
[auth.smtp]
|
|
685
|
+
enabled = true
|
|
686
|
+
host = "smtp.example.com"
|
|
687
|
+
port = 587
|
|
688
|
+
username = "mailer@example.com"
|
|
689
|
+
password = "env(SMTP_PASSWORD)"
|
|
690
|
+
sender_email = "noreply@example.com"
|
|
691
|
+
sender_name = "Example"
|
|
692
|
+
min_interval_seconds = 60
|
|
693
|
+
|
|
694
|
+
[storage]
|
|
695
|
+
max_file_size_mb = 100
|
|
696
|
+
|
|
697
|
+
[realtime]
|
|
698
|
+
retention_days = 7
|
|
699
|
+
|
|
700
|
+
[schedules]
|
|
701
|
+
retention_days = 0 # 0 disables retention cleanup
|
|
702
|
+
|
|
703
|
+
[deployments]
|
|
704
|
+
subdomain = "my-app"
|
|
705
|
+
```
|
|
706
|
+
|
|
707
|
+
`config apply` uses backend admin APIs and skips sections that the connected backend version does not expose. It does not manage external provider resources such as OAuth apps, storage bucket lifecycle, realtime channels, deployment environment variables, functions, or secrets.
|
|
708
|
+
|
|
657
709
|
Global configuration is stored in `~/.insforge/`:
|
|
658
710
|
|
|
659
711
|
```
|
package/dist/index.js
CHANGED
|
@@ -263,10 +263,11 @@ import * as clack4 from "@clack/prompts";
|
|
|
263
263
|
|
|
264
264
|
// src/lib/errors.ts
|
|
265
265
|
var CLIError = class extends Error {
|
|
266
|
-
constructor(message, exitCode = 1, code) {
|
|
266
|
+
constructor(message, exitCode = 1, code, statusCode) {
|
|
267
267
|
super(message);
|
|
268
268
|
this.exitCode = exitCode;
|
|
269
269
|
this.code = code;
|
|
270
|
+
this.statusCode = statusCode;
|
|
270
271
|
this.name = "CLIError";
|
|
271
272
|
}
|
|
272
273
|
};
|
|
@@ -1172,7 +1173,7 @@ import * as clack5 from "@clack/prompts";
|
|
|
1172
1173
|
|
|
1173
1174
|
// src/lib/analytics.ts
|
|
1174
1175
|
import { PostHog } from "posthog-node";
|
|
1175
|
-
var POSTHOG_API_KEY = "";
|
|
1176
|
+
var POSTHOG_API_KEY = "phc_ueV1ii62wdBTkH7E70ugyeqHIHu8dFDdjs0qq3TZhJz";
|
|
1176
1177
|
var POSTHOG_HOST = process.env.POSTHOG_HOST || "https://us.i.posthog.com";
|
|
1177
1178
|
var client = null;
|
|
1178
1179
|
function getClient() {
|
|
@@ -2066,7 +2067,7 @@ ${err.nextActions}`;
|
|
|
2066
2067
|
if (res.status === 404 && isRouteLevel404 && path6.startsWith("/api/ai")) {
|
|
2067
2068
|
message = "AI Model Gateway setup is not available on this backend.\nUpgrade your InsForge project to a version with Model Gateway support, or keep using the legacy @insforge/sdk AI modules for projects that still rely on the older AI API surface.";
|
|
2068
2069
|
}
|
|
2069
|
-
throw new CLIError(message);
|
|
2070
|
+
throw new CLIError(message, 1, err.error, res.status);
|
|
2070
2071
|
}
|
|
2071
2072
|
return res;
|
|
2072
2073
|
}
|
|
@@ -3033,6 +3034,9 @@ Browse available templates: https://insforge.dev/templates`
|
|
|
3033
3034
|
json
|
|
3034
3035
|
);
|
|
3035
3036
|
if (downloaded) {
|
|
3037
|
+
captureEvent(orgId, "marketplace_template_downloaded", {
|
|
3038
|
+
template: opts.marketplace
|
|
3039
|
+
});
|
|
3036
3040
|
void reportMarketplaceDownload(opts.marketplace);
|
|
3037
3041
|
}
|
|
3038
3042
|
} else if (githubTemplates.includes(template)) {
|
|
@@ -7185,7 +7189,7 @@ function registerDiagnoseCommands(diagnoseCmd2) {
|
|
|
7185
7189
|
const s = !json ? clack16.spinner() : null;
|
|
7186
7190
|
s?.start("Collecting diagnostic data...");
|
|
7187
7191
|
const data2 = await collectDiagnosticData(projectId, ossMode, apiUrl);
|
|
7188
|
-
const cliVersion = "0.1.
|
|
7192
|
+
const cliVersion = "0.1.87";
|
|
7189
7193
|
s?.stop("Data collected");
|
|
7190
7194
|
if (!json) {
|
|
7191
7195
|
console.log(`
|
|
@@ -8947,9 +8951,47 @@ function validateConfig(input) {
|
|
|
8947
8951
|
out.project_id = obj.project_id;
|
|
8948
8952
|
}
|
|
8949
8953
|
if ("auth" in obj) out.auth = validateAuth(obj.auth);
|
|
8954
|
+
if ("storage" in obj) out.storage = validateStorage(obj.storage);
|
|
8955
|
+
if ("realtime" in obj) out.realtime = validateRetentionSection("realtime", obj.realtime);
|
|
8956
|
+
if ("schedules" in obj) out.schedules = validateRetentionSection("schedules", obj.schedules);
|
|
8950
8957
|
if ("deployments" in obj) out.deployments = validateDeployments(obj.deployments);
|
|
8951
8958
|
return out;
|
|
8952
8959
|
}
|
|
8960
|
+
function validateStorage(input) {
|
|
8961
|
+
if (input === null || typeof input !== "object" || Array.isArray(input)) {
|
|
8962
|
+
throw new ConfigValidationError("storage", "must be an object");
|
|
8963
|
+
}
|
|
8964
|
+
const obj = input;
|
|
8965
|
+
const out = {};
|
|
8966
|
+
if ("max_file_size_mb" in obj) {
|
|
8967
|
+
if (typeof obj.max_file_size_mb !== "number" || !Number.isInteger(obj.max_file_size_mb) || obj.max_file_size_mb < 1 || obj.max_file_size_mb > 200) {
|
|
8968
|
+
throw new ConfigValidationError(
|
|
8969
|
+
"storage.max_file_size_mb",
|
|
8970
|
+
"must be an integer between 1 and 200"
|
|
8971
|
+
);
|
|
8972
|
+
}
|
|
8973
|
+
out.max_file_size_mb = obj.max_file_size_mb;
|
|
8974
|
+
}
|
|
8975
|
+
return out;
|
|
8976
|
+
}
|
|
8977
|
+
function validateRetentionSection(path6, input) {
|
|
8978
|
+
if (input === null || typeof input !== "object" || Array.isArray(input)) {
|
|
8979
|
+
throw new ConfigValidationError(path6, "must be an object");
|
|
8980
|
+
}
|
|
8981
|
+
const obj = input;
|
|
8982
|
+
const out = {};
|
|
8983
|
+
if ("retention_days" in obj) {
|
|
8984
|
+
const v = obj.retention_days;
|
|
8985
|
+
if (v !== null && (typeof v !== "number" || !Number.isInteger(v) || v < 0)) {
|
|
8986
|
+
throw new ConfigValidationError(
|
|
8987
|
+
`${path6}.retention_days`,
|
|
8988
|
+
"must be a non-negative integer or null"
|
|
8989
|
+
);
|
|
8990
|
+
}
|
|
8991
|
+
out.retention_days = v;
|
|
8992
|
+
}
|
|
8993
|
+
return out;
|
|
8994
|
+
}
|
|
8953
8995
|
function validateDeployments(input) {
|
|
8954
8996
|
if (input === null || typeof input !== "object" || Array.isArray(input)) {
|
|
8955
8997
|
throw new ConfigValidationError("deployments", "must be an object");
|
|
@@ -9005,6 +9047,12 @@ function validateAuth(input) {
|
|
|
9005
9047
|
obj.reset_password_method
|
|
9006
9048
|
);
|
|
9007
9049
|
}
|
|
9050
|
+
if ("disable_signup" in obj) {
|
|
9051
|
+
if (typeof obj.disable_signup !== "boolean") {
|
|
9052
|
+
throw new ConfigValidationError("auth.disable_signup", "must be a boolean");
|
|
9053
|
+
}
|
|
9054
|
+
out.disable_signup = obj.disable_signup;
|
|
9055
|
+
}
|
|
9008
9056
|
if ("password" in obj) out.password = validatePassword(obj.password);
|
|
9009
9057
|
if ("smtp" in obj) out.smtp = validateSmtp(obj.smtp);
|
|
9010
9058
|
return out;
|
|
@@ -9140,6 +9188,21 @@ function stringifyConfigToml(config) {
|
|
|
9140
9188
|
lines.push("");
|
|
9141
9189
|
}
|
|
9142
9190
|
}
|
|
9191
|
+
if (config.storage) {
|
|
9192
|
+
lines.push("[storage]");
|
|
9193
|
+
renderStorageFields(config.storage, lines);
|
|
9194
|
+
lines.push("");
|
|
9195
|
+
}
|
|
9196
|
+
if (config.realtime) {
|
|
9197
|
+
lines.push("[realtime]");
|
|
9198
|
+
renderRetentionFields(config.realtime, lines);
|
|
9199
|
+
lines.push("");
|
|
9200
|
+
}
|
|
9201
|
+
if (config.schedules) {
|
|
9202
|
+
lines.push("[schedules]");
|
|
9203
|
+
renderRetentionFields(config.schedules, lines);
|
|
9204
|
+
lines.push("");
|
|
9205
|
+
}
|
|
9143
9206
|
if (config.deployments) {
|
|
9144
9207
|
if (typeof config.deployments.subdomain === "string" && config.deployments.subdomain !== "") {
|
|
9145
9208
|
lines.push("[deployments]");
|
|
@@ -9163,6 +9226,9 @@ function renderAuthFlatFields(auth, lines) {
|
|
|
9163
9226
|
if (auth.reset_password_method !== void 0) {
|
|
9164
9227
|
lines.push(`reset_password_method = ${JSON.stringify(auth.reset_password_method)}`);
|
|
9165
9228
|
}
|
|
9229
|
+
if (auth.disable_signup !== void 0) {
|
|
9230
|
+
lines.push(`disable_signup = ${auth.disable_signup}`);
|
|
9231
|
+
}
|
|
9166
9232
|
}
|
|
9167
9233
|
function renderPasswordFields(pw, lines) {
|
|
9168
9234
|
if (pw.min_length !== void 0) lines.push(`min_length = ${pw.min_length}`);
|
|
@@ -9199,9 +9265,19 @@ function renderSmtpFields(smtp, lines) {
|
|
|
9199
9265
|
lines.push(`min_interval_seconds = ${smtp.min_interval_seconds}`);
|
|
9200
9266
|
}
|
|
9201
9267
|
}
|
|
9268
|
+
function renderStorageFields(storage, lines) {
|
|
9269
|
+
if (storage.max_file_size_mb !== void 0) {
|
|
9270
|
+
lines.push(`max_file_size_mb = ${storage.max_file_size_mb}`);
|
|
9271
|
+
}
|
|
9272
|
+
}
|
|
9273
|
+
function renderRetentionFields(config, lines) {
|
|
9274
|
+
if ("retention_days" in config) {
|
|
9275
|
+
lines.push(`retention_days = ${config.retention_days ?? 0}`);
|
|
9276
|
+
}
|
|
9277
|
+
}
|
|
9202
9278
|
|
|
9203
9279
|
// src/lib/config-metadata.ts
|
|
9204
|
-
function liveFromMetadata(raw) {
|
|
9280
|
+
function liveFromMetadata(raw, endpointConfig = {}) {
|
|
9205
9281
|
const live = { auth: {} };
|
|
9206
9282
|
const a = isPlainObject(raw.auth) ? raw.auth : void 0;
|
|
9207
9283
|
if (a && "allowedRedirectUrls" in a) {
|
|
@@ -9216,6 +9292,9 @@ function liveFromMetadata(raw) {
|
|
|
9216
9292
|
if (a && "resetPasswordMethod" in a && (a.resetPasswordMethod === "code" || a.resetPasswordMethod === "link")) {
|
|
9217
9293
|
live.auth.reset_password_method = a.resetPasswordMethod;
|
|
9218
9294
|
}
|
|
9295
|
+
if (a && "disableSignup" in a) {
|
|
9296
|
+
live.auth.disable_signup = a.disableSignup ?? false;
|
|
9297
|
+
}
|
|
9219
9298
|
if (a && ("passwordMinLength" in a || "requireNumber" in a || "requireLowercase" in a || "requireUppercase" in a || "requireSpecialChar" in a)) {
|
|
9220
9299
|
live.auth.password = {
|
|
9221
9300
|
min_length: a.passwordMinLength ?? 8,
|
|
@@ -9244,6 +9323,18 @@ function liveFromMetadata(raw) {
|
|
|
9244
9323
|
subdomain: typeof d.customSlug === "string" && d.customSlug ? d.customSlug : null
|
|
9245
9324
|
};
|
|
9246
9325
|
}
|
|
9326
|
+
const maxFileSizeMb = asNumber(endpointConfig.storageConfig?.maxFileSizeMb);
|
|
9327
|
+
if (maxFileSizeMb !== void 0) {
|
|
9328
|
+
live.storage = { max_file_size_mb: maxFileSizeMb };
|
|
9329
|
+
}
|
|
9330
|
+
const realtimeRetention = asRetentionDays(endpointConfig.realtimeConfig?.retentionDays);
|
|
9331
|
+
if (realtimeRetention !== void 0) {
|
|
9332
|
+
live.realtime = { retention_days: realtimeRetention };
|
|
9333
|
+
}
|
|
9334
|
+
const schedulesRetention = asRetentionDays(endpointConfig.schedulesConfig?.retentionDays);
|
|
9335
|
+
if (schedulesRetention !== void 0) {
|
|
9336
|
+
live.schedules = { retention_days: schedulesRetention };
|
|
9337
|
+
}
|
|
9247
9338
|
return live;
|
|
9248
9339
|
}
|
|
9249
9340
|
function isPlainObject(v) {
|
|
@@ -9252,7 +9343,14 @@ function isPlainObject(v) {
|
|
|
9252
9343
|
function asStringArray(v) {
|
|
9253
9344
|
return Array.isArray(v) && v.every((x) => typeof x === "string") ? v : null;
|
|
9254
9345
|
}
|
|
9255
|
-
function
|
|
9346
|
+
function asNumber(v) {
|
|
9347
|
+
return typeof v === "number" && Number.isInteger(v) ? v : void 0;
|
|
9348
|
+
}
|
|
9349
|
+
function asRetentionDays(v) {
|
|
9350
|
+
if (v === null) return null;
|
|
9351
|
+
return asNumber(v);
|
|
9352
|
+
}
|
|
9353
|
+
function configFromMetadata(raw, endpointConfig = {}) {
|
|
9256
9354
|
const config = {};
|
|
9257
9355
|
const skipped = [];
|
|
9258
9356
|
const a = isPlainObject(raw.auth) ? raw.auth : void 0;
|
|
@@ -9280,6 +9378,12 @@ function configFromMetadata(raw) {
|
|
|
9280
9378
|
} else {
|
|
9281
9379
|
skipped.push("auth.reset_password_method");
|
|
9282
9380
|
}
|
|
9381
|
+
if (a && "disableSignup" in a) {
|
|
9382
|
+
config.auth = config.auth ?? {};
|
|
9383
|
+
config.auth.disable_signup = a.disableSignup ?? false;
|
|
9384
|
+
} else {
|
|
9385
|
+
skipped.push("auth.disable_signup");
|
|
9386
|
+
}
|
|
9283
9387
|
if (a && ("passwordMinLength" in a || "requireNumber" in a || "requireLowercase" in a || "requireUppercase" in a || "requireSpecialChar" in a)) {
|
|
9284
9388
|
config.auth = config.auth ?? {};
|
|
9285
9389
|
config.auth.password = {};
|
|
@@ -9323,6 +9427,24 @@ function configFromMetadata(raw) {
|
|
|
9323
9427
|
} else {
|
|
9324
9428
|
skipped.push("deployments.subdomain");
|
|
9325
9429
|
}
|
|
9430
|
+
const maxFileSizeMb = asNumber(endpointConfig.storageConfig?.maxFileSizeMb);
|
|
9431
|
+
if (maxFileSizeMb !== void 0) {
|
|
9432
|
+
config.storage = { max_file_size_mb: maxFileSizeMb };
|
|
9433
|
+
} else {
|
|
9434
|
+
skipped.push("storage.max_file_size_mb");
|
|
9435
|
+
}
|
|
9436
|
+
const realtimeRetention = asRetentionDays(endpointConfig.realtimeConfig?.retentionDays);
|
|
9437
|
+
if (realtimeRetention !== void 0) {
|
|
9438
|
+
config.realtime = { retention_days: realtimeRetention };
|
|
9439
|
+
} else {
|
|
9440
|
+
skipped.push("realtime.retention_days");
|
|
9441
|
+
}
|
|
9442
|
+
const schedulesRetention = asRetentionDays(endpointConfig.schedulesConfig?.retentionDays);
|
|
9443
|
+
if (schedulesRetention !== void 0) {
|
|
9444
|
+
config.schedules = { retention_days: schedulesRetention };
|
|
9445
|
+
} else {
|
|
9446
|
+
skipped.push("schedules.retention_days");
|
|
9447
|
+
}
|
|
9326
9448
|
return { config, skipped };
|
|
9327
9449
|
}
|
|
9328
9450
|
|
|
@@ -9360,7 +9482,16 @@ function registerConfigExportCommand(cfg) {
|
|
|
9360
9482
|
}
|
|
9361
9483
|
const res = await ossFetch("/api/metadata");
|
|
9362
9484
|
const raw = await res.json();
|
|
9363
|
-
const
|
|
9485
|
+
const [storageConfig, realtimeConfig, schedulesConfig] = await Promise.all([
|
|
9486
|
+
fetchOptionalConfig("/api/storage/config"),
|
|
9487
|
+
fetchOptionalConfig("/api/realtime/config"),
|
|
9488
|
+
fetchOptionalConfig("/api/schedules/config")
|
|
9489
|
+
]);
|
|
9490
|
+
const { config, skipped } = configFromMetadata(raw, {
|
|
9491
|
+
storageConfig,
|
|
9492
|
+
realtimeConfig,
|
|
9493
|
+
schedulesConfig
|
|
9494
|
+
});
|
|
9364
9495
|
const toml = stringifyConfigToml(config);
|
|
9365
9496
|
writeFileSync8(target, toml, "utf8");
|
|
9366
9497
|
if (json) {
|
|
@@ -9396,6 +9527,18 @@ function registerConfigExportCommand(cfg) {
|
|
|
9396
9527
|
}
|
|
9397
9528
|
});
|
|
9398
9529
|
}
|
|
9530
|
+
async function fetchOptionalConfig(path6) {
|
|
9531
|
+
try {
|
|
9532
|
+
const res = await ossFetch(path6);
|
|
9533
|
+
return await res.json();
|
|
9534
|
+
} catch (err) {
|
|
9535
|
+
if (isMissingOptionalEndpoint(err)) return void 0;
|
|
9536
|
+
throw err;
|
|
9537
|
+
}
|
|
9538
|
+
}
|
|
9539
|
+
function isMissingOptionalEndpoint(err) {
|
|
9540
|
+
return err instanceof CLIError && err.statusCode === 404 && (err.code === void 0 || err.code === "NOT_FOUND");
|
|
9541
|
+
}
|
|
9399
9542
|
|
|
9400
9543
|
// src/commands/config/plan.ts
|
|
9401
9544
|
import { readFileSync as readFileSync9 } from "fs";
|
|
@@ -9459,6 +9602,19 @@ function diffConfig({ live, file }) {
|
|
|
9459
9602
|
});
|
|
9460
9603
|
}
|
|
9461
9604
|
}
|
|
9605
|
+
if (fileAuth && "disable_signup" in fileAuth) {
|
|
9606
|
+
const fromV = liveAuth.disable_signup ?? false;
|
|
9607
|
+
const toV = fileAuth.disable_signup ?? false;
|
|
9608
|
+
if (fromV !== toV) {
|
|
9609
|
+
changes.push({
|
|
9610
|
+
section: "auth",
|
|
9611
|
+
op: "modify",
|
|
9612
|
+
key: "disable_signup",
|
|
9613
|
+
from: fromV,
|
|
9614
|
+
to: toV
|
|
9615
|
+
});
|
|
9616
|
+
}
|
|
9617
|
+
}
|
|
9462
9618
|
if (fileAuth?.password) {
|
|
9463
9619
|
diffPassword(liveAuth.password, fileAuth.password, changes);
|
|
9464
9620
|
}
|
|
@@ -9466,6 +9622,15 @@ function diffConfig({ live, file }) {
|
|
|
9466
9622
|
const smtpChange = diffSmtp(liveAuth.smtp, fileAuth.smtp);
|
|
9467
9623
|
if (smtpChange) changes.push(smtpChange);
|
|
9468
9624
|
}
|
|
9625
|
+
if (file.storage !== void 0) {
|
|
9626
|
+
diffStorage(live.storage, file.storage, changes);
|
|
9627
|
+
}
|
|
9628
|
+
if (file.realtime !== void 0) {
|
|
9629
|
+
diffRetention("realtime", live.realtime, file.realtime, changes);
|
|
9630
|
+
}
|
|
9631
|
+
if (file.schedules !== void 0) {
|
|
9632
|
+
diffRetention("schedules", live.schedules, file.schedules, changes);
|
|
9633
|
+
}
|
|
9469
9634
|
const fileDeployments = file.deployments;
|
|
9470
9635
|
const liveDeployments = live.deployments ?? {};
|
|
9471
9636
|
if (fileDeployments && "subdomain" in fileDeployments) {
|
|
@@ -9514,6 +9679,36 @@ function diffPassword(live, file, changes) {
|
|
|
9514
9679
|
}
|
|
9515
9680
|
}
|
|
9516
9681
|
}
|
|
9682
|
+
function diffStorage(live, file, changes) {
|
|
9683
|
+
if (file.max_file_size_mb === void 0) return;
|
|
9684
|
+
const fromV = live?.max_file_size_mb ?? EMPTY_STORAGE_CONFIG.max_file_size_mb;
|
|
9685
|
+
if (fromV !== file.max_file_size_mb) {
|
|
9686
|
+
changes.push({
|
|
9687
|
+
section: "storage",
|
|
9688
|
+
op: "modify",
|
|
9689
|
+
key: "max_file_size_mb",
|
|
9690
|
+
from: fromV,
|
|
9691
|
+
to: file.max_file_size_mb
|
|
9692
|
+
});
|
|
9693
|
+
}
|
|
9694
|
+
}
|
|
9695
|
+
function diffRetention(section, live, file, changes) {
|
|
9696
|
+
if (!("retention_days" in file)) return;
|
|
9697
|
+
const fromV = normalizeRetentionDays(live?.retention_days);
|
|
9698
|
+
const toV = normalizeRetentionDays(file.retention_days);
|
|
9699
|
+
if (fromV !== toV) {
|
|
9700
|
+
changes.push({
|
|
9701
|
+
section,
|
|
9702
|
+
op: "modify",
|
|
9703
|
+
key: "retention_days",
|
|
9704
|
+
from: fromV,
|
|
9705
|
+
to: toV
|
|
9706
|
+
});
|
|
9707
|
+
}
|
|
9708
|
+
}
|
|
9709
|
+
function normalizeRetentionDays(value) {
|
|
9710
|
+
return value === void 0 || value === null || value === 0 ? null : value;
|
|
9711
|
+
}
|
|
9517
9712
|
function diffSmtp(live, fileSmtp) {
|
|
9518
9713
|
const livedView = renderLiveSmtp(live);
|
|
9519
9714
|
const tomlView = renderFileSmtp(fileSmtp);
|
|
@@ -9572,6 +9767,9 @@ var EMPTY_SMTP_VIEW = {
|
|
|
9572
9767
|
sender_name: "",
|
|
9573
9768
|
min_interval_seconds: 60
|
|
9574
9769
|
};
|
|
9770
|
+
var EMPTY_STORAGE_CONFIG = {
|
|
9771
|
+
max_file_size_mb: 50
|
|
9772
|
+
};
|
|
9575
9773
|
var EMPTY_PASSWORD_POLICY = {
|
|
9576
9774
|
min_length: 8,
|
|
9577
9775
|
require_number: false,
|
|
@@ -9652,7 +9850,7 @@ function formatChange(c) {
|
|
|
9652
9850
|
}
|
|
9653
9851
|
|
|
9654
9852
|
// src/lib/config-capabilities.ts
|
|
9655
|
-
function metadataSupports(raw, change) {
|
|
9853
|
+
function metadataSupports(raw, change, endpointConfig = {}) {
|
|
9656
9854
|
if (change.section === "auth" && change.key === "allowed_redirect_urls") {
|
|
9657
9855
|
return hasAuthKey(raw, "allowedRedirectUrls");
|
|
9658
9856
|
}
|
|
@@ -9665,12 +9863,24 @@ function metadataSupports(raw, change) {
|
|
|
9665
9863
|
if (change.section === "auth" && change.key === "reset_password_method") {
|
|
9666
9864
|
return hasAuthKey(raw, "resetPasswordMethod");
|
|
9667
9865
|
}
|
|
9866
|
+
if (change.section === "auth" && change.key === "disable_signup") {
|
|
9867
|
+
return hasAuthKey(raw, "disableSignup");
|
|
9868
|
+
}
|
|
9668
9869
|
if (change.section === "auth.password") {
|
|
9669
9870
|
return hasAuthKey(raw, AUTH_PASSWORD_WIRE_KEY[change.key]);
|
|
9670
9871
|
}
|
|
9671
9872
|
if (change.section === "auth.smtp") {
|
|
9672
9873
|
return hasAuthKey(raw, "smtpConfig");
|
|
9673
9874
|
}
|
|
9875
|
+
if (change.section === "storage" && change.key === "max_file_size_mb") {
|
|
9876
|
+
return hasConfigKey(endpointConfig.storageConfig, "maxFileSizeMb");
|
|
9877
|
+
}
|
|
9878
|
+
if (change.section === "realtime" && change.key === "retention_days") {
|
|
9879
|
+
return hasConfigKey(endpointConfig.realtimeConfig, "retentionDays");
|
|
9880
|
+
}
|
|
9881
|
+
if (change.section === "schedules" && change.key === "retention_days") {
|
|
9882
|
+
return hasConfigKey(endpointConfig.schedulesConfig, "retentionDays");
|
|
9883
|
+
}
|
|
9674
9884
|
if (change.section === "deployments" && change.key === "subdomain") {
|
|
9675
9885
|
return raw?.deployments !== void 0 && raw.deployments !== null && typeof raw.deployments === "object";
|
|
9676
9886
|
}
|
|
@@ -9682,6 +9892,9 @@ function hasAuthKey(raw, key) {
|
|
|
9682
9892
|
const auth = raw?.auth;
|
|
9683
9893
|
return auth !== void 0 && auth !== null && typeof auth === "object" && key in auth;
|
|
9684
9894
|
}
|
|
9895
|
+
function hasConfigKey(slice, key) {
|
|
9896
|
+
return slice !== void 0 && slice !== null && typeof slice === "object" && key in slice;
|
|
9897
|
+
}
|
|
9685
9898
|
var AUTH_PASSWORD_WIRE_KEY = {
|
|
9686
9899
|
min_length: "passwordMinLength",
|
|
9687
9900
|
require_number: "requireNumber",
|
|
@@ -9710,9 +9923,25 @@ function registerConfigPlanCommand(cfg) {
|
|
|
9710
9923
|
const file = parseConfigToml(tomlSource);
|
|
9711
9924
|
const res = await ossFetch("/api/metadata");
|
|
9712
9925
|
const raw = await res.json();
|
|
9713
|
-
const
|
|
9926
|
+
const endpointConfig = {};
|
|
9927
|
+
if (file.storage !== void 0) {
|
|
9928
|
+
endpointConfig.storageConfig = await fetchOptionalConfig2(
|
|
9929
|
+
"/api/storage/config"
|
|
9930
|
+
);
|
|
9931
|
+
}
|
|
9932
|
+
if (file.realtime !== void 0) {
|
|
9933
|
+
endpointConfig.realtimeConfig = await fetchOptionalConfig2(
|
|
9934
|
+
"/api/realtime/config"
|
|
9935
|
+
);
|
|
9936
|
+
}
|
|
9937
|
+
if (file.schedules !== void 0) {
|
|
9938
|
+
endpointConfig.schedulesConfig = await fetchOptionalConfig2(
|
|
9939
|
+
"/api/schedules/config"
|
|
9940
|
+
);
|
|
9941
|
+
}
|
|
9942
|
+
const live = liveFromMetadata(raw, endpointConfig);
|
|
9714
9943
|
const result = diffConfig({ live, file });
|
|
9715
|
-
const skipped = result.changes.filter((c) => !metadataSupports(raw, c)).map((c) => changePath(c));
|
|
9944
|
+
const skipped = result.changes.filter((c) => !metadataSupports(raw, c, endpointConfig)).map((c) => changePath(c));
|
|
9716
9945
|
if (json) {
|
|
9717
9946
|
console.log(JSON.stringify({ ...result, skipped }, null, 2));
|
|
9718
9947
|
} else {
|
|
@@ -9748,6 +9977,18 @@ function registerConfigPlanCommand(cfg) {
|
|
|
9748
9977
|
}
|
|
9749
9978
|
});
|
|
9750
9979
|
}
|
|
9980
|
+
async function fetchOptionalConfig2(path6) {
|
|
9981
|
+
try {
|
|
9982
|
+
const res = await ossFetch(path6);
|
|
9983
|
+
return await res.json();
|
|
9984
|
+
} catch (err) {
|
|
9985
|
+
if (isMissingOptionalEndpoint2(err)) return void 0;
|
|
9986
|
+
throw err;
|
|
9987
|
+
}
|
|
9988
|
+
}
|
|
9989
|
+
function isMissingOptionalEndpoint2(err) {
|
|
9990
|
+
return err instanceof CLIError && err.statusCode === 404 && (err.code === void 0 || err.code === "NOT_FOUND");
|
|
9991
|
+
}
|
|
9751
9992
|
|
|
9752
9993
|
// src/commands/config/apply.ts
|
|
9753
9994
|
import { readFileSync as readFileSync10 } from "fs";
|
|
@@ -9766,7 +10007,23 @@ function registerConfigApplyCommand(cfg) {
|
|
|
9766
10007
|
const file = parseConfigToml(tomlSource);
|
|
9767
10008
|
const res = await ossFetch("/api/metadata");
|
|
9768
10009
|
const raw = await res.json();
|
|
9769
|
-
const
|
|
10010
|
+
const endpointConfig = {};
|
|
10011
|
+
if (file.storage !== void 0) {
|
|
10012
|
+
endpointConfig.storageConfig = await fetchOptionalConfig3(
|
|
10013
|
+
"/api/storage/config"
|
|
10014
|
+
);
|
|
10015
|
+
}
|
|
10016
|
+
if (file.realtime !== void 0) {
|
|
10017
|
+
endpointConfig.realtimeConfig = await fetchOptionalConfig3(
|
|
10018
|
+
"/api/realtime/config"
|
|
10019
|
+
);
|
|
10020
|
+
}
|
|
10021
|
+
if (file.schedules !== void 0) {
|
|
10022
|
+
endpointConfig.schedulesConfig = await fetchOptionalConfig3(
|
|
10023
|
+
"/api/schedules/config"
|
|
10024
|
+
);
|
|
10025
|
+
}
|
|
10026
|
+
const live = liveFromMetadata(raw, endpointConfig);
|
|
9770
10027
|
const result = diffConfig({ live, file });
|
|
9771
10028
|
const approved = opts.autoApprove || yes;
|
|
9772
10029
|
const sectionsChanged = Array.from(
|
|
@@ -9819,7 +10076,7 @@ function registerConfigApplyCommand(cfg) {
|
|
|
9819
10076
|
const skipped = [];
|
|
9820
10077
|
for (const change of result.changes) {
|
|
9821
10078
|
const path6 = changePath(change);
|
|
9822
|
-
if (!metadataSupports(raw, change)) {
|
|
10079
|
+
if (!metadataSupports(raw, change, endpointConfig)) {
|
|
9823
10080
|
skipped.push({
|
|
9824
10081
|
key: path6,
|
|
9825
10082
|
reason: `your backend doesn't expose ${path6} \u2014 upgrade the project to apply this section`
|
|
@@ -9870,6 +10127,18 @@ function registerConfigApplyCommand(cfg) {
|
|
|
9870
10127
|
}
|
|
9871
10128
|
});
|
|
9872
10129
|
}
|
|
10130
|
+
async function fetchOptionalConfig3(path6) {
|
|
10131
|
+
try {
|
|
10132
|
+
const res = await ossFetch(path6);
|
|
10133
|
+
return await res.json();
|
|
10134
|
+
} catch (err) {
|
|
10135
|
+
if (isMissingOptionalEndpoint3(err)) return void 0;
|
|
10136
|
+
throw err;
|
|
10137
|
+
}
|
|
10138
|
+
}
|
|
10139
|
+
function isMissingOptionalEndpoint3(err) {
|
|
10140
|
+
return err instanceof CLIError && err.statusCode === 404 && (err.code === void 0 || err.code === "NOT_FOUND");
|
|
10141
|
+
}
|
|
9873
10142
|
async function applyChange(change) {
|
|
9874
10143
|
if (change.section === "auth" && change.key === "allowed_redirect_urls") {
|
|
9875
10144
|
await ossFetch("/api/auth/config", {
|
|
@@ -9899,6 +10168,13 @@ async function applyChange(change) {
|
|
|
9899
10168
|
});
|
|
9900
10169
|
return;
|
|
9901
10170
|
}
|
|
10171
|
+
if (change.section === "auth" && change.key === "disable_signup") {
|
|
10172
|
+
await ossFetch("/api/auth/config", {
|
|
10173
|
+
method: "PUT",
|
|
10174
|
+
body: JSON.stringify({ disableSignup: change.to })
|
|
10175
|
+
});
|
|
10176
|
+
return;
|
|
10177
|
+
}
|
|
9902
10178
|
if (change.section === "auth.password") {
|
|
9903
10179
|
const wireKey = authPasswordWireKey(change.key);
|
|
9904
10180
|
await ossFetch("/api/auth/config", {
|
|
@@ -9931,6 +10207,27 @@ async function applyChange(change) {
|
|
|
9931
10207
|
});
|
|
9932
10208
|
return;
|
|
9933
10209
|
}
|
|
10210
|
+
if (change.section === "storage" && change.key === "max_file_size_mb") {
|
|
10211
|
+
await ossFetch("/api/storage/config", {
|
|
10212
|
+
method: "PUT",
|
|
10213
|
+
body: JSON.stringify({ maxFileSizeMb: change.to })
|
|
10214
|
+
});
|
|
10215
|
+
return;
|
|
10216
|
+
}
|
|
10217
|
+
if (change.section === "realtime" && change.key === "retention_days") {
|
|
10218
|
+
await ossFetch("/api/realtime/config", {
|
|
10219
|
+
method: "PATCH",
|
|
10220
|
+
body: JSON.stringify({ retentionDays: change.to })
|
|
10221
|
+
});
|
|
10222
|
+
return;
|
|
10223
|
+
}
|
|
10224
|
+
if (change.section === "schedules" && change.key === "retention_days") {
|
|
10225
|
+
await ossFetch("/api/schedules/config", {
|
|
10226
|
+
method: "PATCH",
|
|
10227
|
+
body: JSON.stringify({ retentionDays: change.to })
|
|
10228
|
+
});
|
|
10229
|
+
return;
|
|
10230
|
+
}
|
|
9934
10231
|
if (change.section === "deployments" && change.key === "subdomain") {
|
|
9935
10232
|
await ossFetch("/api/deployments/slug", {
|
|
9936
10233
|
method: "PUT",
|