@insforge/cli 0.1.86 → 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 +305 -11
- 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
|
};
|
|
@@ -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
|
}
|
|
@@ -7188,7 +7189,7 @@ function registerDiagnoseCommands(diagnoseCmd2) {
|
|
|
7188
7189
|
const s = !json ? clack16.spinner() : null;
|
|
7189
7190
|
s?.start("Collecting diagnostic data...");
|
|
7190
7191
|
const data2 = await collectDiagnosticData(projectId, ossMode, apiUrl);
|
|
7191
|
-
const cliVersion = "0.1.
|
|
7192
|
+
const cliVersion = "0.1.87";
|
|
7192
7193
|
s?.stop("Data collected");
|
|
7193
7194
|
if (!json) {
|
|
7194
7195
|
console.log(`
|
|
@@ -8950,9 +8951,47 @@ function validateConfig(input) {
|
|
|
8950
8951
|
out.project_id = obj.project_id;
|
|
8951
8952
|
}
|
|
8952
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);
|
|
8953
8957
|
if ("deployments" in obj) out.deployments = validateDeployments(obj.deployments);
|
|
8954
8958
|
return out;
|
|
8955
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
|
+
}
|
|
8956
8995
|
function validateDeployments(input) {
|
|
8957
8996
|
if (input === null || typeof input !== "object" || Array.isArray(input)) {
|
|
8958
8997
|
throw new ConfigValidationError("deployments", "must be an object");
|
|
@@ -9008,6 +9047,12 @@ function validateAuth(input) {
|
|
|
9008
9047
|
obj.reset_password_method
|
|
9009
9048
|
);
|
|
9010
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
|
+
}
|
|
9011
9056
|
if ("password" in obj) out.password = validatePassword(obj.password);
|
|
9012
9057
|
if ("smtp" in obj) out.smtp = validateSmtp(obj.smtp);
|
|
9013
9058
|
return out;
|
|
@@ -9143,6 +9188,21 @@ function stringifyConfigToml(config) {
|
|
|
9143
9188
|
lines.push("");
|
|
9144
9189
|
}
|
|
9145
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
|
+
}
|
|
9146
9206
|
if (config.deployments) {
|
|
9147
9207
|
if (typeof config.deployments.subdomain === "string" && config.deployments.subdomain !== "") {
|
|
9148
9208
|
lines.push("[deployments]");
|
|
@@ -9166,6 +9226,9 @@ function renderAuthFlatFields(auth, lines) {
|
|
|
9166
9226
|
if (auth.reset_password_method !== void 0) {
|
|
9167
9227
|
lines.push(`reset_password_method = ${JSON.stringify(auth.reset_password_method)}`);
|
|
9168
9228
|
}
|
|
9229
|
+
if (auth.disable_signup !== void 0) {
|
|
9230
|
+
lines.push(`disable_signup = ${auth.disable_signup}`);
|
|
9231
|
+
}
|
|
9169
9232
|
}
|
|
9170
9233
|
function renderPasswordFields(pw, lines) {
|
|
9171
9234
|
if (pw.min_length !== void 0) lines.push(`min_length = ${pw.min_length}`);
|
|
@@ -9202,9 +9265,19 @@ function renderSmtpFields(smtp, lines) {
|
|
|
9202
9265
|
lines.push(`min_interval_seconds = ${smtp.min_interval_seconds}`);
|
|
9203
9266
|
}
|
|
9204
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
|
+
}
|
|
9205
9278
|
|
|
9206
9279
|
// src/lib/config-metadata.ts
|
|
9207
|
-
function liveFromMetadata(raw) {
|
|
9280
|
+
function liveFromMetadata(raw, endpointConfig = {}) {
|
|
9208
9281
|
const live = { auth: {} };
|
|
9209
9282
|
const a = isPlainObject(raw.auth) ? raw.auth : void 0;
|
|
9210
9283
|
if (a && "allowedRedirectUrls" in a) {
|
|
@@ -9219,6 +9292,9 @@ function liveFromMetadata(raw) {
|
|
|
9219
9292
|
if (a && "resetPasswordMethod" in a && (a.resetPasswordMethod === "code" || a.resetPasswordMethod === "link")) {
|
|
9220
9293
|
live.auth.reset_password_method = a.resetPasswordMethod;
|
|
9221
9294
|
}
|
|
9295
|
+
if (a && "disableSignup" in a) {
|
|
9296
|
+
live.auth.disable_signup = a.disableSignup ?? false;
|
|
9297
|
+
}
|
|
9222
9298
|
if (a && ("passwordMinLength" in a || "requireNumber" in a || "requireLowercase" in a || "requireUppercase" in a || "requireSpecialChar" in a)) {
|
|
9223
9299
|
live.auth.password = {
|
|
9224
9300
|
min_length: a.passwordMinLength ?? 8,
|
|
@@ -9247,6 +9323,18 @@ function liveFromMetadata(raw) {
|
|
|
9247
9323
|
subdomain: typeof d.customSlug === "string" && d.customSlug ? d.customSlug : null
|
|
9248
9324
|
};
|
|
9249
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
|
+
}
|
|
9250
9338
|
return live;
|
|
9251
9339
|
}
|
|
9252
9340
|
function isPlainObject(v) {
|
|
@@ -9255,7 +9343,14 @@ function isPlainObject(v) {
|
|
|
9255
9343
|
function asStringArray(v) {
|
|
9256
9344
|
return Array.isArray(v) && v.every((x) => typeof x === "string") ? v : null;
|
|
9257
9345
|
}
|
|
9258
|
-
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 = {}) {
|
|
9259
9354
|
const config = {};
|
|
9260
9355
|
const skipped = [];
|
|
9261
9356
|
const a = isPlainObject(raw.auth) ? raw.auth : void 0;
|
|
@@ -9283,6 +9378,12 @@ function configFromMetadata(raw) {
|
|
|
9283
9378
|
} else {
|
|
9284
9379
|
skipped.push("auth.reset_password_method");
|
|
9285
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
|
+
}
|
|
9286
9387
|
if (a && ("passwordMinLength" in a || "requireNumber" in a || "requireLowercase" in a || "requireUppercase" in a || "requireSpecialChar" in a)) {
|
|
9287
9388
|
config.auth = config.auth ?? {};
|
|
9288
9389
|
config.auth.password = {};
|
|
@@ -9326,6 +9427,24 @@ function configFromMetadata(raw) {
|
|
|
9326
9427
|
} else {
|
|
9327
9428
|
skipped.push("deployments.subdomain");
|
|
9328
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
|
+
}
|
|
9329
9448
|
return { config, skipped };
|
|
9330
9449
|
}
|
|
9331
9450
|
|
|
@@ -9363,7 +9482,16 @@ function registerConfigExportCommand(cfg) {
|
|
|
9363
9482
|
}
|
|
9364
9483
|
const res = await ossFetch("/api/metadata");
|
|
9365
9484
|
const raw = await res.json();
|
|
9366
|
-
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
|
+
});
|
|
9367
9495
|
const toml = stringifyConfigToml(config);
|
|
9368
9496
|
writeFileSync8(target, toml, "utf8");
|
|
9369
9497
|
if (json) {
|
|
@@ -9399,6 +9527,18 @@ function registerConfigExportCommand(cfg) {
|
|
|
9399
9527
|
}
|
|
9400
9528
|
});
|
|
9401
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
|
+
}
|
|
9402
9542
|
|
|
9403
9543
|
// src/commands/config/plan.ts
|
|
9404
9544
|
import { readFileSync as readFileSync9 } from "fs";
|
|
@@ -9462,6 +9602,19 @@ function diffConfig({ live, file }) {
|
|
|
9462
9602
|
});
|
|
9463
9603
|
}
|
|
9464
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
|
+
}
|
|
9465
9618
|
if (fileAuth?.password) {
|
|
9466
9619
|
diffPassword(liveAuth.password, fileAuth.password, changes);
|
|
9467
9620
|
}
|
|
@@ -9469,6 +9622,15 @@ function diffConfig({ live, file }) {
|
|
|
9469
9622
|
const smtpChange = diffSmtp(liveAuth.smtp, fileAuth.smtp);
|
|
9470
9623
|
if (smtpChange) changes.push(smtpChange);
|
|
9471
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
|
+
}
|
|
9472
9634
|
const fileDeployments = file.deployments;
|
|
9473
9635
|
const liveDeployments = live.deployments ?? {};
|
|
9474
9636
|
if (fileDeployments && "subdomain" in fileDeployments) {
|
|
@@ -9517,6 +9679,36 @@ function diffPassword(live, file, changes) {
|
|
|
9517
9679
|
}
|
|
9518
9680
|
}
|
|
9519
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
|
+
}
|
|
9520
9712
|
function diffSmtp(live, fileSmtp) {
|
|
9521
9713
|
const livedView = renderLiveSmtp(live);
|
|
9522
9714
|
const tomlView = renderFileSmtp(fileSmtp);
|
|
@@ -9575,6 +9767,9 @@ var EMPTY_SMTP_VIEW = {
|
|
|
9575
9767
|
sender_name: "",
|
|
9576
9768
|
min_interval_seconds: 60
|
|
9577
9769
|
};
|
|
9770
|
+
var EMPTY_STORAGE_CONFIG = {
|
|
9771
|
+
max_file_size_mb: 50
|
|
9772
|
+
};
|
|
9578
9773
|
var EMPTY_PASSWORD_POLICY = {
|
|
9579
9774
|
min_length: 8,
|
|
9580
9775
|
require_number: false,
|
|
@@ -9655,7 +9850,7 @@ function formatChange(c) {
|
|
|
9655
9850
|
}
|
|
9656
9851
|
|
|
9657
9852
|
// src/lib/config-capabilities.ts
|
|
9658
|
-
function metadataSupports(raw, change) {
|
|
9853
|
+
function metadataSupports(raw, change, endpointConfig = {}) {
|
|
9659
9854
|
if (change.section === "auth" && change.key === "allowed_redirect_urls") {
|
|
9660
9855
|
return hasAuthKey(raw, "allowedRedirectUrls");
|
|
9661
9856
|
}
|
|
@@ -9668,12 +9863,24 @@ function metadataSupports(raw, change) {
|
|
|
9668
9863
|
if (change.section === "auth" && change.key === "reset_password_method") {
|
|
9669
9864
|
return hasAuthKey(raw, "resetPasswordMethod");
|
|
9670
9865
|
}
|
|
9866
|
+
if (change.section === "auth" && change.key === "disable_signup") {
|
|
9867
|
+
return hasAuthKey(raw, "disableSignup");
|
|
9868
|
+
}
|
|
9671
9869
|
if (change.section === "auth.password") {
|
|
9672
9870
|
return hasAuthKey(raw, AUTH_PASSWORD_WIRE_KEY[change.key]);
|
|
9673
9871
|
}
|
|
9674
9872
|
if (change.section === "auth.smtp") {
|
|
9675
9873
|
return hasAuthKey(raw, "smtpConfig");
|
|
9676
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
|
+
}
|
|
9677
9884
|
if (change.section === "deployments" && change.key === "subdomain") {
|
|
9678
9885
|
return raw?.deployments !== void 0 && raw.deployments !== null && typeof raw.deployments === "object";
|
|
9679
9886
|
}
|
|
@@ -9685,6 +9892,9 @@ function hasAuthKey(raw, key) {
|
|
|
9685
9892
|
const auth = raw?.auth;
|
|
9686
9893
|
return auth !== void 0 && auth !== null && typeof auth === "object" && key in auth;
|
|
9687
9894
|
}
|
|
9895
|
+
function hasConfigKey(slice, key) {
|
|
9896
|
+
return slice !== void 0 && slice !== null && typeof slice === "object" && key in slice;
|
|
9897
|
+
}
|
|
9688
9898
|
var AUTH_PASSWORD_WIRE_KEY = {
|
|
9689
9899
|
min_length: "passwordMinLength",
|
|
9690
9900
|
require_number: "requireNumber",
|
|
@@ -9713,9 +9923,25 @@ function registerConfigPlanCommand(cfg) {
|
|
|
9713
9923
|
const file = parseConfigToml(tomlSource);
|
|
9714
9924
|
const res = await ossFetch("/api/metadata");
|
|
9715
9925
|
const raw = await res.json();
|
|
9716
|
-
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);
|
|
9717
9943
|
const result = diffConfig({ live, file });
|
|
9718
|
-
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));
|
|
9719
9945
|
if (json) {
|
|
9720
9946
|
console.log(JSON.stringify({ ...result, skipped }, null, 2));
|
|
9721
9947
|
} else {
|
|
@@ -9751,6 +9977,18 @@ function registerConfigPlanCommand(cfg) {
|
|
|
9751
9977
|
}
|
|
9752
9978
|
});
|
|
9753
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
|
+
}
|
|
9754
9992
|
|
|
9755
9993
|
// src/commands/config/apply.ts
|
|
9756
9994
|
import { readFileSync as readFileSync10 } from "fs";
|
|
@@ -9769,7 +10007,23 @@ function registerConfigApplyCommand(cfg) {
|
|
|
9769
10007
|
const file = parseConfigToml(tomlSource);
|
|
9770
10008
|
const res = await ossFetch("/api/metadata");
|
|
9771
10009
|
const raw = await res.json();
|
|
9772
|
-
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);
|
|
9773
10027
|
const result = diffConfig({ live, file });
|
|
9774
10028
|
const approved = opts.autoApprove || yes;
|
|
9775
10029
|
const sectionsChanged = Array.from(
|
|
@@ -9822,7 +10076,7 @@ function registerConfigApplyCommand(cfg) {
|
|
|
9822
10076
|
const skipped = [];
|
|
9823
10077
|
for (const change of result.changes) {
|
|
9824
10078
|
const path6 = changePath(change);
|
|
9825
|
-
if (!metadataSupports(raw, change)) {
|
|
10079
|
+
if (!metadataSupports(raw, change, endpointConfig)) {
|
|
9826
10080
|
skipped.push({
|
|
9827
10081
|
key: path6,
|
|
9828
10082
|
reason: `your backend doesn't expose ${path6} \u2014 upgrade the project to apply this section`
|
|
@@ -9873,6 +10127,18 @@ function registerConfigApplyCommand(cfg) {
|
|
|
9873
10127
|
}
|
|
9874
10128
|
});
|
|
9875
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
|
+
}
|
|
9876
10142
|
async function applyChange(change) {
|
|
9877
10143
|
if (change.section === "auth" && change.key === "allowed_redirect_urls") {
|
|
9878
10144
|
await ossFetch("/api/auth/config", {
|
|
@@ -9902,6 +10168,13 @@ async function applyChange(change) {
|
|
|
9902
10168
|
});
|
|
9903
10169
|
return;
|
|
9904
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
|
+
}
|
|
9905
10178
|
if (change.section === "auth.password") {
|
|
9906
10179
|
const wireKey = authPasswordWireKey(change.key);
|
|
9907
10180
|
await ossFetch("/api/auth/config", {
|
|
@@ -9934,6 +10207,27 @@ async function applyChange(change) {
|
|
|
9934
10207
|
});
|
|
9935
10208
|
return;
|
|
9936
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
|
+
}
|
|
9937
10231
|
if (change.section === "deployments" && change.key === "subdomain") {
|
|
9938
10232
|
await ossFetch("/api/deployments/slug", {
|
|
9939
10233
|
method: "PUT",
|