javer-cli 1.0.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/LICENSE +21 -0
- package/README.md +154 -0
- package/index.js +444 -0
- package/lib/api.js +79 -0
- package/lib/bird.js +129 -0
- package/lib/config.js +43 -0
- package/lib/ui.js +366 -0
- package/lib/zip.js +174 -0
- package/package.json +39 -0
package/lib/zip.js
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/* JAVER-SIGNATURE-START
|
|
2
|
+
JAV
|
|
3
|
+
E RJ
|
|
4
|
+
A o VE
|
|
5
|
+
RJA VERJ
|
|
6
|
+
A V ERJA
|
|
7
|
+
V ER JA
|
|
8
|
+
V ER JA
|
|
9
|
+
VE RJA V
|
|
10
|
+
ER JAVE
|
|
11
|
+
RJ AVE
|
|
12
|
+
RJAVERJAV
|
|
13
|
+
E RJ
|
|
14
|
+
AVERJ AV
|
|
15
|
+
javer.pro — built in-house, not outsourced. signed: lemons
|
|
16
|
+
JAVER-SIGNATURE-END */
|
|
17
|
+
|
|
18
|
+
// A minimal ZIP writer, so `javer deploy` can send the current folder.
|
|
19
|
+
//
|
|
20
|
+
// Node ships zlib but no archive format, and this CLI has no dependencies and
|
|
21
|
+
// is not getting any — a hosting tool people install globally should not drag a
|
|
22
|
+
// tree of packages onto their machine. Shelling out to `zip` was the other
|
|
23
|
+
// option and it is not installed by default on most systems.
|
|
24
|
+
//
|
|
25
|
+
// So: deflate each file with zlib (ZIP method 8 is raw deflate, which
|
|
26
|
+
// deflateRawSync produces directly) and write the three structures the format
|
|
27
|
+
// needs — a local header per file, a central directory, and an end record.
|
|
28
|
+
const fs = require('fs');
|
|
29
|
+
const path = require('path');
|
|
30
|
+
const zlib = require('zlib');
|
|
31
|
+
|
|
32
|
+
let TABLE = null;
|
|
33
|
+
const crcTable = () => {
|
|
34
|
+
if (TABLE) return TABLE;
|
|
35
|
+
TABLE = new Int32Array(256);
|
|
36
|
+
for (let n = 0; n < 256; n++) {
|
|
37
|
+
let c = n;
|
|
38
|
+
for (let k = 0; k < 8; k++) c = (c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1);
|
|
39
|
+
TABLE[n] = c;
|
|
40
|
+
}
|
|
41
|
+
return TABLE;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const crc32 = (buf) => {
|
|
45
|
+
const t = crcTable();
|
|
46
|
+
let c = -1;
|
|
47
|
+
for (let i = 0; i < buf.length; i++) c = t[(c ^ buf[i]) & 0xFF] ^ (c >>> 8);
|
|
48
|
+
return (c ^ -1) >>> 0;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// Anything here is either not the customer's source, or actively harmful to
|
|
52
|
+
// upload. node_modules especially: the server runs npm install itself, and
|
|
53
|
+
// including it would blow the upload limit on even a small project.
|
|
54
|
+
const SKIP_DIRS = new Set([
|
|
55
|
+
'node_modules', '.git', '.svn', '.hg', 'dist', 'build', '.next', '.nuxt',
|
|
56
|
+
'.cache', 'coverage', '.venv', 'venv', '__pycache__', '.idea', '.vscode',
|
|
57
|
+
'vendor', 'target', '.terraform'
|
|
58
|
+
]);
|
|
59
|
+
// .env is deliberate: environment variables belong in the panel, not baked
|
|
60
|
+
// into a deployment. Sending one would put live secrets in an upload and then
|
|
61
|
+
// leave them sitting in the app directory on disk.
|
|
62
|
+
const SKIP_FILES = new Set(['.DS_Store', 'Thumbs.db', '.env']);
|
|
63
|
+
const SKIP_EXT = new Set(['.log', '.zip', '.tar', '.gz', '.tgz', '.sqlite', '.db']);
|
|
64
|
+
|
|
65
|
+
// Walk a directory into a flat list of {name, abs} with POSIX-style names,
|
|
66
|
+
// which is what the ZIP format wants regardless of the host platform.
|
|
67
|
+
const collect = (root) => {
|
|
68
|
+
const out = [];
|
|
69
|
+
const walk = (dir, prefix) => {
|
|
70
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
71
|
+
const name = entry.name;
|
|
72
|
+
if (entry.isDirectory()) {
|
|
73
|
+
if (SKIP_DIRS.has(name) || name.startsWith('.git')) continue;
|
|
74
|
+
walk(path.join(dir, name), prefix ? `${prefix}/${name}` : name);
|
|
75
|
+
} else if (entry.isFile()) {
|
|
76
|
+
if (SKIP_FILES.has(name)) continue;
|
|
77
|
+
if (name.startsWith('.env.')) continue;
|
|
78
|
+
if (SKIP_EXT.has(path.extname(name).toLowerCase())) continue;
|
|
79
|
+
out.push({ name: prefix ? `${prefix}/${name}` : name, abs: path.join(dir, name) });
|
|
80
|
+
}
|
|
81
|
+
// Symlinks are skipped on purpose: following one can walk out of the
|
|
82
|
+
// project entirely, and storing it would be meaningless once extracted.
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
walk(root, '');
|
|
86
|
+
return out.sort((a, b) => a.name.localeCompare(b.name));
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// DOS time/date. The epoch is 1980 and there is no timezone; a file older than
|
|
90
|
+
// that clamps rather than writing a negative year the reader would reject.
|
|
91
|
+
const dosStamp = (mtime) => {
|
|
92
|
+
const d = mtime instanceof Date ? mtime : new Date(mtime);
|
|
93
|
+
const year = Math.max(1980, d.getFullYear());
|
|
94
|
+
return {
|
|
95
|
+
time: ((d.getHours() & 31) << 11) | ((d.getMinutes() & 63) << 5) | ((Math.floor(d.getSeconds() / 2)) & 31),
|
|
96
|
+
date: (((year - 1980) & 127) << 9) | (((d.getMonth() + 1) & 15) << 5) | (d.getDate() & 31)
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const zipDir = (root) => {
|
|
101
|
+
const files = collect(root);
|
|
102
|
+
if (!files.length) throw new Error('nothing to deploy — this folder has no files the platform would accept');
|
|
103
|
+
|
|
104
|
+
const locals = [];
|
|
105
|
+
const central = [];
|
|
106
|
+
let offset = 0;
|
|
107
|
+
let rawTotal = 0;
|
|
108
|
+
|
|
109
|
+
for (const f of files) {
|
|
110
|
+
const stat = fs.statSync(f.abs);
|
|
111
|
+
const data = fs.readFileSync(f.abs);
|
|
112
|
+
rawTotal += data.length;
|
|
113
|
+
const comp = zlib.deflateRawSync(data, { level: 9 });
|
|
114
|
+
const crc = crc32(data);
|
|
115
|
+
const nameBuf = Buffer.from(f.name, 'utf8');
|
|
116
|
+
const { time, date } = dosStamp(stat.mtime);
|
|
117
|
+
|
|
118
|
+
const lh = Buffer.alloc(30);
|
|
119
|
+
lh.writeUInt32LE(0x04034b50, 0);
|
|
120
|
+
lh.writeUInt16LE(20, 4); // version needed
|
|
121
|
+
lh.writeUInt16LE(0, 6); // flags
|
|
122
|
+
lh.writeUInt16LE(8, 8); // method: deflate
|
|
123
|
+
lh.writeUInt16LE(time, 10);
|
|
124
|
+
lh.writeUInt16LE(date, 12);
|
|
125
|
+
lh.writeUInt32LE(crc, 14);
|
|
126
|
+
lh.writeUInt32LE(comp.length, 18);
|
|
127
|
+
lh.writeUInt32LE(data.length, 22);
|
|
128
|
+
lh.writeUInt16LE(nameBuf.length, 26);
|
|
129
|
+
lh.writeUInt16LE(0, 28); // extra field length
|
|
130
|
+
locals.push(lh, nameBuf, comp);
|
|
131
|
+
|
|
132
|
+
const ch = Buffer.alloc(46);
|
|
133
|
+
ch.writeUInt32LE(0x02014b50, 0);
|
|
134
|
+
ch.writeUInt16LE(20, 4); // version made by
|
|
135
|
+
ch.writeUInt16LE(20, 6); // version needed
|
|
136
|
+
ch.writeUInt16LE(0, 8);
|
|
137
|
+
ch.writeUInt16LE(8, 10);
|
|
138
|
+
ch.writeUInt16LE(time, 12);
|
|
139
|
+
ch.writeUInt16LE(date, 14);
|
|
140
|
+
ch.writeUInt32LE(crc, 16);
|
|
141
|
+
ch.writeUInt32LE(comp.length, 20);
|
|
142
|
+
ch.writeUInt32LE(data.length, 24);
|
|
143
|
+
ch.writeUInt16LE(nameBuf.length, 28);
|
|
144
|
+
ch.writeUInt16LE(0, 30); // extra
|
|
145
|
+
ch.writeUInt16LE(0, 32); // comment
|
|
146
|
+
ch.writeUInt16LE(0, 34); // disk number
|
|
147
|
+
ch.writeUInt16LE(0, 36); // internal attrs
|
|
148
|
+
ch.writeUInt32LE(0, 38); // external attrs
|
|
149
|
+
ch.writeUInt32LE(offset, 42); // offset of local header
|
|
150
|
+
central.push(ch, nameBuf);
|
|
151
|
+
|
|
152
|
+
offset += lh.length + nameBuf.length + comp.length;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const centralBuf = Buffer.concat(central);
|
|
156
|
+
const eocd = Buffer.alloc(22);
|
|
157
|
+
eocd.writeUInt32LE(0x06054b50, 0);
|
|
158
|
+
eocd.writeUInt16LE(0, 4);
|
|
159
|
+
eocd.writeUInt16LE(0, 6);
|
|
160
|
+
eocd.writeUInt16LE(files.length, 8);
|
|
161
|
+
eocd.writeUInt16LE(files.length, 10);
|
|
162
|
+
eocd.writeUInt32LE(centralBuf.length, 12);
|
|
163
|
+
eocd.writeUInt32LE(offset, 16);
|
|
164
|
+
eocd.writeUInt16LE(0, 20);
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
buffer: Buffer.concat([...locals, centralBuf, eocd]),
|
|
168
|
+
fileCount: files.length,
|
|
169
|
+
rawBytes: rawTotal,
|
|
170
|
+
names: files.map((f) => f.name)
|
|
171
|
+
};
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
module.exports = { zipDir, collect, crc32 };
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "javer-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Javer Webhost from the command line — apps, VMs, databases and domains. No dependencies.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"javer",
|
|
7
|
+
"hosting",
|
|
8
|
+
"deploy",
|
|
9
|
+
"cli",
|
|
10
|
+
"paas",
|
|
11
|
+
"webhost",
|
|
12
|
+
"vps"
|
|
13
|
+
],
|
|
14
|
+
"homepage": "https://javer.pro/docs/cli",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://javer.pro/support"
|
|
17
|
+
},
|
|
18
|
+
"author": "Javer Studios",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"bin": {
|
|
21
|
+
"javer": "index.js"
|
|
22
|
+
},
|
|
23
|
+
"type": "commonjs",
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=18"
|
|
26
|
+
},
|
|
27
|
+
"os": [
|
|
28
|
+
"linux",
|
|
29
|
+
"darwin",
|
|
30
|
+
"win32"
|
|
31
|
+
],
|
|
32
|
+
"files": [
|
|
33
|
+
"index.js",
|
|
34
|
+
"lib/",
|
|
35
|
+
"README.md",
|
|
36
|
+
"LICENSE"
|
|
37
|
+
],
|
|
38
|
+
"private": false
|
|
39
|
+
}
|