@abtnode/analytics 1.16.13-beta-2eb54cbc
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 +13 -0
- package/README.md +3 -0
- package/goaccess.conf +4 -0
- package/index.js +130 -0
- package/package.json +44 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Copyright 2018-2020 ArcBlock
|
|
2
|
+
|
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License.
|
|
5
|
+
You may obtain a copy of the License at
|
|
6
|
+
|
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
|
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
See the License for the specific language governing permissions and
|
|
13
|
+
limitations under the License.
|
package/README.md
ADDED
package/goaccess.conf
ADDED
package/index.js
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getJsonResultPath = exports.getHtmlResultPath = exports.processLogByDate = exports.hasLogByDate = exports.processLogForGroup = exports.splitLogByGroup = exports.decompressLog = exports.getGoAccessBinary = void 0;
|
|
7
|
+
/* eslint-disable @typescript-eslint/comma-dangle */
|
|
8
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const tar_1 = __importDefault(require("tar"));
|
|
11
|
+
const shelljs_1 = __importDefault(require("shelljs"));
|
|
12
|
+
const debug_1 = __importDefault(require("debug"));
|
|
13
|
+
const child_process_1 = require("child_process");
|
|
14
|
+
const run_script_1 = __importDefault(require("@abtnode/util/lib/run-script"));
|
|
15
|
+
const GOACCESS_CONFIG = path_1.default.join(__dirname, 'goaccess.conf');
|
|
16
|
+
const ANALYTICS_DIR = '.analytics';
|
|
17
|
+
const debug = (0, debug_1.default)('@abtnode/analytics');
|
|
18
|
+
const runAsync = (command, options = {}) => new Promise((resolve, reject) => {
|
|
19
|
+
const startedAt = Date.now();
|
|
20
|
+
(0, child_process_1.exec)(command, { windowsHide: true, ...options }, (err, stdout, stderr) => {
|
|
21
|
+
debug('executed command', { command, time: Date.now() - startedAt, stderr });
|
|
22
|
+
if (err) {
|
|
23
|
+
reject(err);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
resolve(stdout);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
function getGoAccessBinary() {
|
|
31
|
+
const { stdout } = shelljs_1.default.which('goaccess') || { stdout: '' };
|
|
32
|
+
return stdout.trim();
|
|
33
|
+
}
|
|
34
|
+
exports.getGoAccessBinary = getGoAccessBinary;
|
|
35
|
+
async function decompressLog(source, tmpDir) {
|
|
36
|
+
fs_extra_1.default.ensureDirSync(tmpDir);
|
|
37
|
+
const dest = path_1.default.join(tmpDir, path_1.default.basename(source, '.gz'));
|
|
38
|
+
if (fs_extra_1.default.existsSync(dest)) {
|
|
39
|
+
fs_extra_1.default.removeSync(dest);
|
|
40
|
+
}
|
|
41
|
+
await tar_1.default.x({ file: source, C: tmpDir });
|
|
42
|
+
return dest;
|
|
43
|
+
}
|
|
44
|
+
exports.decompressLog = decompressLog;
|
|
45
|
+
function splitLogByGroup(filePath, dataDir, date, groups) {
|
|
46
|
+
return Promise.all(groups.map(async (group) => {
|
|
47
|
+
if (group.type === 'server') {
|
|
48
|
+
group.log = filePath;
|
|
49
|
+
return group;
|
|
50
|
+
}
|
|
51
|
+
const groupDir = path_1.default.join(dataDir, group.did, ANALYTICS_DIR);
|
|
52
|
+
fs_extra_1.default.ensureDirSync(groupDir);
|
|
53
|
+
const dest = path_1.default.join(groupDir, `${date}.log`);
|
|
54
|
+
if (fs_extra_1.default.existsSync(dest)) {
|
|
55
|
+
fs_extra_1.default.removeSync(dest);
|
|
56
|
+
}
|
|
57
|
+
const command = `grep -E "${group.hosts.join('|')}" ${filePath} > ${dest}`;
|
|
58
|
+
try {
|
|
59
|
+
await runAsync(command);
|
|
60
|
+
group.log = dest;
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
console.error('Failed to split log', { command, group, err });
|
|
64
|
+
}
|
|
65
|
+
return group;
|
|
66
|
+
}));
|
|
67
|
+
}
|
|
68
|
+
exports.splitLogByGroup = splitLogByGroup;
|
|
69
|
+
async function processLogForGroup(dataDir, date, group) {
|
|
70
|
+
try {
|
|
71
|
+
const groupDir = path_1.default.join(dataDir, group.did, ANALYTICS_DIR);
|
|
72
|
+
fs_extra_1.default.ensureDirSync(groupDir);
|
|
73
|
+
if (!group.log) {
|
|
74
|
+
return group;
|
|
75
|
+
}
|
|
76
|
+
if (!fs_extra_1.default.existsSync(group.log)) {
|
|
77
|
+
return group;
|
|
78
|
+
}
|
|
79
|
+
if (fs_extra_1.default.statSync(group.log).size === 0) {
|
|
80
|
+
fs_extra_1.default.removeSync(group.log);
|
|
81
|
+
return group;
|
|
82
|
+
}
|
|
83
|
+
// FIXME: @wangshijun does `LANG` work on any location
|
|
84
|
+
const env = { ...process.env, LANG: 'en_US.UTF-8' };
|
|
85
|
+
const command = `goaccess --no-query-string --no-html-last-updated --html-prefs='{"theme":"bright","perPage":10}' --log-file ${group.log} --config-file ${GOACCESS_CONFIG}`;
|
|
86
|
+
await Promise.all([
|
|
87
|
+
(0, run_script_1.default)(`${command} --output ${date}.json`, 'goaccess', { cwd: groupDir, env, silent: true }),
|
|
88
|
+
(0, run_script_1.default)(`${command} --output ${date}.html`, 'goaccess', { cwd: groupDir, env, silent: true }),
|
|
89
|
+
]);
|
|
90
|
+
fs_extra_1.default.removeSync(group.log);
|
|
91
|
+
const result = fs_extra_1.default.readJSONSync(path_1.default.join(groupDir, `${date}.json`));
|
|
92
|
+
group.result = result.general;
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
console.error('Failed to analyze log for group', { group, date });
|
|
96
|
+
}
|
|
97
|
+
return group;
|
|
98
|
+
}
|
|
99
|
+
exports.processLogForGroup = processLogForGroup;
|
|
100
|
+
function hasLogByDate(logDir, date) {
|
|
101
|
+
const compressed = path_1.default.join(logDir, `access-${date}.log.gz`);
|
|
102
|
+
return fs_extra_1.default.existsSync(compressed);
|
|
103
|
+
}
|
|
104
|
+
exports.hasLogByDate = hasLogByDate;
|
|
105
|
+
async function processLogByDate(logDir, tmpDir, dataDir, date, groups) {
|
|
106
|
+
const binary = getGoAccessBinary();
|
|
107
|
+
if (!binary) {
|
|
108
|
+
throw new Error('goaccess binary not found');
|
|
109
|
+
}
|
|
110
|
+
const compressed = path_1.default.join(logDir, `access-${date}.log.gz`);
|
|
111
|
+
if (fs_extra_1.default.existsSync(compressed) === false) {
|
|
112
|
+
return groups;
|
|
113
|
+
}
|
|
114
|
+
const decompressed = await decompressLog(compressed, tmpDir);
|
|
115
|
+
debug('decompressed', decompressed);
|
|
116
|
+
const splitted = await splitLogByGroup(decompressed, dataDir, date, groups);
|
|
117
|
+
debug('splitted', splitted);
|
|
118
|
+
const results = await Promise.all(splitted.map((group) => processLogForGroup(dataDir, date, group)));
|
|
119
|
+
fs_extra_1.default.removeSync(decompressed);
|
|
120
|
+
return results;
|
|
121
|
+
}
|
|
122
|
+
exports.processLogByDate = processLogByDate;
|
|
123
|
+
function getHtmlResultPath(dataDir, date, did) {
|
|
124
|
+
return path_1.default.join(dataDir, did, ANALYTICS_DIR, `${date}.html`);
|
|
125
|
+
}
|
|
126
|
+
exports.getHtmlResultPath = getHtmlResultPath;
|
|
127
|
+
function getJsonResultPath(dataDir, date, did) {
|
|
128
|
+
return path_1.default.join(dataDir, did, ANALYTICS_DIR, `${date}.json`);
|
|
129
|
+
}
|
|
130
|
+
exports.getJsonResultPath = getJsonResultPath;
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@abtnode/analytics",
|
|
3
|
+
"publishConfig": {
|
|
4
|
+
"access": "public"
|
|
5
|
+
},
|
|
6
|
+
"version": "1.16.13-beta-2eb54cbc",
|
|
7
|
+
"description": "Lib to help blocklet developers to get insights of their blocklet",
|
|
8
|
+
"main": "index.js",
|
|
9
|
+
"files": [
|
|
10
|
+
"index.js",
|
|
11
|
+
"goaccess.conf"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"lint": "eslint tests index.ts",
|
|
15
|
+
"lint:fix": "npm run lint -- --fix",
|
|
16
|
+
"test": "NODE_ENV=test node tools/jest.js",
|
|
17
|
+
"coverage": "npm run test -- --coverage",
|
|
18
|
+
"clean": "rm -f index.d.ts index.js",
|
|
19
|
+
"build": "npm run clean && tsc"
|
|
20
|
+
},
|
|
21
|
+
"author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
|
|
22
|
+
"license": "Apache-2.0",
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@abtnode/util": "1.16.13-beta-2eb54cbc",
|
|
25
|
+
"debug": "^4.3.4",
|
|
26
|
+
"fs-extra": "^10.1.0",
|
|
27
|
+
"shelljs": "^0.8.5",
|
|
28
|
+
"tar": "^6.1.11"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@abtnode/types": "1.16.13-beta-2eb54cbc",
|
|
32
|
+
"@arcblock/eslint-config-ts": "^0.2.4",
|
|
33
|
+
"@types/jest": "^29.2.0",
|
|
34
|
+
"@types/node": "^18.11.0",
|
|
35
|
+
"@typescript-eslint/eslint-plugin": "^5.40.1",
|
|
36
|
+
"@typescript-eslint/parser": "^5.40.1",
|
|
37
|
+
"eslint": "^8.25.0",
|
|
38
|
+
"jest": "^27.5.1",
|
|
39
|
+
"prettier": "^2.7.1",
|
|
40
|
+
"ts-jest": "^27.1.5",
|
|
41
|
+
"typescript": "^5.0.4"
|
|
42
|
+
},
|
|
43
|
+
"gitHead": "7adf96c2eb31762d8eb14b8121fce9865d6a458c"
|
|
44
|
+
}
|