@hapico/cli 0.0.18 → 0.0.20
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/bin/index.js +279 -68
- package/bun.lock +6 -0
- package/dist/index.js +279 -68
- package/index.ts +368 -93
- package/package.json +3 -1
- package/test.tsx +0 -0
package/bin/index.js
CHANGED
|
@@ -37,7 +37,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
37
37
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
38
38
|
};
|
|
39
39
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
-
exports.compileES5 = void 0;
|
|
40
|
+
exports.compileES5 = exports.tryJSONParse = void 0;
|
|
41
41
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
42
42
|
const lodash_1 = require("lodash");
|
|
43
43
|
const commander_1 = require("commander");
|
|
@@ -54,6 +54,7 @@ const crypto_1 = require("crypto");
|
|
|
54
54
|
const child_process_1 = require("child_process");
|
|
55
55
|
const util_1 = require("util");
|
|
56
56
|
const chalk_1 = __importDefault(require("chalk"));
|
|
57
|
+
const pako_1 = __importDefault(require("pako"));
|
|
57
58
|
// Promisify exec for async usage
|
|
58
59
|
const execPromise = (0, util_1.promisify)(child_process_1.exec);
|
|
59
60
|
// Directory to store the token and project config
|
|
@@ -101,12 +102,27 @@ const getStoredProjectId = (projectDir) => {
|
|
|
101
102
|
if (fs.existsSync(configFile)) {
|
|
102
103
|
const data = fs.readFileSync(configFile, { encoding: "utf8" });
|
|
103
104
|
const json = JSON.parse(data);
|
|
104
|
-
return
|
|
105
|
+
return {
|
|
106
|
+
projectId: json.projectId || null,
|
|
107
|
+
replicate: json.replicate || [],
|
|
108
|
+
};
|
|
105
109
|
}
|
|
106
|
-
return
|
|
110
|
+
return {
|
|
111
|
+
projectId: null,
|
|
112
|
+
replicate: [],
|
|
113
|
+
};
|
|
107
114
|
};
|
|
108
115
|
// Khởi tạo cache bằng Map để lưu trữ kết quả compile
|
|
109
116
|
const compileCache = new Map();
|
|
117
|
+
const tryJSONParse = (str) => {
|
|
118
|
+
try {
|
|
119
|
+
return JSON.parse(str);
|
|
120
|
+
}
|
|
121
|
+
catch (_a) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
exports.tryJSONParse = tryJSONParse;
|
|
110
126
|
const compileES5 = (code, filePath) => {
|
|
111
127
|
if (filePath && !filePath.endsWith(".ts") && !filePath.endsWith(".tsx")) {
|
|
112
128
|
return code;
|
|
@@ -127,6 +143,7 @@ const compileES5 = (code, filePath) => {
|
|
|
127
143
|
return result.code;
|
|
128
144
|
}
|
|
129
145
|
catch (error) {
|
|
146
|
+
console.log(chalk_1.default.red(`Error compiling code: ${error === null || error === void 0 ? void 0 : error.message}`));
|
|
130
147
|
return "";
|
|
131
148
|
}
|
|
132
149
|
};
|
|
@@ -249,24 +266,38 @@ class RoomState {
|
|
|
249
266
|
if (this.reconnectTimeout) {
|
|
250
267
|
clearTimeout(this.reconnectTimeout);
|
|
251
268
|
}
|
|
252
|
-
this.ws = new ws_1.WebSocket(`
|
|
253
|
-
|
|
269
|
+
this.ws = new ws_1.WebSocket(`wss://ws3.myworkbeast.com/ws?room=${this.roomId}`);
|
|
270
|
+
// Set binaryType to 'arraybuffer' to handle binary data
|
|
271
|
+
this.ws.binaryType = "arraybuffer";
|
|
272
|
+
this.ws.on("open", () => {
|
|
254
273
|
console.log(chalk_1.default.green(`Connected to room: ${this.roomId}`));
|
|
255
274
|
this.isConnected = true;
|
|
256
275
|
this.reconnectAttempts = 0;
|
|
257
276
|
onConnected === null || onConnected === void 0 ? void 0 : onConnected(); // Call the onConnected callback if provided
|
|
258
|
-
};
|
|
259
|
-
this.ws.
|
|
277
|
+
});
|
|
278
|
+
this.ws.on("close", () => {
|
|
260
279
|
console.log(chalk_1.default.yellow(`⚠ Disconnected from room: ${this.roomId}`));
|
|
261
280
|
this.isConnected = false;
|
|
262
281
|
this.reconnectAttempts++;
|
|
263
282
|
const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
|
|
264
283
|
console.log(chalk_1.default.yellow(`Attempting to reconnect in ${delay / 1000}s...`));
|
|
265
284
|
this.reconnectTimeout = setTimeout(() => this.connect(onConnected), delay);
|
|
266
|
-
};
|
|
285
|
+
});
|
|
267
286
|
this.ws.on("message", (data) => {
|
|
268
287
|
try {
|
|
269
|
-
|
|
288
|
+
let jsonStr;
|
|
289
|
+
if (data instanceof ArrayBuffer) {
|
|
290
|
+
jsonStr = pako_1.default.inflate(data, { to: "string" });
|
|
291
|
+
}
|
|
292
|
+
else if (typeof data === "string") {
|
|
293
|
+
jsonStr = data;
|
|
294
|
+
}
|
|
295
|
+
else if (Buffer.isBuffer(data)) {
|
|
296
|
+
jsonStr = pako_1.default.inflate(data, { to: "string" });
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
jsonStr = data.toString(); // Fallback nếu không nén
|
|
300
|
+
}
|
|
270
301
|
const parsedData = JSON.parse(jsonStr);
|
|
271
302
|
const includes = [
|
|
272
303
|
"view",
|
|
@@ -316,10 +347,12 @@ class RoomState {
|
|
|
316
347
|
}
|
|
317
348
|
updateState(key, value) {
|
|
318
349
|
if (this.ws && this.ws.readyState === ws_1.WebSocket.OPEN) {
|
|
319
|
-
|
|
350
|
+
const message = JSON.stringify({
|
|
320
351
|
type: "update",
|
|
321
352
|
state: { [key]: value },
|
|
322
|
-
})
|
|
353
|
+
});
|
|
354
|
+
const compressed = pako_1.default.deflate(message); // Nén dữ liệu
|
|
355
|
+
this.ws.send(compressed); // Gửi binary
|
|
323
356
|
}
|
|
324
357
|
}
|
|
325
358
|
disconnect() {
|
|
@@ -327,7 +360,7 @@ class RoomState {
|
|
|
327
360
|
clearTimeout(this.reconnectTimeout);
|
|
328
361
|
}
|
|
329
362
|
if (this.ws) {
|
|
330
|
-
this.ws.
|
|
363
|
+
this.ws.removeAllListeners("close"); // Prevent reconnect on intentional close
|
|
331
364
|
this.ws.close();
|
|
332
365
|
}
|
|
333
366
|
}
|
|
@@ -338,11 +371,12 @@ class RoomState {
|
|
|
338
371
|
return this.isConnected;
|
|
339
372
|
}
|
|
340
373
|
}
|
|
341
|
-
commander_1.program.version("0.0.
|
|
374
|
+
commander_1.program.version("0.0.20").description("Hapico CLI for project management");
|
|
342
375
|
commander_1.program
|
|
343
376
|
.command("clone <id>")
|
|
344
377
|
.description("Clone a project by ID")
|
|
345
378
|
.action(async (id) => {
|
|
379
|
+
var _a, _b;
|
|
346
380
|
const { accessToken } = getStoredToken();
|
|
347
381
|
if (!accessToken) {
|
|
348
382
|
console.error(chalk_1.default.red("✗ You need to login first. Use 'hapico login' command."));
|
|
@@ -357,7 +391,9 @@ commander_1.program
|
|
|
357
391
|
const apiSpinner = (0, ora_1.default)(chalk_1.default.blue("Fetching project data...")).start();
|
|
358
392
|
try {
|
|
359
393
|
const response = await axios_1.default.get(`https://base.myworkbeast.com/api/views/${id}`);
|
|
360
|
-
|
|
394
|
+
const code = (_a = response === null || response === void 0 ? void 0 : response.data) === null || _a === void 0 ? void 0 : _a.code;
|
|
395
|
+
const decompressedCode = pako_1.default.inflate(Uint8Array.from(atob(code), (c) => c.charCodeAt(0)), { to: "string" });
|
|
396
|
+
files = ((_b = (0, exports.tryJSONParse)(decompressedCode)) === null || _b === void 0 ? void 0 : _b.files) || [];
|
|
361
397
|
apiSpinner.succeed(chalk_1.default.green("Project data fetched successfully!"));
|
|
362
398
|
const templateSpinner = (0, ora_1.default)(chalk_1.default.blue("Downloading template...")).start();
|
|
363
399
|
const TEMPLATE_URL = "https://files.hcm04.vstorage.vngcloud.vn/assets/template_zalominiapp_devmode.zip";
|
|
@@ -399,6 +435,7 @@ commander_1.program
|
|
|
399
435
|
.command("dev")
|
|
400
436
|
.description("Start the project in development mode")
|
|
401
437
|
.action(() => {
|
|
438
|
+
var _a;
|
|
402
439
|
const { accessToken } = getStoredToken();
|
|
403
440
|
if (!accessToken) {
|
|
404
441
|
console.error(chalk_1.default.red("✗ You need to login first. Use 'hapico login' command."));
|
|
@@ -440,7 +477,7 @@ commander_1.program
|
|
|
440
477
|
const info = JSON.stringify({
|
|
441
478
|
id: sessionId,
|
|
442
479
|
createdAt: new Date().toISOString(),
|
|
443
|
-
viewId: getStoredProjectId(pwd),
|
|
480
|
+
viewId: (_a = getStoredProjectId(pwd)) === null || _a === void 0 ? void 0 : _a.projectId,
|
|
444
481
|
});
|
|
445
482
|
// Convert info to base64
|
|
446
483
|
const projectId = Buffer.from(info).toString("base64");
|
|
@@ -452,10 +489,44 @@ commander_1.program
|
|
|
452
489
|
const room = new RoomState(`view_${projectId}`, []);
|
|
453
490
|
const fileManager = new FileManager(srcDir);
|
|
454
491
|
const initialFiles = fileManager.listFiles();
|
|
455
|
-
|
|
492
|
+
// get tsconfig.json
|
|
493
|
+
const tsconfigPath = path.join(srcDir, "..", "tsconfig.json");
|
|
494
|
+
if (fs.existsSync(tsconfigPath)) {
|
|
495
|
+
const content = fs.readFileSync(tsconfigPath, { encoding: "utf8" });
|
|
496
|
+
initialFiles.push({
|
|
497
|
+
path: "./tsconfig.json",
|
|
498
|
+
content,
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
// Remove All binary files
|
|
502
|
+
const supportExtensions = [
|
|
503
|
+
".ts",
|
|
504
|
+
".tsx",
|
|
505
|
+
".js",
|
|
506
|
+
".jsx",
|
|
507
|
+
".json",
|
|
508
|
+
".css",
|
|
509
|
+
".html",
|
|
510
|
+
".env",
|
|
511
|
+
".env.local",
|
|
512
|
+
".env.development",
|
|
513
|
+
".env.production",
|
|
514
|
+
".jsonc",
|
|
515
|
+
".yml",
|
|
516
|
+
".yaml",
|
|
517
|
+
".md",
|
|
518
|
+
".markdown",
|
|
519
|
+
".txt",
|
|
520
|
+
".xml",
|
|
521
|
+
".config",
|
|
522
|
+
];
|
|
523
|
+
const filteredFiles = initialFiles.filter((file) => {
|
|
524
|
+
return supportExtensions.some((ext) => file.path.endsWith(ext));
|
|
525
|
+
});
|
|
526
|
+
room.files = filteredFiles;
|
|
456
527
|
room.connect(async () => {
|
|
457
528
|
devSpinner.succeed(chalk_1.default.green("Project started in development mode!"));
|
|
458
|
-
room.updateState("view",
|
|
529
|
+
room.updateState("view", filteredFiles);
|
|
459
530
|
fileManager.setOnFileChange((filePath, content) => {
|
|
460
531
|
var _a;
|
|
461
532
|
const es5 = (_a = (0, exports.compileES5)(content, filePath)) !== null && _a !== void 0 ? _a : "";
|
|
@@ -470,12 +541,12 @@ commander_1.program
|
|
|
470
541
|
room.updateState("view", updatedFiles);
|
|
471
542
|
});
|
|
472
543
|
// Fetch project info
|
|
473
|
-
const
|
|
474
|
-
if (!
|
|
544
|
+
const store = getStoredProjectId(pwd);
|
|
545
|
+
if (!store.projectId) {
|
|
475
546
|
console.error(chalk_1.default.red("✗ Project ID not found. Please ensure hapico.config.json exists in the project directory."));
|
|
476
547
|
return;
|
|
477
548
|
}
|
|
478
|
-
const project = await axios_1.default.get(`https://base.myworkbeast.com/api/views/${
|
|
549
|
+
const project = await axios_1.default.get(`https://base.myworkbeast.com/api/views/${store.projectId}`, {
|
|
479
550
|
headers: {
|
|
480
551
|
Authorization: `Bearer ${accessToken}`,
|
|
481
552
|
"Content-Type": "application/json",
|
|
@@ -487,7 +558,7 @@ commander_1.program
|
|
|
487
558
|
}
|
|
488
559
|
const projectType = project.data.type || "view";
|
|
489
560
|
if (projectType === "zalominiapp") {
|
|
490
|
-
qrcode_terminal_1.default.generate(`https://zalo.me/s/3218692650896662017/player/${projectId}`, { small: true }, (qrcode) => {
|
|
561
|
+
qrcode_terminal_1.default.generate(`https://zalo.me/s/3218692650896662017/player/${projectId}?env=TESTING&version=75`, { small: true }, (qrcode) => {
|
|
491
562
|
console.log(chalk_1.default.cyan("📱 Scan this QR code to connect to the project:"));
|
|
492
563
|
console.log(qrcode);
|
|
493
564
|
});
|
|
@@ -500,59 +571,99 @@ commander_1.program
|
|
|
500
571
|
}
|
|
501
572
|
});
|
|
502
573
|
});
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
.description("Push the project source code to the server")
|
|
506
|
-
.action(() => {
|
|
574
|
+
// Hàm tái sử dụng để push mã nguồn lên server
|
|
575
|
+
async function pushProject(spinner, projectId) {
|
|
507
576
|
const data = getStoredToken();
|
|
508
577
|
const { accessToken } = data || {};
|
|
509
578
|
if (!accessToken) {
|
|
510
|
-
|
|
511
|
-
return;
|
|
579
|
+
spinner.fail(chalk_1.default.red("✗ You need to login first. Use 'hapico login' command."));
|
|
580
|
+
return false;
|
|
512
581
|
}
|
|
513
|
-
const saveSpinner = (0, ora_1.default)(chalk_1.default.blue("Saving project source code...")).start();
|
|
514
582
|
const pwd = process.cwd();
|
|
515
|
-
const projectId = getStoredProjectId(pwd);
|
|
516
|
-
if (!projectId) {
|
|
517
|
-
saveSpinner.fail(chalk_1.default.red("✗ Project ID not found. Please ensure hapico.config.json exists in the project directory."));
|
|
518
|
-
return;
|
|
519
|
-
}
|
|
520
583
|
const srcDir = path.join(pwd, "src");
|
|
521
584
|
if (!fs.existsSync(srcDir)) {
|
|
522
|
-
|
|
523
|
-
return;
|
|
585
|
+
spinner.fail(chalk_1.default.red("✗ Source directory 'src' does not exist. Please clone a project first."));
|
|
586
|
+
return false;
|
|
524
587
|
}
|
|
525
588
|
const fileManager = new FileManager(srcDir);
|
|
526
589
|
const files = fileManager.listFiles();
|
|
527
|
-
//
|
|
528
|
-
const
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
590
|
+
// Supported files
|
|
591
|
+
const SUPPORT_FILES = [
|
|
592
|
+
"./.env",
|
|
593
|
+
"./.env.local",
|
|
594
|
+
"./.env.development",
|
|
595
|
+
"./.env.production",
|
|
596
|
+
"./package.json",
|
|
597
|
+
"./tsconfig.json",
|
|
598
|
+
];
|
|
599
|
+
// Include supported files
|
|
600
|
+
SUPPORT_FILES.forEach((relativePath) => {
|
|
601
|
+
var _a;
|
|
602
|
+
const fullPath = path.join(pwd, relativePath);
|
|
603
|
+
if (fs.existsSync(fullPath)) {
|
|
604
|
+
console.log(chalk_1.default.green(`Including ${relativePath} in push for project ${projectId}.`));
|
|
605
|
+
const content = fs.readFileSync(fullPath, { encoding: "utf8" });
|
|
606
|
+
files.push({
|
|
607
|
+
path: relativePath,
|
|
608
|
+
content,
|
|
609
|
+
es5: (_a = (0, exports.compileES5)(content, fullPath)) !== null && _a !== void 0 ? _a : "",
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
});
|
|
613
|
+
spinner.text = `Pushing project source code to server for project ${projectId}...`;
|
|
614
|
+
const apiUrl = `https://base.myworkbeast.com/api/views/${projectId}`;
|
|
615
|
+
try {
|
|
616
|
+
await axios_1.default.put(apiUrl, {
|
|
617
|
+
code: JSON.stringify({
|
|
618
|
+
files,
|
|
619
|
+
}),
|
|
620
|
+
}, {
|
|
621
|
+
headers: {
|
|
622
|
+
Authorization: `Bearer ${accessToken}`,
|
|
623
|
+
"Content-Type": "application/json",
|
|
624
|
+
},
|
|
536
625
|
});
|
|
626
|
+
return true;
|
|
627
|
+
}
|
|
628
|
+
catch (error) {
|
|
629
|
+
spinner.fail(chalk_1.default.red(`✗ Error saving project ${projectId}: ${error.message}`));
|
|
630
|
+
return false;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
commander_1.program
|
|
634
|
+
.command("push")
|
|
635
|
+
.description("Push the project source code to the server")
|
|
636
|
+
.action(async () => {
|
|
637
|
+
const saveSpinner = (0, ora_1.default)(chalk_1.default.blue("Saving project source code...")).start();
|
|
638
|
+
const pwd = process.cwd();
|
|
639
|
+
const { projectId, replicate } = getStoredProjectId(pwd);
|
|
640
|
+
if (!projectId) {
|
|
641
|
+
saveSpinner.fail(chalk_1.default.red("✗ Project ID not found. Please ensure hapico.config.json exists in the project directory."));
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
// Push to the main project
|
|
645
|
+
const mainSuccess = await pushProject(saveSpinner, projectId);
|
|
646
|
+
let allSuccess = mainSuccess;
|
|
647
|
+
// Push to replicated projects if replicate array exists
|
|
648
|
+
if (replicate && Array.isArray(replicate) && replicate.length > 0) {
|
|
649
|
+
saveSpinner.text = chalk_1.default.blue("Pushing to replicated projects...");
|
|
650
|
+
for (const repId of replicate) {
|
|
651
|
+
const success = await pushProject(saveSpinner, repId);
|
|
652
|
+
if (!success) {
|
|
653
|
+
allSuccess = false;
|
|
654
|
+
console.warn(chalk_1.default.yellow(`⚠ Failed to push to replicated project ${repId}. Continuing...`));
|
|
655
|
+
}
|
|
656
|
+
else {
|
|
657
|
+
console.log(chalk_1.default.green(`✓ Successfully pushed to replicated project ${repId}.`));
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
if (allSuccess) {
|
|
662
|
+
saveSpinner.succeed(chalk_1.default.green("Project source code saved successfully to all projects!"));
|
|
663
|
+
}
|
|
664
|
+
else {
|
|
665
|
+
saveSpinner.warn(chalk_1.default.yellow("Project source code saved with some errors."));
|
|
537
666
|
}
|
|
538
|
-
const apiUrl = `https://base.myworkbeast.com/api/views/${projectId}`;
|
|
539
|
-
axios_1.default
|
|
540
|
-
.put(apiUrl, {
|
|
541
|
-
code: JSON.stringify({
|
|
542
|
-
files,
|
|
543
|
-
}),
|
|
544
|
-
}, {
|
|
545
|
-
headers: {
|
|
546
|
-
Authorization: `Bearer ${accessToken}`,
|
|
547
|
-
"Content-Type": "application/json",
|
|
548
|
-
},
|
|
549
|
-
})
|
|
550
|
-
.then(() => {
|
|
551
|
-
saveSpinner.succeed(chalk_1.default.green("Project source code saved successfully!"));
|
|
552
|
-
})
|
|
553
|
-
.catch((error) => {
|
|
554
|
-
saveSpinner.fail(chalk_1.default.red(`✗ Error saving project: ${error.message}`));
|
|
555
|
-
});
|
|
556
667
|
});
|
|
557
668
|
commander_1.program
|
|
558
669
|
.command("login")
|
|
@@ -613,26 +724,29 @@ commander_1.program
|
|
|
613
724
|
.command("pull")
|
|
614
725
|
.description("Pull the latest project files from the server")
|
|
615
726
|
.action(async () => {
|
|
727
|
+
var _a, _b;
|
|
616
728
|
const token = getStoredToken();
|
|
617
729
|
if (!token) {
|
|
618
730
|
console.error(chalk_1.default.red("✗ You need to login first. Use 'hapico login' command."));
|
|
619
731
|
return;
|
|
620
732
|
}
|
|
621
733
|
const pwd = process.cwd();
|
|
622
|
-
const projectId = getStoredProjectId(pwd);
|
|
734
|
+
const { projectId } = getStoredProjectId(pwd);
|
|
623
735
|
if (!projectId) {
|
|
624
736
|
console.error(chalk_1.default.red("✗ Project ID not found. Please ensure hapico.config.json exists in the project directory."));
|
|
625
737
|
return;
|
|
626
738
|
}
|
|
627
739
|
const apiSpinner = (0, ora_1.default)(chalk_1.default.blue("Fetching latest project files...")).start();
|
|
628
740
|
try {
|
|
629
|
-
const response = await axios_1.default.get(`https://base.myworkbeast.com/api/views/${projectId}`, {
|
|
741
|
+
const response = await axios_1.default.get(`https://base.myworkbeast.com/api/views/v3/${projectId}`, {
|
|
630
742
|
headers: {
|
|
631
|
-
Authorization: `Bearer ${token}`,
|
|
743
|
+
Authorization: `Bearer ${token.accessToken}`,
|
|
632
744
|
"Content-Type": "application/json",
|
|
633
745
|
},
|
|
634
746
|
});
|
|
635
|
-
const
|
|
747
|
+
const code = (_a = response === null || response === void 0 ? void 0 : response.data) === null || _a === void 0 ? void 0 : _a.code;
|
|
748
|
+
const decompressedCode = pako_1.default.inflate(Uint8Array.from(atob(code), (c) => c.charCodeAt(0)), { to: "string" });
|
|
749
|
+
const files = ((_b = (0, exports.tryJSONParse)(decompressedCode)) === null || _b === void 0 ? void 0 : _b.files) || [];
|
|
636
750
|
const fileManager = new FileManager(path.join(pwd, "src"));
|
|
637
751
|
fileManager.syncFiles(files);
|
|
638
752
|
apiSpinner.succeed(chalk_1.default.green("Project files updated successfully!"));
|
|
@@ -730,14 +844,15 @@ commander_1.program
|
|
|
730
844
|
.replace(/[-:]/g, "")
|
|
731
845
|
.replace(/\..+/, "");
|
|
732
846
|
const filename = path.join(migrationsDir, `${index + 1}_migration_${timestamp}.sql`);
|
|
733
|
-
fs.writeFileSync(filename, migration.sql, {
|
|
847
|
+
fs.writeFileSync(filename, migration.sql, {
|
|
848
|
+
encoding: "utf8",
|
|
849
|
+
});
|
|
734
850
|
});
|
|
735
851
|
console.log(chalk_1.default.green("✓ Migrations saved successfully!"));
|
|
736
852
|
// Download project files and save to project
|
|
737
853
|
let files = [];
|
|
738
854
|
const apiSpinner = (0, ora_1.default)(chalk_1.default.blue("Fetching project data...")).start();
|
|
739
855
|
try {
|
|
740
|
-
files = response.data.files || [];
|
|
741
856
|
apiSpinner.succeed(chalk_1.default.green("Project data fetched successfully!"));
|
|
742
857
|
const saveSpinner = (0, ora_1.default)(chalk_1.default.blue("Saving project files...")).start();
|
|
743
858
|
files.forEach((file) => {
|
|
@@ -893,4 +1008,100 @@ commander_1.program
|
|
|
893
1008
|
});
|
|
894
1009
|
}
|
|
895
1010
|
});
|
|
1011
|
+
// hapico publish
|
|
1012
|
+
commander_1.program.command("publish").action(async () => {
|
|
1013
|
+
const publishSpinner = (0, ora_1.default)(chalk_1.default.blue("Publishing to Hapico...")).start();
|
|
1014
|
+
const pwd = process.cwd();
|
|
1015
|
+
const { projectId, replicate } = getStoredProjectId(pwd);
|
|
1016
|
+
if (!projectId) {
|
|
1017
|
+
publishSpinner.fail(chalk_1.default.red("✗ Project ID not found. Please ensure hapico.config.json exists in the project directory."));
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1020
|
+
// Step 1: Push source code to main project and replicas
|
|
1021
|
+
const pushSuccess = await pushProject(publishSpinner, projectId);
|
|
1022
|
+
if (!pushSuccess) {
|
|
1023
|
+
return; // Stop if push to main project fails
|
|
1024
|
+
}
|
|
1025
|
+
// Push to replicated projects
|
|
1026
|
+
let allPushSuccess = true;
|
|
1027
|
+
if (replicate && Array.isArray(replicate) && replicate.length > 0) {
|
|
1028
|
+
publishSpinner.text = chalk_1.default.blue("Pushing to replicated projects...");
|
|
1029
|
+
for (const repId of replicate) {
|
|
1030
|
+
const success = await pushProject(publishSpinner, repId);
|
|
1031
|
+
if (!success) {
|
|
1032
|
+
allPushSuccess = false;
|
|
1033
|
+
console.warn(chalk_1.default.yellow(`⚠ Failed to push to replicated project ${repId}. Continuing...`));
|
|
1034
|
+
}
|
|
1035
|
+
else {
|
|
1036
|
+
console.log(chalk_1.default.green(`✓ Successfully pushed to replicated project ${repId}.`));
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
// Step 2: Publish main project
|
|
1041
|
+
const { accessToken } = getStoredToken();
|
|
1042
|
+
const apiUrl = "https://base.myworkbeast.com/api/views/publish";
|
|
1043
|
+
let allPublishSuccess = true;
|
|
1044
|
+
try {
|
|
1045
|
+
await axios_1.default.post(apiUrl, { view_id: parseInt(projectId, 10) }, {
|
|
1046
|
+
headers: {
|
|
1047
|
+
Authorization: `Bearer ${accessToken}`,
|
|
1048
|
+
"Content-Type": "application/json",
|
|
1049
|
+
},
|
|
1050
|
+
});
|
|
1051
|
+
publishSpinner.succeed(chalk_1.default.green(`Project ${projectId} published successfully!`));
|
|
1052
|
+
}
|
|
1053
|
+
catch (error) {
|
|
1054
|
+
allPublishSuccess = false;
|
|
1055
|
+
publishSpinner.fail(chalk_1.default.red(`✗ Error publishing project ${projectId}: ${error.message}`));
|
|
1056
|
+
}
|
|
1057
|
+
// Step 3: Publish replicated projects
|
|
1058
|
+
if (replicate && Array.isArray(replicate) && replicate.length > 0) {
|
|
1059
|
+
publishSpinner.text = chalk_1.default.blue("Publishing replicated projects...");
|
|
1060
|
+
for (const repId of replicate) {
|
|
1061
|
+
try {
|
|
1062
|
+
await axios_1.default.post(apiUrl, { view_id: parseInt(repId, 10) }, {
|
|
1063
|
+
headers: {
|
|
1064
|
+
Authorization: `Bearer ${accessToken}`,
|
|
1065
|
+
"Content-Type": "application/json",
|
|
1066
|
+
},
|
|
1067
|
+
});
|
|
1068
|
+
console.log(chalk_1.default.green(`✓ Successfully published replicated project ${repId}.`));
|
|
1069
|
+
}
|
|
1070
|
+
catch (error) {
|
|
1071
|
+
allPublishSuccess = false;
|
|
1072
|
+
console.warn(chalk_1.default.yellow(`⚠ Error publishing replicated project ${repId}: ${error.message}`));
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
if (allPushSuccess && allPublishSuccess) {
|
|
1077
|
+
publishSpinner.succeed(chalk_1.default.green("All projects published successfully!"));
|
|
1078
|
+
}
|
|
1079
|
+
else {
|
|
1080
|
+
publishSpinner.warn(chalk_1.default.yellow("Publishing completed with some errors."));
|
|
1081
|
+
}
|
|
1082
|
+
});
|
|
1083
|
+
commander_1.program.command("mirror").action(() => {
|
|
1084
|
+
console.log(chalk_1.default.cyan("🌐 Starting mirror mode..."));
|
|
1085
|
+
const pwd = process.cwd();
|
|
1086
|
+
const srcDir = path.join(pwd, "src");
|
|
1087
|
+
if (!fs.existsSync(srcDir)) {
|
|
1088
|
+
console.error(chalk_1.default.red("✗ Source directory 'src' does not exist. Please clone a project first."));
|
|
1089
|
+
return;
|
|
1090
|
+
}
|
|
1091
|
+
const fileManager = new FileManager(srcDir);
|
|
1092
|
+
const initialFiles = fileManager.listFiles();
|
|
1093
|
+
// Lấy danh sách file và viết ra 1 file .txt
|
|
1094
|
+
let content = ``;
|
|
1095
|
+
(0, lodash_1.map)(initialFiles, (file) => {
|
|
1096
|
+
content += `\`\`\`typescript
|
|
1097
|
+
// Path: ${file.path}
|
|
1098
|
+
${file.content}
|
|
1099
|
+
|
|
1100
|
+
\`\`\`
|
|
1101
|
+
`;
|
|
1102
|
+
});
|
|
1103
|
+
const outputFile = path.join(pwd, "hapico_files.txt");
|
|
1104
|
+
fs.writeFileSync(outputFile, content, { encoding: "utf8" });
|
|
1105
|
+
console.log(chalk_1.default.green(`✓ File list saved to ${outputFile}`));
|
|
1106
|
+
});
|
|
896
1107
|
commander_1.program.parse(process.argv);
|
package/bun.lock
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"lodash": "^4.17.21",
|
|
12
12
|
"open": "^10.2.0",
|
|
13
13
|
"ora": "^8.2.0",
|
|
14
|
+
"pako": "^2.1.0",
|
|
14
15
|
"prettier": "^3.6.2",
|
|
15
16
|
"qrcode-terminal": "^0.12.0",
|
|
16
17
|
"unzipper": "^0.12.3",
|
|
@@ -22,6 +23,7 @@
|
|
|
22
23
|
"@types/commander": "^2.12.5",
|
|
23
24
|
"@types/lodash": "^4.17.20",
|
|
24
25
|
"@types/node": "^24.1.0",
|
|
26
|
+
"@types/pako": "^2.0.4",
|
|
25
27
|
"@types/qrcode-terminal": "^0.12.2",
|
|
26
28
|
"@types/unzipper": "^0.10.11",
|
|
27
29
|
"@types/ws": "^8.18.1",
|
|
@@ -88,6 +90,8 @@
|
|
|
88
90
|
|
|
89
91
|
"@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="],
|
|
90
92
|
|
|
93
|
+
"@types/pako": ["@types/pako@2.0.4", "", {}, "sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw=="],
|
|
94
|
+
|
|
91
95
|
"@types/qrcode-terminal": ["@types/qrcode-terminal@0.12.2", "", {}, "sha512-v+RcIEJ+Uhd6ygSQ0u5YYY7ZM+la7GgPbs0V/7l/kFs2uO4S8BcIUEMoP7za4DNIqNnUD5npf0A/7kBhrCKG5Q=="],
|
|
92
96
|
|
|
93
97
|
"@types/unzipper": ["@types/unzipper@0.10.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-D25im2zjyMCcgL9ag6N46+wbtJBnXIr7SI4zHf9eJD2Dw2tEB5e+p5MYkrxKIVRscs5QV0EhtU9rgXSPx90oJg=="],
|
|
@@ -218,6 +222,8 @@
|
|
|
218
222
|
|
|
219
223
|
"ora": ["ora@8.2.0", "", { "dependencies": { "chalk": "^5.3.0", "cli-cursor": "^5.0.0", "cli-spinners": "^2.9.2", "is-interactive": "^2.0.0", "is-unicode-supported": "^2.0.0", "log-symbols": "^6.0.0", "stdin-discarder": "^0.2.2", "string-width": "^7.2.0", "strip-ansi": "^7.1.0" } }, "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw=="],
|
|
220
224
|
|
|
225
|
+
"pako": ["pako@2.1.0", "", {}, "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug=="],
|
|
226
|
+
|
|
221
227
|
"prettier": ["prettier@3.6.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="],
|
|
222
228
|
|
|
223
229
|
"process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="],
|