@gravito/zenith 1.0.0-beta.1 → 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/CHANGELOG.md +9 -0
- package/dist/bin.js +436 -43
- package/dist/client/assets/index-C332gZ-J.css +1 -0
- package/dist/client/assets/{index-oXEse8ih.js → index-D4HibwTK.js} +88 -88
- package/dist/client/index.html +2 -2
- package/dist/server/index.js +436 -43
- package/docs/LARAVEL_ZENITH_ROADMAP.md +109 -0
- package/{QUASAR_MASTER_PLAN.md → docs/QUASAR_MASTER_PLAN.md} +6 -3
- package/package.json +1 -1
- package/scripts/debug_redis_keys.ts +24 -0
- package/src/client/App.tsx +1 -1
- package/src/client/Layout.tsx +11 -12
- package/src/client/WorkerStatus.tsx +97 -56
- package/src/client/components/BrandIcons.tsx +119 -44
- package/src/client/components/ConfirmDialog.tsx +0 -1
- package/src/client/components/JobInspector.tsx +18 -6
- package/src/client/components/PageHeader.tsx +32 -28
- package/src/client/pages/OverviewPage.tsx +0 -1
- package/src/client/pages/PulsePage.tsx +422 -340
- package/src/client/pages/SettingsPage.tsx +69 -15
- package/src/client/pages/WorkersPage.tsx +70 -2
- package/src/server/index.ts +171 -11
- package/src/server/services/QueueService.ts +6 -3
- package/src/shared/types.ts +2 -0
- package/ARCHITECTURE.md +0 -88
- package/BATCH_OPERATIONS_IMPLEMENTATION.md +0 -159
- package/EVOLUTION_BLUEPRINT.md +0 -112
- package/JOBINSPECTOR_SCROLL_FIX.md +0 -152
- package/TESTING_BATCH_OPERATIONS.md +0 -252
- package/dist/client/assets/index-BSTyMCFd.css +0 -1
- /package/{ALERTING_GUIDE.md → docs/ALERTING_GUIDE.md} +0 -0
- /package/{DEPLOYMENT.md → docs/DEPLOYMENT.md} +0 -0
- /package/{DOCS_INTERNAL.md → docs/DOCS_INTERNAL.md} +0 -0
- /package/{QUICK_TEST_GUIDE.md → docs/QUICK_TEST_GUIDE.md} +0 -0
- /package/{ROADMAP.md → docs/ROADMAP.md} +0 -0
package/CHANGELOG.md
ADDED
package/dist/bin.js
CHANGED
|
@@ -68749,19 +68749,19 @@ class Grammar {
|
|
|
68749
68749
|
return `INSERT INTO ${this.wrapTable(query.table)} (${columnList}) VALUES ${valuesList}`;
|
|
68750
68750
|
}
|
|
68751
68751
|
compileUpdate(query, values) {
|
|
68752
|
-
const columns = Object.keys(values);
|
|
68753
68752
|
let bindingIndex = 0;
|
|
68754
|
-
const setClause =
|
|
68753
|
+
const setClause = Object.entries(values).map(([col, value]) => {
|
|
68754
|
+
if (value instanceof Expression) {
|
|
68755
|
+
return `${this.wrapColumn(col)} = ${value.getValue()}`;
|
|
68756
|
+
}
|
|
68755
68757
|
const placeholder = this.getPlaceholder(bindingIndex);
|
|
68756
68758
|
bindingIndex++;
|
|
68757
68759
|
return `${this.wrapColumn(col)} = ${placeholder}`;
|
|
68758
68760
|
}).join(", ");
|
|
68759
68761
|
let sql = `UPDATE ${this.wrapTable(query.table)} SET ${setClause}`;
|
|
68760
68762
|
if (query.wheres.length > 0) {
|
|
68761
|
-
const
|
|
68762
|
-
|
|
68763
|
-
const wheres = this.compileWheres(offsetQuery);
|
|
68764
|
-
sql += ` ${this.offsetPlaceholders(wheres, whereBindingsOffset)}`;
|
|
68763
|
+
const wheres = this.compileWheres(query, bindingIndex);
|
|
68764
|
+
sql += ` ${wheres}`;
|
|
68765
68765
|
}
|
|
68766
68766
|
return sql;
|
|
68767
68767
|
}
|
|
@@ -70367,7 +70367,14 @@ class QueryBuilder {
|
|
|
70367
70367
|
return id;
|
|
70368
70368
|
}
|
|
70369
70369
|
async update(data) {
|
|
70370
|
-
const values =
|
|
70370
|
+
const values = [];
|
|
70371
|
+
for (const value of Object.values(data)) {
|
|
70372
|
+
if (value instanceof Expression) {
|
|
70373
|
+
values.push(...value.getBindings());
|
|
70374
|
+
} else {
|
|
70375
|
+
values.push(value);
|
|
70376
|
+
}
|
|
70377
|
+
}
|
|
70371
70378
|
const allBindings = [...values, ...this.bindingsList];
|
|
70372
70379
|
const compiled = this.getCompiledQuery();
|
|
70373
70380
|
compiled.bindings = allBindings;
|
|
@@ -71473,7 +71480,7 @@ var require_dist_cjs = __commonJS((exports) => {
|
|
|
71473
71480
|
|
|
71474
71481
|
// ../../node_modules/.bun/@smithy+protocol-http@5.3.7/node_modules/@smithy/protocol-http/dist-cjs/index.js
|
|
71475
71482
|
var require_dist_cjs2 = __commonJS((exports) => {
|
|
71476
|
-
var
|
|
71483
|
+
var types4 = require_dist_cjs();
|
|
71477
71484
|
var getHttpHandlerExtensionConfiguration = (runtimeConfig) => {
|
|
71478
71485
|
return {
|
|
71479
71486
|
setHttpHandler(handler) {
|
|
@@ -71500,7 +71507,7 @@ var require_dist_cjs2 = __commonJS((exports) => {
|
|
|
71500
71507
|
name;
|
|
71501
71508
|
kind;
|
|
71502
71509
|
values;
|
|
71503
|
-
constructor({ name, kind =
|
|
71510
|
+
constructor({ name, kind = types4.FieldPosition.HEADER, values = [] }) {
|
|
71504
71511
|
this.name = name;
|
|
71505
71512
|
this.kind = kind;
|
|
71506
71513
|
this.values = values;
|
|
@@ -73987,8 +73994,8 @@ var require_dist_cjs16 = __commonJS((exports) => {
|
|
|
73987
73994
|
|
|
73988
73995
|
// ../../node_modules/.bun/@smithy+util-middleware@4.2.7/node_modules/@smithy/util-middleware/dist-cjs/index.js
|
|
73989
73996
|
var require_dist_cjs17 = __commonJS((exports) => {
|
|
73990
|
-
var
|
|
73991
|
-
var getSmithyContext = (context) => context[
|
|
73997
|
+
var types4 = require_dist_cjs();
|
|
73998
|
+
var getSmithyContext = (context) => context[types4.SMITHY_CONTEXT_KEY] || (context[types4.SMITHY_CONTEXT_KEY] = {});
|
|
73992
73999
|
var normalizeProvider = (input) => {
|
|
73993
74000
|
if (typeof input === "function")
|
|
73994
74001
|
return input;
|
|
@@ -76933,7 +76940,7 @@ var require_protocols = __commonJS((exports) => {
|
|
|
76933
76940
|
var require_dist_cjs19 = __commonJS((exports) => {
|
|
76934
76941
|
var middlewareStack = require_dist_cjs6();
|
|
76935
76942
|
var protocols = require_protocols();
|
|
76936
|
-
var
|
|
76943
|
+
var types4 = require_dist_cjs();
|
|
76937
76944
|
var schema2 = require_schema();
|
|
76938
76945
|
var serde = require_serde();
|
|
76939
76946
|
|
|
@@ -77032,7 +77039,7 @@ var require_dist_cjs19 = __commonJS((exports) => {
|
|
|
77032
77039
|
commandName,
|
|
77033
77040
|
inputFilterSensitiveLog,
|
|
77034
77041
|
outputFilterSensitiveLog,
|
|
77035
|
-
[
|
|
77042
|
+
[types4.SMITHY_CONTEXT_KEY]: {
|
|
77036
77043
|
commandInstance: this,
|
|
77037
77044
|
...smithyContext
|
|
77038
77045
|
},
|
|
@@ -77257,8 +77264,8 @@ var require_dist_cjs19 = __commonJS((exports) => {
|
|
|
77257
77264
|
};
|
|
77258
77265
|
var getChecksumConfiguration = (runtimeConfig) => {
|
|
77259
77266
|
const checksumAlgorithms = [];
|
|
77260
|
-
for (const id in
|
|
77261
|
-
const algorithmId =
|
|
77267
|
+
for (const id in types4.AlgorithmId) {
|
|
77268
|
+
const algorithmId = types4.AlgorithmId[id];
|
|
77262
77269
|
if (runtimeConfig[algorithmId] === undefined) {
|
|
77263
77270
|
continue;
|
|
77264
77271
|
}
|
|
@@ -77766,12 +77773,12 @@ var require_dist_cjs20 = __commonJS((exports) => {
|
|
|
77766
77773
|
|
|
77767
77774
|
// ../../node_modules/.bun/@smithy+core@3.20.0/node_modules/@smithy/core/dist-cjs/index.js
|
|
77768
77775
|
var require_dist_cjs21 = __commonJS((exports) => {
|
|
77769
|
-
var
|
|
77776
|
+
var types4 = require_dist_cjs();
|
|
77770
77777
|
var utilMiddleware = require_dist_cjs17();
|
|
77771
77778
|
var middlewareSerde = require_dist_cjs20();
|
|
77772
77779
|
var protocolHttp = require_dist_cjs2();
|
|
77773
77780
|
var protocols = require_protocols();
|
|
77774
|
-
var getSmithyContext = (context) => context[
|
|
77781
|
+
var getSmithyContext = (context) => context[types4.SMITHY_CONTEXT_KEY] || (context[types4.SMITHY_CONTEXT_KEY] = {});
|
|
77775
77782
|
var resolveAuthOptions = (candidateAuthOptions, authSchemePreference) => {
|
|
77776
77783
|
if (!authSchemePreference || authSchemePreference.length === 0) {
|
|
77777
77784
|
return candidateAuthOptions;
|
|
@@ -77986,9 +77993,9 @@ var require_dist_cjs21 = __commonJS((exports) => {
|
|
|
77986
77993
|
throw new Error("request could not be signed with `apiKey` since the `apiKey` is not defined");
|
|
77987
77994
|
}
|
|
77988
77995
|
const clonedRequest = protocolHttp.HttpRequest.clone(httpRequest);
|
|
77989
|
-
if (signingProperties.in ===
|
|
77996
|
+
if (signingProperties.in === types4.HttpApiKeyAuthLocation.QUERY) {
|
|
77990
77997
|
clonedRequest.query[signingProperties.name] = identity.apiKey;
|
|
77991
|
-
} else if (signingProperties.in ===
|
|
77998
|
+
} else if (signingProperties.in === types4.HttpApiKeyAuthLocation.HEADER) {
|
|
77992
77999
|
clonedRequest.headers[signingProperties.name] = signingProperties.scheme ? `${signingProperties.scheme} ${identity.apiKey}` : identity.apiKey;
|
|
77993
78000
|
} else {
|
|
77994
78001
|
throw new Error("request can only be signed with `apiKey` locations `query` or `header`, " + "but found: `" + signingProperties.in + "`");
|
|
@@ -78098,7 +78105,7 @@ var require_dist_cjs21 = __commonJS((exports) => {
|
|
|
78098
78105
|
|
|
78099
78106
|
// ../../node_modules/.bun/@smithy+util-endpoints@3.2.7/node_modules/@smithy/util-endpoints/dist-cjs/index.js
|
|
78100
78107
|
var require_dist_cjs22 = __commonJS((exports) => {
|
|
78101
|
-
var
|
|
78108
|
+
var types4 = require_dist_cjs();
|
|
78102
78109
|
|
|
78103
78110
|
class EndpointCache {
|
|
78104
78111
|
capacity;
|
|
@@ -78221,8 +78228,8 @@ var require_dist_cjs22 = __commonJS((exports) => {
|
|
|
78221
78228
|
var isSet = (value) => value != null;
|
|
78222
78229
|
var not = (value) => !value;
|
|
78223
78230
|
var DEFAULT_PORTS = {
|
|
78224
|
-
[
|
|
78225
|
-
[
|
|
78231
|
+
[types4.EndpointURLScheme.HTTP]: 80,
|
|
78232
|
+
[types4.EndpointURLScheme.HTTPS]: 443
|
|
78226
78233
|
};
|
|
78227
78234
|
var parseURL = (value) => {
|
|
78228
78235
|
const whatwgURL = (() => {
|
|
@@ -78251,7 +78258,7 @@ var require_dist_cjs22 = __commonJS((exports) => {
|
|
|
78251
78258
|
return null;
|
|
78252
78259
|
}
|
|
78253
78260
|
const scheme = protocol.slice(0, -1);
|
|
78254
|
-
if (!Object.values(
|
|
78261
|
+
if (!Object.values(types4.EndpointURLScheme).includes(scheme)) {
|
|
78255
78262
|
return null;
|
|
78256
78263
|
}
|
|
78257
78264
|
const isIp = isIpAddress(hostname);
|
|
@@ -84381,7 +84388,7 @@ var require_dist_cjs35 = __commonJS((exports) => {
|
|
|
84381
84388
|
var getSSOTokenFilepath = require_getSSOTokenFilepath();
|
|
84382
84389
|
var getSSOTokenFromFile = require_getSSOTokenFromFile();
|
|
84383
84390
|
var path = __require("path");
|
|
84384
|
-
var
|
|
84391
|
+
var types4 = require_dist_cjs();
|
|
84385
84392
|
var readFile = require_readFile();
|
|
84386
84393
|
var ENV_PROFILE = "AWS_PROFILE";
|
|
84387
84394
|
var DEFAULT_PROFILE = "default";
|
|
@@ -84392,10 +84399,10 @@ var require_dist_cjs35 = __commonJS((exports) => {
|
|
|
84392
84399
|
if (indexOfSeparator === -1) {
|
|
84393
84400
|
return false;
|
|
84394
84401
|
}
|
|
84395
|
-
return Object.values(
|
|
84402
|
+
return Object.values(types4.IniSectionType).includes(key.substring(0, indexOfSeparator));
|
|
84396
84403
|
}).reduce((acc, [key, value]) => {
|
|
84397
84404
|
const indexOfSeparator = key.indexOf(CONFIG_PREFIX_SEPARATOR);
|
|
84398
|
-
const updatedKey = key.substring(0, indexOfSeparator) ===
|
|
84405
|
+
const updatedKey = key.substring(0, indexOfSeparator) === types4.IniSectionType.PROFILE ? key.substring(indexOfSeparator + 1) : key;
|
|
84399
84406
|
acc[updatedKey] = value;
|
|
84400
84407
|
return acc;
|
|
84401
84408
|
}, {
|
|
@@ -84421,7 +84428,7 @@ var require_dist_cjs35 = __commonJS((exports) => {
|
|
|
84421
84428
|
const matches = prefixKeyRegex.exec(sectionName);
|
|
84422
84429
|
if (matches) {
|
|
84423
84430
|
const [, prefix, , name] = matches;
|
|
84424
|
-
if (Object.values(
|
|
84431
|
+
if (Object.values(types4.IniSectionType).includes(prefix)) {
|
|
84425
84432
|
currentSection = [prefix, name].join(CONFIG_PREFIX_SEPARATOR);
|
|
84426
84433
|
}
|
|
84427
84434
|
} else {
|
|
@@ -84478,7 +84485,7 @@ var require_dist_cjs35 = __commonJS((exports) => {
|
|
|
84478
84485
|
credentialsFile: parsedFiles[1]
|
|
84479
84486
|
};
|
|
84480
84487
|
};
|
|
84481
|
-
var getSsoSessionData = (data) => Object.entries(data).filter(([key]) => key.startsWith(
|
|
84488
|
+
var getSsoSessionData = (data) => Object.entries(data).filter(([key]) => key.startsWith(types4.IniSectionType.SSO_SESSION + CONFIG_PREFIX_SEPARATOR)).reduce((acc, [key, value]) => ({ ...acc, [key.substring(key.indexOf(CONFIG_PREFIX_SEPARATOR) + 1)]: value }), {});
|
|
84482
84489
|
var swallowError = () => ({});
|
|
84483
84490
|
var loadSsoSessionData = async (init = {}) => readFile.readFile(init.configFilepath ?? getConfigFilepath()).then(parseIni).then(getSsoSessionData).catch(swallowError);
|
|
84484
84491
|
var mergeConfigFiles = (...files) => {
|
|
@@ -113211,6 +113218,8 @@ class Migrator {
|
|
|
113211
113218
|
return MigrationClass;
|
|
113212
113219
|
}
|
|
113213
113220
|
}
|
|
113221
|
+
// ../atlas/src/OrbitAtlas.ts
|
|
113222
|
+
init_DB();
|
|
113214
113223
|
// ../atlas/src/orm/model/DirtyTracker.ts
|
|
113215
113224
|
class DirtyTracker {
|
|
113216
113225
|
original = new Map;
|
|
@@ -115485,6 +115494,155 @@ var Hono2 = class extends Hono {
|
|
|
115485
115494
|
}
|
|
115486
115495
|
};
|
|
115487
115496
|
|
|
115497
|
+
// ../quasar/src/bridges/BaseZenithBridge.ts
|
|
115498
|
+
class BaseZenithBridge {
|
|
115499
|
+
redis;
|
|
115500
|
+
prefix;
|
|
115501
|
+
workerId;
|
|
115502
|
+
listeners = [];
|
|
115503
|
+
constructor(redis, prefix = "flux_console:", workerId) {
|
|
115504
|
+
this.redis = redis;
|
|
115505
|
+
this.prefix = prefix;
|
|
115506
|
+
this.workerId = workerId;
|
|
115507
|
+
}
|
|
115508
|
+
async publishLog(payload) {
|
|
115509
|
+
try {
|
|
115510
|
+
const fullPayload = {
|
|
115511
|
+
...payload,
|
|
115512
|
+
workerId: payload.workerId || this.workerId,
|
|
115513
|
+
timestamp: payload.timestamp || new Date().toISOString()
|
|
115514
|
+
};
|
|
115515
|
+
await this.redis.publish(`${this.prefix}logs`, JSON.stringify(fullPayload));
|
|
115516
|
+
const historyKey = `${this.prefix}logs:history`;
|
|
115517
|
+
if (typeof this.redis.pipeline === "function") {
|
|
115518
|
+
const pipe = this.redis.pipeline();
|
|
115519
|
+
pipe.lpush(historyKey, JSON.stringify(fullPayload));
|
|
115520
|
+
pipe.ltrim(historyKey, 0, 99);
|
|
115521
|
+
await pipe.exec();
|
|
115522
|
+
} else {
|
|
115523
|
+
await this.redis.lpush(historyKey, JSON.stringify(fullPayload));
|
|
115524
|
+
}
|
|
115525
|
+
} catch (err) {
|
|
115526
|
+
console.error("[BaseZenithBridge] Failed to publish log:", err);
|
|
115527
|
+
}
|
|
115528
|
+
}
|
|
115529
|
+
registerListener(target, event, handler) {
|
|
115530
|
+
this.listeners.push({ target, event, handler });
|
|
115531
|
+
}
|
|
115532
|
+
detach() {
|
|
115533
|
+
for (const { target, event, handler } of this.listeners) {
|
|
115534
|
+
if (typeof target.off === "function") {
|
|
115535
|
+
target.off(event, handler);
|
|
115536
|
+
} else if (typeof target.removeListener === "function") {
|
|
115537
|
+
target.removeListener(event, handler);
|
|
115538
|
+
}
|
|
115539
|
+
}
|
|
115540
|
+
this.listeners = [];
|
|
115541
|
+
}
|
|
115542
|
+
}
|
|
115543
|
+
// ../quasar/src/bridges/BeeQueueBridge.ts
|
|
115544
|
+
class BeeQueueBridge extends BaseZenithBridge {
|
|
115545
|
+
attach(queue) {
|
|
115546
|
+
const onSucceeded = async (job, result) => {
|
|
115547
|
+
await this.publishLog({
|
|
115548
|
+
level: "success",
|
|
115549
|
+
message: `Completed job: ${job.id}`,
|
|
115550
|
+
jobId: job.id,
|
|
115551
|
+
context: {
|
|
115552
|
+
data: job.data,
|
|
115553
|
+
result: typeof result === "object" ? JSON.stringify(result) : result
|
|
115554
|
+
}
|
|
115555
|
+
});
|
|
115556
|
+
};
|
|
115557
|
+
const onFailed = async (job, error) => {
|
|
115558
|
+
await this.publishLog({
|
|
115559
|
+
level: "error",
|
|
115560
|
+
message: `Job failed: ${job.id} - ${error.message}`,
|
|
115561
|
+
jobId: job.id,
|
|
115562
|
+
context: {
|
|
115563
|
+
data: job.data,
|
|
115564
|
+
error: error.message,
|
|
115565
|
+
stack: error.stack
|
|
115566
|
+
}
|
|
115567
|
+
});
|
|
115568
|
+
};
|
|
115569
|
+
const onProgress = async (job, progress) => {
|
|
115570
|
+
await this.publishLog({
|
|
115571
|
+
level: "info",
|
|
115572
|
+
message: `Job progress: ${job.id}`,
|
|
115573
|
+
jobId: job.id,
|
|
115574
|
+
context: {
|
|
115575
|
+
data: job.data,
|
|
115576
|
+
progress
|
|
115577
|
+
}
|
|
115578
|
+
});
|
|
115579
|
+
};
|
|
115580
|
+
queue.on("job succeeded", onSucceeded);
|
|
115581
|
+
queue.on("job failed", onFailed);
|
|
115582
|
+
queue.on("job progress", onProgress);
|
|
115583
|
+
this.registerListener(queue, "job succeeded", onSucceeded);
|
|
115584
|
+
this.registerListener(queue, "job failed", onFailed);
|
|
115585
|
+
this.registerListener(queue, "job progress", onProgress);
|
|
115586
|
+
}
|
|
115587
|
+
}
|
|
115588
|
+
// ../quasar/src/bridges/BullMQBridge.ts
|
|
115589
|
+
class BullMQBridge extends BaseZenithBridge {
|
|
115590
|
+
attach(worker) {
|
|
115591
|
+
const onActive = async (job) => {
|
|
115592
|
+
await this.publishLog({
|
|
115593
|
+
level: "info",
|
|
115594
|
+
message: `Processing job: ${job.name || job.id}`,
|
|
115595
|
+
jobId: job.id,
|
|
115596
|
+
context: {
|
|
115597
|
+
name: job.name,
|
|
115598
|
+
data: job.data
|
|
115599
|
+
}
|
|
115600
|
+
});
|
|
115601
|
+
};
|
|
115602
|
+
const onCompleted = async (job, result) => {
|
|
115603
|
+
await this.publishLog({
|
|
115604
|
+
level: "success",
|
|
115605
|
+
message: `Completed job: ${job.name || job.id}`,
|
|
115606
|
+
jobId: job.id,
|
|
115607
|
+
context: {
|
|
115608
|
+
name: job.name,
|
|
115609
|
+
result: typeof result === "object" ? JSON.stringify(result) : result
|
|
115610
|
+
}
|
|
115611
|
+
});
|
|
115612
|
+
};
|
|
115613
|
+
const onFailed = async (job, error) => {
|
|
115614
|
+
await this.publishLog({
|
|
115615
|
+
level: "error",
|
|
115616
|
+
message: `Job failed: ${job?.name || job?.id} - ${error.message}`,
|
|
115617
|
+
jobId: job?.id,
|
|
115618
|
+
context: {
|
|
115619
|
+
name: job?.name,
|
|
115620
|
+
error: error.message,
|
|
115621
|
+
stack: error.stack
|
|
115622
|
+
}
|
|
115623
|
+
});
|
|
115624
|
+
};
|
|
115625
|
+
const onProgress = async (job, progress) => {
|
|
115626
|
+
await this.publishLog({
|
|
115627
|
+
level: "info",
|
|
115628
|
+
message: `Job progress: ${job.name || job.id}`,
|
|
115629
|
+
jobId: job.id,
|
|
115630
|
+
context: {
|
|
115631
|
+
name: job.name,
|
|
115632
|
+
progress
|
|
115633
|
+
}
|
|
115634
|
+
});
|
|
115635
|
+
};
|
|
115636
|
+
worker.on("active", onActive);
|
|
115637
|
+
worker.on("completed", onCompleted);
|
|
115638
|
+
worker.on("failed", onFailed);
|
|
115639
|
+
worker.on("progress", onProgress);
|
|
115640
|
+
this.registerListener(worker, "active", onActive);
|
|
115641
|
+
this.registerListener(worker, "completed", onCompleted);
|
|
115642
|
+
this.registerListener(worker, "failed", onFailed);
|
|
115643
|
+
this.registerListener(worker, "progress", onProgress);
|
|
115644
|
+
}
|
|
115645
|
+
}
|
|
115488
115646
|
// ../quasar/src/executors/BaseExecutor.ts
|
|
115489
115647
|
class BaseExecutor {
|
|
115490
115648
|
success(commandId, message) {
|
|
@@ -115724,6 +115882,76 @@ class CommandListener {
|
|
|
115724
115882
|
}
|
|
115725
115883
|
}
|
|
115726
115884
|
}
|
|
115885
|
+
// ../quasar/src/probes/BeeQueueProbe.ts
|
|
115886
|
+
class BeeQueueProbe {
|
|
115887
|
+
redis;
|
|
115888
|
+
queueName;
|
|
115889
|
+
prefix;
|
|
115890
|
+
constructor(redis, queueName, prefix = "bq") {
|
|
115891
|
+
this.redis = redis;
|
|
115892
|
+
this.queueName = queueName;
|
|
115893
|
+
this.prefix = prefix;
|
|
115894
|
+
}
|
|
115895
|
+
async getSnapshot() {
|
|
115896
|
+
const key = (suffix) => `${this.prefix}:${this.queueName}:${suffix}`;
|
|
115897
|
+
const pipeline = this.redis.pipeline();
|
|
115898
|
+
pipeline.llen(key("waiting"));
|
|
115899
|
+
pipeline.llen(key("active"));
|
|
115900
|
+
pipeline.llen(key("failed"));
|
|
115901
|
+
const results = await pipeline.exec();
|
|
115902
|
+
if (!results)
|
|
115903
|
+
throw new Error("Redis pipeline failed");
|
|
115904
|
+
const [_waitingErr, waiting] = results[0];
|
|
115905
|
+
const [_activeErr, active] = results[1];
|
|
115906
|
+
const [_failedErr, failed] = results[2];
|
|
115907
|
+
return {
|
|
115908
|
+
name: this.queueName,
|
|
115909
|
+
driver: "redis",
|
|
115910
|
+
size: {
|
|
115911
|
+
waiting: Number(waiting || 0),
|
|
115912
|
+
active: Number(active || 0),
|
|
115913
|
+
failed: Number(failed || 0),
|
|
115914
|
+
delayed: 0
|
|
115915
|
+
}
|
|
115916
|
+
};
|
|
115917
|
+
}
|
|
115918
|
+
}
|
|
115919
|
+
// ../quasar/src/probes/BullMQProbe.ts
|
|
115920
|
+
class BullMQProbe {
|
|
115921
|
+
redis;
|
|
115922
|
+
queueName;
|
|
115923
|
+
prefix;
|
|
115924
|
+
constructor(redis, queueName, prefix = "bull") {
|
|
115925
|
+
this.redis = redis;
|
|
115926
|
+
this.queueName = queueName;
|
|
115927
|
+
this.prefix = prefix;
|
|
115928
|
+
}
|
|
115929
|
+
async getSnapshot() {
|
|
115930
|
+
const key = (suffix) => `${this.prefix}:${this.queueName}:${suffix}`;
|
|
115931
|
+
const pipeline = this.redis.pipeline();
|
|
115932
|
+
pipeline.llen(key("wait"));
|
|
115933
|
+
pipeline.llen(key("active"));
|
|
115934
|
+
pipeline.zcard(key("delayed"));
|
|
115935
|
+
pipeline.scard(key("failed"));
|
|
115936
|
+
const results = await pipeline.exec();
|
|
115937
|
+
if (!results)
|
|
115938
|
+
throw new Error("Redis pipeline failed");
|
|
115939
|
+
const [_waitErr, waiting] = results[0];
|
|
115940
|
+
const [_activeErr, active] = results[1];
|
|
115941
|
+
const [_delayedErr, delayed] = results[2];
|
|
115942
|
+
const [_failedErr, failed] = results[3];
|
|
115943
|
+
return {
|
|
115944
|
+
name: this.queueName,
|
|
115945
|
+
driver: "redis",
|
|
115946
|
+
size: {
|
|
115947
|
+
waiting: Number(waiting || 0),
|
|
115948
|
+
active: Number(active || 0),
|
|
115949
|
+
failed: Number(failed || 0),
|
|
115950
|
+
delayed: Number(delayed || 0)
|
|
115951
|
+
}
|
|
115952
|
+
};
|
|
115953
|
+
}
|
|
115954
|
+
}
|
|
115727
115955
|
// ../quasar/src/probes/NodeProbe.ts
|
|
115728
115956
|
import os from "os";
|
|
115729
115957
|
import process2 from "process";
|
|
@@ -115822,10 +116050,10 @@ class BullProbe {
|
|
|
115822
116050
|
const results = await pipeline.exec();
|
|
115823
116051
|
if (!results)
|
|
115824
116052
|
throw new Error("Redis pipeline failed");
|
|
115825
|
-
const [
|
|
115826
|
-
const [
|
|
115827
|
-
const [
|
|
115828
|
-
const [
|
|
116053
|
+
const [_waitingErr, waiting] = results[0];
|
|
116054
|
+
const [_activeErr, active] = results[1];
|
|
116055
|
+
const [_delayedErr, delayed] = results[2];
|
|
116056
|
+
const [_failedErr, failed] = results[3];
|
|
115829
116057
|
return {
|
|
115830
116058
|
name: this.queueName,
|
|
115831
116059
|
driver: "redis",
|
|
@@ -115908,6 +116136,7 @@ class QuasarAgent {
|
|
|
115908
116136
|
interval;
|
|
115909
116137
|
probe;
|
|
115910
116138
|
queueProbes = [];
|
|
116139
|
+
bridges = [];
|
|
115911
116140
|
timer = null;
|
|
115912
116141
|
prefix = "gravito:quasar:node:";
|
|
115913
116142
|
nodeId;
|
|
@@ -115984,7 +116213,30 @@ class QuasarAgent {
|
|
|
115984
116213
|
this.queueProbes.push(new LaravelProbe(this.monitorRedis, name));
|
|
115985
116214
|
} else if (type === "bull") {
|
|
115986
116215
|
this.queueProbes.push(new BullProbe(this.monitorRedis, name));
|
|
116216
|
+
} else if (type === "bullmq") {
|
|
116217
|
+
this.queueProbes.push(new BullMQProbe(this.monitorRedis, name));
|
|
116218
|
+
} else if (type === "bee-queue") {
|
|
116219
|
+
this.queueProbes.push(new BeeQueueProbe(this.monitorRedis, name));
|
|
116220
|
+
}
|
|
116221
|
+
}
|
|
116222
|
+
attachBridge(worker, type) {
|
|
116223
|
+
if (!this.transportRedis) {
|
|
116224
|
+
console.warn("[Quasar] Cannot attach bridge: transport connection required");
|
|
116225
|
+
return;
|
|
115987
116226
|
}
|
|
116227
|
+
const workerId = this.nodeId || `${this.service}-${process.pid}`;
|
|
116228
|
+
let bridge;
|
|
116229
|
+
if (type === "bullmq") {
|
|
116230
|
+
bridge = new BullMQBridge(this.transportRedis, "flux_console:", workerId);
|
|
116231
|
+
} else if (type === "bee-queue") {
|
|
116232
|
+
bridge = new BeeQueueBridge(this.transportRedis, "flux_console:", workerId);
|
|
116233
|
+
} else {
|
|
116234
|
+
console.warn(`[Quasar] Unknown bridge type: ${type}`);
|
|
116235
|
+
return;
|
|
116236
|
+
}
|
|
116237
|
+
bridge.attach(worker);
|
|
116238
|
+
this.bridges.push(bridge);
|
|
116239
|
+
console.log(`[Quasar] \uD83D\uDD17 Attached ${type} bridge to worker`);
|
|
115988
116240
|
}
|
|
115989
116241
|
async enableRemoteControl() {
|
|
115990
116242
|
if (!this.monitorRedis) {
|
|
@@ -118486,7 +118738,7 @@ class QueueService {
|
|
|
118486
118738
|
return jobs;
|
|
118487
118739
|
}
|
|
118488
118740
|
}
|
|
118489
|
-
async recordStatusMetrics(nodes = {}) {
|
|
118741
|
+
async recordStatusMetrics(nodes = {}, injectedWorkers) {
|
|
118490
118742
|
const stats = await this.listQueues();
|
|
118491
118743
|
const totals = stats.reduce((acc, q3) => {
|
|
118492
118744
|
acc.waiting += q3.waiting;
|
|
@@ -118499,7 +118751,7 @@ class QueueService {
|
|
|
118499
118751
|
pipe.set(`flux_console:metrics:waiting:${now}`, totals.waiting, "EX", 3600);
|
|
118500
118752
|
pipe.set(`flux_console:metrics:delayed:${now}`, totals.delayed, "EX", 3600);
|
|
118501
118753
|
pipe.set(`flux_console:metrics:failed:${now}`, totals.failed, "EX", 3600);
|
|
118502
|
-
const workers = await this.listWorkers();
|
|
118754
|
+
const workers = injectedWorkers || await this.listWorkers();
|
|
118503
118755
|
pipe.set(`flux_console:metrics:workers:${now}`, workers.length, "EX", 3600);
|
|
118504
118756
|
await pipe.exec();
|
|
118505
118757
|
this.logEmitter.emit("stats", {
|
|
@@ -118688,8 +118940,8 @@ class QueueService {
|
|
|
118688
118940
|
if (results.length >= limit) {
|
|
118689
118941
|
break;
|
|
118690
118942
|
}
|
|
118691
|
-
const
|
|
118692
|
-
for (const jobType of
|
|
118943
|
+
const types7 = type === "all" ? ["waiting", "delayed", "failed"] : [type];
|
|
118944
|
+
for (const jobType of types7) {
|
|
118693
118945
|
if (results.length >= limit) {
|
|
118694
118946
|
break;
|
|
118695
118947
|
}
|
|
@@ -118831,14 +119083,85 @@ queueService.connect().then(() => pulseService.connect()).then(() => commandServ
|
|
|
118831
119083
|
});
|
|
118832
119084
|
agent.start().catch((err) => console.error("[FluxConsole] Quasar Agent Error:", err));
|
|
118833
119085
|
console.log(`[FluxConsole] Connected to Redis at ${REDIS_URL}`);
|
|
118834
|
-
|
|
118835
|
-
|
|
118836
|
-
|
|
118837
|
-
|
|
119086
|
+
const updateMetrics = async () => {
|
|
119087
|
+
try {
|
|
119088
|
+
const [pulseNodes, legacyWorkers] = await Promise.all([
|
|
119089
|
+
pulseService.getNodes(),
|
|
119090
|
+
queueService.listWorkers()
|
|
119091
|
+
]);
|
|
119092
|
+
const pulseWorkers = Object.values(pulseNodes).flat().flatMap((node) => {
|
|
119093
|
+
const mainNode = {
|
|
119094
|
+
id: node.id,
|
|
119095
|
+
service: node.service,
|
|
119096
|
+
status: node.runtime.status || "online",
|
|
119097
|
+
pid: node.pid,
|
|
119098
|
+
uptime: node.runtime.uptime,
|
|
119099
|
+
metrics: {
|
|
119100
|
+
cpu: node.cpu.process,
|
|
119101
|
+
cores: node.cpu.cores,
|
|
119102
|
+
ram: {
|
|
119103
|
+
rss: node.memory.process.rss,
|
|
119104
|
+
heapUsed: node.memory.process.heapUsed,
|
|
119105
|
+
total: node.memory.system.total
|
|
119106
|
+
}
|
|
119107
|
+
},
|
|
119108
|
+
queues: node.queues,
|
|
119109
|
+
meta: node.meta
|
|
119110
|
+
};
|
|
119111
|
+
const subWorkers = [];
|
|
119112
|
+
if (node.meta?.laravel?.workers && Array.isArray(node.meta.laravel.workers)) {
|
|
119113
|
+
node.meta.laravel.workers.forEach((w3) => {
|
|
119114
|
+
subWorkers.push({
|
|
119115
|
+
id: `${node.id}-php-${w3.pid}`,
|
|
119116
|
+
service: `${node.service} / LARAVEL`,
|
|
119117
|
+
status: w3.status === "running" || w3.status === "sleep" ? "online" : "idle",
|
|
119118
|
+
pid: w3.pid,
|
|
119119
|
+
uptime: node.runtime.uptime,
|
|
119120
|
+
metrics: {
|
|
119121
|
+
cpu: w3.cpu,
|
|
119122
|
+
cores: 1,
|
|
119123
|
+
ram: {
|
|
119124
|
+
rss: w3.memory,
|
|
119125
|
+
heapUsed: w3.memory,
|
|
119126
|
+
total: node.memory.system.total
|
|
119127
|
+
}
|
|
119128
|
+
},
|
|
119129
|
+
meta: { isVirtual: true, cmdline: w3.cmdline }
|
|
119130
|
+
});
|
|
119131
|
+
});
|
|
119132
|
+
}
|
|
119133
|
+
return [mainNode, ...subWorkers];
|
|
119134
|
+
});
|
|
119135
|
+
const formattedLegacy = legacyWorkers.map((w3) => ({
|
|
119136
|
+
id: w3.id,
|
|
119137
|
+
status: "online",
|
|
119138
|
+
pid: w3.pid,
|
|
119139
|
+
uptime: w3.uptime,
|
|
119140
|
+
metrics: {
|
|
119141
|
+
cpu: (w3.loadAvg[0] || 0) * 100,
|
|
119142
|
+
cores: 0,
|
|
119143
|
+
ram: {
|
|
119144
|
+
rss: parseInt(w3.memory.rss || "0", 10),
|
|
119145
|
+
heapUsed: parseInt(w3.memory.heapUsed || "0", 10),
|
|
119146
|
+
total: 0
|
|
119147
|
+
}
|
|
119148
|
+
},
|
|
119149
|
+
queues: w3.queues.map((q3) => ({
|
|
119150
|
+
name: q3,
|
|
119151
|
+
size: { waiting: 0, active: 0, failed: 0, delayed: 0 }
|
|
119152
|
+
})),
|
|
119153
|
+
meta: {}
|
|
119154
|
+
}));
|
|
119155
|
+
await queueService.recordStatusMetrics(pulseNodes, [...pulseWorkers, ...formattedLegacy]);
|
|
119156
|
+
} catch (err) {
|
|
119157
|
+
console.error("[FluxConsole] Metrics Update Error:", err);
|
|
119158
|
+
}
|
|
119159
|
+
};
|
|
119160
|
+
setInterval(updateMetrics, 2000);
|
|
118838
119161
|
setInterval(() => {
|
|
118839
119162
|
queueService.tickScheduler().catch(console.error);
|
|
118840
119163
|
}, 5000);
|
|
118841
|
-
|
|
119164
|
+
updateMetrics();
|
|
118842
119165
|
}).catch((err) => {
|
|
118843
119166
|
console.error("[FluxConsole] Failed to connect to Redis", err);
|
|
118844
119167
|
});
|
|
@@ -119032,9 +119355,79 @@ api.get("/throughput", async (c3) => {
|
|
|
119032
119355
|
});
|
|
119033
119356
|
api.get("/workers", async (c3) => {
|
|
119034
119357
|
try {
|
|
119035
|
-
const
|
|
119036
|
-
|
|
119358
|
+
const [legacyWorkers, pulseNodes] = await Promise.all([
|
|
119359
|
+
queueService.listWorkers(),
|
|
119360
|
+
pulseService.getNodes()
|
|
119361
|
+
]);
|
|
119362
|
+
const pulseWorkers = Object.values(pulseNodes).flat().flatMap((node) => {
|
|
119363
|
+
const mainNode = {
|
|
119364
|
+
id: node.id,
|
|
119365
|
+
service: node.service,
|
|
119366
|
+
status: node.runtime.status || "online",
|
|
119367
|
+
pid: node.pid,
|
|
119368
|
+
uptime: node.runtime.uptime,
|
|
119369
|
+
metrics: {
|
|
119370
|
+
cpu: node.cpu.process,
|
|
119371
|
+
cores: node.cpu.cores,
|
|
119372
|
+
ram: {
|
|
119373
|
+
rss: node.memory.process.rss,
|
|
119374
|
+
heapUsed: node.memory.process.heapUsed,
|
|
119375
|
+
total: node.memory.system.total
|
|
119376
|
+
}
|
|
119377
|
+
},
|
|
119378
|
+
queues: node.queues,
|
|
119379
|
+
meta: node.meta
|
|
119380
|
+
};
|
|
119381
|
+
const subWorkers = [];
|
|
119382
|
+
if (node.meta?.laravel?.workers && Array.isArray(node.meta.laravel.workers)) {
|
|
119383
|
+
node.meta.laravel.workers.forEach((w3) => {
|
|
119384
|
+
subWorkers.push({
|
|
119385
|
+
id: `${node.id}-php-${w3.pid}`,
|
|
119386
|
+
service: `${node.service} / LARAVEL`,
|
|
119387
|
+
status: w3.status === "running" || w3.status === "sleep" ? "online" : "idle",
|
|
119388
|
+
pid: w3.pid,
|
|
119389
|
+
uptime: node.runtime.uptime,
|
|
119390
|
+
metrics: {
|
|
119391
|
+
cpu: w3.cpu,
|
|
119392
|
+
cores: 1,
|
|
119393
|
+
ram: {
|
|
119394
|
+
rss: w3.memory,
|
|
119395
|
+
heapUsed: w3.memory,
|
|
119396
|
+
total: node.memory.system.total
|
|
119397
|
+
}
|
|
119398
|
+
},
|
|
119399
|
+
meta: {
|
|
119400
|
+
isVirtual: true,
|
|
119401
|
+
cmdline: w3.cmdline
|
|
119402
|
+
}
|
|
119403
|
+
});
|
|
119404
|
+
});
|
|
119405
|
+
}
|
|
119406
|
+
return [mainNode, ...subWorkers];
|
|
119407
|
+
});
|
|
119408
|
+
const formattedLegacy = legacyWorkers.map((w3) => ({
|
|
119409
|
+
id: w3.id,
|
|
119410
|
+
status: "online",
|
|
119411
|
+
pid: w3.pid,
|
|
119412
|
+
uptime: w3.uptime,
|
|
119413
|
+
metrics: {
|
|
119414
|
+
cpu: (w3.loadAvg[0] || 0) * 100,
|
|
119415
|
+
cores: 0,
|
|
119416
|
+
ram: {
|
|
119417
|
+
rss: parseInt(w3.memory.rss || "0", 10),
|
|
119418
|
+
heapUsed: parseInt(w3.memory.heapUsed || "0", 10),
|
|
119419
|
+
total: 0
|
|
119420
|
+
}
|
|
119421
|
+
},
|
|
119422
|
+
queues: w3.queues.map((q3) => ({
|
|
119423
|
+
name: q3,
|
|
119424
|
+
size: { waiting: 0, active: 0, failed: 0, delayed: 0 }
|
|
119425
|
+
})),
|
|
119426
|
+
meta: {}
|
|
119427
|
+
}));
|
|
119428
|
+
return c3.json({ workers: [...pulseWorkers, ...formattedLegacy] });
|
|
119037
119429
|
} catch (_err) {
|
|
119430
|
+
console.error(_err);
|
|
119038
119431
|
return c3.json({ error: "Failed to fetch workers" }, 500);
|
|
119039
119432
|
}
|
|
119040
119433
|
});
|