gencow 0.1.169 → 0.1.170
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/lib/backup-command.mjs +70 -20
- package/package.json +1 -1
- package/server/index.js +13 -9
- package/server/index.js.map +2 -2
package/lib/backup-command.mjs
CHANGED
|
@@ -20,7 +20,7 @@ export function resolveBackupAppId(restArgs, gencowJson = null) {
|
|
|
20
20
|
export function resolveBackupFilePath(restArgs) {
|
|
21
21
|
for (let i = 0; i < restArgs.length; i++) {
|
|
22
22
|
const arg = restArgs[i];
|
|
23
|
-
if (arg === "--app" || arg === "-a") {
|
|
23
|
+
if (arg === "--app" || arg === "-a" || arg === "--limit" || arg === "--cursor") {
|
|
24
24
|
i++;
|
|
25
25
|
continue;
|
|
26
26
|
}
|
|
@@ -45,10 +45,34 @@ export function formatBackupDate(value) {
|
|
|
45
45
|
});
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
export function parseBackupListOptions(restArgs) {
|
|
49
|
+
const options = { all: false, cursor: null, limit: 20 };
|
|
50
|
+
for (let i = 0; i < restArgs.length; i++) {
|
|
51
|
+
const arg = restArgs[i];
|
|
52
|
+
if (arg === "--all") {
|
|
53
|
+
options.all = true;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (arg === "--limit") {
|
|
57
|
+
const next = Number(restArgs[i + 1]);
|
|
58
|
+
if (Number.isFinite(next)) options.limit = Math.min(100, Math.max(1, Math.trunc(next)));
|
|
59
|
+
i++;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (arg === "--cursor") {
|
|
63
|
+
options.cursor = restArgs[i + 1] || null;
|
|
64
|
+
i++;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return options;
|
|
68
|
+
}
|
|
69
|
+
|
|
48
70
|
function renderBackupHelp(logImpl = log, errorImpl = error, subCmd = null) {
|
|
49
71
|
if (subCmd) errorImpl(`Unknown sub-command: ${subCmd}`);
|
|
50
72
|
logImpl(`\n ${BOLD}Usage:${RESET}`);
|
|
51
|
-
logImpl(` gencow backup list List
|
|
73
|
+
logImpl(` gencow backup list List recent backups`);
|
|
74
|
+
logImpl(` gencow backup list --all List all backups by following cursors`);
|
|
75
|
+
logImpl(` gencow backup list --limit 50 List a page with up to 50 backups`);
|
|
52
76
|
logImpl(` gencow backup create [note] Create a manual backup`);
|
|
53
77
|
logImpl(` gencow backup restore <id> Restore from backup`);
|
|
54
78
|
logImpl(` gencow backup restore-file <path> Restore from a downloaded dump file`);
|
|
@@ -108,19 +132,39 @@ export function createBackupCommand({
|
|
|
108
132
|
switch (subCmd) {
|
|
109
133
|
case "list":
|
|
110
134
|
case "ls": {
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
135
|
+
const options = parseBackupListOptions(restArgs);
|
|
136
|
+
const backups = [];
|
|
137
|
+
let data = null;
|
|
138
|
+
let cursor = options.cursor;
|
|
139
|
+
for (let page = 0; page < 1000; page++) {
|
|
140
|
+
const response = await rpcQueryImpl(creds, "backup.list", {
|
|
141
|
+
appName: appId,
|
|
142
|
+
limit: options.limit,
|
|
143
|
+
cursor,
|
|
144
|
+
});
|
|
145
|
+
if (!response.ok) {
|
|
146
|
+
errorImpl(
|
|
147
|
+
`Failed to fetch backup list: ${(await response.json().catch(() => ({}))).error || response.statusText}`,
|
|
148
|
+
);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
data = await response.json();
|
|
152
|
+
backups.push(...(data.backups || []));
|
|
153
|
+
cursor = data.pageInfo?.nextCursor || null;
|
|
154
|
+
if (options.all && cursor && page === 999) {
|
|
155
|
+
errorImpl(
|
|
156
|
+
"Backup list is too large to fetch with --all. Resume with --cursor from the previous page.",
|
|
157
|
+
);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (!options.all || !cursor) break;
|
|
117
161
|
}
|
|
118
|
-
const
|
|
119
|
-
const
|
|
120
|
-
const
|
|
162
|
+
const limits = data?.limits || {};
|
|
163
|
+
const summary = data?.summary || {};
|
|
164
|
+
const pageInfo = data?.pageInfo || {};
|
|
121
165
|
|
|
122
166
|
logImpl(
|
|
123
|
-
`\n${BOLD}${CYAN}Database Backups${RESET} — ${appId} ${DIM}(Plan: ${data
|
|
167
|
+
`\n${BOLD}${CYAN}Database Backups${RESET} — ${appId} ${DIM}(Plan: ${data?.plan || "free"})${RESET}\n`,
|
|
124
168
|
);
|
|
125
169
|
|
|
126
170
|
if (backups.length === 0) {
|
|
@@ -136,6 +180,16 @@ export function createBackupCommand({
|
|
|
136
180
|
}
|
|
137
181
|
}
|
|
138
182
|
|
|
183
|
+
const totalCount = summary.totalCount ?? backups.length;
|
|
184
|
+
const pendingCount = summary.pendingCount ?? 0;
|
|
185
|
+
const shownLabel = options.all
|
|
186
|
+
? `${backups.length}/${totalCount}`
|
|
187
|
+
: `${backups.length} of ${totalCount}`;
|
|
188
|
+
logImpl(`\n ${DIM}Showing: ${shownLabel} | Pending: ${pendingCount}${RESET}`);
|
|
189
|
+
if (!options.all && pageInfo.nextCursor) {
|
|
190
|
+
logImpl(` ${DIM}Next page: gencow backup list --cursor ${pageInfo.nextCursor}${RESET}`);
|
|
191
|
+
logImpl(` ${DIM}Full history: gencow backup list --all${RESET}`);
|
|
192
|
+
}
|
|
139
193
|
logImpl(
|
|
140
194
|
`\n ${DIM}Limits: ${limits.maxManualBackups || "?"} manual | ${limits.retentionDays || "?"} days | Auto: ${limits.autoBackupEnabled ? "ON" : "OFF"}${RESET}\n`,
|
|
141
195
|
);
|
|
@@ -209,14 +263,10 @@ export function createBackupCommand({
|
|
|
209
263
|
const buffer = await readFileAsync(filePath);
|
|
210
264
|
const form = new FormData();
|
|
211
265
|
form.append("file", new Blob([buffer]), filePath.split(/[\\/]/).pop() || "backup.dump");
|
|
212
|
-
const response = await platformFetchImpl(
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
method: "POST",
|
|
217
|
-
body: form,
|
|
218
|
-
},
|
|
219
|
-
);
|
|
266
|
+
const response = await platformFetchImpl(creds, `/platform/apps/${appId}/backups/restore-file`, {
|
|
267
|
+
method: "POST",
|
|
268
|
+
body: form,
|
|
269
|
+
});
|
|
220
270
|
if (!response.ok) {
|
|
221
271
|
errorImpl(
|
|
222
272
|
`Restore failed: ${(await response.json?.().catch(() => ({}))).error || response.statusText}`,
|
package/package.json
CHANGED
package/server/index.js
CHANGED
|
@@ -109005,9 +109005,9 @@ var IMAGE_DEFAULTS = {
|
|
|
109005
109005
|
}
|
|
109006
109006
|
};
|
|
109007
109007
|
var PRICING_TIER_POLICY = {
|
|
109008
|
-
free: { displayName: "Hobby", maxApps: 5, wsConnections:
|
|
109009
|
-
pro: { displayName: "Legacy Paid", maxApps: 10, wsConnections:
|
|
109010
|
-
scale: { displayName: "Startup", maxApps: 50, wsConnections:
|
|
109008
|
+
free: { displayName: "Hobby", maxApps: 5, wsConnections: 1e3, wsConnectionsPerAppSafety: 500, customDomain: false },
|
|
109009
|
+
pro: { displayName: "Legacy Paid", maxApps: 10, wsConnections: 1e4, wsConnectionsPerAppSafety: 5e3, customDomain: true },
|
|
109010
|
+
scale: { displayName: "Startup", maxApps: 50, wsConnections: 1e5, wsConnectionsPerAppSafety: 25e3, customDomain: true }
|
|
109011
109011
|
};
|
|
109012
109012
|
function rowsFromResult6(result2) {
|
|
109013
109013
|
if (Array.isArray(result2)) return result2;
|
|
@@ -109201,6 +109201,7 @@ async function reconcilePricingTierPolicy(params) {
|
|
|
109201
109201
|
const existingDisplayName = typeof row.display_name === "string" ? row.display_name : "";
|
|
109202
109202
|
const { features, changed: featuresChanged } = mergePricingTierFeatureOverrides(row.features, {
|
|
109203
109203
|
wsConnections: policy.wsConnections,
|
|
109204
|
+
wsConnectionsPerAppSafety: policy.wsConnectionsPerAppSafety,
|
|
109204
109205
|
customDomain: policy.customDomain
|
|
109205
109206
|
});
|
|
109206
109207
|
if (existingMaxApps === policy.maxApps && existingDisplayName === policy.displayName && !featuresChanged) {
|
|
@@ -109505,9 +109506,9 @@ async function ensurePlatformSchemaUpgrade(params) {
|
|
|
109505
109506
|
)`);
|
|
109506
109507
|
await params.rawSql(`INSERT INTO pricing_tiers (id, display_name, sort_order, max_apps, monthly_credits, max_storage_bytes, features)
|
|
109507
109508
|
VALUES
|
|
109508
|
-
('free', 'Hobby', 0, 5, 20000, 1073741824, '{"autoBackup":false,"wsConnections":
|
|
109509
|
-
('pro', 'Legacy Paid', 10, 10, 100000, 5368709120, '{"autoBackup":true,"autoBackupIntervalHours":24,"wsConnections":
|
|
109510
|
-
('scale', 'Startup', 20, 50, 500000, 21474836480, '{"autoBackup":true,"autoBackupIntervalHours":6,"wsConnections":
|
|
109509
|
+
('free', 'Hobby', 0, 5, 20000, 1073741824, '{"autoBackup":false,"wsConnections":1000,"wsConnectionsPerAppSafety":500,"customDomain":false}'),
|
|
109510
|
+
('pro', 'Legacy Paid', 10, 10, 100000, 5368709120, '{"autoBackup":true,"autoBackupIntervalHours":24,"wsConnections":10000,"wsConnectionsPerAppSafety":5000,"customDomain":true}'),
|
|
109511
|
+
('scale', 'Startup', 20, 50, 500000, 21474836480, '{"autoBackup":true,"autoBackupIntervalHours":6,"wsConnections":100000,"wsConnectionsPerAppSafety":25000,"customDomain":true}')
|
|
109511
109512
|
ON CONFLICT (id) DO NOTHING`);
|
|
109512
109513
|
await params.rawSql(`CREATE TABLE IF NOT EXISTS model_pricing (
|
|
109513
109514
|
id SERIAL PRIMARY KEY,
|
|
@@ -109799,6 +109800,11 @@ function rowsFromResult7(result2) {
|
|
|
109799
109800
|
}
|
|
109800
109801
|
return [];
|
|
109801
109802
|
}
|
|
109803
|
+
function positiveInt(value) {
|
|
109804
|
+
const parsed = Number(value);
|
|
109805
|
+
if (!Number.isFinite(parsed) || parsed <= 0) return void 0;
|
|
109806
|
+
return Math.floor(parsed);
|
|
109807
|
+
}
|
|
109802
109808
|
function parseRealtimeConnectionLimit(rawFeatures) {
|
|
109803
109809
|
let features = {};
|
|
109804
109810
|
if (typeof rawFeatures === "string" && rawFeatures.trim()) {
|
|
@@ -109810,9 +109816,7 @@ function parseRealtimeConnectionLimit(rawFeatures) {
|
|
|
109810
109816
|
} else if (rawFeatures && typeof rawFeatures === "object") {
|
|
109811
109817
|
features = rawFeatures;
|
|
109812
109818
|
}
|
|
109813
|
-
|
|
109814
|
-
if (!Number.isFinite(limit) || limit <= 0) return void 0;
|
|
109815
|
-
return Math.floor(limit);
|
|
109819
|
+
return positiveInt(features.wsConnectionsPerAppSafety) ?? positiveInt(features.wsConnectionsPerApp) ?? positiveInt(features.wsConnections);
|
|
109816
109820
|
}
|
|
109817
109821
|
async function resolveRealtimeConnectionLimit(params) {
|
|
109818
109822
|
try {
|