@meetploy/cli 1.14.1 → 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 +440 -40
- package/package.json +1 -1
- package/dist/dashboard-dist/assets/main-UY1Z1kG0.js +0 -334
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>Ploy Dev Dashboard</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/main-
|
|
8
|
-
<link rel="stylesheet" crossorigin href="/assets/main-
|
|
7
|
+
<script type="module" crossorigin src="/assets/main-duAiLjPq.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="/assets/main-B-euJxpr.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
11
11
|
<div id="root"></div>
|
package/dist/dev.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createRequire } from 'module';
|
|
2
2
|
import 'child_process';
|
|
3
|
-
import { readFile, existsSync, readFileSync, mkdirSync } from 'fs';
|
|
3
|
+
import { readFile, existsSync, readFileSync, mkdirSync, unlinkSync, writeFileSync } from 'fs';
|
|
4
4
|
import { dirname, join } from 'path';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
6
|
import 'esbuild';
|
|
@@ -10,11 +10,23 @@ import { parse } from 'yaml';
|
|
|
10
10
|
import { serve } from '@hono/node-server';
|
|
11
11
|
import { Hono } from 'hono';
|
|
12
12
|
import { randomUUID, createHmac, pbkdf2Sync, timingSafeEqual, randomBytes } from 'crypto';
|
|
13
|
-
import { getCookie, deleteCookie, setCookie } from 'hono/cookie';
|
|
14
13
|
import 'os';
|
|
14
|
+
import { getCookie, deleteCookie, setCookie } from 'hono/cookie';
|
|
15
15
|
import Database from 'better-sqlite3';
|
|
16
16
|
|
|
17
17
|
createRequire(import.meta.url);
|
|
18
|
+
|
|
19
|
+
// ../emulator/dist/utils/logger.js
|
|
20
|
+
var COLORS = {
|
|
21
|
+
reset: "\x1B[0m",
|
|
22
|
+
dim: "\x1B[2m",
|
|
23
|
+
red: "\x1B[31m"};
|
|
24
|
+
function timestamp() {
|
|
25
|
+
return (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false });
|
|
26
|
+
}
|
|
27
|
+
function error(message) {
|
|
28
|
+
console.error(`${COLORS.dim}[${timestamp()}]${COLORS.reset} ${COLORS.red}[ploy]${COLORS.reset} ${message}`);
|
|
29
|
+
}
|
|
18
30
|
promisify(readFile);
|
|
19
31
|
function readPloyConfigSync(projectDir, configPath) {
|
|
20
32
|
const configFile = configPath ?? "ploy.yaml";
|
|
@@ -37,6 +49,18 @@ function readPloyConfig(projectDir, configPath) {
|
|
|
37
49
|
}
|
|
38
50
|
return config;
|
|
39
51
|
}
|
|
52
|
+
function getDataDir(projectDir) {
|
|
53
|
+
return join(projectDir, ".ploy");
|
|
54
|
+
}
|
|
55
|
+
function ensureDir(dir) {
|
|
56
|
+
mkdirSync(dir, { recursive: true });
|
|
57
|
+
}
|
|
58
|
+
function ensureDataDir(projectDir) {
|
|
59
|
+
const dataDir = getDataDir(projectDir);
|
|
60
|
+
ensureDir(dataDir);
|
|
61
|
+
ensureDir(join(dataDir, "db"));
|
|
62
|
+
return dataDir;
|
|
63
|
+
}
|
|
40
64
|
function generateId() {
|
|
41
65
|
return randomBytes(16).toString("hex");
|
|
42
66
|
}
|
|
@@ -354,6 +378,7 @@ function createDashboardRoutes(app, dbManager2, config) {
|
|
|
354
378
|
queue: config.queue,
|
|
355
379
|
cache: config.cache,
|
|
356
380
|
state: config.state,
|
|
381
|
+
fs: config.fs,
|
|
357
382
|
workflow: config.workflow,
|
|
358
383
|
auth: config.auth
|
|
359
384
|
});
|
|
@@ -848,6 +873,37 @@ function createDashboardRoutes(app, dbManager2, config) {
|
|
|
848
873
|
return c.json({ error: err instanceof Error ? err.message : String(err) }, 500);
|
|
849
874
|
}
|
|
850
875
|
});
|
|
876
|
+
app.get("/api/fs/:binding/entries", (c) => {
|
|
877
|
+
const binding = c.req.param("binding");
|
|
878
|
+
const fsName = config.fs?.[binding];
|
|
879
|
+
const limit = parseInt(c.req.query("limit") || "20", 10);
|
|
880
|
+
const offset = parseInt(c.req.query("offset") || "0", 10);
|
|
881
|
+
if (!fsName) {
|
|
882
|
+
return c.json({ error: "File storage binding not found" }, 404);
|
|
883
|
+
}
|
|
884
|
+
try {
|
|
885
|
+
const db = dbManager2.emulatorDb;
|
|
886
|
+
const total = db.prepare(`SELECT COUNT(*) as count FROM fs_entries WHERE fs_name = ?`).get(fsName).count;
|
|
887
|
+
const entries = db.prepare(`SELECT key, size, content_type, created_at
|
|
888
|
+
FROM fs_entries
|
|
889
|
+
WHERE fs_name = ?
|
|
890
|
+
ORDER BY key ASC
|
|
891
|
+
LIMIT ? OFFSET ?`).all(fsName, limit, offset);
|
|
892
|
+
return c.json({
|
|
893
|
+
entries: entries.map((e) => ({
|
|
894
|
+
key: e.key,
|
|
895
|
+
size: e.size,
|
|
896
|
+
contentType: e.content_type,
|
|
897
|
+
createdAt: new Date(e.created_at * 1e3).toISOString()
|
|
898
|
+
})),
|
|
899
|
+
total,
|
|
900
|
+
limit,
|
|
901
|
+
offset
|
|
902
|
+
});
|
|
903
|
+
} catch (err) {
|
|
904
|
+
return c.json({ error: err instanceof Error ? err.message : String(err) }, 500);
|
|
905
|
+
}
|
|
906
|
+
});
|
|
851
907
|
if (hasDashboard) {
|
|
852
908
|
app.get("/assets/*", (c) => {
|
|
853
909
|
const path = c.req.path;
|
|
@@ -975,6 +1031,120 @@ function createDbHandler(getDatabase) {
|
|
|
975
1031
|
}
|
|
976
1032
|
};
|
|
977
1033
|
}
|
|
1034
|
+
function createFsHandlers(db, dataDir) {
|
|
1035
|
+
const fsBaseDir = join(dataDir, "fs");
|
|
1036
|
+
mkdirSync(fsBaseDir, { recursive: true });
|
|
1037
|
+
function getFsDir(fsName) {
|
|
1038
|
+
const dir = join(fsBaseDir, fsName);
|
|
1039
|
+
mkdirSync(dir, { recursive: true });
|
|
1040
|
+
return dir;
|
|
1041
|
+
}
|
|
1042
|
+
function encodedKeyPath(fsDir, key) {
|
|
1043
|
+
return join(fsDir, encodeURIComponent(key));
|
|
1044
|
+
}
|
|
1045
|
+
const putHandler = async (c) => {
|
|
1046
|
+
try {
|
|
1047
|
+
const body = await c.req.json();
|
|
1048
|
+
const { fsName, key, value, contentType } = body;
|
|
1049
|
+
if (!fsName || !key) {
|
|
1050
|
+
return c.json({ error: "Missing required fields: fsName, key, value" }, 400);
|
|
1051
|
+
}
|
|
1052
|
+
const fsDir = getFsDir(fsName);
|
|
1053
|
+
const filePath = encodedKeyPath(fsDir, key);
|
|
1054
|
+
writeFileSync(filePath, value, "utf-8");
|
|
1055
|
+
const size = Buffer.byteLength(value, "utf-8");
|
|
1056
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
1057
|
+
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);
|
|
1058
|
+
return c.json({ success: true });
|
|
1059
|
+
} catch (err) {
|
|
1060
|
+
error(`[fs-service] put error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1061
|
+
return c.json({
|
|
1062
|
+
error: `put failed: ${err instanceof Error ? err.message : String(err)}`
|
|
1063
|
+
}, 500);
|
|
1064
|
+
}
|
|
1065
|
+
};
|
|
1066
|
+
const getHandler = async (c) => {
|
|
1067
|
+
try {
|
|
1068
|
+
const body = await c.req.json();
|
|
1069
|
+
const { fsName, key } = body;
|
|
1070
|
+
if (!fsName || !key) {
|
|
1071
|
+
return c.json({ error: "Missing required fields: fsName, key" }, 400);
|
|
1072
|
+
}
|
|
1073
|
+
const row = db.prepare(`SELECT content_type, size FROM fs_entries WHERE fs_name = ? AND key = ?`).get(fsName, key);
|
|
1074
|
+
if (!row) {
|
|
1075
|
+
return c.json({ found: false });
|
|
1076
|
+
}
|
|
1077
|
+
const fsDir = getFsDir(fsName);
|
|
1078
|
+
const filePath = encodedKeyPath(fsDir, key);
|
|
1079
|
+
if (!existsSync(filePath)) {
|
|
1080
|
+
db.prepare(`DELETE FROM fs_entries WHERE fs_name = ? AND key = ?`).run(fsName, key);
|
|
1081
|
+
return c.json({ found: false });
|
|
1082
|
+
}
|
|
1083
|
+
const fileContent = readFileSync(filePath, "utf-8");
|
|
1084
|
+
return c.json({
|
|
1085
|
+
found: true,
|
|
1086
|
+
body: fileContent,
|
|
1087
|
+
contentType: row.content_type,
|
|
1088
|
+
size: row.size
|
|
1089
|
+
});
|
|
1090
|
+
} catch (err) {
|
|
1091
|
+
error(`[fs-service] get error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1092
|
+
return c.json({
|
|
1093
|
+
error: `get failed: ${err instanceof Error ? err.message : String(err)}`
|
|
1094
|
+
}, 500);
|
|
1095
|
+
}
|
|
1096
|
+
};
|
|
1097
|
+
const deleteHandler = async (c) => {
|
|
1098
|
+
try {
|
|
1099
|
+
const body = await c.req.json();
|
|
1100
|
+
const { fsName, key } = body;
|
|
1101
|
+
if (!fsName || !key) {
|
|
1102
|
+
return c.json({ error: "Missing required fields: fsName, key" }, 400);
|
|
1103
|
+
}
|
|
1104
|
+
db.prepare(`DELETE FROM fs_entries WHERE fs_name = ? AND key = ?`).run(fsName, key);
|
|
1105
|
+
const fsDir = getFsDir(fsName);
|
|
1106
|
+
const filePath = encodedKeyPath(fsDir, key);
|
|
1107
|
+
if (existsSync(filePath)) {
|
|
1108
|
+
unlinkSync(filePath);
|
|
1109
|
+
}
|
|
1110
|
+
return c.json({ success: true });
|
|
1111
|
+
} catch (err) {
|
|
1112
|
+
error(`[fs-service] delete error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1113
|
+
return c.json({
|
|
1114
|
+
error: `delete failed: ${err instanceof Error ? err.message : String(err)}`
|
|
1115
|
+
}, 500);
|
|
1116
|
+
}
|
|
1117
|
+
};
|
|
1118
|
+
const listHandler = async (c) => {
|
|
1119
|
+
try {
|
|
1120
|
+
const body = await c.req.json();
|
|
1121
|
+
const { fsName, prefix, limit } = body;
|
|
1122
|
+
if (!fsName) {
|
|
1123
|
+
return c.json({ error: "Missing required field: fsName" }, 400);
|
|
1124
|
+
}
|
|
1125
|
+
const effectiveLimit = limit ?? 1e3;
|
|
1126
|
+
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);
|
|
1127
|
+
return c.json({
|
|
1128
|
+
keys: keys.map((k) => ({
|
|
1129
|
+
key: k.key,
|
|
1130
|
+
size: k.size,
|
|
1131
|
+
contentType: k.content_type
|
|
1132
|
+
}))
|
|
1133
|
+
});
|
|
1134
|
+
} catch (err) {
|
|
1135
|
+
error(`[fs-service] list error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1136
|
+
return c.json({
|
|
1137
|
+
error: `list failed: ${err instanceof Error ? err.message : String(err)}`
|
|
1138
|
+
}, 500);
|
|
1139
|
+
}
|
|
1140
|
+
};
|
|
1141
|
+
return {
|
|
1142
|
+
putHandler,
|
|
1143
|
+
getHandler,
|
|
1144
|
+
deleteHandler,
|
|
1145
|
+
listHandler
|
|
1146
|
+
};
|
|
1147
|
+
}
|
|
978
1148
|
function createQueueHandlers(db) {
|
|
979
1149
|
const sendHandler = async (c) => {
|
|
980
1150
|
try {
|
|
@@ -1329,6 +1499,14 @@ async function startMockServer(dbManager2, config, options = {}) {
|
|
|
1329
1499
|
app.post("/state/set", stateHandlers.setHandler);
|
|
1330
1500
|
app.post("/state/delete", stateHandlers.deleteHandler);
|
|
1331
1501
|
}
|
|
1502
|
+
if (config.fs) {
|
|
1503
|
+
const dataDir = getDataDir(process.cwd());
|
|
1504
|
+
const fsHandlers = createFsHandlers(dbManager2.emulatorDb, dataDir);
|
|
1505
|
+
app.post("/fs/put", fsHandlers.putHandler);
|
|
1506
|
+
app.post("/fs/get", fsHandlers.getHandler);
|
|
1507
|
+
app.post("/fs/delete", fsHandlers.deleteHandler);
|
|
1508
|
+
app.post("/fs/list", fsHandlers.listHandler);
|
|
1509
|
+
}
|
|
1332
1510
|
if (config.auth) {
|
|
1333
1511
|
const authHandlers = createAuthHandlers(dbManager2.emulatorDb);
|
|
1334
1512
|
app.post("/auth/signup", authHandlers.signupHandler);
|
|
@@ -1357,18 +1535,6 @@ async function startMockServer(dbManager2, config, options = {}) {
|
|
|
1357
1535
|
});
|
|
1358
1536
|
});
|
|
1359
1537
|
}
|
|
1360
|
-
function getDataDir(projectDir) {
|
|
1361
|
-
return join(projectDir, ".ploy");
|
|
1362
|
-
}
|
|
1363
|
-
function ensureDir(dir) {
|
|
1364
|
-
mkdirSync(dir, { recursive: true });
|
|
1365
|
-
}
|
|
1366
|
-
function ensureDataDir(projectDir) {
|
|
1367
|
-
const dataDir = getDataDir(projectDir);
|
|
1368
|
-
ensureDir(dataDir);
|
|
1369
|
-
ensureDir(join(dataDir, "db"));
|
|
1370
|
-
return dataDir;
|
|
1371
|
-
}
|
|
1372
1538
|
var EMULATOR_SCHEMA = `
|
|
1373
1539
|
-- Queue messages table
|
|
1374
1540
|
CREATE TABLE IF NOT EXISTS queue_messages (
|
|
@@ -1478,6 +1644,16 @@ CREATE TABLE IF NOT EXISTS state_entries (
|
|
|
1478
1644
|
value TEXT NOT NULL,
|
|
1479
1645
|
PRIMARY KEY (state_name, key)
|
|
1480
1646
|
);
|
|
1647
|
+
|
|
1648
|
+
-- File storage entries table (metadata for stored files)
|
|
1649
|
+
CREATE TABLE IF NOT EXISTS fs_entries (
|
|
1650
|
+
fs_name TEXT NOT NULL,
|
|
1651
|
+
key TEXT NOT NULL,
|
|
1652
|
+
content_type TEXT NOT NULL DEFAULT 'application/octet-stream',
|
|
1653
|
+
size INTEGER NOT NULL DEFAULT 0,
|
|
1654
|
+
created_at INTEGER DEFAULT (strftime('%s', 'now')),
|
|
1655
|
+
PRIMARY KEY (fs_name, key)
|
|
1656
|
+
);
|
|
1481
1657
|
`;
|
|
1482
1658
|
function initializeDatabases(projectDir) {
|
|
1483
1659
|
const dataDir = ensureDataDir(projectDir);
|