clawfire 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -10
- package/dist/cli.js +76 -16
- package/dist/codegen.cjs.map +1 -1
- package/dist/codegen.d.cts +1 -1
- package/dist/codegen.d.ts +1 -1
- package/dist/codegen.js.map +1 -1
- package/dist/{dev-server-5ATZVQJT.js → dev-server-GD445Q6F.js} +247 -6
- package/dist/dev.cjs +247 -6
- package/dist/dev.cjs.map +1 -1
- package/dist/dev.js +247 -6
- package/dist/dev.js.map +1 -1
- package/dist/{discover-DYNqz_ym.d.cts → discover-8p9Mujyt.d.cts} +3 -3
- package/dist/{discover-DYNqz_ym.d.ts → discover-8p9Mujyt.d.ts} +3 -3
- package/dist/functions.cjs.map +1 -1
- package/dist/functions.d.cts +1 -1
- package/dist/functions.d.ts +1 -1
- package/dist/functions.js.map +1 -1
- package/package.json +1 -1
- package/templates/CLAUDE.md +22 -19
- package/templates/functions/index.ts +3 -3
- package/templates/starter/.claude/skills/clawfire-api/SKILL.md +8 -8
- package/templates/starter/.claude/skills/clawfire-diagnose/SKILL.md +6 -6
- package/templates/starter/.claude/skills/clawfire-model/SKILL.md +2 -2
- package/templates/starter/CLAUDE.md +33 -31
- package/templates/starter/app/pages/index.html +7 -6
- package/templates/starter/functions/index.ts +52 -0
- package/templates/starter/functions/package.json +22 -0
- package/templates/starter/functions/tsconfig.json +18 -0
- package/templates/starter/package.json +4 -2
- package/templates/starter/tsconfig.json +1 -1
- /package/templates/{app → functions}/routes/auth/login.ts +0 -0
- /package/templates/{app → functions}/routes/health.ts +0 -0
- /package/templates/{app → functions}/schemas/user.ts +0 -0
- /package/templates/starter/{app → functions}/routes/health.ts +0 -0
- /package/templates/starter/{app → functions}/routes/todos/create.ts +0 -0
- /package/templates/starter/{app → functions}/routes/todos/delete.ts +0 -0
- /package/templates/starter/{app → functions}/routes/todos/list.ts +0 -0
- /package/templates/starter/{app → functions}/routes/todos/update.ts +0 -0
- /package/templates/starter/{app → functions}/schemas/todo.ts +0 -0
- /package/templates/starter/{app → functions}/store.ts +0 -0
package/dist/dev.js
CHANGED
|
@@ -1756,7 +1756,29 @@ function generateDashboardHtml(options) {
|
|
|
1756
1756
|
<div id="service-grid" style="display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:12px;"></div>
|
|
1757
1757
|
</div>
|
|
1758
1758
|
|
|
1759
|
-
<!-- Section 2:
|
|
1759
|
+
<!-- Section 2: Deploy -->
|
|
1760
|
+
<div style="margin-bottom:32px;">
|
|
1761
|
+
<h2 style="font-size:18px;font-weight:700;color:#f97316;margin-bottom:16px;">Deploy</h2>
|
|
1762
|
+
<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:12px;">
|
|
1763
|
+
<div style="padding:16px;border-radius:8px;border:1px solid #2a2a2a;background:#141414;">
|
|
1764
|
+
<div style="display:flex;align-items:center;gap:8px;margin-bottom:8px;">
|
|
1765
|
+
<span style="font-size:16px;">🌐</span>
|
|
1766
|
+
<span style="font-weight:600;color:#e5e5e5;">Hosting</span>
|
|
1767
|
+
</div>
|
|
1768
|
+
<div style="font-size:13px;color:#a3a3a3;margin-bottom:12px;">Deploy your app to Firebase Hosting</div>
|
|
1769
|
+
<button id="deploy-hosting-btn" onclick="deployHosting()" style="padding:8px 20px;background:#3b82f6;color:#fff;border:none;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;">
|
|
1770
|
+
Deploy Hosting
|
|
1771
|
+
</button>
|
|
1772
|
+
<div id="deploy-hosting-status" style="display:none;margin-top:10px;font-size:13px;padding:10px 14px;border-radius:6px;"></div>
|
|
1773
|
+
<div id="deploy-hosting-url" style="display:none;margin-top:8px;padding:10px 14px;border-radius:6px;background:#0a1a0a;border:1px solid #22c55e;">
|
|
1774
|
+
<div style="font-size:11px;color:#a3a3a3;margin-bottom:4px;">Live URL</div>
|
|
1775
|
+
<a id="deploy-hosting-link" href="#" target="_blank" style="color:#22c55e;font-family:monospace;font-size:14px;text-decoration:none;word-break:break-all;"></a>
|
|
1776
|
+
</div>
|
|
1777
|
+
</div>
|
|
1778
|
+
</div>
|
|
1779
|
+
</div>
|
|
1780
|
+
|
|
1781
|
+
<!-- Section 3: Config Overview -->
|
|
1760
1782
|
<div style="margin-bottom:32px;">
|
|
1761
1783
|
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;">
|
|
1762
1784
|
<h2 style="font-size:18px;font-weight:700;color:#f97316;">Config Overview</h2>
|
|
@@ -1780,7 +1802,7 @@ function generateDashboardHtml(options) {
|
|
|
1780
1802
|
</div>
|
|
1781
1803
|
</div>
|
|
1782
1804
|
|
|
1783
|
-
<!-- Section
|
|
1805
|
+
<!-- Section 4: Environment Variables -->
|
|
1784
1806
|
<div style="margin-bottom:32px;">
|
|
1785
1807
|
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;">
|
|
1786
1808
|
<h2 style="font-size:18px;font-weight:700;color:#f97316;">Environment Variables</h2>
|
|
@@ -2380,16 +2402,22 @@ function generateDashboardHtml(options) {
|
|
|
2380
2402
|
var statusColors = { configured: '#22c55e', placeholder: '#eab308', missing: '#666' };
|
|
2381
2403
|
var statusLabels = { configured: 'Ready', placeholder: 'Needs Setup', missing: 'Not Configured' };
|
|
2382
2404
|
|
|
2405
|
+
var enableableServices = { 'Hosting': 'hosting', 'Firestore': 'firestore', 'Storage': 'storage' };
|
|
2383
2406
|
data.services.forEach(function(svc) {
|
|
2384
2407
|
var card = document.createElement('div');
|
|
2385
2408
|
card.style.cssText = 'padding:16px;border-radius:8px;border:1px solid #2a2a2a;background:#141414;';
|
|
2409
|
+
var enableBtn = '';
|
|
2410
|
+
if (svc.status === 'missing' && enableableServices[svc.name]) {
|
|
2411
|
+
enableBtn = '<button onclick="enableService(\\'' + enableableServices[svc.name] + '\\')" style="margin-top:8px;padding:4px 12px;background:#f97316;color:#000;border:none;border-radius:4px;font-size:11px;font-weight:600;cursor:pointer;">Enable</button>';
|
|
2412
|
+
}
|
|
2386
2413
|
card.innerHTML =
|
|
2387
2414
|
'<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px;">' +
|
|
2388
2415
|
'<span style="width:8px;height:8px;border-radius:50%;background:' + statusColors[svc.status] + ';display:inline-block;"></span>' +
|
|
2389
2416
|
'<span style="font-weight:600;color:#e5e5e5;">' + svc.name + '</span>' +
|
|
2390
2417
|
'</div>' +
|
|
2391
2418
|
'<div style="font-size:12px;color:' + statusColors[svc.status] + ';">' + statusLabels[svc.status] + '</div>' +
|
|
2392
|
-
(svc.detail ? '<div style="font-size:11px;color:#666;margin-top:4px;">' + svc.detail + '</div>' : '')
|
|
2419
|
+
(svc.detail ? '<div style="font-size:11px;color:#666;margin-top:4px;">' + svc.detail + '</div>' : '') +
|
|
2420
|
+
enableBtn;
|
|
2393
2421
|
grid.appendChild(card);
|
|
2394
2422
|
});
|
|
2395
2423
|
|
|
@@ -2574,6 +2602,67 @@ function generateDashboardHtml(options) {
|
|
|
2574
2602
|
});
|
|
2575
2603
|
}
|
|
2576
2604
|
|
|
2605
|
+
// \u2500\u2500\u2500 Deploy Hosting \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
2606
|
+
window.deployHosting = function() {
|
|
2607
|
+
var btn = document.getElementById('deploy-hosting-btn');
|
|
2608
|
+
var status = document.getElementById('deploy-hosting-status');
|
|
2609
|
+
var urlBox = document.getElementById('deploy-hosting-url');
|
|
2610
|
+
btn.disabled = true;
|
|
2611
|
+
btn.textContent = 'Deploying...';
|
|
2612
|
+
status.textContent = 'Deploying to Firebase Hosting... This may take up to 2 minutes.';
|
|
2613
|
+
status.style.cssText = 'display:block;margin-top:10px;font-size:13px;padding:10px 14px;border-radius:6px;background:#0a0a1a;border:1px solid #3b82f6;color:#3b82f6;';
|
|
2614
|
+
urlBox.style.display = 'none';
|
|
2615
|
+
|
|
2616
|
+
fetch(API + '/__dev/deploy/hosting', { method: 'POST' })
|
|
2617
|
+
.then(function(r) { return r.json(); })
|
|
2618
|
+
.then(function(data) {
|
|
2619
|
+
if (data.success) {
|
|
2620
|
+
status.textContent = data.message;
|
|
2621
|
+
status.style.cssText = 'display:block;margin-top:10px;font-size:13px;padding:10px 14px;border-radius:6px;background:#0a1a0a;border:1px solid #22c55e;color:#22c55e;';
|
|
2622
|
+
if (data.url) {
|
|
2623
|
+
var link = document.getElementById('deploy-hosting-link');
|
|
2624
|
+
link.href = data.url;
|
|
2625
|
+
link.textContent = data.url;
|
|
2626
|
+
urlBox.style.display = 'block';
|
|
2627
|
+
}
|
|
2628
|
+
} else {
|
|
2629
|
+
status.textContent = data.message;
|
|
2630
|
+
status.style.cssText = 'display:block;margin-top:10px;font-size:13px;padding:10px 14px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';
|
|
2631
|
+
}
|
|
2632
|
+
btn.disabled = false;
|
|
2633
|
+
btn.textContent = 'Deploy Hosting';
|
|
2634
|
+
// Refresh firebase status
|
|
2635
|
+
fetch(API + '/__dev/firebase-status').then(function(r) { return r.json(); }).then(function(d) { renderFirebaseStatus(d); }).catch(function(){});
|
|
2636
|
+
})
|
|
2637
|
+
.catch(function(err) {
|
|
2638
|
+
status.textContent = 'Error: ' + err.message;
|
|
2639
|
+
status.style.cssText = 'display:block;margin-top:10px;font-size:13px;padding:10px 14px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';
|
|
2640
|
+
btn.disabled = false;
|
|
2641
|
+
btn.textContent = 'Deploy Hosting';
|
|
2642
|
+
});
|
|
2643
|
+
};
|
|
2644
|
+
|
|
2645
|
+
// \u2500\u2500\u2500 Enable Service \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
2646
|
+
window.enableService = function(service) {
|
|
2647
|
+
fetch(API + '/__dev/enable-service', {
|
|
2648
|
+
method: 'POST',
|
|
2649
|
+
headers: { 'Content-Type': 'application/json' },
|
|
2650
|
+
body: JSON.stringify({ service: service })
|
|
2651
|
+
})
|
|
2652
|
+
.then(function(r) { return r.json(); })
|
|
2653
|
+
.then(function(data) {
|
|
2654
|
+
if (data.success) {
|
|
2655
|
+
// Refresh firebase status to show updated service cards
|
|
2656
|
+
fetch(API + '/__dev/firebase-status').then(function(r) { return r.json(); }).then(function(d) { renderFirebaseStatus(d); }).catch(function(){});
|
|
2657
|
+
} else {
|
|
2658
|
+
alert('Failed to enable ' + service + ': ' + data.message);
|
|
2659
|
+
}
|
|
2660
|
+
})
|
|
2661
|
+
.catch(function(err) {
|
|
2662
|
+
alert('Error: ' + err.message);
|
|
2663
|
+
});
|
|
2664
|
+
};
|
|
2665
|
+
|
|
2577
2666
|
// \u2500\u2500\u2500 Environment Variables \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
2578
2667
|
function renderEnvVars(data) {
|
|
2579
2668
|
envData = data.variables || [];
|
|
@@ -3043,6 +3132,113 @@ var FirebaseSetup = class {
|
|
|
3043
3132
|
this.saveState({ webAppId: appId, webAppDisplayName: displayName });
|
|
3044
3133
|
return { success: true, message: `Web app "${displayName}" selected.` };
|
|
3045
3134
|
}
|
|
3135
|
+
// ─── Deploy ─────────────────────────────────────────────────────────
|
|
3136
|
+
async deployHosting() {
|
|
3137
|
+
const firebaseJsonPath = resolve5(this.projectDir, "firebase.json");
|
|
3138
|
+
if (!existsSync6(firebaseJsonPath)) {
|
|
3139
|
+
return { success: false, message: "firebase.json not found. Enable hosting first." };
|
|
3140
|
+
}
|
|
3141
|
+
try {
|
|
3142
|
+
const config = JSON.parse(readFileSync4(firebaseJsonPath, "utf-8"));
|
|
3143
|
+
if (!config.hosting) {
|
|
3144
|
+
return { success: false, message: "Hosting not configured in firebase.json. Click Enable first." };
|
|
3145
|
+
}
|
|
3146
|
+
} catch {
|
|
3147
|
+
return { success: false, message: "Invalid firebase.json." };
|
|
3148
|
+
}
|
|
3149
|
+
try {
|
|
3150
|
+
const output = await this.execTimeout(
|
|
3151
|
+
"firebase",
|
|
3152
|
+
["deploy", "--only", "hosting", "--json"],
|
|
3153
|
+
12e4
|
|
3154
|
+
// 2 min timeout
|
|
3155
|
+
);
|
|
3156
|
+
let url = "";
|
|
3157
|
+
try {
|
|
3158
|
+
const data = JSON.parse(output);
|
|
3159
|
+
if (data?.result) {
|
|
3160
|
+
for (const key of Object.keys(data.result)) {
|
|
3161
|
+
if (key.startsWith("hosting") && typeof data.result[key] === "object") {
|
|
3162
|
+
url = data.result[key]?.url || data.result[key]?.site?.url || "";
|
|
3163
|
+
if (url) break;
|
|
3164
|
+
}
|
|
3165
|
+
}
|
|
3166
|
+
}
|
|
3167
|
+
} catch {
|
|
3168
|
+
const urlMatch = output.match(/https:\/\/[a-z0-9-]+\.web\.app/i);
|
|
3169
|
+
if (urlMatch) url = urlMatch[0];
|
|
3170
|
+
}
|
|
3171
|
+
if (!url) {
|
|
3172
|
+
const state = this.loadState();
|
|
3173
|
+
if (state.projectId) {
|
|
3174
|
+
url = `https://${state.projectId}.web.app`;
|
|
3175
|
+
}
|
|
3176
|
+
}
|
|
3177
|
+
return { success: true, url: url || void 0, message: "Hosting deployed successfully!" };
|
|
3178
|
+
} catch (err) {
|
|
3179
|
+
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
3180
|
+
return { success: false, message: `Deploy failed: ${msg}` };
|
|
3181
|
+
}
|
|
3182
|
+
}
|
|
3183
|
+
// ─── Service Enable ────────────────────────────────────────────────
|
|
3184
|
+
enableService(service) {
|
|
3185
|
+
const firebaseJsonPath = resolve5(this.projectDir, "firebase.json");
|
|
3186
|
+
let config = {};
|
|
3187
|
+
if (existsSync6(firebaseJsonPath)) {
|
|
3188
|
+
try {
|
|
3189
|
+
config = JSON.parse(readFileSync4(firebaseJsonPath, "utf-8"));
|
|
3190
|
+
} catch {
|
|
3191
|
+
config = {};
|
|
3192
|
+
}
|
|
3193
|
+
}
|
|
3194
|
+
switch (service) {
|
|
3195
|
+
case "hosting": {
|
|
3196
|
+
if (!config.hosting) {
|
|
3197
|
+
config.hosting = {
|
|
3198
|
+
public: "public",
|
|
3199
|
+
ignore: ["firebase.json", "**/.*", "**/node_modules/**"],
|
|
3200
|
+
rewrites: [{ source: "**", destination: "/index.html" }]
|
|
3201
|
+
};
|
|
3202
|
+
}
|
|
3203
|
+
break;
|
|
3204
|
+
}
|
|
3205
|
+
case "firestore": {
|
|
3206
|
+
if (!config.firestore) {
|
|
3207
|
+
config.firestore = {
|
|
3208
|
+
rules: "firestore.rules",
|
|
3209
|
+
indexes: "firestore.indexes.json"
|
|
3210
|
+
};
|
|
3211
|
+
}
|
|
3212
|
+
const rulesPath = resolve5(this.projectDir, "firestore.rules");
|
|
3213
|
+
if (!existsSync6(rulesPath)) {
|
|
3214
|
+
writeFileSync2(
|
|
3215
|
+
rulesPath,
|
|
3216
|
+
"rules_version = '2';\nservice cloud.firestore {\n match /databases/{database}/documents {\n match /{document=**} {\n allow read, write: if request.auth != null;\n }\n }\n}\n"
|
|
3217
|
+
);
|
|
3218
|
+
}
|
|
3219
|
+
const indexesPath = resolve5(this.projectDir, "firestore.indexes.json");
|
|
3220
|
+
if (!existsSync6(indexesPath)) {
|
|
3221
|
+
writeFileSync2(indexesPath, JSON.stringify({ indexes: [], fieldOverrides: [] }, null, 2) + "\n");
|
|
3222
|
+
}
|
|
3223
|
+
break;
|
|
3224
|
+
}
|
|
3225
|
+
case "storage": {
|
|
3226
|
+
if (!config.storage) {
|
|
3227
|
+
config.storage = { rules: "storage.rules" };
|
|
3228
|
+
}
|
|
3229
|
+
const storageRulesPath = resolve5(this.projectDir, "storage.rules");
|
|
3230
|
+
if (!existsSync6(storageRulesPath)) {
|
|
3231
|
+
writeFileSync2(
|
|
3232
|
+
storageRulesPath,
|
|
3233
|
+
"rules_version = '2';\nservice firebase.storage {\n match /b/{bucket}/o {\n match /{allPaths=**} {\n allow read, write: if request.auth != null;\n }\n }\n}\n"
|
|
3234
|
+
);
|
|
3235
|
+
}
|
|
3236
|
+
break;
|
|
3237
|
+
}
|
|
3238
|
+
}
|
|
3239
|
+
writeFileSync2(firebaseJsonPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
3240
|
+
return { success: true, message: `${service} enabled in firebase.json.` };
|
|
3241
|
+
}
|
|
3046
3242
|
// ─── Helpers ───────────────────────────────────────────────────────
|
|
3047
3243
|
execTimeout(command, args, timeoutMs) {
|
|
3048
3244
|
return new Promise((resolve7, reject) => {
|
|
@@ -3324,8 +3520,8 @@ var DevServer = class {
|
|
|
3324
3520
|
onSetupRoutes: options.onSetupRoutes || (() => {
|
|
3325
3521
|
})
|
|
3326
3522
|
};
|
|
3327
|
-
this.routesDir = resolve6(this.options.projectDir, "
|
|
3328
|
-
this.schemasDir = resolve6(this.options.projectDir, "
|
|
3523
|
+
this.routesDir = resolve6(this.options.projectDir, "functions/routes");
|
|
3524
|
+
this.schemasDir = resolve6(this.options.projectDir, "functions/schemas");
|
|
3329
3525
|
this.publicDir = resolve6(this.options.projectDir, "public");
|
|
3330
3526
|
this.pagesDir = resolve6(this.options.projectDir, "app/pages");
|
|
3331
3527
|
this.componentsDir = resolve6(this.options.projectDir, "app/components");
|
|
@@ -3565,7 +3761,23 @@ function switchTab(tab) {
|
|
|
3565
3761
|
// Lazy-load dashboard data on first click
|
|
3566
3762
|
if (window._loadDashboard) window._loadDashboard();
|
|
3567
3763
|
}
|
|
3764
|
+
// Persist active tab across reloads (auto-fill writes config \u2192 watcher reloads page)
|
|
3765
|
+
try { localStorage.setItem('clawfire-active-tab', tab); } catch(e) {}
|
|
3568
3766
|
}
|
|
3767
|
+
// Restore saved tab on page load
|
|
3768
|
+
(function() {
|
|
3769
|
+
try {
|
|
3770
|
+
var saved = localStorage.getItem('clawfire-active-tab');
|
|
3771
|
+
if (saved === 'dashboard') {
|
|
3772
|
+
var fn = function() { switchTab('dashboard'); };
|
|
3773
|
+
if (document.readyState === 'loading') {
|
|
3774
|
+
document.addEventListener('DOMContentLoaded', fn);
|
|
3775
|
+
} else {
|
|
3776
|
+
fn();
|
|
3777
|
+
}
|
|
3778
|
+
}
|
|
3779
|
+
} catch(e) {}
|
|
3780
|
+
})();
|
|
3569
3781
|
</script>`;
|
|
3570
3782
|
const liveReloadScript = `
|
|
3571
3783
|
<script>
|
|
@@ -3871,7 +4083,7 @@ ${liveReloadScript}
|
|
|
3871
4083
|
}
|
|
3872
4084
|
console.log("");
|
|
3873
4085
|
if (watching) {
|
|
3874
|
-
const watchDirs = ["
|
|
4086
|
+
const watchDirs = ["functions/routes/", "functions/schemas/", "public/"];
|
|
3875
4087
|
if (pagesActive) watchDirs.push("app/pages/", "app/components/");
|
|
3876
4088
|
console.log(` \x1B[35mHot Reload\x1B[0m : \x1B[32mON\x1B[0m`);
|
|
3877
4089
|
console.log(` \x1B[2mWatching: ${watchDirs.join(", ")}\x1B[0m`);
|
|
@@ -4044,6 +4256,35 @@ ${liveReloadScript}
|
|
|
4044
4256
|
});
|
|
4045
4257
|
return;
|
|
4046
4258
|
}
|
|
4259
|
+
if (url.pathname === "/__dev/deploy/hosting" && req.method === "POST") {
|
|
4260
|
+
this.firebaseSetup.deployHosting().then((result) => {
|
|
4261
|
+
clearFirebaseStatusCache();
|
|
4262
|
+
sendJson(result);
|
|
4263
|
+
}).catch((err) => sendJson({ success: false, message: err instanceof Error ? err.message : "Failed" }, 500));
|
|
4264
|
+
return;
|
|
4265
|
+
}
|
|
4266
|
+
if (url.pathname === "/__dev/enable-service" && req.method === "POST") {
|
|
4267
|
+
let body = "";
|
|
4268
|
+
req.on("data", (chunk) => {
|
|
4269
|
+
body += chunk;
|
|
4270
|
+
});
|
|
4271
|
+
req.on("end", () => {
|
|
4272
|
+
try {
|
|
4273
|
+
const data = JSON.parse(body);
|
|
4274
|
+
const service = data.service;
|
|
4275
|
+
if (!service || !["hosting", "firestore", "storage"].includes(service)) {
|
|
4276
|
+
sendJson({ success: false, message: "Invalid service. Use: hosting, firestore, storage" }, 400);
|
|
4277
|
+
return;
|
|
4278
|
+
}
|
|
4279
|
+
const result = this.firebaseSetup.enableService(service);
|
|
4280
|
+
clearFirebaseStatusCache();
|
|
4281
|
+
sendJson(result);
|
|
4282
|
+
} catch {
|
|
4283
|
+
sendJson({ success: false, message: "Invalid JSON body" }, 400);
|
|
4284
|
+
}
|
|
4285
|
+
});
|
|
4286
|
+
return;
|
|
4287
|
+
}
|
|
4047
4288
|
if (url.pathname === "/__dev/setup/select-web-app" && req.method === "POST") {
|
|
4048
4289
|
let body = "";
|
|
4049
4290
|
req.on("data", (chunk) => {
|