@bprotsyk/aso-core 2.0.32 → 2.0.34
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/lib/utils/server-util.d.ts +3 -1
- package/lib/utils/server-util.js +66 -7
- package/package.json +4 -2
- package/src/utils/server-util.ts +92 -23
|
@@ -29,7 +29,9 @@ export declare class ServerUtil {
|
|
|
29
29
|
setupCertbot(): Promise<boolean>;
|
|
30
30
|
setupSSL(email: string, host: string): Promise<void>;
|
|
31
31
|
refresh(): Promise<boolean>;
|
|
32
|
-
uploadDirectoryToSftp(localDir: string, remoteDir: string, sftpConfig:
|
|
32
|
+
uploadDirectoryToSftp(localDir: string, remoteDir: string, sftpConfig: any): Promise<void>;
|
|
33
33
|
generateNginxConfig(domain: string, rootPath: string): Promise<string>;
|
|
34
|
+
extractUploadedZip(name: string, file: any): Promise<void>;
|
|
35
|
+
ensureDirectoryExistsViaSSH(sshConfig: ISFTP, remoteDir: string): Promise<void>;
|
|
34
36
|
}
|
|
35
37
|
export {};
|
package/lib/utils/server-util.js
CHANGED
|
@@ -4,11 +4,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.ServerUtil = exports.PORT = exports.HOST = exports.PASSWORD = exports.IP = void 0;
|
|
7
|
+
const node_ssh_1 = require("node-ssh");
|
|
7
8
|
const child_process_1 = __importDefault(require("child_process"));
|
|
8
9
|
const ssh2_sftp_client_1 = __importDefault(require("ssh2-sftp-client"));
|
|
9
10
|
const fs_1 = __importDefault(require("fs"));
|
|
10
11
|
const mustache_1 = __importDefault(require("mustache"));
|
|
11
12
|
const path_1 = __importDefault(require("path"));
|
|
13
|
+
const unzipper_1 = __importDefault(require("unzipper"));
|
|
12
14
|
const { promisify } = require("util");
|
|
13
15
|
const execPromise = promisify(child_process_1.default.exec);
|
|
14
16
|
exports.IP = "185.123.53.227";
|
|
@@ -170,18 +172,22 @@ class ServerUtil {
|
|
|
170
172
|
try {
|
|
171
173
|
await sftp.connect(sftpConfig);
|
|
172
174
|
const items = fs_1.default.readdirSync(localDir, { withFileTypes: true });
|
|
173
|
-
// Перебираємо кожен елемент (файл чи папку)
|
|
174
175
|
for (const item of items) {
|
|
175
176
|
const localPath = path_1.default.join(localDir, item.name);
|
|
176
177
|
const remotePath = `${remoteDir}/${item.name}`;
|
|
177
178
|
if (item.isDirectory()) {
|
|
178
|
-
//
|
|
179
|
-
await sftp.mkdir(remotePath, true)
|
|
180
|
-
|
|
179
|
+
// Створення підкаталогу на віддаленому сервері
|
|
180
|
+
await sftp.mkdir(remotePath, true).catch(async (err) => {
|
|
181
|
+
if (err.code !== 4) { // Ігноруємо помилку "Directory already exists"
|
|
182
|
+
console.error(`Помилка під час створення папки: ${remotePath}`, err);
|
|
183
|
+
throw err;
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
// Рекурсивне копіювання вмісту директорії
|
|
181
187
|
await this.uploadDirectoryToSftp(localPath, remotePath, sftpConfig);
|
|
182
188
|
}
|
|
183
189
|
else {
|
|
184
|
-
//
|
|
190
|
+
// Завантаження файлу на віддалений сервер
|
|
185
191
|
await sftp.put(localPath, remotePath);
|
|
186
192
|
console.log(`Передано файл: ${localPath} -> ${remotePath}`);
|
|
187
193
|
}
|
|
@@ -192,15 +198,68 @@ class ServerUtil {
|
|
|
192
198
|
console.error('Помилка під час передавання файлів через SFTP:', err);
|
|
193
199
|
}
|
|
194
200
|
finally {
|
|
195
|
-
// Закриваємо підключення до SFTP
|
|
196
201
|
await sftp.end();
|
|
197
202
|
}
|
|
198
203
|
}
|
|
199
204
|
async generateNginxConfig(domain, rootPath) {
|
|
200
|
-
const templatePath = path_1.default.join(__dirname,
|
|
205
|
+
const templatePath = path_1.default.join(__dirname, "..", "templates", "nginx-template.conf");
|
|
201
206
|
const template = fs_1.default.readFileSync(templatePath, "utf8");
|
|
202
207
|
const config = mustache_1.default.render(template, { domain, rootPath });
|
|
203
208
|
return config;
|
|
204
209
|
}
|
|
210
|
+
async extractUploadedZip(name, file) {
|
|
211
|
+
const zipFilePath = file.path;
|
|
212
|
+
const outputDir = path_1.default.join(__dirname, '..', 'uploads', name);
|
|
213
|
+
console.log('Looking for ZIP file at:', zipFilePath);
|
|
214
|
+
console.log('Extracting to:', outputDir);
|
|
215
|
+
ensureDirectoryExistence(outputDir);
|
|
216
|
+
if (!fs_1.default.existsSync(zipFilePath)) {
|
|
217
|
+
console.error('Error: File not found:', zipFilePath);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
try {
|
|
221
|
+
await new Promise((resolve, reject) => {
|
|
222
|
+
fs_1.default.createReadStream(zipFilePath)
|
|
223
|
+
.pipe(unzipper_1.default.Parse())
|
|
224
|
+
.on('entry', (entry) => {
|
|
225
|
+
// Видаляємо перший сегмент шляху, щоб уникнути кореневої директорії
|
|
226
|
+
const entryPath = path_1.default.join(outputDir, ...entry.path.split('/').slice(1));
|
|
227
|
+
if (entry.type === 'Directory') {
|
|
228
|
+
ensureDirectoryExistence(entryPath);
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
ensureDirectoryExistence(path_1.default.dirname(entryPath));
|
|
232
|
+
entry.pipe(fs_1.default.createWriteStream(entryPath));
|
|
233
|
+
}
|
|
234
|
+
})
|
|
235
|
+
.on('close', resolve)
|
|
236
|
+
.on('error', reject);
|
|
237
|
+
});
|
|
238
|
+
console.log('File successfully extracted.');
|
|
239
|
+
}
|
|
240
|
+
catch (err) {
|
|
241
|
+
console.error('Error extracting file:', err);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
async ensureDirectoryExistsViaSSH(sshConfig, remoteDir) {
|
|
245
|
+
const ssh = new node_ssh_1.NodeSSH();
|
|
246
|
+
try {
|
|
247
|
+
await ssh.connect(sshConfig);
|
|
248
|
+
// Перевірка, чи існує директорія, і створення її, якщо вона не існує
|
|
249
|
+
const command = `if [ ! -d "${remoteDir}" ]; then mkdir -p "${remoteDir}"; fi`;
|
|
250
|
+
await ssh.execCommand(command);
|
|
251
|
+
console.log(`Перевірено або створено папку: ${remoteDir}`);
|
|
252
|
+
}
|
|
253
|
+
catch (err) {
|
|
254
|
+
console.error('Помилка під час перевірки або створення папки через SSH:', err);
|
|
255
|
+
throw err;
|
|
256
|
+
}
|
|
257
|
+
finally {
|
|
258
|
+
ssh.dispose();
|
|
259
|
+
}
|
|
260
|
+
}
|
|
205
261
|
}
|
|
206
262
|
exports.ServerUtil = ServerUtil;
|
|
263
|
+
function ensureDirectoryExistence(outputDir) {
|
|
264
|
+
throw new Error("Function not implemented.");
|
|
265
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bprotsyk/aso-core",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.34",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
5
|
"types": "lib/index.d.ts",
|
|
6
6
|
"scripts": {
|
|
@@ -37,7 +37,8 @@
|
|
|
37
37
|
"replace-in-file": "^7.0.1",
|
|
38
38
|
"sleep-promise": "^9.1.0",
|
|
39
39
|
"ssh2-sftp-client": "^11.0.0",
|
|
40
|
-
"styled-components": "^5.3.9"
|
|
40
|
+
"styled-components": "^5.3.9",
|
|
41
|
+
"unzipper": "^0.12.3"
|
|
41
42
|
},
|
|
42
43
|
"devDependencies": {
|
|
43
44
|
"@types/mustache": "^4.2.5",
|
|
@@ -45,6 +46,7 @@
|
|
|
45
46
|
"@types/react": "^18.2.14",
|
|
46
47
|
"@types/react-dom": "^18.3.0",
|
|
47
48
|
"@types/ssh2-sftp-client": "^9.0.4",
|
|
49
|
+
"@types/unzipper": "^0.10.10",
|
|
48
50
|
"copyfiles": "^2.4.1",
|
|
49
51
|
"typedoc": "^0.23.28",
|
|
50
52
|
"typescript": "^4.9.5"
|
package/src/utils/server-util.ts
CHANGED
|
@@ -4,6 +4,8 @@ import Client from "ssh2-sftp-client";
|
|
|
4
4
|
import fs from "fs";
|
|
5
5
|
import mustache from "mustache";
|
|
6
6
|
import path from "path";
|
|
7
|
+
import unzipper from 'unzipper';
|
|
8
|
+
|
|
7
9
|
const { promisify } = require("util");
|
|
8
10
|
const execPromise = promisify(ChildProcess.exec);
|
|
9
11
|
|
|
@@ -14,17 +16,17 @@ export let PORT = 56777;
|
|
|
14
16
|
|
|
15
17
|
interface FileObject {
|
|
16
18
|
name: string;
|
|
17
|
-
content: string;
|
|
19
|
+
content: string; // Це буде Base64 закодовані дані
|
|
18
20
|
}
|
|
19
21
|
|
|
20
|
-
interface ISFTP{
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
interface ISFTP {
|
|
23
|
+
host: string;
|
|
24
|
+
port: number;
|
|
25
|
+
username: string;
|
|
26
|
+
password: string;
|
|
25
27
|
}
|
|
26
28
|
|
|
27
|
-
export
|
|
29
|
+
export class ServerUtil {
|
|
28
30
|
DOMAIN_HOME = "/etc/nginx/sites-enabled";
|
|
29
31
|
|
|
30
32
|
ssh?: NodeSSH;
|
|
@@ -156,7 +158,7 @@ export class ServerUtil {
|
|
|
156
158
|
return true;
|
|
157
159
|
}
|
|
158
160
|
|
|
159
|
-
|
|
161
|
+
// checking if the ssl certificate exists
|
|
160
162
|
async domainNginxCertsExist(host: string): Promise<boolean> {
|
|
161
163
|
try {
|
|
162
164
|
let result = await this.exec(`ls /etc/letsencrypt/live/${host}`);
|
|
@@ -204,34 +206,35 @@ export class ServerUtil {
|
|
|
204
206
|
|
|
205
207
|
return true;
|
|
206
208
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
209
|
|
|
210
210
|
async uploadDirectoryToSftp(
|
|
211
211
|
localDir: string,
|
|
212
212
|
remoteDir: string,
|
|
213
|
-
sftpConfig:
|
|
213
|
+
sftpConfig: any
|
|
214
214
|
) {
|
|
215
215
|
const sftp = new Client();
|
|
216
216
|
|
|
217
217
|
try {
|
|
218
218
|
await sftp.connect(sftpConfig);
|
|
219
|
-
|
|
220
219
|
const items = fs.readdirSync(localDir, { withFileTypes: true });
|
|
221
220
|
|
|
222
|
-
// Перебираємо кожен елемент (файл чи папку)
|
|
223
221
|
for (const item of items) {
|
|
224
222
|
const localPath = path.join(localDir, item.name);
|
|
225
223
|
const remotePath = `${remoteDir}/${item.name}`;
|
|
226
224
|
|
|
227
225
|
if (item.isDirectory()) {
|
|
228
|
-
//
|
|
229
|
-
await sftp.mkdir(remotePath, true)
|
|
226
|
+
// Створення підкаталогу на віддаленому сервері
|
|
227
|
+
await sftp.mkdir(remotePath, true).catch(async (err) => {
|
|
228
|
+
if (err.code !== 4) { // Ігноруємо помилку "Directory already exists"
|
|
229
|
+
console.error(`Помилка під час створення папки: ${remotePath}`, err);
|
|
230
|
+
throw err;
|
|
231
|
+
}
|
|
232
|
+
});
|
|
230
233
|
|
|
231
|
-
//
|
|
234
|
+
// Рекурсивне копіювання вмісту директорії
|
|
232
235
|
await this.uploadDirectoryToSftp(localPath, remotePath, sftpConfig);
|
|
233
236
|
} else {
|
|
234
|
-
//
|
|
237
|
+
// Завантаження файлу на віддалений сервер
|
|
235
238
|
await sftp.put(localPath, remotePath);
|
|
236
239
|
console.log(`Передано файл: ${localPath} -> ${remotePath}`);
|
|
237
240
|
}
|
|
@@ -241,21 +244,87 @@ export class ServerUtil {
|
|
|
241
244
|
} catch (err) {
|
|
242
245
|
console.error('Помилка під час передавання файлів через SFTP:', err);
|
|
243
246
|
} finally {
|
|
244
|
-
// Закриваємо підключення до SFTP
|
|
245
247
|
await sftp.end();
|
|
246
248
|
}
|
|
247
249
|
}
|
|
248
|
-
|
|
249
250
|
|
|
250
251
|
async generateNginxConfig(domain: string, rootPath: string): Promise<string> {
|
|
251
|
-
const templatePath = path.join(
|
|
252
|
+
const templatePath = path.join(
|
|
253
|
+
__dirname,
|
|
254
|
+
"..",
|
|
255
|
+
"templates",
|
|
256
|
+
"nginx-template.conf"
|
|
257
|
+
);
|
|
252
258
|
const template = fs.readFileSync(templatePath, "utf8");
|
|
253
259
|
const config = mustache.render(template, { domain, rootPath });
|
|
254
260
|
return config;
|
|
255
261
|
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
|
|
259
262
|
|
|
260
263
|
|
|
264
|
+
async extractUploadedZip(name:string, file:any) {
|
|
265
|
+
const zipFilePath = file.path;
|
|
266
|
+
const outputDir = path.join(__dirname, '..', 'uploads', name);
|
|
267
|
+
|
|
268
|
+
console.log('Looking for ZIP file at:', zipFilePath);
|
|
269
|
+
console.log('Extracting to:', outputDir);
|
|
270
|
+
|
|
271
|
+
ensureDirectoryExistence(outputDir);
|
|
272
|
+
|
|
273
|
+
if (!fs.existsSync(zipFilePath)) {
|
|
274
|
+
console.error('Error: File not found:', zipFilePath);
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
try {
|
|
279
|
+
await new Promise((resolve, reject) => {
|
|
280
|
+
fs.createReadStream(zipFilePath)
|
|
281
|
+
.pipe(unzipper.Parse())
|
|
282
|
+
.on('entry', (entry) => {
|
|
283
|
+
// Видаляємо перший сегмент шляху, щоб уникнути кореневої директорії
|
|
284
|
+
const entryPath = path.join(
|
|
285
|
+
outputDir,
|
|
286
|
+
...entry.path.split('/').slice(1)
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
if (entry.type === 'Directory') {
|
|
290
|
+
ensureDirectoryExistence(entryPath);
|
|
291
|
+
} else {
|
|
292
|
+
ensureDirectoryExistence(path.dirname(entryPath));
|
|
293
|
+
entry.pipe(fs.createWriteStream(entryPath));
|
|
294
|
+
}
|
|
295
|
+
})
|
|
296
|
+
.on('close', resolve)
|
|
297
|
+
.on('error', reject);
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
console.log('File successfully extracted.');
|
|
301
|
+
} catch (err) {
|
|
302
|
+
console.error('Error extracting file:', err);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
async ensureDirectoryExistsViaSSH(sshConfig:ISFTP, remoteDir:string) {
|
|
307
|
+
const ssh = new NodeSSH();
|
|
308
|
+
|
|
309
|
+
try {
|
|
310
|
+
await ssh.connect(sshConfig);
|
|
311
|
+
|
|
312
|
+
// Перевірка, чи існує директорія, і створення її, якщо вона не існує
|
|
313
|
+
const command = `if [ ! -d "${remoteDir}" ]; then mkdir -p "${remoteDir}"; fi`;
|
|
314
|
+
await ssh.execCommand(command);
|
|
315
|
+
console.log(`Перевірено або створено папку: ${remoteDir}`);
|
|
316
|
+
} catch (err) {
|
|
317
|
+
console.error(
|
|
318
|
+
'Помилка під час перевірки або створення папки через SSH:',
|
|
319
|
+
err
|
|
320
|
+
);
|
|
321
|
+
throw err;
|
|
322
|
+
} finally {
|
|
323
|
+
ssh.dispose();
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
function ensureDirectoryExistence(outputDir: string) {
|
|
328
|
+
throw new Error("Function not implemented.");
|
|
329
|
+
}
|
|
261
330
|
|