@harperfast/harper-pro 5.0.0-beta.5 → 5.0.0-beta.7
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/core/bin/harper.js +1 -1
- package/core/bin/run.js +2 -2
- package/core/components/Application.ts +9 -5
- package/core/components/ApplicationScope.ts +3 -3
- package/core/components/componentLoader.ts +28 -15
- package/core/components/operations.js +13 -13
- package/core/components/operationsValidation.js +3 -3
- package/core/config/configUtils.js +20 -4
- package/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js +3 -2
- package/core/resources/Resource.ts +17 -6
- package/core/resources/RocksTransactionLogStore.ts +8 -1
- package/core/resources/analytics/metadata.ts +1 -0
- package/core/resources/analytics/read.ts +1 -1
- package/core/resources/analytics/write.ts +43 -2
- package/core/resources/databases.ts +24 -15
- package/core/security/jsLoader.ts +182 -91
- package/core/server/REST.ts +20 -11
- package/core/server/http.ts +3 -3
- package/core/server/itc/serverHandlers.js +1 -1
- package/core/static/defaultConfig.yaml +1 -1
- package/core/utility/hdbTerms.ts +1 -0
- package/core/utility/logging/harper_logger.js +44 -11
- package/core/utility/logging/readLog.js +2 -2
- package/core/utility/npmUtilities.js +2 -2
- package/core/validation/configValidator.js +16 -8
- package/core/validation/readLogValidator.js +2 -2
- package/dist/core/bin/harper.js +1 -1
- package/dist/core/bin/run.js +2 -2
- package/dist/core/bin/run.js.map +1 -1
- package/dist/core/components/Application.js +9 -4
- package/dist/core/components/Application.js.map +1 -1
- package/dist/core/components/ApplicationScope.js +2 -2
- package/dist/core/components/ApplicationScope.js.map +1 -1
- package/dist/core/components/componentLoader.js +30 -16
- package/dist/core/components/componentLoader.js.map +1 -1
- package/dist/core/components/operations.js +13 -13
- package/dist/core/components/operations.js.map +1 -1
- package/dist/core/components/operationsValidation.js +3 -3
- package/dist/core/components/operationsValidation.js.map +1 -1
- package/dist/core/config/configUtils.js +23 -3
- package/dist/core/config/configUtils.js.map +1 -1
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js +3 -2
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js.map +1 -1
- package/dist/core/resources/Resource.js +11 -4
- package/dist/core/resources/Resource.js.map +1 -1
- package/dist/core/resources/RocksTransactionLogStore.js +8 -1
- package/dist/core/resources/RocksTransactionLogStore.js.map +1 -1
- package/dist/core/resources/analytics/metadata.js +1 -0
- package/dist/core/resources/analytics/metadata.js.map +1 -1
- package/dist/core/resources/analytics/read.js +1 -1
- package/dist/core/resources/analytics/read.js.map +1 -1
- package/dist/core/resources/analytics/write.js +42 -0
- package/dist/core/resources/analytics/write.js.map +1 -1
- package/dist/core/resources/databases.js +19 -13
- package/dist/core/resources/databases.js.map +1 -1
- package/dist/core/security/jsLoader.js +167 -81
- package/dist/core/security/jsLoader.js.map +1 -1
- package/dist/core/server/REST.js +17 -10
- package/dist/core/server/REST.js.map +1 -1
- package/dist/core/server/http.js +2 -2
- package/dist/core/server/http.js.map +1 -1
- package/dist/core/server/itc/serverHandlers.js +1 -1
- package/dist/core/server/itc/serverHandlers.js.map +1 -1
- package/dist/core/utility/hdbTerms.js +1 -0
- package/dist/core/utility/hdbTerms.js.map +1 -1
- package/dist/core/utility/logging/harper_logger.js +47 -11
- package/dist/core/utility/logging/harper_logger.js.map +1 -1
- package/dist/core/utility/logging/readLog.js +2 -2
- package/dist/core/utility/logging/readLog.js.map +1 -1
- package/dist/core/utility/npmUtilities.js +2 -2
- package/dist/core/utility/npmUtilities.js.map +1 -1
- package/dist/core/validation/configValidator.js +18 -8
- package/dist/core/validation/configValidator.js.map +1 -1
- package/dist/core/validation/readLogValidator.js +2 -2
- package/dist/core/validation/readLogValidator.js.map +1 -1
- package/dist/replication/nodeIdMapping.js +1 -1
- package/dist/replication/nodeIdMapping.js.map +1 -1
- package/dist/replication/replicationConnection.js +1 -4
- package/dist/replication/replicationConnection.js.map +1 -1
- package/npm-shrinkwrap.json +506 -501
- package/package.json +7 -4
- package/replication/nodeIdMapping.ts +1 -1
- package/replication/replicationConnection.ts +9 -4
- package/static/defaultConfig.yaml +3 -0
- package/studio/web/assets/{index-ZhLX9iRh.js → index-ClD_q6ya.js} +5 -5
- package/studio/web/assets/{index-ZhLX9iRh.js.map → index-ClD_q6ya.js.map} +1 -1
- package/studio/web/assets/{index.lazy-DzgnppiN.js → index.lazy-CXzU1gVu.js} +2 -2
- package/studio/web/assets/{index.lazy-DzgnppiN.js.map → index.lazy-CXzU1gVu.js.map} +1 -1
- package/studio/web/assets/{profile-DJ9V18dX.js → profile-DCNVg5yY.js} +2 -2
- package/studio/web/assets/{profile-DJ9V18dX.js.map → profile-DCNVg5yY.js.map} +1 -1
- package/studio/web/assets/{status-DKZUoEUd.js → status-CoGlcjSB.js} +2 -2
- package/studio/web/assets/{status-DKZUoEUd.js.map → status-CoGlcjSB.js.map} +1 -1
- package/studio/web/index.html +1 -1
package/core/bin/harper.js
CHANGED
package/core/bin/run.js
CHANGED
|
@@ -266,7 +266,7 @@ function startupLog(portResolutions) {
|
|
|
266
266
|
? `, TCP: ${env.get(CONFIG_PARAMS.THREADS_DEBUG_PORT)}\n`
|
|
267
267
|
: '\n';
|
|
268
268
|
}
|
|
269
|
-
const logFilePath = path.join(
|
|
269
|
+
const logFilePath = path.join(configUtils.getConfigPath(CONFIG_PARAMS.LOGGING_ROOT), 'hdb.log');
|
|
270
270
|
logMsg += `${pad('Logging:')}level: ${env.get(CONFIG_PARAMS.LOGGING_LEVEL)}, location: ${
|
|
271
271
|
logFilePath + (env.get(CONFIG_PARAMS.LOGGING_STDSTREAMS) ? ', stdout/err' : '')
|
|
272
272
|
}\n`;
|
|
@@ -292,7 +292,7 @@ function startupLog(portResolutions) {
|
|
|
292
292
|
? `enabled for ${env.get(CONFIG_PARAMS.OPERATIONSAPI_NETWORK_CORSACCESSLIST)}`
|
|
293
293
|
: 'disabled'
|
|
294
294
|
}`;
|
|
295
|
-
logMsg += `, unix socket: ${
|
|
295
|
+
logMsg += `, unix socket: ${configUtils.getConfigPath(CONFIG_PARAMS.OPERATIONSAPI_NETWORK_DOMAINSOCKET)}\n`;
|
|
296
296
|
if (env.get(CONFIG_PARAMS.OPERATIONSAPI_NETWORK_PORT)) {
|
|
297
297
|
logMsg +=
|
|
298
298
|
pad('') +
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Logger } from '../utility/logging/logger.ts';
|
|
2
|
-
import { getConfigObj, getConfigValue } from '../config/configUtils.js';
|
|
2
|
+
import { getConfigObj, getConfigValue, getConfigPath } from '../config/configUtils.js';
|
|
3
3
|
import { CONFIG_PARAMS } from '../utility/hdbTerms.js';
|
|
4
4
|
import logger from '../utility/logging/harper_logger.js';
|
|
5
5
|
|
|
@@ -359,7 +359,8 @@ export async function installApplication(application: Application) {
|
|
|
359
359
|
application.name,
|
|
360
360
|
(application.packageManagerPrefix ? application.packageManagerPrefix + ' ' : '') + 'npm',
|
|
361
361
|
['install', '--force'],
|
|
362
|
-
application.dirPath
|
|
362
|
+
application.dirPath,
|
|
363
|
+
application.install?.timeout
|
|
363
364
|
);
|
|
364
365
|
|
|
365
366
|
// if it succeeds, return
|
|
@@ -401,7 +402,9 @@ export class Application {
|
|
|
401
402
|
this.payload = payload;
|
|
402
403
|
this.packageIdentifier = packageIdentifier && derivePackageIdentifier(packageIdentifier);
|
|
403
404
|
this.install = install;
|
|
404
|
-
|
|
405
|
+
const componentsRoot = getConfigPath(CONFIG_PARAMS.COMPONENTSROOT);
|
|
406
|
+
if (!componentsRoot) throw new Error('componentsRoot is not configured');
|
|
407
|
+
this.dirPath = join(componentsRoot, name);
|
|
405
408
|
this.logger = logger.loggerWithTag(name);
|
|
406
409
|
this.packageManagerPrefix = getConfigValue(CONFIG_PARAMS.APPLICATIONS_PACKAGEMANAGERPREFIX);
|
|
407
410
|
}
|
|
@@ -463,7 +466,8 @@ export async function installApplications() {
|
|
|
463
466
|
|
|
464
467
|
const config = getConfigObj();
|
|
465
468
|
|
|
466
|
-
const componentsRootDirPath =
|
|
469
|
+
const componentsRootDirPath = getConfigPath(CONFIG_PARAMS.COMPONENTSROOT);
|
|
470
|
+
if (!componentsRootDirPath) throw new Error('componentsRoot is not configured');
|
|
467
471
|
|
|
468
472
|
// Ensure component directory exists
|
|
469
473
|
await mkdir(componentsRootDirPath, { recursive: true });
|
|
@@ -554,7 +558,7 @@ export function nonInteractiveSpawn(
|
|
|
554
558
|
command: string,
|
|
555
559
|
args: string[],
|
|
556
560
|
cwd: string,
|
|
557
|
-
timeoutMs: number =
|
|
561
|
+
timeoutMs: number = 60 * 60 * 1000
|
|
558
562
|
): Promise<{ stdout: string; stderr: string; code: number }> {
|
|
559
563
|
return new Promise((resolve, reject) => {
|
|
560
564
|
logger
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Resources } from '../resources/Resources.ts';
|
|
2
2
|
import { type Server } from '../server/Server.ts';
|
|
3
|
-
import {
|
|
3
|
+
import { forComponent } from '../utility/logging/harper_logger.js';
|
|
4
4
|
import { scopedImport } from '../security/jsLoader.ts';
|
|
5
5
|
import * as env from '../utility/environment/environmentManager.js';
|
|
6
6
|
import { CONFIG_PARAMS } from '../utility/hdbTerms.ts';
|
|
@@ -23,8 +23,8 @@ export class ApplicationScope {
|
|
|
23
23
|
dependencyContainment?: boolean; // option to set this from the scope
|
|
24
24
|
verifyPath?: string;
|
|
25
25
|
config: any;
|
|
26
|
-
constructor(name: string, resources: Resources, server: Server, verifyPath?: string) {
|
|
27
|
-
this.logger =
|
|
26
|
+
constructor(name: string, resources: Resources, server: Server, isInternal = false, verifyPath?: string) {
|
|
27
|
+
this.logger = forComponent(name, !isInternal);
|
|
28
28
|
|
|
29
29
|
this.resources = resources;
|
|
30
30
|
this.server = server;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import { onMessageByType } from '../server/threads/manageThreads.js';
|
|
1
2
|
import { readdirSync, readFileSync, existsSync, realpathSync, mkdirSync, rmSync, symlinkSync } from 'node:fs';
|
|
2
3
|
import { join, basename, dirname } from 'node:path';
|
|
3
4
|
import { isMainThread } from 'node:worker_threads';
|
|
4
5
|
import { parseDocument } from 'yaml';
|
|
5
6
|
import * as env from '../utility/environment/environmentManager.js';
|
|
6
7
|
import { PACKAGE_ROOT } from '../utility/packageUtils.js';
|
|
7
|
-
import { CONFIG_PARAMS, HDB_ROOT_DIR_NAME } from '../utility/hdbTerms.ts';
|
|
8
|
+
import { CONFIG_PARAMS, HDB_ROOT_DIR_NAME, ITC_EVENT_TYPES } from '../utility/hdbTerms.ts';
|
|
8
9
|
import * as graphqlHandler from '../resources/graphql.ts';
|
|
9
10
|
import * as graphqlQueryHandler from '../server/graphqlQuerying.ts';
|
|
10
11
|
import * as roles from '../resources/roles.ts';
|
|
@@ -26,7 +27,7 @@ import { getHdbBasePath } from '../utility/environment/environmentManager.js';
|
|
|
26
27
|
import * as operationsServer from '../server/operationsServer.ts';
|
|
27
28
|
import * as auth from '../security/auth.ts';
|
|
28
29
|
import * as mqtt from '../server/mqtt.ts';
|
|
29
|
-
import { getConfigObj,
|
|
30
|
+
import { getConfigObj, getConfigPath } from '../config/configUtils.js';
|
|
30
31
|
import { createReuseportFd } from '../server/serverHelpers/Request.ts';
|
|
31
32
|
import { ErrorResource } from '../resources/ErrorResource.ts';
|
|
32
33
|
import { Scope } from './Scope.ts';
|
|
@@ -39,7 +40,7 @@ import { DEFAULT_CONFIG } from './DEFAULT_CONFIG.ts';
|
|
|
39
40
|
import { PluginModule } from './PluginModule.ts';
|
|
40
41
|
import { getEnvBuiltInComponents } from './Application.ts';
|
|
41
42
|
|
|
42
|
-
const CF_ROUTES_DIR =
|
|
43
|
+
const CF_ROUTES_DIR = getConfigPath(CONFIG_PARAMS.COMPONENTSROOT);
|
|
43
44
|
let loadedComponents = new Map<any, any>();
|
|
44
45
|
let watchesSetup;
|
|
45
46
|
let resources;
|
|
@@ -146,20 +147,28 @@ function symlinkHarperModule(componentDirectory: string) {
|
|
|
146
147
|
mkdirSync(nodeModulesDir);
|
|
147
148
|
}
|
|
148
149
|
|
|
149
|
-
// validate
|
|
150
|
+
// validate harper module
|
|
150
151
|
const harperModule = join(nodeModulesDir, 'harper');
|
|
151
152
|
if (existsSync(harperModule)) {
|
|
152
|
-
if (realpathSync(harperModule)
|
|
153
|
-
// if it exists
|
|
154
|
-
|
|
153
|
+
if (realpathSync(harperModule) !== realpathSync(PACKAGE_ROOT)) {
|
|
154
|
+
// if it exists but is incorrectly linked, fix it
|
|
155
|
+
rmSync(harperModule, { recursive: true, force: true });
|
|
156
|
+
// create link to harper module
|
|
157
|
+
symlinkSync(PACKAGE_ROOT, harperModule, 'dir');
|
|
155
158
|
}
|
|
156
|
-
|
|
157
|
-
//
|
|
158
|
-
|
|
159
|
+
} else {
|
|
160
|
+
// create link to harper module
|
|
161
|
+
symlinkSync(PACKAGE_ROOT, harperModule, 'dir');
|
|
162
|
+
}
|
|
163
|
+
// if there is a harperdb module, fix that too
|
|
164
|
+
const harperdbModule = join(nodeModulesDir, 'harperdb');
|
|
165
|
+
if (existsSync(harperdbModule) && realpathSync(harperdbModule) !== realpathSync(PACKAGE_ROOT)) {
|
|
166
|
+
// if it exists but is incorrectly linked, fix it
|
|
167
|
+
rmSync(harperdbModule, { recursive: true, force: true });
|
|
168
|
+
// create link to harper module
|
|
169
|
+
symlinkSync(PACKAGE_ROOT, harperdbModule, 'dir');
|
|
159
170
|
}
|
|
160
171
|
|
|
161
|
-
// create link to harperdb module
|
|
162
|
-
symlinkSync(PACKAGE_ROOT, harperModule, 'dir');
|
|
163
172
|
resolve();
|
|
164
173
|
} finally {
|
|
165
174
|
// finally release the lock
|
|
@@ -259,7 +268,7 @@ export async function loadComponent(
|
|
|
259
268
|
|
|
260
269
|
const {
|
|
261
270
|
providedLoadedComponents,
|
|
262
|
-
applicationScope = new ApplicationScope(basename(componentDirectory), resources, server),
|
|
271
|
+
applicationScope = new ApplicationScope(basename(componentDirectory), resources, server, options.isRoot),
|
|
263
272
|
isRoot,
|
|
264
273
|
autoReload,
|
|
265
274
|
appName,
|
|
@@ -310,7 +319,9 @@ export async function loadComponent(
|
|
|
310
319
|
// Initialize loading status for all components (applications and extensions)
|
|
311
320
|
componentLifecycle.loading(componentStatusName);
|
|
312
321
|
|
|
313
|
-
const subApplicationScope = isRoot
|
|
322
|
+
const subApplicationScope = isRoot
|
|
323
|
+
? new ApplicationScope(componentName, resources, server, TRUSTED_RESOURCE_PLUGINS.hasOwnProperty(componentName))
|
|
324
|
+
: applicationScope;
|
|
314
325
|
|
|
315
326
|
let extensionModule: any;
|
|
316
327
|
const pkg = componentConfig.package;
|
|
@@ -332,7 +343,7 @@ export async function loadComponent(
|
|
|
332
343
|
}
|
|
333
344
|
}
|
|
334
345
|
if (componentPath) {
|
|
335
|
-
subApplicationScope.verifyPath
|
|
346
|
+
subApplicationScope.verifyPath ??= componentPath;
|
|
336
347
|
if (!process.env.HARPER_SAFE_MODE) {
|
|
337
348
|
extensionModule = await loadComponent(componentPath, resources, origin, {
|
|
338
349
|
isRoot: false,
|
|
@@ -392,6 +403,8 @@ export async function loadComponent(
|
|
|
392
403
|
if (resources.isWorker && extensionModule.handleApplication) {
|
|
393
404
|
const scope = new Scope(appName || 'harper', componentName, componentDirectory, configPath, applicationScope);
|
|
394
405
|
|
|
406
|
+
onMessageByType(ITC_EVENT_TYPES.SHUTDOWN, () => scope.close());
|
|
407
|
+
|
|
395
408
|
await sequentiallyHandleApplication(scope, extensionModule);
|
|
396
409
|
|
|
397
410
|
// Mark component as loaded after successful handleApplication call
|
|
@@ -31,7 +31,7 @@ function customFunctionsStatus() {
|
|
|
31
31
|
try {
|
|
32
32
|
response = {
|
|
33
33
|
port: env.get(hdbTerms.CONFIG_PARAMS.HTTP_PORT),
|
|
34
|
-
directory:
|
|
34
|
+
directory: configUtils.getConfigPath(hdbTerms.CONFIG_PARAMS.COMPONENTSROOT),
|
|
35
35
|
is_enabled: true,
|
|
36
36
|
};
|
|
37
37
|
} catch (err) {
|
|
@@ -54,7 +54,7 @@ function customFunctionsStatus() {
|
|
|
54
54
|
function getCustomFunctions() {
|
|
55
55
|
log.trace(`getting custom api endpoints`);
|
|
56
56
|
let response = {};
|
|
57
|
-
const dir =
|
|
57
|
+
const dir = configUtils.getConfigPath(hdbTerms.CONFIG_PARAMS.COMPONENTSROOT);
|
|
58
58
|
|
|
59
59
|
try {
|
|
60
60
|
const projectFolders = fg.sync(normalize(`${dir}/*`), { onlyDirectories: true });
|
|
@@ -103,7 +103,7 @@ function getCustomFunction(req) {
|
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
log.trace(`getting custom api endpoint file content`);
|
|
106
|
-
const cfDir =
|
|
106
|
+
const cfDir = configUtils.getConfigPath(hdbTerms.CONFIG_PARAMS.COMPONENTSROOT);
|
|
107
107
|
const { project, type, file } = req;
|
|
108
108
|
const fileLocation = path.join(cfDir, project, type, file + '.js');
|
|
109
109
|
|
|
@@ -141,7 +141,7 @@ async function setCustomFunction(req) {
|
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
log.trace(`setting custom function file content`);
|
|
144
|
-
const cfDir =
|
|
144
|
+
const cfDir = configUtils.getConfigPath(hdbTerms.CONFIG_PARAMS.COMPONENTSROOT);
|
|
145
145
|
const { project, type, file, function_content } = req;
|
|
146
146
|
|
|
147
147
|
try {
|
|
@@ -181,7 +181,7 @@ async function dropCustomFunction(req) {
|
|
|
181
181
|
}
|
|
182
182
|
|
|
183
183
|
log.trace(`dropping custom function file`);
|
|
184
|
-
const cfDir =
|
|
184
|
+
const cfDir = configUtils.getConfigPath(hdbTerms.CONFIG_PARAMS.COMPONENTSROOT);
|
|
185
185
|
const { project, type, file } = req;
|
|
186
186
|
|
|
187
187
|
try {
|
|
@@ -216,7 +216,7 @@ async function addComponent(req) {
|
|
|
216
216
|
}
|
|
217
217
|
|
|
218
218
|
log.trace(`adding component`);
|
|
219
|
-
const cfDir =
|
|
219
|
+
const cfDir = configUtils.getConfigPath(hdbTerms.CONFIG_PARAMS.COMPONENTSROOT);
|
|
220
220
|
const { project, install_command, install_timeout } = req;
|
|
221
221
|
|
|
222
222
|
const template = req.template || 'https://github.com/harperdb/application-template';
|
|
@@ -264,7 +264,7 @@ async function dropCustomFunctionProject(req) {
|
|
|
264
264
|
}
|
|
265
265
|
|
|
266
266
|
log.trace(`dropping custom function project`);
|
|
267
|
-
const cfDir =
|
|
267
|
+
const cfDir = configUtils.getConfigPath(hdbTerms.CONFIG_PARAMS.COMPONENTSROOT);
|
|
268
268
|
const { project } = req;
|
|
269
269
|
|
|
270
270
|
let apps = env.get(hdbTerms.CONFIG_PARAMS.APPS);
|
|
@@ -318,7 +318,7 @@ async function packageComponent(req) {
|
|
|
318
318
|
throw handleHDBError(validation, validation.message, HTTP_STATUS_CODES.BAD_REQUEST);
|
|
319
319
|
}
|
|
320
320
|
|
|
321
|
-
const cfDir =
|
|
321
|
+
const cfDir = configUtils.getConfigPath(hdbTerms.CONFIG_PARAMS.COMPONENTSROOT);
|
|
322
322
|
const { project } = req;
|
|
323
323
|
log.trace(`packaging component`, project);
|
|
324
324
|
|
|
@@ -507,8 +507,8 @@ async function getComponents() {
|
|
|
507
507
|
}
|
|
508
508
|
};
|
|
509
509
|
|
|
510
|
-
const results = await walkDir(
|
|
511
|
-
name:
|
|
510
|
+
const results = await walkDir(configUtils.getConfigPath(hdbTerms.CONFIG_PARAMS.COMPONENTSROOT), {
|
|
511
|
+
name: configUtils.getConfigPath(hdbTerms.CONFIG_PARAMS.COMPONENTSROOT).split(path.sep).slice(-1).pop(),
|
|
512
512
|
entries: [],
|
|
513
513
|
});
|
|
514
514
|
for (let entry of results.entries) {
|
|
@@ -557,7 +557,7 @@ async function getComponentFile(req) {
|
|
|
557
557
|
throw handleHDBError(validation, validation.message, HTTP_STATUS_CODES.BAD_REQUEST);
|
|
558
558
|
}
|
|
559
559
|
|
|
560
|
-
const compRoot =
|
|
560
|
+
const compRoot = configUtils.getConfigPath(hdbTerms.CONFIG_PARAMS.COMPONENTSROOT);
|
|
561
561
|
const options = req.encoding ? { encoding: req.encoding } : { encoding: 'utf8' };
|
|
562
562
|
|
|
563
563
|
try {
|
|
@@ -588,7 +588,7 @@ async function setComponentFile(req) {
|
|
|
588
588
|
}
|
|
589
589
|
|
|
590
590
|
const options = req.encoding ? { encoding: req.encoding } : { encoding: 'utf8' };
|
|
591
|
-
const pathToComp = path.join(
|
|
591
|
+
const pathToComp = path.join(configUtils.getConfigPath(hdbTerms.CONFIG_PARAMS.COMPONENTSROOT), req.project, req.file);
|
|
592
592
|
if (req.payload !== undefined) {
|
|
593
593
|
await fs.ensureFile(pathToComp);
|
|
594
594
|
await fs.outputFile(pathToComp, req.payload, options);
|
|
@@ -613,7 +613,7 @@ async function dropComponent(req) {
|
|
|
613
613
|
|
|
614
614
|
const { project, file } = req;
|
|
615
615
|
const projectPath = req.file ? path.join(project, file) : project;
|
|
616
|
-
const pathToComponent = path.join(
|
|
616
|
+
const pathToComponent = path.join(configUtils.getConfigPath(hdbTerms.CONFIG_PARAMS.COMPONENTSROOT), projectPath);
|
|
617
617
|
|
|
618
618
|
const componentSymlink = path.join(env.get(hdbTerms.CONFIG_PARAMS.ROOTPATH), 'node_modules', project);
|
|
619
619
|
if (await fs.pathExists(componentSymlink)) {
|
|
@@ -4,9 +4,9 @@ const Joi = require('joi');
|
|
|
4
4
|
const fs = require('fs-extra');
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const validator = require('../validation/validationWrapper.js');
|
|
7
|
-
const envMangr = require('../utility/environment/environmentManager.js');
|
|
8
7
|
const hdbTerms = require('../utility/hdbTerms.ts');
|
|
9
8
|
const hdbLogger = require('../utility/logging/harper_logger.js');
|
|
9
|
+
const configUtils = require('../config/configUtils.js');
|
|
10
10
|
const { hdbErrors } = require('../utility/errors/hdbError.js');
|
|
11
11
|
const { HDB_ERROR_MSGS } = hdbErrors;
|
|
12
12
|
|
|
@@ -34,7 +34,7 @@ module.exports = {
|
|
|
34
34
|
*/
|
|
35
35
|
function checkProjectExists(checkExists, project, helpers) {
|
|
36
36
|
try {
|
|
37
|
-
const cfDir =
|
|
37
|
+
const cfDir = configUtils.getConfigPath(hdbTerms.CONFIG_PARAMS.COMPONENTSROOT);
|
|
38
38
|
const projectDir = path.join(cfDir, project);
|
|
39
39
|
|
|
40
40
|
if (!fs.existsSync(projectDir)) {
|
|
@@ -71,7 +71,7 @@ function checkFilePath(path, helpers) {
|
|
|
71
71
|
*/
|
|
72
72
|
function checkFileExists(project, type, file, helpers) {
|
|
73
73
|
try {
|
|
74
|
-
const cfDir =
|
|
74
|
+
const cfDir = configUtils.getConfigPath(hdbTerms.CONFIG_PARAMS.COMPONENTSROOT);
|
|
75
75
|
const filePath = path.join(cfDir, project, type, file + '.js');
|
|
76
76
|
if (!fs.existsSync(filePath)) {
|
|
77
77
|
return helpers.message(HDB_ERROR_MSGS.NO_FILE);
|
|
@@ -57,6 +57,7 @@ exports.deleteConfigFromFile = deleteConfigFromFile;
|
|
|
57
57
|
exports.getConfigObj = getConfigObj;
|
|
58
58
|
exports.resolvePath = resolvePath;
|
|
59
59
|
exports.getFlatConfigObj = getFlatConfigObj;
|
|
60
|
+
exports.getConfigPath = getConfigPath;
|
|
60
61
|
|
|
61
62
|
function resolvePath(relativePath) {
|
|
62
63
|
if (relativePath?.startsWith('~/')) {
|
|
@@ -70,7 +71,23 @@ function resolvePath(relativePath) {
|
|
|
70
71
|
return relativePath;
|
|
71
72
|
}
|
|
72
73
|
}
|
|
73
|
-
|
|
74
|
+
/**
|
|
75
|
+
* Get a config value and resolve it as a path relative to rootPath.
|
|
76
|
+
* Use this for any config param that represents a file/directory path.
|
|
77
|
+
* @param param
|
|
78
|
+
*/
|
|
79
|
+
function getConfigPath(param) {
|
|
80
|
+
const env = require('../utility/environment/environmentManager.js');
|
|
81
|
+
const value = env.get(param);
|
|
82
|
+
if (!value || typeof value !== 'string') return value;
|
|
83
|
+
if (value.startsWith('~/')) {
|
|
84
|
+
return path.join(hdbUtils.getHomeDir(), value.slice(1));
|
|
85
|
+
}
|
|
86
|
+
if (path.isAbsolute(value)) return value;
|
|
87
|
+
const rootPath = env.getHdbBasePath();
|
|
88
|
+
if (!rootPath) return value;
|
|
89
|
+
return path.resolve(rootPath, value);
|
|
90
|
+
}
|
|
74
91
|
/**
|
|
75
92
|
* Builds the Harper config file using user inputs and default values from defaultConfig.yaml
|
|
76
93
|
* @param args - any args that the user provided.
|
|
@@ -332,15 +349,14 @@ function initConfig(force = false) {
|
|
|
332
349
|
* @param configFilePath
|
|
333
350
|
*/
|
|
334
351
|
function checkForUpdatedConfig(configDoc, configFilePath) {
|
|
335
|
-
const rootPath = configDoc.getIn(['rootPath']);
|
|
336
352
|
let updateFile = false;
|
|
337
353
|
if (!configDoc.hasIn(['storage', 'path'])) {
|
|
338
|
-
configDoc.setIn(['storage', 'path'],
|
|
354
|
+
configDoc.setIn(['storage', 'path'], 'database');
|
|
339
355
|
updateFile = true;
|
|
340
356
|
}
|
|
341
357
|
|
|
342
358
|
if (!configDoc.hasIn(['logging', 'rotation', 'path'])) {
|
|
343
|
-
configDoc.setIn(['logging', 'rotation', 'path'],
|
|
359
|
+
configDoc.setIn(['logging', 'rotation', 'path'], 'log');
|
|
344
360
|
updateFile = true;
|
|
345
361
|
}
|
|
346
362
|
|
|
@@ -7,6 +7,7 @@ const path = require('path');
|
|
|
7
7
|
const minimist = require('minimist');
|
|
8
8
|
const fs = require('fs-extra');
|
|
9
9
|
const _ = require('lodash');
|
|
10
|
+
const { getConfigPath } = require('../../../../config/configUtils.js');
|
|
10
11
|
env.initSync();
|
|
11
12
|
|
|
12
13
|
const { CONFIG_PARAMS, DATABASES_PARAM_CONFIG, SYSTEM_SCHEMA_NAME } = hdbTerms;
|
|
@@ -25,7 +26,7 @@ function getBaseSchemaPath() {
|
|
|
25
26
|
|
|
26
27
|
if (env.getHdbBasePath() !== undefined) {
|
|
27
28
|
BASE_SCHEMA_PATH =
|
|
28
|
-
|
|
29
|
+
getConfigPath(CONFIG_PARAMS.STORAGE_PATH) || path.join(env.getHdbBasePath(), hdbTerms.DATABASES_DIR_NAME);
|
|
29
30
|
return BASE_SCHEMA_PATH;
|
|
30
31
|
}
|
|
31
32
|
}
|
|
@@ -52,7 +53,7 @@ function getTransactionAuditStoreBasePath() {
|
|
|
52
53
|
|
|
53
54
|
if (env.getHdbBasePath() !== undefined) {
|
|
54
55
|
TRANSACTION_STORE_PATH =
|
|
55
|
-
|
|
56
|
+
getConfigPath(hdbTerms.CONFIG_PARAMS.STORAGE_AUDIT_PATH) ||
|
|
56
57
|
path.join(env.getHdbBasePath(), hdbTerms.TRANSACTIONS_DIR_NAME);
|
|
57
58
|
return TRANSACTION_STORE_PATH;
|
|
58
59
|
}
|
|
@@ -683,15 +683,26 @@ function transactional(
|
|
|
683
683
|
}
|
|
684
684
|
}
|
|
685
685
|
}
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
686
|
+
const KNOWN_METHODS = ['get', 'head', 'put', 'post', 'delete', 'patch', 'query', 'move', 'copy'];
|
|
687
|
+
type ClientErrorWithMethods = ClientError & { allow: string[]; method: string };
|
|
688
|
+
export function missingMethod(resource: any, method: string) {
|
|
689
|
+
const error = new ClientError(
|
|
690
|
+
`The ${resource.constructor.name} does not have a ${method} method implemented`,
|
|
691
|
+
405
|
|
692
|
+
) as ClientErrorWithMethods;
|
|
693
|
+
error.allow = allowedMethods(resource);
|
|
689
694
|
error.method = method;
|
|
690
|
-
for (const method of ['get', 'put', 'post', 'delete', 'query', 'move', 'copy']) {
|
|
691
|
-
if (typeof resource[method] === 'function') error.allow.push(method);
|
|
692
|
-
}
|
|
693
695
|
throw error;
|
|
694
696
|
}
|
|
697
|
+
|
|
698
|
+
export function allowedMethods(resource: any) {
|
|
699
|
+
const allow = [];
|
|
700
|
+
for (const method of KNOWN_METHODS) {
|
|
701
|
+
if (typeof resource[method] === 'function') allow.push(method);
|
|
702
|
+
}
|
|
703
|
+
return allow;
|
|
704
|
+
}
|
|
705
|
+
|
|
695
706
|
/**
|
|
696
707
|
* This is responsible for handling a select query parameter/call that selects specific
|
|
697
708
|
* properties from the returned record(s).
|
|
@@ -3,6 +3,7 @@ import { ExtendedIterable } from '@harperfast/extended-iterable';
|
|
|
3
3
|
import { Decoder, readAuditEntry, ENTRY_DATAVIEW, AuditRecord, createAuditEntry } from './auditStore.ts';
|
|
4
4
|
import { isMainThread } from 'node:worker_threads';
|
|
5
5
|
import { EventEmitter } from 'node:events';
|
|
6
|
+
import { asBinary } from 'lmdb';
|
|
6
7
|
|
|
7
8
|
if (!process.env.HARPER_NO_FLUSH_ON_EXIT && isMainThread) {
|
|
8
9
|
// we want to be able to test log replay
|
|
@@ -81,7 +82,7 @@ export class RocksTransactionLogStore extends EventEmitter {
|
|
|
81
82
|
|
|
82
83
|
putSync(suggestedKey: any, value: any, options: any) {
|
|
83
84
|
if (typeof suggestedKey === 'symbol') {
|
|
84
|
-
this.rootStore.putSync(suggestedKey, value, options);
|
|
85
|
+
this.rootStore.putSync(suggestedKey, asBinary(value), options);
|
|
85
86
|
} else {
|
|
86
87
|
this.put(suggestedKey, value, options);
|
|
87
88
|
}
|
|
@@ -107,6 +108,12 @@ export class RocksTransactionLogStore extends EventEmitter {
|
|
|
107
108
|
return this.rootStore.getSync(key);
|
|
108
109
|
}
|
|
109
110
|
}
|
|
111
|
+
getBinary(key: any) {
|
|
112
|
+
if (typeof key === 'number') {
|
|
113
|
+
throw new Error('Unsupported binary access by number');
|
|
114
|
+
}
|
|
115
|
+
return this.rootStore.getBinarySync(key);
|
|
116
|
+
}
|
|
110
117
|
getEntry() {
|
|
111
118
|
throw new Error('Not implemented');
|
|
112
119
|
}
|
|
@@ -131,7 +131,7 @@ export async function get(metric: string, opts?: GetAnalyticsOpts): Promise<Metr
|
|
|
131
131
|
// remove nodeId from 'id' attr and resolve it to the actual hostname and
|
|
132
132
|
// add back in as 'node' attr if selected
|
|
133
133
|
const nodeId = result.id[1];
|
|
134
|
-
result
|
|
134
|
+
result = { ...result, id: result.id[0] };
|
|
135
135
|
if (isSelected(select, 'node')) {
|
|
136
136
|
log.trace?.(`get_analytics lookup hostname for nodeId: ${nodeId}`);
|
|
137
137
|
const hostname = await lookupHostname(nodeId);
|
|
@@ -3,12 +3,12 @@ import { onMessageByType } from '../../server/threads/manageThreads.js';
|
|
|
3
3
|
import { getDatabases, table } from '../databases.ts';
|
|
4
4
|
import type { Databases, Table, Tables } from '../databases.ts';
|
|
5
5
|
import harperLogger from '../../utility/logging/harper_logger.js';
|
|
6
|
-
import { stat } from 'node:fs/promises';
|
|
6
|
+
import { stat, readdir } from 'node:fs/promises';
|
|
7
7
|
const { getLogFilePath, forComponent } = harperLogger;
|
|
8
8
|
import { dirname, join } from 'path';
|
|
9
9
|
import { open } from 'fs/promises';
|
|
10
10
|
import { getNextMonotonicTime } from '../../utility/lmdb/commonUtility.js';
|
|
11
|
-
import { get as envGet, initSync } from '../../utility/environment/environmentManager.js';
|
|
11
|
+
import { get as envGet, getHdbBasePath, initSync } from '../../utility/environment/environmentManager.js';
|
|
12
12
|
import { CONFIG_PARAMS } from '../../utility/hdbTerms.ts';
|
|
13
13
|
import { server } from '../../server/Server.ts';
|
|
14
14
|
import * as fs from 'node:fs';
|
|
@@ -382,6 +382,43 @@ function storeVolumeMetrics(analyticsTable: Table, databases: Databases) {
|
|
|
382
382
|
}
|
|
383
383
|
}
|
|
384
384
|
|
|
385
|
+
export async function getDirectorySizeAsync(dirPath: string): Promise<number> {
|
|
386
|
+
try {
|
|
387
|
+
const entries = await readdir(dirPath, { withFileTypes: true });
|
|
388
|
+
const sizes = await Promise.all(
|
|
389
|
+
entries.map((entry) => {
|
|
390
|
+
const fullPath = join(dirPath, entry.name);
|
|
391
|
+
if (entry.isDirectory()) return getDirectorySizeAsync(fullPath);
|
|
392
|
+
if (entry.isFile()) return stat(fullPath).then((s) => s.size);
|
|
393
|
+
return 0;
|
|
394
|
+
})
|
|
395
|
+
);
|
|
396
|
+
let total = 0;
|
|
397
|
+
for (const size of sizes) total += size;
|
|
398
|
+
return total;
|
|
399
|
+
} catch {
|
|
400
|
+
// directory may not exist or be inaccessible
|
|
401
|
+
return 0;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const DEFAULT_STORAGE_INTERVAL = 10;
|
|
406
|
+
let nodeStorageInterval = DEFAULT_STORAGE_INTERVAL;
|
|
407
|
+
let nodeStorageCycleCount = 0;
|
|
408
|
+
|
|
409
|
+
async function storeNodeStorageMetric(analyticsTable: Table) {
|
|
410
|
+
if (nodeStorageInterval <= 0 || ++nodeStorageCycleCount % nodeStorageInterval !== 1) return;
|
|
411
|
+
try {
|
|
412
|
+
const size = await getDirectorySizeAsync(getHdbBasePath());
|
|
413
|
+
storeMetric(analyticsTable, {
|
|
414
|
+
metric: METRIC.NODE_STORAGE,
|
|
415
|
+
size,
|
|
416
|
+
});
|
|
417
|
+
} catch (error) {
|
|
418
|
+
log.warn?.('Error getting node storage metric', error);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
385
422
|
async function aggregation(fromPeriod, toPeriod = 60000) {
|
|
386
423
|
const rawAnalyticsTable = getRawAnalyticsTable();
|
|
387
424
|
const analyticsTable = getAnalyticsTable();
|
|
@@ -574,6 +611,9 @@ async function aggregation(fromPeriod, toPeriod = 60000) {
|
|
|
574
611
|
// database storage volume metrics
|
|
575
612
|
storeVolumeMetrics(analyticsTable, databases);
|
|
576
613
|
storeVolumeMetrics(analyticsTable, { system: databases.system });
|
|
614
|
+
|
|
615
|
+
// node storage metric (total HDB directory size)
|
|
616
|
+
await storeNodeStorageMetric(analyticsTable);
|
|
577
617
|
}
|
|
578
618
|
let lastIdle = 0;
|
|
579
619
|
let lastActive = 0;
|
|
@@ -654,6 +694,7 @@ if (!parentPort) onMessageByType(ANALYTICS_REPORT_TYPE, recordAnalytics);
|
|
|
654
694
|
let scheduledTasksRunning;
|
|
655
695
|
function startScheduledTasks() {
|
|
656
696
|
scheduledTasksRunning = true;
|
|
697
|
+
nodeStorageInterval = envGet(CONFIG_PARAMS.ANALYTICS_STORAGEINTERVAL) ?? DEFAULT_STORAGE_INTERVAL;
|
|
657
698
|
const AGGREGATE_PERIOD = envGet(CONFIG_PARAMS.ANALYTICS_AGGREGATEPERIOD) * 1000;
|
|
658
699
|
if (AGGREGATE_PERIOD) {
|
|
659
700
|
setInterval(
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
import { makeTable } from './Table.ts';
|
|
13
13
|
import OpenEnvironmentObject from '../utility/lmdb/OpenEnvironmentObject.js';
|
|
14
14
|
import { CONFIG_PARAMS, LEGACY_DATABASES_DIR_NAME, DATABASES_DIR_NAME } from '../utility/hdbTerms.ts';
|
|
15
|
+
import { getConfigPath } from '../config/configUtils.js';
|
|
15
16
|
import { _assignPackageExport } from '../globals.js';
|
|
16
17
|
import { getIndexedValues } from '../utility/lmdb/commonUtility.js';
|
|
17
18
|
import * as signalling from '../utility/signalling.js';
|
|
@@ -176,7 +177,7 @@ export function getDatabases(): Databases {
|
|
|
176
177
|
if (process.env.SCHEMAS_DATA_PATH) schemaConfigs.data = { path: process.env.SCHEMAS_DATA_PATH };
|
|
177
178
|
databasePath =
|
|
178
179
|
process.env.STORAGE_PATH ||
|
|
179
|
-
|
|
180
|
+
getConfigPath(CONFIG_PARAMS.STORAGE_PATH) ||
|
|
180
181
|
(databasePath && (existsSync(databasePath) ? databasePath : join(getHdbBasePath(), LEGACY_DATABASES_DIR_NAME)));
|
|
181
182
|
if (!databasePath) return;
|
|
182
183
|
|
|
@@ -694,7 +695,7 @@ export function database({ database: databaseName, table: tableName }) {
|
|
|
694
695
|
tablePath ||
|
|
695
696
|
databaseConfig[databaseName]?.path ||
|
|
696
697
|
process.env.STORAGE_PATH ||
|
|
697
|
-
|
|
698
|
+
getConfigPath(CONFIG_PARAMS.STORAGE_PATH) ||
|
|
698
699
|
(existsSync(join(hdbBasePath, DATABASES_DIR_NAME))
|
|
699
700
|
? join(hdbBasePath, DATABASES_DIR_NAME)
|
|
700
701
|
: join(hdbBasePath, LEGACY_DATABASES_DIR_NAME));
|
|
@@ -734,41 +735,49 @@ export async function dropDatabase(databaseName) {
|
|
|
734
735
|
if (!databases[databaseName]) throw new Error('Database does not exist');
|
|
735
736
|
const dbTables = databases[databaseName];
|
|
736
737
|
let rootStore;
|
|
738
|
+
|
|
737
739
|
for (const tableName in dbTables) {
|
|
738
740
|
const table = dbTables[tableName];
|
|
739
741
|
rootStore = table.primaryStore.rootStore;
|
|
740
|
-
|
|
741
742
|
lmdbDatabaseEnvs.delete(rootStore.path);
|
|
742
743
|
rocksdbDatabaseEnvs.delete(rootStore.path);
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
for (const tableName in dbTables) {
|
|
747
|
+
databaseEventsEmitter.emit('dropTable', tableName, databaseName);
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
if (databaseName === 'data') {
|
|
751
|
+
for (const tableName in tables) {
|
|
752
|
+
delete tables[tableName];
|
|
753
|
+
}
|
|
754
|
+
delete tables[DEFINED_TABLES];
|
|
755
|
+
}
|
|
756
|
+
delete databases[databaseName];
|
|
743
757
|
|
|
758
|
+
databaseEventsEmitter.emit('dropDatabase', databaseName);
|
|
759
|
+
|
|
760
|
+
if (rootStore) {
|
|
744
761
|
if (rootStore.status === 'open') {
|
|
745
762
|
if (rootStore instanceof RocksDatabase) {
|
|
746
763
|
rootStore.close();
|
|
747
764
|
rootStore.destroy();
|
|
748
|
-
} else
|
|
765
|
+
} else {
|
|
749
766
|
await rootStore.close();
|
|
750
767
|
await unlink(rootStore.path);
|
|
751
768
|
}
|
|
752
769
|
}
|
|
753
|
-
|
|
754
|
-
}
|
|
755
|
-
if (!rootStore) {
|
|
770
|
+
} else {
|
|
756
771
|
rootStore = database({ database: databaseName, table: null });
|
|
757
772
|
if (rootStore instanceof RocksDatabase) {
|
|
773
|
+
rootStore.close();
|
|
758
774
|
rootStore.destroy();
|
|
759
775
|
} else if (rootStore.status === 'open') {
|
|
760
776
|
await rootStore.close();
|
|
761
777
|
await unlink(rootStore.path);
|
|
762
778
|
}
|
|
763
779
|
}
|
|
764
|
-
|
|
765
|
-
for (const tableName in tables) {
|
|
766
|
-
delete tables[tableName];
|
|
767
|
-
}
|
|
768
|
-
delete tables[DEFINED_TABLES];
|
|
769
|
-
}
|
|
770
|
-
delete databases[databaseName];
|
|
771
|
-
databaseEventsEmitter.emit('dropDatabase', databaseName);
|
|
780
|
+
|
|
772
781
|
await deleteRootBlobPathsForDB(rootStore);
|
|
773
782
|
}
|
|
774
783
|
// opens an index, consulting with custom indexes that may use alternate store configuration
|