@meetploy/cli 1.15.0 → 1.16.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/dist/dashboard-dist/assets/{main-qA3kxECS.css → main-B-euJxpr.css} +1 -1
- package/dist/dashboard-dist/assets/main-duAiLjPq.js +339 -0
- package/dist/dashboard-dist/index.html +2 -2
- package/dist/dev.js +190 -14
- package/dist/index.js +338 -30
- package/package.json +1 -1
- package/dist/dashboard-dist/assets/main-UY1Z1kG0.js +0 -334
package/dist/index.js
CHANGED
|
@@ -8,11 +8,11 @@ import { parse } from 'yaml';
|
|
|
8
8
|
import { build } from 'esbuild';
|
|
9
9
|
import { watch } from 'chokidar';
|
|
10
10
|
import { randomBytes, randomUUID, createHmac, pbkdf2Sync, timingSafeEqual, createHash } from 'crypto';
|
|
11
|
+
import { homedir, tmpdir } from 'os';
|
|
11
12
|
import { getCookie, deleteCookie, setCookie } from 'hono/cookie';
|
|
12
13
|
import { URL, fileURLToPath } from 'url';
|
|
13
14
|
import { serve } from '@hono/node-server';
|
|
14
15
|
import { Hono } from 'hono';
|
|
15
|
-
import { homedir, tmpdir } from 'os';
|
|
16
16
|
import Database from 'better-sqlite3';
|
|
17
17
|
import { spawn, exec } from 'child_process';
|
|
18
18
|
import createClient from 'openapi-fetch';
|
|
@@ -200,6 +200,7 @@ function validatePloyConfig(config, configFile = "ploy.yaml", options = {}) {
|
|
|
200
200
|
validateBindings(config.queue, "queue", configFile);
|
|
201
201
|
validateBindings(config.cache, "cache", configFile);
|
|
202
202
|
validateBindings(config.state, "state", configFile);
|
|
203
|
+
validateBindings(config.fs, "fs", configFile);
|
|
203
204
|
validateBindings(config.workflow, "workflow", configFile);
|
|
204
205
|
if (config.ai !== void 0 && typeof config.ai !== "boolean") {
|
|
205
206
|
throw new Error(`'ai' in ${configFile} must be a boolean`);
|
|
@@ -276,7 +277,7 @@ function readAndValidatePloyConfigSync(projectDir, configPath, validationOptions
|
|
|
276
277
|
return validatePloyConfig(config, configFile, validationOptions);
|
|
277
278
|
}
|
|
278
279
|
function hasBindings(config) {
|
|
279
|
-
return !!(config.env ?? config.db ?? config.queue ?? config.cache ?? config.state ?? config.workflow ?? config.ai ?? config.auth);
|
|
280
|
+
return !!(config.env ?? config.db ?? config.queue ?? config.cache ?? config.state ?? config.fs ?? config.workflow ?? config.ai ?? config.auth);
|
|
280
281
|
}
|
|
281
282
|
function parseDotEnv(content) {
|
|
282
283
|
const result = {};
|
|
@@ -757,6 +758,124 @@ var init_db_runtime = __esm({
|
|
|
757
758
|
}
|
|
758
759
|
});
|
|
759
760
|
|
|
761
|
+
// ../emulator/dist/runtime/fs-runtime.js
|
|
762
|
+
var FS_RUNTIME_CODE;
|
|
763
|
+
var init_fs_runtime = __esm({
|
|
764
|
+
"../emulator/dist/runtime/fs-runtime.js"() {
|
|
765
|
+
FS_RUNTIME_CODE = `
|
|
766
|
+
interface FileStoragePutOptions {
|
|
767
|
+
contentType?: string;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
interface FileStorageListOptions {
|
|
771
|
+
prefix?: string;
|
|
772
|
+
limit?: number;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
interface FileStorageObject {
|
|
776
|
+
body: string;
|
|
777
|
+
contentType: string;
|
|
778
|
+
size: number;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
interface FileStorageKey {
|
|
782
|
+
key: string;
|
|
783
|
+
size: number;
|
|
784
|
+
contentType: string;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
interface FileStorageListResult {
|
|
788
|
+
keys: FileStorageKey[];
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
interface FileStorageBinding {
|
|
792
|
+
put: (key: string, value: string, options?: FileStoragePutOptions) => Promise<void>;
|
|
793
|
+
get: (key: string) => Promise<FileStorageObject | null>;
|
|
794
|
+
delete: (key: string) => Promise<void>;
|
|
795
|
+
list: (options?: FileStorageListOptions) => Promise<FileStorageListResult>;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
export function initializeFileStorage(fsName: string, serviceUrl: string): FileStorageBinding {
|
|
799
|
+
return {
|
|
800
|
+
async put(key: string, value: string, options?: FileStoragePutOptions): Promise<void> {
|
|
801
|
+
const response = await fetch(serviceUrl + "/fs/put", {
|
|
802
|
+
method: "POST",
|
|
803
|
+
headers: { "Content-Type": "application/json" },
|
|
804
|
+
body: JSON.stringify({
|
|
805
|
+
fsName,
|
|
806
|
+
key,
|
|
807
|
+
value,
|
|
808
|
+
contentType: options?.contentType || "application/octet-stream",
|
|
809
|
+
}),
|
|
810
|
+
});
|
|
811
|
+
|
|
812
|
+
if (!response.ok) {
|
|
813
|
+
const errorText = await response.text();
|
|
814
|
+
throw new Error("File storage put failed: " + errorText);
|
|
815
|
+
}
|
|
816
|
+
},
|
|
817
|
+
|
|
818
|
+
async get(key: string): Promise<FileStorageObject | null> {
|
|
819
|
+
const response = await fetch(serviceUrl + "/fs/get", {
|
|
820
|
+
method: "POST",
|
|
821
|
+
headers: { "Content-Type": "application/json" },
|
|
822
|
+
body: JSON.stringify({ fsName, key }),
|
|
823
|
+
});
|
|
824
|
+
|
|
825
|
+
if (!response.ok) {
|
|
826
|
+
const errorText = await response.text();
|
|
827
|
+
throw new Error("File storage get failed: " + errorText);
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
const result = await response.json();
|
|
831
|
+
if (result.found === false) {
|
|
832
|
+
return null;
|
|
833
|
+
}
|
|
834
|
+
return {
|
|
835
|
+
body: result.body,
|
|
836
|
+
contentType: result.contentType,
|
|
837
|
+
size: result.size,
|
|
838
|
+
};
|
|
839
|
+
},
|
|
840
|
+
|
|
841
|
+
async delete(key: string): Promise<void> {
|
|
842
|
+
const response = await fetch(serviceUrl + "/fs/delete", {
|
|
843
|
+
method: "POST",
|
|
844
|
+
headers: { "Content-Type": "application/json" },
|
|
845
|
+
body: JSON.stringify({ fsName, key }),
|
|
846
|
+
});
|
|
847
|
+
|
|
848
|
+
if (!response.ok) {
|
|
849
|
+
const errorText = await response.text();
|
|
850
|
+
throw new Error("File storage delete failed: " + errorText);
|
|
851
|
+
}
|
|
852
|
+
},
|
|
853
|
+
|
|
854
|
+
async list(options?: FileStorageListOptions): Promise<FileStorageListResult> {
|
|
855
|
+
const response = await fetch(serviceUrl + "/fs/list", {
|
|
856
|
+
method: "POST",
|
|
857
|
+
headers: { "Content-Type": "application/json" },
|
|
858
|
+
body: JSON.stringify({
|
|
859
|
+
fsName,
|
|
860
|
+
prefix: options?.prefix,
|
|
861
|
+
limit: options?.limit,
|
|
862
|
+
}),
|
|
863
|
+
});
|
|
864
|
+
|
|
865
|
+
if (!response.ok) {
|
|
866
|
+
const errorText = await response.text();
|
|
867
|
+
throw new Error("File storage list failed: " + errorText);
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
const result = await response.json();
|
|
871
|
+
return { keys: result.keys };
|
|
872
|
+
},
|
|
873
|
+
};
|
|
874
|
+
}
|
|
875
|
+
`;
|
|
876
|
+
}
|
|
877
|
+
});
|
|
878
|
+
|
|
760
879
|
// ../emulator/dist/runtime/queue-runtime.js
|
|
761
880
|
var QUEUE_RUNTIME_CODE;
|
|
762
881
|
var init_queue_runtime = __esm({
|
|
@@ -1121,6 +1240,12 @@ function generateWrapperCode(config, mockServiceUrl, envVars) {
|
|
|
1121
1240
|
bindings.push(` ${bindingName}: initializeState("${stateName}", "${mockServiceUrl}"),`);
|
|
1122
1241
|
}
|
|
1123
1242
|
}
|
|
1243
|
+
if (config.fs) {
|
|
1244
|
+
imports.push('import { initializeFileStorage } from "__ploy_fs_runtime__";');
|
|
1245
|
+
for (const [bindingName, fsName] of Object.entries(config.fs)) {
|
|
1246
|
+
bindings.push(` ${bindingName}: initializeFileStorage("${fsName}", "${mockServiceUrl}"),`);
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1124
1249
|
if (config.workflow) {
|
|
1125
1250
|
imports.push('import { initializeWorkflow, createStepContext, executeWorkflow } from "__ploy_workflow_runtime__";');
|
|
1126
1251
|
for (const [bindingName, workflowName] of Object.entries(config.workflow)) {
|
|
@@ -1237,6 +1362,10 @@ function createRuntimePlugin(_config) {
|
|
|
1237
1362
|
path: "__ploy_state_runtime__",
|
|
1238
1363
|
namespace: "ploy-runtime"
|
|
1239
1364
|
}));
|
|
1365
|
+
build2.onResolve({ filter: /^__ploy_fs_runtime__$/ }, () => ({
|
|
1366
|
+
path: "__ploy_fs_runtime__",
|
|
1367
|
+
namespace: "ploy-runtime"
|
|
1368
|
+
}));
|
|
1240
1369
|
build2.onResolve({ filter: /^__ploy_workflow_runtime__$/ }, () => ({
|
|
1241
1370
|
path: "__ploy_workflow_runtime__",
|
|
1242
1371
|
namespace: "ploy-runtime"
|
|
@@ -1257,6 +1386,10 @@ function createRuntimePlugin(_config) {
|
|
|
1257
1386
|
contents: STATE_RUNTIME_CODE,
|
|
1258
1387
|
loader: "ts"
|
|
1259
1388
|
}));
|
|
1389
|
+
build2.onLoad({ filter: /^__ploy_fs_runtime__$/, namespace: "ploy-runtime" }, () => ({
|
|
1390
|
+
contents: FS_RUNTIME_CODE,
|
|
1391
|
+
loader: "ts"
|
|
1392
|
+
}));
|
|
1260
1393
|
build2.onLoad({ filter: /^__ploy_workflow_runtime__$/, namespace: "ploy-runtime" }, () => ({
|
|
1261
1394
|
contents: WORKFLOW_RUNTIME_CODE,
|
|
1262
1395
|
loader: "ts"
|
|
@@ -1299,6 +1432,7 @@ var init_bundler = __esm({
|
|
|
1299
1432
|
"../emulator/dist/bundler/bundler.js"() {
|
|
1300
1433
|
init_cache_runtime();
|
|
1301
1434
|
init_db_runtime();
|
|
1435
|
+
init_fs_runtime();
|
|
1302
1436
|
init_queue_runtime();
|
|
1303
1437
|
init_state_runtime();
|
|
1304
1438
|
init_workflow_runtime();
|
|
@@ -1559,6 +1693,34 @@ var init_workerd_config = __esm({
|
|
|
1559
1693
|
"../emulator/dist/config/workerd-config.js"() {
|
|
1560
1694
|
}
|
|
1561
1695
|
});
|
|
1696
|
+
function getProjectHash(projectDir) {
|
|
1697
|
+
return createHash("sha256").update(projectDir).digest("hex").slice(0, 12);
|
|
1698
|
+
}
|
|
1699
|
+
function getTempDir(projectDir) {
|
|
1700
|
+
const hash = getProjectHash(projectDir);
|
|
1701
|
+
return join(tmpdir(), `ploy-emulator-${hash}`);
|
|
1702
|
+
}
|
|
1703
|
+
function getDataDir(projectDir) {
|
|
1704
|
+
return join(projectDir, ".ploy");
|
|
1705
|
+
}
|
|
1706
|
+
function ensureDir(dir) {
|
|
1707
|
+
mkdirSync(dir, { recursive: true });
|
|
1708
|
+
}
|
|
1709
|
+
function ensureTempDir(projectDir) {
|
|
1710
|
+
const tempDir = getTempDir(projectDir);
|
|
1711
|
+
ensureDir(tempDir);
|
|
1712
|
+
return tempDir;
|
|
1713
|
+
}
|
|
1714
|
+
function ensureDataDir(projectDir) {
|
|
1715
|
+
const dataDir = getDataDir(projectDir);
|
|
1716
|
+
ensureDir(dataDir);
|
|
1717
|
+
ensureDir(join(dataDir, "db"));
|
|
1718
|
+
return dataDir;
|
|
1719
|
+
}
|
|
1720
|
+
var init_paths = __esm({
|
|
1721
|
+
"../emulator/dist/utils/paths.js"() {
|
|
1722
|
+
}
|
|
1723
|
+
});
|
|
1562
1724
|
function generateId() {
|
|
1563
1725
|
return randomBytes(16).toString("hex");
|
|
1564
1726
|
}
|
|
@@ -1871,6 +2033,7 @@ function createDashboardRoutes(app, dbManager2, config) {
|
|
|
1871
2033
|
queue: config.queue,
|
|
1872
2034
|
cache: config.cache,
|
|
1873
2035
|
state: config.state,
|
|
2036
|
+
fs: config.fs,
|
|
1874
2037
|
workflow: config.workflow,
|
|
1875
2038
|
auth: config.auth
|
|
1876
2039
|
});
|
|
@@ -2365,6 +2528,37 @@ function createDashboardRoutes(app, dbManager2, config) {
|
|
|
2365
2528
|
return c.json({ error: err instanceof Error ? err.message : String(err) }, 500);
|
|
2366
2529
|
}
|
|
2367
2530
|
});
|
|
2531
|
+
app.get("/api/fs/:binding/entries", (c) => {
|
|
2532
|
+
const binding = c.req.param("binding");
|
|
2533
|
+
const fsName = config.fs?.[binding];
|
|
2534
|
+
const limit = parseInt(c.req.query("limit") || "20", 10);
|
|
2535
|
+
const offset = parseInt(c.req.query("offset") || "0", 10);
|
|
2536
|
+
if (!fsName) {
|
|
2537
|
+
return c.json({ error: "File storage binding not found" }, 404);
|
|
2538
|
+
}
|
|
2539
|
+
try {
|
|
2540
|
+
const db = dbManager2.emulatorDb;
|
|
2541
|
+
const total = db.prepare(`SELECT COUNT(*) as count FROM fs_entries WHERE fs_name = ?`).get(fsName).count;
|
|
2542
|
+
const entries = db.prepare(`SELECT key, size, content_type, created_at
|
|
2543
|
+
FROM fs_entries
|
|
2544
|
+
WHERE fs_name = ?
|
|
2545
|
+
ORDER BY key ASC
|
|
2546
|
+
LIMIT ? OFFSET ?`).all(fsName, limit, offset);
|
|
2547
|
+
return c.json({
|
|
2548
|
+
entries: entries.map((e) => ({
|
|
2549
|
+
key: e.key,
|
|
2550
|
+
size: e.size,
|
|
2551
|
+
contentType: e.content_type,
|
|
2552
|
+
createdAt: new Date(e.created_at * 1e3).toISOString()
|
|
2553
|
+
})),
|
|
2554
|
+
total,
|
|
2555
|
+
limit,
|
|
2556
|
+
offset
|
|
2557
|
+
});
|
|
2558
|
+
} catch (err) {
|
|
2559
|
+
return c.json({ error: err instanceof Error ? err.message : String(err) }, 500);
|
|
2560
|
+
}
|
|
2561
|
+
});
|
|
2368
2562
|
if (hasDashboard) {
|
|
2369
2563
|
app.get("/assets/*", (c) => {
|
|
2370
2564
|
const path = c.req.path;
|
|
@@ -2515,6 +2709,125 @@ var init_db_service = __esm({
|
|
|
2515
2709
|
"../emulator/dist/services/db-service.js"() {
|
|
2516
2710
|
}
|
|
2517
2711
|
});
|
|
2712
|
+
function createFsHandlers(db, dataDir) {
|
|
2713
|
+
const fsBaseDir = join(dataDir, "fs");
|
|
2714
|
+
mkdirSync(fsBaseDir, { recursive: true });
|
|
2715
|
+
function getFsDir(fsName) {
|
|
2716
|
+
const dir = join(fsBaseDir, fsName);
|
|
2717
|
+
mkdirSync(dir, { recursive: true });
|
|
2718
|
+
return dir;
|
|
2719
|
+
}
|
|
2720
|
+
function encodedKeyPath(fsDir, key) {
|
|
2721
|
+
return join(fsDir, encodeURIComponent(key));
|
|
2722
|
+
}
|
|
2723
|
+
const putHandler = async (c) => {
|
|
2724
|
+
try {
|
|
2725
|
+
const body = await c.req.json();
|
|
2726
|
+
const { fsName, key, value, contentType } = body;
|
|
2727
|
+
if (!fsName || !key) {
|
|
2728
|
+
return c.json({ error: "Missing required fields: fsName, key, value" }, 400);
|
|
2729
|
+
}
|
|
2730
|
+
const fsDir = getFsDir(fsName);
|
|
2731
|
+
const filePath = encodedKeyPath(fsDir, key);
|
|
2732
|
+
writeFileSync(filePath, value, "utf-8");
|
|
2733
|
+
const size = Buffer.byteLength(value, "utf-8");
|
|
2734
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
2735
|
+
db.prepare(`INSERT OR REPLACE INTO fs_entries (fs_name, key, content_type, size, created_at) VALUES (?, ?, ?, ?, ?)`).run(fsName, key, contentType ?? "application/octet-stream", size, now);
|
|
2736
|
+
return c.json({ success: true });
|
|
2737
|
+
} catch (err) {
|
|
2738
|
+
error(`[fs-service] put error: ${err instanceof Error ? err.message : String(err)}`);
|
|
2739
|
+
return c.json({
|
|
2740
|
+
error: `put failed: ${err instanceof Error ? err.message : String(err)}`
|
|
2741
|
+
}, 500);
|
|
2742
|
+
}
|
|
2743
|
+
};
|
|
2744
|
+
const getHandler = async (c) => {
|
|
2745
|
+
try {
|
|
2746
|
+
const body = await c.req.json();
|
|
2747
|
+
const { fsName, key } = body;
|
|
2748
|
+
if (!fsName || !key) {
|
|
2749
|
+
return c.json({ error: "Missing required fields: fsName, key" }, 400);
|
|
2750
|
+
}
|
|
2751
|
+
const row = db.prepare(`SELECT content_type, size FROM fs_entries WHERE fs_name = ? AND key = ?`).get(fsName, key);
|
|
2752
|
+
if (!row) {
|
|
2753
|
+
return c.json({ found: false });
|
|
2754
|
+
}
|
|
2755
|
+
const fsDir = getFsDir(fsName);
|
|
2756
|
+
const filePath = encodedKeyPath(fsDir, key);
|
|
2757
|
+
if (!existsSync(filePath)) {
|
|
2758
|
+
db.prepare(`DELETE FROM fs_entries WHERE fs_name = ? AND key = ?`).run(fsName, key);
|
|
2759
|
+
return c.json({ found: false });
|
|
2760
|
+
}
|
|
2761
|
+
const fileContent = readFileSync(filePath, "utf-8");
|
|
2762
|
+
return c.json({
|
|
2763
|
+
found: true,
|
|
2764
|
+
body: fileContent,
|
|
2765
|
+
contentType: row.content_type,
|
|
2766
|
+
size: row.size
|
|
2767
|
+
});
|
|
2768
|
+
} catch (err) {
|
|
2769
|
+
error(`[fs-service] get error: ${err instanceof Error ? err.message : String(err)}`);
|
|
2770
|
+
return c.json({
|
|
2771
|
+
error: `get failed: ${err instanceof Error ? err.message : String(err)}`
|
|
2772
|
+
}, 500);
|
|
2773
|
+
}
|
|
2774
|
+
};
|
|
2775
|
+
const deleteHandler = async (c) => {
|
|
2776
|
+
try {
|
|
2777
|
+
const body = await c.req.json();
|
|
2778
|
+
const { fsName, key } = body;
|
|
2779
|
+
if (!fsName || !key) {
|
|
2780
|
+
return c.json({ error: "Missing required fields: fsName, key" }, 400);
|
|
2781
|
+
}
|
|
2782
|
+
db.prepare(`DELETE FROM fs_entries WHERE fs_name = ? AND key = ?`).run(fsName, key);
|
|
2783
|
+
const fsDir = getFsDir(fsName);
|
|
2784
|
+
const filePath = encodedKeyPath(fsDir, key);
|
|
2785
|
+
if (existsSync(filePath)) {
|
|
2786
|
+
unlinkSync(filePath);
|
|
2787
|
+
}
|
|
2788
|
+
return c.json({ success: true });
|
|
2789
|
+
} catch (err) {
|
|
2790
|
+
error(`[fs-service] delete error: ${err instanceof Error ? err.message : String(err)}`);
|
|
2791
|
+
return c.json({
|
|
2792
|
+
error: `delete failed: ${err instanceof Error ? err.message : String(err)}`
|
|
2793
|
+
}, 500);
|
|
2794
|
+
}
|
|
2795
|
+
};
|
|
2796
|
+
const listHandler = async (c) => {
|
|
2797
|
+
try {
|
|
2798
|
+
const body = await c.req.json();
|
|
2799
|
+
const { fsName, prefix, limit } = body;
|
|
2800
|
+
if (!fsName) {
|
|
2801
|
+
return c.json({ error: "Missing required field: fsName" }, 400);
|
|
2802
|
+
}
|
|
2803
|
+
const effectiveLimit = limit ?? 1e3;
|
|
2804
|
+
const keys = prefix ? db.prepare(`SELECT key, size, content_type FROM fs_entries WHERE fs_name = ? AND key LIKE ? ORDER BY key ASC LIMIT ?`).all(fsName, `${prefix}%`, effectiveLimit) : db.prepare(`SELECT key, size, content_type FROM fs_entries WHERE fs_name = ? ORDER BY key ASC LIMIT ?`).all(fsName, effectiveLimit);
|
|
2805
|
+
return c.json({
|
|
2806
|
+
keys: keys.map((k) => ({
|
|
2807
|
+
key: k.key,
|
|
2808
|
+
size: k.size,
|
|
2809
|
+
contentType: k.content_type
|
|
2810
|
+
}))
|
|
2811
|
+
});
|
|
2812
|
+
} catch (err) {
|
|
2813
|
+
error(`[fs-service] list error: ${err instanceof Error ? err.message : String(err)}`);
|
|
2814
|
+
return c.json({
|
|
2815
|
+
error: `list failed: ${err instanceof Error ? err.message : String(err)}`
|
|
2816
|
+
}, 500);
|
|
2817
|
+
}
|
|
2818
|
+
};
|
|
2819
|
+
return {
|
|
2820
|
+
putHandler,
|
|
2821
|
+
getHandler,
|
|
2822
|
+
deleteHandler,
|
|
2823
|
+
listHandler
|
|
2824
|
+
};
|
|
2825
|
+
}
|
|
2826
|
+
var init_fs_service = __esm({
|
|
2827
|
+
"../emulator/dist/services/fs-service.js"() {
|
|
2828
|
+
init_logger();
|
|
2829
|
+
}
|
|
2830
|
+
});
|
|
2518
2831
|
function createQueueHandlers(db) {
|
|
2519
2832
|
const sendHandler = async (c) => {
|
|
2520
2833
|
try {
|
|
@@ -2948,6 +3261,14 @@ async function startMockServer(dbManager2, config, options = {}) {
|
|
|
2948
3261
|
app.post("/state/set", stateHandlers.setHandler);
|
|
2949
3262
|
app.post("/state/delete", stateHandlers.deleteHandler);
|
|
2950
3263
|
}
|
|
3264
|
+
if (config.fs) {
|
|
3265
|
+
const dataDir = getDataDir(process.cwd());
|
|
3266
|
+
const fsHandlers = createFsHandlers(dbManager2.emulatorDb, dataDir);
|
|
3267
|
+
app.post("/fs/put", fsHandlers.putHandler);
|
|
3268
|
+
app.post("/fs/get", fsHandlers.getHandler);
|
|
3269
|
+
app.post("/fs/delete", fsHandlers.deleteHandler);
|
|
3270
|
+
app.post("/fs/list", fsHandlers.listHandler);
|
|
3271
|
+
}
|
|
2951
3272
|
if (config.auth) {
|
|
2952
3273
|
const authHandlers = createAuthHandlers(dbManager2.emulatorDb);
|
|
2953
3274
|
app.post("/auth/signup", authHandlers.signupHandler);
|
|
@@ -2979,44 +3300,18 @@ async function startMockServer(dbManager2, config, options = {}) {
|
|
|
2979
3300
|
var DEFAULT_MOCK_SERVER_PORT;
|
|
2980
3301
|
var init_mock_server = __esm({
|
|
2981
3302
|
"../emulator/dist/services/mock-server.js"() {
|
|
3303
|
+
init_paths();
|
|
2982
3304
|
init_auth_service();
|
|
2983
3305
|
init_cache_service();
|
|
2984
3306
|
init_dashboard_routes();
|
|
2985
3307
|
init_db_service();
|
|
3308
|
+
init_fs_service();
|
|
2986
3309
|
init_queue_service();
|
|
2987
3310
|
init_state_service();
|
|
2988
3311
|
init_workflow_service();
|
|
2989
3312
|
DEFAULT_MOCK_SERVER_PORT = 4003;
|
|
2990
3313
|
}
|
|
2991
3314
|
});
|
|
2992
|
-
function getProjectHash(projectDir) {
|
|
2993
|
-
return createHash("sha256").update(projectDir).digest("hex").slice(0, 12);
|
|
2994
|
-
}
|
|
2995
|
-
function getTempDir(projectDir) {
|
|
2996
|
-
const hash = getProjectHash(projectDir);
|
|
2997
|
-
return join(tmpdir(), `ploy-emulator-${hash}`);
|
|
2998
|
-
}
|
|
2999
|
-
function getDataDir(projectDir) {
|
|
3000
|
-
return join(projectDir, ".ploy");
|
|
3001
|
-
}
|
|
3002
|
-
function ensureDir(dir) {
|
|
3003
|
-
mkdirSync(dir, { recursive: true });
|
|
3004
|
-
}
|
|
3005
|
-
function ensureTempDir(projectDir) {
|
|
3006
|
-
const tempDir = getTempDir(projectDir);
|
|
3007
|
-
ensureDir(tempDir);
|
|
3008
|
-
return tempDir;
|
|
3009
|
-
}
|
|
3010
|
-
function ensureDataDir(projectDir) {
|
|
3011
|
-
const dataDir = getDataDir(projectDir);
|
|
3012
|
-
ensureDir(dataDir);
|
|
3013
|
-
ensureDir(join(dataDir, "db"));
|
|
3014
|
-
return dataDir;
|
|
3015
|
-
}
|
|
3016
|
-
var init_paths = __esm({
|
|
3017
|
-
"../emulator/dist/utils/paths.js"() {
|
|
3018
|
-
}
|
|
3019
|
-
});
|
|
3020
3315
|
function initializeDatabases(projectDir) {
|
|
3021
3316
|
const dataDir = ensureDataDir(projectDir);
|
|
3022
3317
|
const d1Databases = /* @__PURE__ */ new Map();
|
|
@@ -3158,6 +3453,16 @@ CREATE TABLE IF NOT EXISTS state_entries (
|
|
|
3158
3453
|
value TEXT NOT NULL,
|
|
3159
3454
|
PRIMARY KEY (state_name, key)
|
|
3160
3455
|
);
|
|
3456
|
+
|
|
3457
|
+
-- File storage entries table (metadata for stored files)
|
|
3458
|
+
CREATE TABLE IF NOT EXISTS fs_entries (
|
|
3459
|
+
fs_name TEXT NOT NULL,
|
|
3460
|
+
key TEXT NOT NULL,
|
|
3461
|
+
content_type TEXT NOT NULL DEFAULT 'application/octet-stream',
|
|
3462
|
+
size INTEGER NOT NULL DEFAULT 0,
|
|
3463
|
+
created_at INTEGER DEFAULT (strftime('%s', 'now')),
|
|
3464
|
+
PRIMARY KEY (fs_name, key)
|
|
3465
|
+
);
|
|
3161
3466
|
`;
|
|
3162
3467
|
}
|
|
3163
3468
|
});
|
|
@@ -3270,6 +3575,9 @@ var init_emulator = __esm({
|
|
|
3270
3575
|
if (this.config.cache) {
|
|
3271
3576
|
log(` Cache bindings: ${Object.keys(this.config.cache).join(", ")}`);
|
|
3272
3577
|
}
|
|
3578
|
+
if (this.config.fs) {
|
|
3579
|
+
log(` File Storage bindings: ${Object.keys(this.config.fs).join(", ")}`);
|
|
3580
|
+
}
|
|
3273
3581
|
if (this.config.workflow) {
|
|
3274
3582
|
log(` Workflow bindings: ${Object.keys(this.config.workflow).join(", ")}`);
|
|
3275
3583
|
}
|