@tothalex/nulljs 0.0.58 → 0.0.59
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/cli.js +152 -72
- package/package.json +4 -4
package/dist/cli.js
CHANGED
|
@@ -53,6 +53,10 @@ var buildUrl = (path, options) => {
|
|
|
53
53
|
var init_deployments = () => {};
|
|
54
54
|
|
|
55
55
|
// ../../packages/api/actions/deploy.ts
|
|
56
|
+
import { mkdtempSync, writeFileSync, rmSync } from "fs";
|
|
57
|
+
import { tmpdir } from "os";
|
|
58
|
+
import { join } from "path";
|
|
59
|
+
var {$ } = globalThis.Bun;
|
|
56
60
|
var getMimeType = (fileName) => {
|
|
57
61
|
if (fileName.endsWith(".js"))
|
|
58
62
|
return "application/javascript";
|
|
@@ -79,15 +83,45 @@ var getMimeType = (fileName) => {
|
|
|
79
83
|
const dataString = sortedBlobs.map((b) => `${b.fileName}:${b.size}:${b.type}`).join("|");
|
|
80
84
|
const sign = await crypto.subtle.sign("Ed25519", privateKey, Buffer.from(dataString));
|
|
81
85
|
return btoa(String.fromCharCode(...new Uint8Array(sign)));
|
|
86
|
+
}, postWithCurl = async (url, signature, assets2) => {
|
|
87
|
+
const tempDir = mkdtempSync(join(tmpdir(), "nulljs-deploy-"));
|
|
88
|
+
try {
|
|
89
|
+
const formParts = [];
|
|
90
|
+
for (const asset of assets2) {
|
|
91
|
+
const filePath = join(tempDir, asset.fileName);
|
|
92
|
+
writeFileSync(filePath, asset.code);
|
|
93
|
+
const mime = getMimeType(asset.fileName);
|
|
94
|
+
formParts.push(`-F`);
|
|
95
|
+
formParts.push(`${asset.fileName}=@${filePath};type=${mime}`);
|
|
96
|
+
}
|
|
97
|
+
const result = await $`curl -s -w '\n%{http_code}' -X POST -H "authorization: ${signature}" ${formParts} ${url}`.text();
|
|
98
|
+
const lines = result.trim().split(`
|
|
99
|
+
`);
|
|
100
|
+
const statusCode = parseInt(lines.pop() || "0", 10);
|
|
101
|
+
const body = lines.join(`
|
|
102
|
+
`);
|
|
103
|
+
return { ok: statusCode >= 200 && statusCode < 300, status: statusCode, text: body };
|
|
104
|
+
} finally {
|
|
105
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
106
|
+
}
|
|
82
107
|
}, createFunctionDeployment = async (deployment2, options) => {
|
|
83
108
|
const handler = deployment2.assets.find((asset) => asset.fileName === "handler.js");
|
|
84
109
|
if (!handler) {
|
|
85
110
|
throw new Error(`Handler not found for ${deployment2.name}`);
|
|
86
111
|
}
|
|
87
|
-
const {
|
|
112
|
+
const { blobs } = createFormDataWithInfo([handler]);
|
|
88
113
|
const signature = await createSignature(blobs, options.privateKey);
|
|
89
114
|
const baseUrl = options.url || "";
|
|
90
|
-
const
|
|
115
|
+
const url = `${baseUrl}/api/deployment`;
|
|
116
|
+
if (options.useCurl) {
|
|
117
|
+
const result = await postWithCurl(url, signature, [handler]);
|
|
118
|
+
if (!result.ok) {
|
|
119
|
+
throw new Error(`Deployment failed (${result.status}): ${result.text}`);
|
|
120
|
+
}
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const { form } = createFormDataWithInfo([handler]);
|
|
124
|
+
const response = await fetch(url, {
|
|
91
125
|
method: "POST",
|
|
92
126
|
headers: {
|
|
93
127
|
authorization: signature
|
|
@@ -99,10 +133,19 @@ var getMimeType = (fileName) => {
|
|
|
99
133
|
throw new Error(`Deployment failed (${response.status}): ${errorText}`);
|
|
100
134
|
}
|
|
101
135
|
}, createReactDeployment = async (deployment2, options) => {
|
|
102
|
-
const {
|
|
136
|
+
const { blobs } = createFormDataWithInfo(deployment2.assets);
|
|
103
137
|
const signature = await createSignature(blobs, options.privateKey);
|
|
104
138
|
const baseUrl = options.url || "";
|
|
105
|
-
const
|
|
139
|
+
const url = `${baseUrl}/api/deployment/react`;
|
|
140
|
+
if (options.useCurl) {
|
|
141
|
+
const result = await postWithCurl(url, signature, deployment2.assets);
|
|
142
|
+
if (!result.ok) {
|
|
143
|
+
throw new Error(`Deployment failed (${result.status}): ${result.text}`);
|
|
144
|
+
}
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const { form } = createFormDataWithInfo(deployment2.assets);
|
|
148
|
+
const response = await fetch(url, {
|
|
106
149
|
method: "POST",
|
|
107
150
|
headers: {
|
|
108
151
|
authorization: signature
|
|
@@ -114,6 +157,7 @@ var getMimeType = (fileName) => {
|
|
|
114
157
|
throw new Error(`Deployment failed (${response.status}): ${errorText}`);
|
|
115
158
|
}
|
|
116
159
|
};
|
|
160
|
+
var init_deploy = () => {};
|
|
117
161
|
|
|
118
162
|
// ../../packages/api/actions/invocations.ts
|
|
119
163
|
var init_invocations = () => {};
|
|
@@ -195,6 +239,7 @@ var createSignatureHeader = async (privateKey, props) => {
|
|
|
195
239
|
// ../../packages/api/actions/index.ts
|
|
196
240
|
var init_actions = __esm(() => {
|
|
197
241
|
init_deployments();
|
|
242
|
+
init_deploy();
|
|
198
243
|
init_invocations();
|
|
199
244
|
init_logs();
|
|
200
245
|
init_assets();
|
|
@@ -208,10 +253,10 @@ var init_api = __esm(() => {
|
|
|
208
253
|
});
|
|
209
254
|
|
|
210
255
|
// src/config/index.ts
|
|
211
|
-
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
212
|
-
import { join } from "path";
|
|
256
|
+
import { readFileSync, writeFileSync as writeFileSync2, existsSync, mkdirSync } from "fs";
|
|
257
|
+
import { join as join2 } from "path";
|
|
213
258
|
import chalk from "chalk";
|
|
214
|
-
var getLocalConfigDir = () =>
|
|
259
|
+
var getLocalConfigDir = () => join2(process.cwd(), ".nulljs"), getLocalConfigFile = () => join2(getLocalConfigDir(), "config.json"), ensureLocalConfigDir = () => {
|
|
215
260
|
const localDir = getLocalConfigDir();
|
|
216
261
|
if (!existsSync(localDir)) {
|
|
217
262
|
mkdirSync(localDir, { recursive: true });
|
|
@@ -229,7 +274,7 @@ var getLocalConfigDir = () => join(process.cwd(), ".nulljs"), getLocalConfigFile
|
|
|
229
274
|
}
|
|
230
275
|
}, writeConfigStore = (store) => {
|
|
231
276
|
ensureLocalConfigDir();
|
|
232
|
-
|
|
277
|
+
writeFileSync2(getLocalConfigFile(), JSON.stringify(store, null, 2));
|
|
233
278
|
}, readLocalConfig = () => {
|
|
234
279
|
const store = readConfigStore();
|
|
235
280
|
if (!store)
|
|
@@ -328,14 +373,14 @@ var init_config = () => {};
|
|
|
328
373
|
|
|
329
374
|
// src/lib/server.ts
|
|
330
375
|
import { existsSync as existsSync2, mkdirSync as mkdirSync2, realpathSync } from "fs";
|
|
331
|
-
import { join as
|
|
376
|
+
import { join as join3, dirname } from "path";
|
|
332
377
|
import chalk2 from "chalk";
|
|
333
378
|
var CLI_DIR, PLATFORMS, getPlatformKey = () => {
|
|
334
379
|
return `${process.platform}-${process.arch}`;
|
|
335
380
|
}, findProjectRoot = (startPath = process.cwd()) => {
|
|
336
381
|
let currentPath = startPath;
|
|
337
382
|
while (currentPath !== dirname(currentPath)) {
|
|
338
|
-
if (existsSync2(
|
|
383
|
+
if (existsSync2(join3(currentPath, "package.json"))) {
|
|
339
384
|
return currentPath;
|
|
340
385
|
}
|
|
341
386
|
currentPath = dirname(currentPath);
|
|
@@ -344,7 +389,7 @@ var CLI_DIR, PLATFORMS, getPlatformKey = () => {
|
|
|
344
389
|
}, findMonorepoRoot = (startPath = process.cwd()) => {
|
|
345
390
|
let currentPath = startPath;
|
|
346
391
|
while (currentPath !== dirname(currentPath)) {
|
|
347
|
-
if (existsSync2(
|
|
392
|
+
if (existsSync2(join3(currentPath, "Cargo.toml")) && existsSync2(join3(currentPath, "package.json"))) {
|
|
348
393
|
return currentPath;
|
|
349
394
|
}
|
|
350
395
|
currentPath = dirname(currentPath);
|
|
@@ -357,11 +402,11 @@ var CLI_DIR, PLATFORMS, getPlatformKey = () => {
|
|
|
357
402
|
}
|
|
358
403
|
const monorepoRoot = findMonorepoRoot() || findMonorepoRoot(CLI_DIR);
|
|
359
404
|
if (monorepoRoot) {
|
|
360
|
-
const debugBinary =
|
|
405
|
+
const debugBinary = join3(monorepoRoot, "target", "debug", "server");
|
|
361
406
|
if (existsSync2(debugBinary)) {
|
|
362
407
|
return { path: debugBinary, source: "local-debug" };
|
|
363
408
|
}
|
|
364
|
-
const releaseBinary =
|
|
409
|
+
const releaseBinary = join3(monorepoRoot, "target", "release", "server");
|
|
365
410
|
if (existsSync2(releaseBinary)) {
|
|
366
411
|
return { path: releaseBinary, source: "local-release" };
|
|
367
412
|
}
|
|
@@ -384,7 +429,7 @@ var CLI_DIR, PLATFORMS, getPlatformKey = () => {
|
|
|
384
429
|
args.push("--api-port", "3000");
|
|
385
430
|
args.push("--gateway-port", "3001");
|
|
386
431
|
const projectRoot = findProjectRoot();
|
|
387
|
-
const cloudPath =
|
|
432
|
+
const cloudPath = join3(projectRoot, ".nulljs");
|
|
388
433
|
args.push("--cloud-path", cloudPath);
|
|
389
434
|
if (!existsSync2(cloudPath)) {
|
|
390
435
|
mkdirSync2(cloudPath, { recursive: true });
|
|
@@ -648,7 +693,7 @@ var deployedHashes, clearDeployCache = () => {
|
|
|
648
693
|
deployedHashes.clear();
|
|
649
694
|
}, hashCode = (code) => {
|
|
650
695
|
return Bun.hash(code).toString(16);
|
|
651
|
-
}, deployFunction = async (filePath, privateKey, apiUrl) => {
|
|
696
|
+
}, deployFunction = async (filePath, privateKey, apiUrl, useCurl) => {
|
|
652
697
|
const fileName = basename2(filePath);
|
|
653
698
|
const result = await build(functionConfig(filePath));
|
|
654
699
|
const handler = result.output.find((output) => output.type === "chunk" && output.fileName === "handler.js");
|
|
@@ -660,10 +705,10 @@ var deployedHashes, clearDeployCache = () => {
|
|
|
660
705
|
if (lastHash === hash) {
|
|
661
706
|
return { deployed: false, skipped: true };
|
|
662
707
|
}
|
|
663
|
-
await createFunctionDeployment({ name: fileName, assets: [{ fileName: "handler.js", code: handler.code }] }, { privateKey, url: apiUrl });
|
|
708
|
+
await createFunctionDeployment({ name: fileName, assets: [{ fileName: "handler.js", code: handler.code }] }, { privateKey, url: apiUrl, useCurl });
|
|
664
709
|
deployedHashes.set(filePath, hash);
|
|
665
710
|
return { deployed: true, skipped: false };
|
|
666
|
-
}, deployReact = async (filePath, privateKey, apiUrl) => {
|
|
711
|
+
}, deployReact = async (filePath, privateKey, apiUrl, useCurl) => {
|
|
667
712
|
const fileName = basename2(filePath);
|
|
668
713
|
const result = await build(spaClientConfig(filePath));
|
|
669
714
|
const assets3 = [];
|
|
@@ -680,11 +725,11 @@ var deployedHashes, clearDeployCache = () => {
|
|
|
680
725
|
if (lastHash === hash) {
|
|
681
726
|
return { deployed: false, skipped: true };
|
|
682
727
|
}
|
|
683
|
-
await createReactDeployment({ name: fileName, assets: assets3 }, { privateKey, url: apiUrl });
|
|
728
|
+
await createReactDeployment({ name: fileName, assets: assets3 }, { privateKey, url: apiUrl, useCurl });
|
|
684
729
|
deployedHashes.set(filePath, hash);
|
|
685
730
|
return { deployed: true, skipped: false };
|
|
686
731
|
};
|
|
687
|
-
var
|
|
732
|
+
var init_deploy2 = __esm(() => {
|
|
688
733
|
init_api();
|
|
689
734
|
init_bundle();
|
|
690
735
|
deployedHashes = new Map;
|
|
@@ -693,19 +738,19 @@ var init_deploy = __esm(() => {
|
|
|
693
738
|
// src/lib/watcher.ts
|
|
694
739
|
import { watch, existsSync as existsSync3 } from "fs";
|
|
695
740
|
import { readdir } from "fs/promises";
|
|
696
|
-
import { join as
|
|
741
|
+
import { join as join4 } from "path";
|
|
697
742
|
import { build as build2 } from "vite";
|
|
698
743
|
var FUNCTION_DIRS, deployedHashes2, hashCode2 = (code) => {
|
|
699
744
|
return Bun.hash(code).toString(16);
|
|
700
745
|
}, findFunctionEntries = async (functionsPath) => {
|
|
701
746
|
const entries = [];
|
|
702
747
|
for (const dir of FUNCTION_DIRS) {
|
|
703
|
-
const dirPath =
|
|
748
|
+
const dirPath = join4(functionsPath, dir);
|
|
704
749
|
try {
|
|
705
750
|
const items = await readdir(dirPath, { withFileTypes: true });
|
|
706
751
|
for (const item of items) {
|
|
707
752
|
if (item.isFile() && (item.name.endsWith(".ts") || item.name.endsWith(".tsx"))) {
|
|
708
|
-
const fullPath =
|
|
753
|
+
const fullPath = join4(dirPath, item.name);
|
|
709
754
|
const name = `${dir}/${item.name.replace(/\.tsx?$/, "")}`;
|
|
710
755
|
entries.push({
|
|
711
756
|
name,
|
|
@@ -725,7 +770,7 @@ var FUNCTION_DIRS, deployedHashes2, hashCode2 = (code) => {
|
|
|
725
770
|
console.error('No local configuration found. Run "nulljs dev" first.');
|
|
726
771
|
return () => {};
|
|
727
772
|
}
|
|
728
|
-
const functionsPath =
|
|
773
|
+
const functionsPath = join4(srcPath, "function");
|
|
729
774
|
let entries = await findFunctionEntries(functionsPath);
|
|
730
775
|
let viteWatcher = null;
|
|
731
776
|
let isRestarting = false;
|
|
@@ -796,7 +841,7 @@ var FUNCTION_DIRS, deployedHashes2, hashCode2 = (code) => {
|
|
|
796
841
|
viteWatcher = await startViteWatcher();
|
|
797
842
|
const dirWatchers = [];
|
|
798
843
|
for (const dir of FUNCTION_DIRS) {
|
|
799
|
-
const dirPath =
|
|
844
|
+
const dirPath = join4(functionsPath, dir);
|
|
800
845
|
if (!existsSync3(dirPath))
|
|
801
846
|
continue;
|
|
802
847
|
try {
|
|
@@ -805,7 +850,7 @@ var FUNCTION_DIRS, deployedHashes2, hashCode2 = (code) => {
|
|
|
805
850
|
return;
|
|
806
851
|
if (!filename.endsWith(".ts") && !filename.endsWith(".tsx"))
|
|
807
852
|
return;
|
|
808
|
-
const fullPath =
|
|
853
|
+
const fullPath = join4(dirPath, filename);
|
|
809
854
|
const entryName = `${dir}/${filename.replace(/\.tsx?$/, "")}`;
|
|
810
855
|
const existingEntry = entries.find((e) => e.name === entryName);
|
|
811
856
|
const fileExists = existsSync3(fullPath);
|
|
@@ -832,14 +877,14 @@ var FUNCTION_DIRS, deployedHashes2, hashCode2 = (code) => {
|
|
|
832
877
|
}
|
|
833
878
|
const privateKey = await loadPrivateKey(config);
|
|
834
879
|
deployedHashes2.clear();
|
|
835
|
-
const functionsPath =
|
|
880
|
+
const functionsPath = join4(srcPath, "function");
|
|
836
881
|
const entries = await findFunctionEntries(functionsPath);
|
|
837
|
-
const reactEntry =
|
|
882
|
+
const reactEntry = join4(srcPath, "index.tsx");
|
|
838
883
|
const hasReact = existsSync3(reactEntry);
|
|
839
884
|
let total = entries.length + (hasReact ? 1 : 0);
|
|
840
885
|
let successful = 0;
|
|
841
886
|
let failed = 0;
|
|
842
|
-
const { deployFunction: deployFunction2, deployReact: deployReact2, clearDeployCache: clearDeployCache2 } = await Promise.resolve().then(() => (
|
|
887
|
+
const { deployFunction: deployFunction2, deployReact: deployReact2, clearDeployCache: clearDeployCache2 } = await Promise.resolve().then(() => (init_deploy2(), exports_deploy));
|
|
843
888
|
clearDeployCache2();
|
|
844
889
|
for (const entry of entries) {
|
|
845
890
|
try {
|
|
@@ -1685,9 +1730,9 @@ var registerDevCommand = (program) => {
|
|
|
1685
1730
|
// src/commands/deploy.ts
|
|
1686
1731
|
init_config();
|
|
1687
1732
|
init_bundle();
|
|
1688
|
-
|
|
1733
|
+
init_deploy2();
|
|
1689
1734
|
import chalk3 from "chalk";
|
|
1690
|
-
import { basename as basename4, resolve as resolve2, join as
|
|
1735
|
+
import { basename as basename4, resolve as resolve2, join as join5 } from "path";
|
|
1691
1736
|
import { existsSync as existsSync5 } from "fs";
|
|
1692
1737
|
import { readdir as readdir2 } from "fs/promises";
|
|
1693
1738
|
import * as p from "@clack/prompts";
|
|
@@ -1717,24 +1762,24 @@ var selectConfig = async () => {
|
|
|
1717
1762
|
};
|
|
1718
1763
|
var findAllDeployables = async (srcPath) => {
|
|
1719
1764
|
const functions = [];
|
|
1720
|
-
const functionsPath =
|
|
1765
|
+
const functionsPath = join5(srcPath, "function");
|
|
1721
1766
|
for (const dir of FUNCTION_DIRS2) {
|
|
1722
|
-
const dirPath =
|
|
1767
|
+
const dirPath = join5(functionsPath, dir);
|
|
1723
1768
|
try {
|
|
1724
1769
|
const items = await readdir2(dirPath, { withFileTypes: true });
|
|
1725
1770
|
for (const item of items) {
|
|
1726
1771
|
if (item.isFile() && (item.name.endsWith(".ts") || item.name.endsWith(".tsx"))) {
|
|
1727
|
-
functions.push(
|
|
1772
|
+
functions.push(join5(dirPath, item.name));
|
|
1728
1773
|
}
|
|
1729
1774
|
}
|
|
1730
1775
|
} catch {}
|
|
1731
1776
|
}
|
|
1732
|
-
const reactEntry =
|
|
1777
|
+
const reactEntry = join5(srcPath, "index.tsx");
|
|
1733
1778
|
const hasReact = existsSync5(reactEntry);
|
|
1734
1779
|
return { functions, reactEntry: hasReact ? reactEntry : null };
|
|
1735
1780
|
};
|
|
1736
1781
|
var deployAll = async (config, options) => {
|
|
1737
|
-
const srcPath =
|
|
1782
|
+
const srcPath = join5(process.cwd(), "src");
|
|
1738
1783
|
if (!existsSync5(srcPath)) {
|
|
1739
1784
|
console.error(chalk3.red("\u2717 No src directory found"));
|
|
1740
1785
|
process.exit(1);
|
|
@@ -1758,7 +1803,7 @@ var deployAll = async (config, options) => {
|
|
|
1758
1803
|
const fileName = basename4(filePath);
|
|
1759
1804
|
try {
|
|
1760
1805
|
console.log(chalk3.yellow("Bundling ") + chalk3.bold(fileName));
|
|
1761
|
-
const result = await deployFunction(filePath, privateKey, config.api);
|
|
1806
|
+
const result = await deployFunction(filePath, privateKey, config.api, options.curl);
|
|
1762
1807
|
if (result.skipped) {
|
|
1763
1808
|
console.log(chalk3.gray("\u2013 Skipped ") + chalk3.bold(fileName) + chalk3.gray(" (unchanged)"));
|
|
1764
1809
|
skipped++;
|
|
@@ -1775,7 +1820,7 @@ var deployAll = async (config, options) => {
|
|
|
1775
1820
|
const fileName = basename4(reactEntry);
|
|
1776
1821
|
try {
|
|
1777
1822
|
console.log(chalk3.yellow("Bundling React SPA ") + chalk3.bold(fileName));
|
|
1778
|
-
const result = await deployReact(reactEntry, privateKey, config.api);
|
|
1823
|
+
const result = await deployReact(reactEntry, privateKey, config.api, options.curl);
|
|
1779
1824
|
if (result.skipped) {
|
|
1780
1825
|
console.log(chalk3.gray("\u2013 Skipped ") + chalk3.bold(fileName) + chalk3.gray(" (unchanged)"));
|
|
1781
1826
|
skipped++;
|
|
@@ -1797,7 +1842,7 @@ var deployAll = async (config, options) => {
|
|
|
1797
1842
|
}
|
|
1798
1843
|
};
|
|
1799
1844
|
var registerDeployCommand = (program) => {
|
|
1800
|
-
program.command("deploy").description("Bundle and deploy functions and React SPAs").argument("[file]", "Path to a specific file (deploys all if omitted)").option("-e, --env <name>", "Use a specific config environment").option("-f, --force", "Force deploy even if unchanged").action(async (file, options) => {
|
|
1845
|
+
program.command("deploy").description("Bundle and deploy functions and React SPAs").argument("[file]", "Path to a specific file (deploys all if omitted)").option("-e, --env <name>", "Use a specific config environment").option("-f, --force", "Force deploy even if unchanged").option("--curl", "Use curl instead of fetch (workaround for macOS local network issues)").action(async (file, options) => {
|
|
1801
1846
|
let config;
|
|
1802
1847
|
if (options.env) {
|
|
1803
1848
|
config = getConfig(options.env);
|
|
@@ -1831,7 +1876,7 @@ var registerDeployCommand = (program) => {
|
|
|
1831
1876
|
try {
|
|
1832
1877
|
if (isReact(filePath)) {
|
|
1833
1878
|
console.log(chalk3.yellow("Bundling React SPA ") + chalk3.bold(fileName));
|
|
1834
|
-
const result = await deployReact(filePath, privateKey, config.api);
|
|
1879
|
+
const result = await deployReact(filePath, privateKey, config.api, options.curl);
|
|
1835
1880
|
if (result.skipped) {
|
|
1836
1881
|
console.log(chalk3.gray("\u2013 Skipped ") + chalk3.bold(fileName) + chalk3.gray(" (unchanged)"));
|
|
1837
1882
|
} else {
|
|
@@ -1839,7 +1884,7 @@ var registerDeployCommand = (program) => {
|
|
|
1839
1884
|
}
|
|
1840
1885
|
} else {
|
|
1841
1886
|
console.log(chalk3.yellow("Bundling ") + chalk3.bold(fileName));
|
|
1842
|
-
const result = await deployFunction(filePath, privateKey, config.api);
|
|
1887
|
+
const result = await deployFunction(filePath, privateKey, config.api, options.curl);
|
|
1843
1888
|
if (result.skipped) {
|
|
1844
1889
|
console.log(chalk3.gray("\u2013 Skipped ") + chalk3.bold(fileName) + chalk3.gray(" (unchanged)"));
|
|
1845
1890
|
} else {
|
|
@@ -2256,10 +2301,10 @@ Secret keys:`));
|
|
|
2256
2301
|
}));
|
|
2257
2302
|
};
|
|
2258
2303
|
// src/commands/host.ts
|
|
2259
|
-
import { existsSync as existsSync7, writeFileSync as
|
|
2260
|
-
import { join as
|
|
2261
|
-
import { execSync } from "child_process";
|
|
2304
|
+
import { existsSync as existsSync7, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync2 } from "fs";
|
|
2305
|
+
import { join as join6 } from "path";
|
|
2262
2306
|
import { homedir } from "os";
|
|
2307
|
+
var {$: $2 } = globalThis.Bun;
|
|
2263
2308
|
import chalk7 from "chalk";
|
|
2264
2309
|
var PLATFORMS2 = {
|
|
2265
2310
|
"linux-x64": "@tothalex/nulljs-linux-x64",
|
|
@@ -2289,8 +2334,8 @@ var generateProductionKeys = async () => {
|
|
|
2289
2334
|
const publicKey = btoa(String.fromCharCode(...new Uint8Array(publicKeyBuffer)));
|
|
2290
2335
|
return { privateKey, publicKey };
|
|
2291
2336
|
};
|
|
2292
|
-
var saveProductionConfig = (cloudPath, privateKey, publicKey) => {
|
|
2293
|
-
const configPath =
|
|
2337
|
+
var saveProductionConfig = async (cloudPath, privateKey, publicKey) => {
|
|
2338
|
+
const configPath = join6(cloudPath, "config.json");
|
|
2294
2339
|
const config = {
|
|
2295
2340
|
key: {
|
|
2296
2341
|
private: privateKey,
|
|
@@ -2298,8 +2343,8 @@ var saveProductionConfig = (cloudPath, privateKey, publicKey) => {
|
|
|
2298
2343
|
}
|
|
2299
2344
|
};
|
|
2300
2345
|
console.log(chalk7.blue("Saving production configuration..."));
|
|
2301
|
-
|
|
2302
|
-
|
|
2346
|
+
writeFileSync3(configPath, JSON.stringify(config, null, 2));
|
|
2347
|
+
await $2`chmod 600 ${configPath}`;
|
|
2303
2348
|
console.log(chalk7.green("Production configuration saved"));
|
|
2304
2349
|
};
|
|
2305
2350
|
var createSystemdService = (cloudPath, publicKey, serverBinPath, userName) => {
|
|
@@ -2337,14 +2382,15 @@ var ensureCloudDirectory = (cloudPath) => {
|
|
|
2337
2382
|
console.log(chalk7.gray(`Cloud directory already exists: ${cloudPath}`));
|
|
2338
2383
|
}
|
|
2339
2384
|
};
|
|
2340
|
-
var installSystemdService = (serviceContent) => {
|
|
2385
|
+
var installSystemdService = async (serviceContent) => {
|
|
2341
2386
|
const servicePath = "/etc/systemd/system/nulljs.service";
|
|
2342
2387
|
console.log(chalk7.blue("Installing systemd service..."));
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2388
|
+
writeFileSync3("/tmp/nulljs.service", serviceContent);
|
|
2389
|
+
await $2`sudo mv /tmp/nulljs.service ${servicePath}`;
|
|
2390
|
+
await $2`sudo systemctl daemon-reload`;
|
|
2391
|
+
await $2`sudo systemctl enable nulljs.service`;
|
|
2392
|
+
await $2`sudo systemctl start nulljs.service`;
|
|
2393
|
+
console.log(chalk7.green("Systemd service installed, enabled and started"));
|
|
2348
2394
|
};
|
|
2349
2395
|
var validateLinux = () => {
|
|
2350
2396
|
if (process.platform !== "linux") {
|
|
@@ -2360,7 +2406,7 @@ var validateServerBinary = (serverBinPath) => {
|
|
|
2360
2406
|
}
|
|
2361
2407
|
};
|
|
2362
2408
|
var checkExistingProductionConfig = (cloudPath) => {
|
|
2363
|
-
const configPath =
|
|
2409
|
+
const configPath = join6(cloudPath, "config.json");
|
|
2364
2410
|
if (!existsSync7(configPath)) {
|
|
2365
2411
|
return null;
|
|
2366
2412
|
}
|
|
@@ -2380,13 +2426,13 @@ var unhost = async (options = {}) => {
|
|
|
2380
2426
|
try {
|
|
2381
2427
|
console.log(chalk7.blue("Stopping NullJS service..."));
|
|
2382
2428
|
try {
|
|
2383
|
-
|
|
2429
|
+
await $2`sudo systemctl stop nulljs`;
|
|
2384
2430
|
console.log(chalk7.green("Service stopped"));
|
|
2385
2431
|
} catch {
|
|
2386
2432
|
console.log(chalk7.yellow("Service was not running"));
|
|
2387
2433
|
}
|
|
2388
2434
|
try {
|
|
2389
|
-
|
|
2435
|
+
await $2`sudo systemctl disable nulljs`;
|
|
2390
2436
|
console.log(chalk7.green("Service disabled"));
|
|
2391
2437
|
} catch {
|
|
2392
2438
|
console.log(chalk7.yellow("Service was not enabled"));
|
|
@@ -2394,15 +2440,15 @@ var unhost = async (options = {}) => {
|
|
|
2394
2440
|
const servicePath = "/etc/systemd/system/nulljs.service";
|
|
2395
2441
|
if (existsSync7(servicePath)) {
|
|
2396
2442
|
console.log(chalk7.blue("Removing systemd service file..."));
|
|
2397
|
-
|
|
2398
|
-
|
|
2443
|
+
await $2`sudo rm ${servicePath}`;
|
|
2444
|
+
await $2`sudo systemctl daemon-reload`;
|
|
2399
2445
|
console.log(chalk7.green("Systemd service file removed"));
|
|
2400
2446
|
}
|
|
2401
2447
|
if (!options.keepData) {
|
|
2402
|
-
const defaultCloudPath =
|
|
2448
|
+
const defaultCloudPath = join6(homedir(), ".nulljs");
|
|
2403
2449
|
if (existsSync7(defaultCloudPath)) {
|
|
2404
2450
|
console.log(chalk7.blue("Removing cloud directory..."));
|
|
2405
|
-
|
|
2451
|
+
await $2`rm -rf ${defaultCloudPath}`;
|
|
2406
2452
|
console.log(chalk7.green("Cloud directory removed"));
|
|
2407
2453
|
} else {
|
|
2408
2454
|
console.log(chalk7.gray("Cloud directory does not exist"));
|
|
@@ -2422,9 +2468,42 @@ var unhost = async (options = {}) => {
|
|
|
2422
2468
|
process.exit(1);
|
|
2423
2469
|
}
|
|
2424
2470
|
};
|
|
2471
|
+
var update = async () => {
|
|
2472
|
+
validateLinux();
|
|
2473
|
+
const cloudPath = join6(homedir(), ".nulljs");
|
|
2474
|
+
const servicePath = "/etc/systemd/system/nulljs.service";
|
|
2475
|
+
if (!existsSync7(servicePath)) {
|
|
2476
|
+
console.log(chalk7.red("NullJS service is not installed."));
|
|
2477
|
+
console.log(chalk7.gray('Run "nulljs host" to set up hosting first.'));
|
|
2478
|
+
process.exit(1);
|
|
2479
|
+
}
|
|
2480
|
+
const productionConfig = checkExistingProductionConfig(cloudPath);
|
|
2481
|
+
if (!productionConfig) {
|
|
2482
|
+
console.log(chalk7.red("Production config not found."));
|
|
2483
|
+
console.log(chalk7.gray('Run "nulljs host" to set up hosting first.'));
|
|
2484
|
+
process.exit(1);
|
|
2485
|
+
}
|
|
2486
|
+
console.log(chalk7.blue("Updating NullJS service..."));
|
|
2487
|
+
console.log(chalk7.blue("Stopping service..."));
|
|
2488
|
+
try {
|
|
2489
|
+
await $2`sudo systemctl stop nulljs`;
|
|
2490
|
+
} catch {}
|
|
2491
|
+
const serverBinPath = getServerBinPath();
|
|
2492
|
+
validateServerBinary(serverBinPath);
|
|
2493
|
+
console.log(chalk7.gray(` Binary path: ${serverBinPath}`));
|
|
2494
|
+
const currentUser = process.env.USER || process.env.USERNAME || "root";
|
|
2495
|
+
const serviceContent = createSystemdService(cloudPath, productionConfig.key.public, serverBinPath, currentUser);
|
|
2496
|
+
console.log(chalk7.blue("Updating systemd service..."));
|
|
2497
|
+
writeFileSync3("/tmp/nulljs.service", serviceContent);
|
|
2498
|
+
await $2`sudo mv /tmp/nulljs.service ${servicePath}`;
|
|
2499
|
+
await $2`sudo systemctl daemon-reload`;
|
|
2500
|
+
console.log(chalk7.blue("Starting service..."));
|
|
2501
|
+
await $2`sudo systemctl start nulljs.service`;
|
|
2502
|
+
console.log(chalk7.green("NullJS service updated and restarted!"));
|
|
2503
|
+
};
|
|
2425
2504
|
var host = async (cloudPath) => {
|
|
2426
2505
|
validateLinux();
|
|
2427
|
-
const resolvedCloudPath = cloudPath ? cloudPath.startsWith("/") ? cloudPath :
|
|
2506
|
+
const resolvedCloudPath = cloudPath ? cloudPath.startsWith("/") ? cloudPath : join6(process.cwd(), cloudPath) : join6(homedir(), ".nulljs");
|
|
2428
2507
|
console.log(chalk7.blue("Setting up NullJS production hosting..."));
|
|
2429
2508
|
console.log(chalk7.gray(` Cloud path: ${resolvedCloudPath}`));
|
|
2430
2509
|
const serverBinPath = getServerBinPath();
|
|
@@ -2435,7 +2514,7 @@ var host = async (cloudPath) => {
|
|
|
2435
2514
|
let productionConfig = checkExistingProductionConfig(resolvedCloudPath);
|
|
2436
2515
|
if (!productionConfig) {
|
|
2437
2516
|
const { privateKey, publicKey } = await generateProductionKeys();
|
|
2438
|
-
saveProductionConfig(resolvedCloudPath, privateKey, publicKey);
|
|
2517
|
+
await saveProductionConfig(resolvedCloudPath, privateKey, publicKey);
|
|
2439
2518
|
productionConfig = { key: { private: privateKey, public: publicKey } };
|
|
2440
2519
|
console.log(chalk7.green("Production keys generated"));
|
|
2441
2520
|
console.log(chalk7.blue("Production Public Key:"));
|
|
@@ -2446,32 +2525,33 @@ var host = async (cloudPath) => {
|
|
|
2446
2525
|
console.log(chalk7.gray(productionConfig.key.public));
|
|
2447
2526
|
}
|
|
2448
2527
|
const serviceContent = createSystemdService(resolvedCloudPath, productionConfig.key.public, serverBinPath, currentUser);
|
|
2449
|
-
installSystemdService(serviceContent);
|
|
2528
|
+
await installSystemdService(serviceContent);
|
|
2450
2529
|
console.log(chalk7.green("NullJS hosting setup complete!"));
|
|
2451
2530
|
console.log("");
|
|
2452
2531
|
console.log(chalk7.blue("Service management commands:"));
|
|
2453
|
-
console.log(chalk7.gray(" Start: "), chalk7.white("sudo systemctl start nulljs"));
|
|
2454
2532
|
console.log(chalk7.gray(" Stop: "), chalk7.white("sudo systemctl stop nulljs"));
|
|
2533
|
+
console.log(chalk7.gray(" Restart: "), chalk7.white("sudo systemctl restart nulljs"));
|
|
2455
2534
|
console.log(chalk7.gray(" Status: "), chalk7.white("sudo systemctl status nulljs"));
|
|
2456
2535
|
console.log(chalk7.gray(" Logs: "), chalk7.white("sudo journalctl -u nulljs -f"));
|
|
2457
|
-
console.log("");
|
|
2458
|
-
console.log(chalk7.yellow("Remember to start the service: sudo systemctl start nulljs"));
|
|
2459
2536
|
};
|
|
2460
2537
|
var registerHostCommand = (program) => {
|
|
2461
2538
|
const hostCmd = program.command("host").description("Set up NullJS production hosting with systemd").argument("[cloud-path]", "Path for cloud storage (default: ~/.nulljs)").action(async (cloudPath) => {
|
|
2462
2539
|
await host(cloudPath);
|
|
2463
2540
|
});
|
|
2541
|
+
hostCmd.command("update").description("Update NullJS service after package upgrade").action(async () => {
|
|
2542
|
+
await update();
|
|
2543
|
+
});
|
|
2464
2544
|
hostCmd.command("remove").description("Remove NullJS production hosting").option("--keep-data", "Keep cloud data directory").action(async (options) => {
|
|
2465
2545
|
await unhost(options);
|
|
2466
2546
|
});
|
|
2467
|
-
hostCmd.command("logs").description("View NullJS server logs").option("-n, --lines <number>", "Number of lines to show", "100").option("--no-follow", "Do not follow log output").action((options) => {
|
|
2547
|
+
hostCmd.command("logs").description("View NullJS server logs").option("-n, --lines <number>", "Number of lines to show", "100").option("--no-follow", "Do not follow log output").action(async (options) => {
|
|
2468
2548
|
validateLinux();
|
|
2469
|
-
const args = ["-u", "nulljs", "-n", options.lines];
|
|
2470
|
-
if (options.follow) {
|
|
2471
|
-
args.push("-f");
|
|
2472
|
-
}
|
|
2473
2549
|
try {
|
|
2474
|
-
|
|
2550
|
+
if (options.follow) {
|
|
2551
|
+
await $2`journalctl -u nulljs -n ${options.lines} -f`;
|
|
2552
|
+
} else {
|
|
2553
|
+
await $2`journalctl -u nulljs -n ${options.lines}`;
|
|
2554
|
+
}
|
|
2475
2555
|
} catch {}
|
|
2476
2556
|
});
|
|
2477
2557
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tothalex/nulljs",
|
|
3
3
|
"module": "dist/index.js",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.59",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"nulljs": "./dist/cli.js"
|
|
@@ -18,9 +18,9 @@
|
|
|
18
18
|
"typescript": "^5"
|
|
19
19
|
},
|
|
20
20
|
"optionalDependencies": {
|
|
21
|
-
"@tothalex/nulljs-darwin-arm64": "^0.0.
|
|
22
|
-
"@tothalex/nulljs-linux-arm64": "^0.0.
|
|
23
|
-
"@tothalex/nulljs-linux-x64": "^0.0.
|
|
21
|
+
"@tothalex/nulljs-darwin-arm64": "^0.0.81",
|
|
22
|
+
"@tothalex/nulljs-linux-arm64": "^0.0.81",
|
|
23
|
+
"@tothalex/nulljs-linux-x64": "^0.0.81"
|
|
24
24
|
},
|
|
25
25
|
"files": [
|
|
26
26
|
"dist"
|