s3db.js 13.6.0 → 14.0.2
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/README.md +139 -43
- package/dist/s3db.cjs +72425 -38970
- package/dist/s3db.cjs.map +1 -1
- package/dist/s3db.es.js +72177 -38764
- package/dist/s3db.es.js.map +1 -1
- package/mcp/lib/base-handler.js +157 -0
- package/mcp/lib/handlers/connection-handler.js +280 -0
- package/mcp/lib/handlers/query-handler.js +533 -0
- package/mcp/lib/handlers/resource-handler.js +428 -0
- package/mcp/lib/tool-registry.js +336 -0
- package/mcp/lib/tools/connection-tools.js +161 -0
- package/mcp/lib/tools/query-tools.js +267 -0
- package/mcp/lib/tools/resource-tools.js +404 -0
- package/package.json +94 -49
- package/src/clients/memory-client.class.js +346 -191
- package/src/clients/memory-storage.class.js +300 -84
- package/src/clients/s3-client.class.js +7 -6
- package/src/concerns/geo-encoding.js +19 -2
- package/src/concerns/ip.js +59 -9
- package/src/concerns/money.js +8 -1
- package/src/concerns/password-hashing.js +49 -8
- package/src/concerns/plugin-storage.js +186 -18
- package/src/concerns/storage-drivers/filesystem-driver.js +284 -0
- package/src/database.class.js +139 -29
- package/src/errors.js +332 -42
- package/src/plugins/api/auth/oidc-auth.js +66 -17
- package/src/plugins/api/auth/strategies/base-strategy.class.js +74 -0
- package/src/plugins/api/auth/strategies/factory.class.js +63 -0
- package/src/plugins/api/auth/strategies/global-strategy.class.js +44 -0
- package/src/plugins/api/auth/strategies/path-based-strategy.class.js +83 -0
- package/src/plugins/api/auth/strategies/path-rules-strategy.class.js +118 -0
- package/src/plugins/api/concerns/failban-manager.js +106 -57
- package/src/plugins/api/concerns/opengraph-helper.js +116 -0
- package/src/plugins/api/concerns/route-context.js +601 -0
- package/src/plugins/api/concerns/state-machine.js +288 -0
- package/src/plugins/api/index.js +180 -41
- package/src/plugins/api/routes/auth-routes.js +198 -30
- package/src/plugins/api/routes/resource-routes.js +19 -4
- package/src/plugins/api/server/health-manager.class.js +163 -0
- package/src/plugins/api/server/middleware-chain.class.js +310 -0
- package/src/plugins/api/server/router.class.js +472 -0
- package/src/plugins/api/server.js +280 -1303
- package/src/plugins/api/utils/custom-routes.js +17 -5
- package/src/plugins/api/utils/guards.js +76 -17
- package/src/plugins/api/utils/openapi-generator-cached.class.js +133 -0
- package/src/plugins/api/utils/openapi-generator.js +7 -6
- package/src/plugins/api/utils/template-engine.js +77 -3
- package/src/plugins/audit.plugin.js +30 -8
- package/src/plugins/backup.plugin.js +110 -14
- package/src/plugins/cache/cache.class.js +22 -5
- package/src/plugins/cache/filesystem-cache.class.js +116 -19
- package/src/plugins/cache/memory-cache.class.js +211 -57
- package/src/plugins/cache/multi-tier-cache.class.js +371 -0
- package/src/plugins/cache/partition-aware-filesystem-cache.class.js +168 -47
- package/src/plugins/cache/redis-cache.class.js +552 -0
- package/src/plugins/cache/s3-cache.class.js +17 -8
- package/src/plugins/cache.plugin.js +176 -61
- package/src/plugins/cloud-inventory/drivers/alibaba-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/aws-driver.js +60 -29
- package/src/plugins/cloud-inventory/drivers/azure-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/base-driver.js +16 -2
- package/src/plugins/cloud-inventory/drivers/cloudflare-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/digitalocean-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/hetzner-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/linode-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/mongodb-atlas-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/vultr-driver.js +8 -1
- package/src/plugins/cloud-inventory/index.js +29 -8
- package/src/plugins/cloud-inventory/registry.js +64 -42
- package/src/plugins/cloud-inventory.plugin.js +240 -138
- package/src/plugins/concerns/plugin-dependencies.js +54 -0
- package/src/plugins/concerns/resource-names.js +100 -0
- package/src/plugins/consumers/index.js +10 -2
- package/src/plugins/consumers/sqs-consumer.js +12 -2
- package/src/plugins/cookie-farm-suite.plugin.js +278 -0
- package/src/plugins/cookie-farm.errors.js +73 -0
- package/src/plugins/cookie-farm.plugin.js +869 -0
- package/src/plugins/costs.plugin.js +7 -1
- package/src/plugins/eventual-consistency/analytics.js +94 -19
- package/src/plugins/eventual-consistency/config.js +15 -7
- package/src/plugins/eventual-consistency/consolidation.js +29 -11
- package/src/plugins/eventual-consistency/garbage-collection.js +3 -1
- package/src/plugins/eventual-consistency/helpers.js +39 -14
- package/src/plugins/eventual-consistency/install.js +21 -2
- package/src/plugins/eventual-consistency/utils.js +32 -10
- package/src/plugins/fulltext.plugin.js +38 -11
- package/src/plugins/geo.plugin.js +61 -9
- package/src/plugins/identity/concerns/config.js +61 -0
- package/src/plugins/identity/concerns/mfa-manager.js +15 -2
- package/src/plugins/identity/concerns/rate-limit.js +124 -0
- package/src/plugins/identity/concerns/resource-schemas.js +9 -1
- package/src/plugins/identity/concerns/token-generator.js +29 -4
- package/src/plugins/identity/drivers/auth-driver.interface.js +76 -0
- package/src/plugins/identity/drivers/client-credentials-driver.js +127 -0
- package/src/plugins/identity/drivers/index.js +18 -0
- package/src/plugins/identity/drivers/password-driver.js +122 -0
- package/src/plugins/identity/email-service.js +17 -2
- package/src/plugins/identity/index.js +413 -69
- package/src/plugins/identity/oauth2-server.js +413 -30
- package/src/plugins/identity/oidc-discovery.js +16 -8
- package/src/plugins/identity/rsa-keys.js +115 -35
- package/src/plugins/identity/server.js +166 -45
- package/src/plugins/identity/session-manager.js +53 -7
- package/src/plugins/identity/ui/pages/mfa-verification.js +17 -15
- package/src/plugins/identity/ui/routes.js +363 -255
- package/src/plugins/importer/index.js +153 -20
- package/src/plugins/index.js +9 -2
- package/src/plugins/kubernetes-inventory/index.js +6 -0
- package/src/plugins/kubernetes-inventory/k8s-driver.js +867 -0
- package/src/plugins/kubernetes-inventory/resource-types.js +274 -0
- package/src/plugins/kubernetes-inventory.plugin.js +980 -0
- package/src/plugins/metrics.plugin.js +64 -16
- package/src/plugins/ml/base-model.class.js +25 -15
- package/src/plugins/ml/regression-model.class.js +1 -1
- package/src/plugins/ml.errors.js +57 -25
- package/src/plugins/ml.plugin.js +28 -4
- package/src/plugins/namespace.js +210 -0
- package/src/plugins/plugin.class.js +180 -8
- package/src/plugins/puppeteer/console-monitor.js +729 -0
- package/src/plugins/puppeteer/cookie-manager.js +492 -0
- package/src/plugins/puppeteer/network-monitor.js +816 -0
- package/src/plugins/puppeteer/performance-manager.js +746 -0
- package/src/plugins/puppeteer/proxy-manager.js +478 -0
- package/src/plugins/puppeteer/stealth-manager.js +556 -0
- package/src/plugins/puppeteer.errors.js +81 -0
- package/src/plugins/puppeteer.plugin.js +1327 -0
- package/src/plugins/queue-consumer.plugin.js +69 -14
- package/src/plugins/recon/behaviors/uptime-behavior.js +691 -0
- package/src/plugins/recon/concerns/command-runner.js +148 -0
- package/src/plugins/recon/concerns/diff-detector.js +372 -0
- package/src/plugins/recon/concerns/fingerprint-builder.js +307 -0
- package/src/plugins/recon/concerns/process-manager.js +338 -0
- package/src/plugins/recon/concerns/report-generator.js +478 -0
- package/src/plugins/recon/concerns/security-analyzer.js +571 -0
- package/src/plugins/recon/concerns/target-normalizer.js +68 -0
- package/src/plugins/recon/config/defaults.js +321 -0
- package/src/plugins/recon/config/resources.js +370 -0
- package/src/plugins/recon/index.js +778 -0
- package/src/plugins/recon/managers/dependency-manager.js +174 -0
- package/src/plugins/recon/managers/scheduler-manager.js +179 -0
- package/src/plugins/recon/managers/storage-manager.js +745 -0
- package/src/plugins/recon/managers/target-manager.js +274 -0
- package/src/plugins/recon/stages/asn-stage.js +314 -0
- package/src/plugins/recon/stages/certificate-stage.js +84 -0
- package/src/plugins/recon/stages/dns-stage.js +107 -0
- package/src/plugins/recon/stages/dnsdumpster-stage.js +362 -0
- package/src/plugins/recon/stages/fingerprint-stage.js +71 -0
- package/src/plugins/recon/stages/google-dorks-stage.js +440 -0
- package/src/plugins/recon/stages/http-stage.js +89 -0
- package/src/plugins/recon/stages/latency-stage.js +148 -0
- package/src/plugins/recon/stages/massdns-stage.js +302 -0
- package/src/plugins/recon/stages/osint-stage.js +1373 -0
- package/src/plugins/recon/stages/ports-stage.js +169 -0
- package/src/plugins/recon/stages/screenshot-stage.js +94 -0
- package/src/plugins/recon/stages/secrets-stage.js +514 -0
- package/src/plugins/recon/stages/subdomains-stage.js +295 -0
- package/src/plugins/recon/stages/tls-audit-stage.js +78 -0
- package/src/plugins/recon/stages/vulnerability-stage.js +78 -0
- package/src/plugins/recon/stages/web-discovery-stage.js +113 -0
- package/src/plugins/recon/stages/whois-stage.js +349 -0
- package/src/plugins/recon.plugin.js +75 -0
- package/src/plugins/recon.plugin.js.backup +2635 -0
- package/src/plugins/relation.errors.js +87 -14
- package/src/plugins/replicator.plugin.js +514 -137
- package/src/plugins/replicators/base-replicator.class.js +89 -1
- package/src/plugins/replicators/bigquery-replicator.class.js +66 -22
- package/src/plugins/replicators/dynamodb-replicator.class.js +22 -15
- package/src/plugins/replicators/mongodb-replicator.class.js +22 -15
- package/src/plugins/replicators/mysql-replicator.class.js +52 -17
- package/src/plugins/replicators/planetscale-replicator.class.js +30 -4
- package/src/plugins/replicators/postgres-replicator.class.js +62 -27
- package/src/plugins/replicators/s3db-replicator.class.js +25 -18
- package/src/plugins/replicators/schema-sync.helper.js +3 -3
- package/src/plugins/replicators/sqs-replicator.class.js +8 -2
- package/src/plugins/replicators/turso-replicator.class.js +23 -3
- package/src/plugins/replicators/webhook-replicator.class.js +42 -4
- package/src/plugins/s3-queue.plugin.js +464 -65
- package/src/plugins/scheduler.plugin.js +20 -6
- package/src/plugins/state-machine.plugin.js +40 -9
- package/src/plugins/tfstate/README.md +126 -126
- package/src/plugins/tfstate/base-driver.js +28 -4
- package/src/plugins/tfstate/errors.js +65 -10
- package/src/plugins/tfstate/filesystem-driver.js +52 -8
- package/src/plugins/tfstate/index.js +163 -90
- package/src/plugins/tfstate/s3-driver.js +64 -6
- package/src/plugins/ttl.plugin.js +72 -17
- package/src/plugins/vector/distances.js +18 -12
- package/src/plugins/vector/kmeans.js +26 -4
- package/src/resource.class.js +115 -19
- package/src/testing/factory.class.js +20 -3
- package/src/testing/seeder.class.js +7 -1
- package/src/clients/memory-client.md +0 -917
- package/src/plugins/cloud-inventory/drivers/mock-drivers.js +0 -449
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { PluginError } from '../../errors.js';
|
|
2
|
+
|
|
3
|
+
const PREFIX = 'plg_';
|
|
4
|
+
|
|
5
|
+
function normalizeNamespace(namespace) {
|
|
6
|
+
if (!namespace) return null;
|
|
7
|
+
const text = String(namespace).trim().toLowerCase();
|
|
8
|
+
if (!text) return null;
|
|
9
|
+
const normalized = text
|
|
10
|
+
.replace(/[^a-z0-9]+/g, '-') // Use hyphens instead of underscores
|
|
11
|
+
.replace(/^-+/, '')
|
|
12
|
+
.replace(/-+$/, '');
|
|
13
|
+
return normalized || null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function applyNamespace(name, namespace) {
|
|
17
|
+
const ensured = ensurePlgPrefix(name);
|
|
18
|
+
if (!namespace) {
|
|
19
|
+
return ensured;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const withoutPrefix = ensured.slice(PREFIX.length);
|
|
23
|
+
if (withoutPrefix.startsWith(`${namespace}_`)) {
|
|
24
|
+
return ensured;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return `${PREFIX}${namespace}_${withoutPrefix}`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function sanitizeName(name) {
|
|
31
|
+
if (!name || typeof name !== 'string') {
|
|
32
|
+
throw new PluginError('[resource-names] Resource name must be a non-empty string', {
|
|
33
|
+
pluginName: 'SharedConcerns',
|
|
34
|
+
operation: 'resourceNames:sanitize',
|
|
35
|
+
statusCode: 400,
|
|
36
|
+
retriable: false,
|
|
37
|
+
suggestion: 'Pass a non-empty string when deriving resource names.'
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
return name.trim();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function ensurePlgPrefix(name) {
|
|
44
|
+
const sanitized = sanitizeName(name);
|
|
45
|
+
if (sanitized.startsWith(PREFIX)) {
|
|
46
|
+
return sanitized;
|
|
47
|
+
}
|
|
48
|
+
return `${PREFIX}${sanitized.replace(/^\_+/, '')}`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function resolveResourceName(pluginKey, { defaultName, override, suffix } = {}, options = {}) {
|
|
52
|
+
const namespace = normalizeNamespace(options.namespace);
|
|
53
|
+
const applyOverrideNamespace = options.applyNamespaceToOverrides === true;
|
|
54
|
+
|
|
55
|
+
if (!defaultName && !override && !suffix) {
|
|
56
|
+
throw new PluginError(`[resource-names] Missing name parameters for plugin "${pluginKey}"`, {
|
|
57
|
+
pluginName: 'SharedConcerns',
|
|
58
|
+
operation: 'resourceNames:resolve',
|
|
59
|
+
statusCode: 400,
|
|
60
|
+
retriable: false,
|
|
61
|
+
suggestion: 'Provide at least one of defaultName, override, or suffix when resolving resource names.'
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (override) {
|
|
66
|
+
const ensured = ensurePlgPrefix(override);
|
|
67
|
+
return applyOverrideNamespace ? applyNamespace(ensured, namespace) : ensured;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (defaultName) {
|
|
71
|
+
const ensured = defaultName.startsWith(PREFIX) ? defaultName : ensurePlgPrefix(defaultName);
|
|
72
|
+
return applyNamespace(ensured, namespace);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (!suffix) {
|
|
76
|
+
throw new PluginError(`[resource-names] Cannot derive resource name for plugin "${pluginKey}" without suffix`, {
|
|
77
|
+
pluginName: 'SharedConcerns',
|
|
78
|
+
operation: 'resourceNames:resolve',
|
|
79
|
+
statusCode: 400,
|
|
80
|
+
retriable: false,
|
|
81
|
+
suggestion: 'Provide a suffix or defaultName when computing derived resource names.'
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const ensured = ensurePlgPrefix(`${pluginKey}_${suffix}`);
|
|
86
|
+
return applyNamespace(ensured, namespace);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function resolveResourceNames(pluginKey, descriptors = {}, options = {}) {
|
|
90
|
+
const result = {};
|
|
91
|
+
for (const [key, descriptor] of Object.entries(descriptors)) {
|
|
92
|
+
if (typeof descriptor === 'string') {
|
|
93
|
+
result[key] = resolveResourceName(pluginKey, { defaultName: descriptor }, options);
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
result[key] = resolveResourceName(pluginKey, descriptor, options);
|
|
98
|
+
}
|
|
99
|
+
return result;
|
|
100
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SqsConsumer } from './sqs-consumer.js';
|
|
2
2
|
import { RabbitMqConsumer } from './rabbitmq-consumer.js';
|
|
3
|
+
import { PluginError } from '../../errors.js';
|
|
3
4
|
|
|
4
5
|
export { SqsConsumer, RabbitMqConsumer };
|
|
5
6
|
|
|
@@ -18,7 +19,14 @@ export const CONSUMER_DRIVERS = {
|
|
|
18
19
|
export function createConsumer(driver, config) {
|
|
19
20
|
const ConsumerClass = CONSUMER_DRIVERS[driver];
|
|
20
21
|
if (!ConsumerClass) {
|
|
21
|
-
throw new
|
|
22
|
+
throw new PluginError(`Unknown consumer driver: ${driver}`, {
|
|
23
|
+
pluginName: 'ConsumersPlugin',
|
|
24
|
+
operation: 'createConsumer',
|
|
25
|
+
statusCode: 400,
|
|
26
|
+
retriable: false,
|
|
27
|
+
suggestion: `Use one of the available drivers: ${Object.keys(CONSUMER_DRIVERS).join(', ')}`,
|
|
28
|
+
driver
|
|
29
|
+
});
|
|
22
30
|
}
|
|
23
31
|
return new ConsumerClass(config);
|
|
24
|
-
}
|
|
32
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import tryFn from "../../concerns/try-fn.js";
|
|
2
2
|
import requirePluginDependency from "../concerns/plugin-dependencies.js";
|
|
3
|
+
import { PluginError } from '../../errors.js';
|
|
3
4
|
// Remove static SDK import
|
|
4
5
|
// import { SQSClient, ReceiveMessageCommand, DeleteMessageCommand } from '@aws-sdk/client-sqs';
|
|
5
6
|
|
|
@@ -31,7 +32,16 @@ export class SqsConsumer {
|
|
|
31
32
|
|
|
32
33
|
// Carregar SDK dinamicamente
|
|
33
34
|
const [ok, err, sdk] = await tryFn(() => import('@aws-sdk/client-sqs'));
|
|
34
|
-
if (!ok)
|
|
35
|
+
if (!ok) {
|
|
36
|
+
throw new PluginError('SqsConsumer requires @aws-sdk/client-sqs', {
|
|
37
|
+
pluginName: 'ConsumersPlugin',
|
|
38
|
+
operation: 'SqsConsumer.start',
|
|
39
|
+
statusCode: 500,
|
|
40
|
+
retriable: false,
|
|
41
|
+
suggestion: 'Install @aws-sdk/client-sqs as a dependency to enable SQS consumption.',
|
|
42
|
+
original: err
|
|
43
|
+
});
|
|
44
|
+
}
|
|
35
45
|
const { SQSClient, ReceiveMessageCommand, DeleteMessageCommand } = sdk;
|
|
36
46
|
this._SQSClient = SQSClient;
|
|
37
47
|
this._ReceiveMessageCommand = ReceiveMessageCommand;
|
|
@@ -103,4 +113,4 @@ export class SqsConsumer {
|
|
|
103
113
|
}
|
|
104
114
|
return { $body: body, $attributes: attributes, $raw: msg };
|
|
105
115
|
}
|
|
106
|
-
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import tryFn from "../concerns/try-fn.js";
|
|
2
|
+
import { Plugin } from "./plugin.class.js";
|
|
3
|
+
import { PuppeteerPlugin } from "./puppeteer.plugin.js";
|
|
4
|
+
import { CookieFarmPlugin } from "./cookie-farm.plugin.js";
|
|
5
|
+
import { S3QueuePlugin } from "./s3-queue.plugin.js";
|
|
6
|
+
import { TTLPlugin } from "./ttl.plugin.js";
|
|
7
|
+
import { PluginError } from "../errors.js";
|
|
8
|
+
|
|
9
|
+
function sanitizeNamespace(value) {
|
|
10
|
+
return (value || 'persona')
|
|
11
|
+
.toString()
|
|
12
|
+
.trim()
|
|
13
|
+
.toLowerCase()
|
|
14
|
+
.replace(/[^a-z0-9]+/g, '-');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function defaultJobsResource(namespace) {
|
|
18
|
+
return `${namespace.replace(/[^a-z0-9]+/g, '_')}_persona_jobs`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* CookieFarmSuitePlugin
|
|
23
|
+
*
|
|
24
|
+
* Bundles CookieFarm + Puppeteer + S3Queue (+ optional TTL) with shared
|
|
25
|
+
* namespace handling for persona farming workloads.
|
|
26
|
+
*/
|
|
27
|
+
export class CookieFarmSuitePlugin extends Plugin {
|
|
28
|
+
constructor(options = {}) {
|
|
29
|
+
const namespace = options.namespace || 'persona';
|
|
30
|
+
super({ ...options, namespace });
|
|
31
|
+
|
|
32
|
+
this.namespace = this.namespace || sanitizeNamespace(namespace);
|
|
33
|
+
|
|
34
|
+
const jobsResource =
|
|
35
|
+
options.jobsResource ||
|
|
36
|
+
(options.resources && options.resources.jobs) ||
|
|
37
|
+
defaultJobsResource(this.namespace);
|
|
38
|
+
|
|
39
|
+
this.config = {
|
|
40
|
+
namespace: this.namespace,
|
|
41
|
+
jobsResource,
|
|
42
|
+
queue: {
|
|
43
|
+
resource: jobsResource,
|
|
44
|
+
deadLetterResource: options.queue?.deadLetterResource || null,
|
|
45
|
+
visibilityTimeout: options.queue?.visibilityTimeout || 30000,
|
|
46
|
+
pollInterval: options.queue?.pollInterval || 1000,
|
|
47
|
+
maxAttempts: options.queue?.maxAttempts || 3,
|
|
48
|
+
concurrency: options.queue?.concurrency || 1,
|
|
49
|
+
autoStart: options.queue?.autoStart === true,
|
|
50
|
+
...options.queue
|
|
51
|
+
},
|
|
52
|
+
puppeteer: {
|
|
53
|
+
pool: { enabled: false },
|
|
54
|
+
...options.puppeteer
|
|
55
|
+
},
|
|
56
|
+
cookieFarm: {
|
|
57
|
+
...options.cookieFarm
|
|
58
|
+
},
|
|
59
|
+
ttl: options.ttl || null,
|
|
60
|
+
processor: typeof options.processor === 'function' ? options.processor : null
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
this.pluginFactories = {
|
|
64
|
+
puppeteer: options.pluginFactories?.puppeteer ||
|
|
65
|
+
((pluginOptions) => new PuppeteerPlugin(pluginOptions)),
|
|
66
|
+
cookieFarm: options.pluginFactories?.cookieFarm ||
|
|
67
|
+
((pluginOptions) => new CookieFarmPlugin(pluginOptions)),
|
|
68
|
+
queue: options.pluginFactories?.queue ||
|
|
69
|
+
((queueOptions) => new S3QueuePlugin(queueOptions)),
|
|
70
|
+
ttl: options.pluginFactories?.ttl ||
|
|
71
|
+
((ttlOptions) => new TTLPlugin(ttlOptions))
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
this.dependencies = [];
|
|
75
|
+
this.jobsResource = null;
|
|
76
|
+
this.puppeteerPlugin = null;
|
|
77
|
+
this.cookieFarmPlugin = null;
|
|
78
|
+
this.queuePlugin = null;
|
|
79
|
+
this.ttlPlugin = null;
|
|
80
|
+
this.processor = this.config.processor;
|
|
81
|
+
|
|
82
|
+
this.queueHandler = this.queueHandler.bind(this);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
_dependencyName(alias) {
|
|
86
|
+
return `${this.namespace}-${alias}`.toLowerCase();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async _installDependency(alias, plugin) {
|
|
90
|
+
const name = this._dependencyName(alias);
|
|
91
|
+
const instance = await this.database.usePlugin(plugin, name);
|
|
92
|
+
this.dependencies.push({ name, instance });
|
|
93
|
+
return instance;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async _ensureJobsResource() {
|
|
97
|
+
if (this.database.resources?.[this.config.jobsResource]) {
|
|
98
|
+
this.jobsResource = this.database.resources[this.config.jobsResource];
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const [created, err, resource] = await tryFn(() => this.database.createResource({
|
|
103
|
+
name: this.config.jobsResource,
|
|
104
|
+
attributes: {
|
|
105
|
+
id: 'string|required',
|
|
106
|
+
jobType: 'string|required',
|
|
107
|
+
payload: 'json|optional',
|
|
108
|
+
priority: 'number|default:0',
|
|
109
|
+
requestedBy: 'string|optional',
|
|
110
|
+
metadata: 'json|optional',
|
|
111
|
+
createdAt: 'string|required'
|
|
112
|
+
},
|
|
113
|
+
behavior: 'body-overflow',
|
|
114
|
+
timestamps: true,
|
|
115
|
+
asyncPartitions: true,
|
|
116
|
+
partitions: {
|
|
117
|
+
byJobType: { fields: { jobType: 'string' } },
|
|
118
|
+
byPriority: { fields: { priority: 'number' } },
|
|
119
|
+
byDate: { fields: { createdAt: 'string|maxlength:10' } }
|
|
120
|
+
}
|
|
121
|
+
}));
|
|
122
|
+
|
|
123
|
+
if (!created) {
|
|
124
|
+
if (resource) {
|
|
125
|
+
this.jobsResource = resource;
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
throw err;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
this.jobsResource = this.database.resources[this.config.jobsResource];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async onInstall() {
|
|
135
|
+
await this._ensureJobsResource();
|
|
136
|
+
|
|
137
|
+
this.puppeteerPlugin = await this._installDependency('puppeteer',
|
|
138
|
+
this.pluginFactories.puppeteer({
|
|
139
|
+
namespace: this.namespace,
|
|
140
|
+
...this.config.puppeteer
|
|
141
|
+
})
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
this.cookieFarmPlugin = await this._installDependency('cookie-farm',
|
|
145
|
+
this.pluginFactories.cookieFarm({
|
|
146
|
+
namespace: this.namespace,
|
|
147
|
+
...this.config.cookieFarm
|
|
148
|
+
})
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
const queueOptions = {
|
|
152
|
+
namespace: this.namespace,
|
|
153
|
+
resource: this.config.queue.resource,
|
|
154
|
+
deadLetterResource: this.config.queue.deadLetterResource,
|
|
155
|
+
visibilityTimeout: this.config.queue.visibilityTimeout,
|
|
156
|
+
pollInterval: this.config.queue.pollInterval,
|
|
157
|
+
maxAttempts: this.config.queue.maxAttempts,
|
|
158
|
+
concurrency: this.config.queue.concurrency,
|
|
159
|
+
autoStart: this.config.queue.autoStart && typeof this.processor === 'function',
|
|
160
|
+
onMessage: this.queueHandler,
|
|
161
|
+
verbose: this.config.queue.verbose
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
this.queuePlugin = await this._installDependency('queue', this.pluginFactories.queue(queueOptions));
|
|
165
|
+
|
|
166
|
+
if (this.config.ttl) {
|
|
167
|
+
const ttlConfig = {
|
|
168
|
+
namespace: this.namespace,
|
|
169
|
+
...this.config.ttl
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
ttlConfig.resources = ttlConfig.resources || {};
|
|
173
|
+
|
|
174
|
+
if (!ttlConfig.resources[this.queuePlugin.queueResourceName]) {
|
|
175
|
+
ttlConfig.resources[this.queuePlugin.queueResourceName] = {
|
|
176
|
+
ttl: ttlConfig.queue?.ttl || 7200,
|
|
177
|
+
onExpire: ttlConfig.queue?.onExpire || 'hard-delete',
|
|
178
|
+
field: ttlConfig.queue?.field || null
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
delete ttlConfig.queue;
|
|
183
|
+
|
|
184
|
+
this.ttlPlugin = await this._installDependency('ttl', this.pluginFactories.ttl(ttlConfig));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
this.emit('cookieFarmSuite.installed', {
|
|
188
|
+
namespace: this.namespace,
|
|
189
|
+
jobsResource: this.config.jobsResource
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async onStart() {
|
|
194
|
+
if (this.config.queue.autoStart && typeof this.processor === 'function') {
|
|
195
|
+
await this.startProcessing();
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async onStop() {
|
|
200
|
+
await this.stopProcessing();
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async onUninstall(options = {}) {
|
|
204
|
+
await this.onStop();
|
|
205
|
+
|
|
206
|
+
for (const dep of [...this.dependencies].reverse()) {
|
|
207
|
+
await this.database.uninstallPlugin(dep.name, { purgeData: options.purgeData === true });
|
|
208
|
+
}
|
|
209
|
+
this.dependencies = [];
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Register a job processor.
|
|
214
|
+
*/
|
|
215
|
+
async setProcessor(handler, { autoStart = true, concurrency } = {}) {
|
|
216
|
+
this.processor = handler;
|
|
217
|
+
|
|
218
|
+
if (autoStart && typeof handler === 'function') {
|
|
219
|
+
await this.startProcessing({ concurrency });
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Enqueue a persona job.
|
|
225
|
+
*/
|
|
226
|
+
async enqueueJob(data, options = {}) {
|
|
227
|
+
if (!this.jobsResource?.enqueue) {
|
|
228
|
+
throw new PluginError('[CookieFarmSuitePlugin] Queue helpers not initialized yet', {
|
|
229
|
+
pluginName: 'CookieFarmSuitePlugin',
|
|
230
|
+
operation: 'enqueueJob',
|
|
231
|
+
statusCode: 500,
|
|
232
|
+
retriable: false,
|
|
233
|
+
suggestion: 'Call plugin.initialize() before enqueuing jobs so queue helpers are registered.'
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
return await this.jobsResource.enqueue({
|
|
237
|
+
createdAt: new Date().toISOString().slice(0, 10),
|
|
238
|
+
...data
|
|
239
|
+
}, options);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
async startProcessing(options = {}) {
|
|
243
|
+
if (!this.jobsResource?.startProcessing) return;
|
|
244
|
+
const concurrency = options.concurrency || this.config.queue.concurrency;
|
|
245
|
+
await this.jobsResource.startProcessing(this.queueHandler, { concurrency });
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
async stopProcessing() {
|
|
249
|
+
if (this.jobsResource?.stopProcessing) {
|
|
250
|
+
await this.jobsResource.stopProcessing();
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
async queueHandler(record, context) {
|
|
255
|
+
if (typeof this.processor !== 'function') {
|
|
256
|
+
throw new PluginError('[CookieFarmSuitePlugin] No processor registered. Call setProcessor(fn) first.', {
|
|
257
|
+
pluginName: 'CookieFarmSuitePlugin',
|
|
258
|
+
operation: 'queueHandler',
|
|
259
|
+
statusCode: 500,
|
|
260
|
+
retriable: false,
|
|
261
|
+
suggestion: 'Register a processor via plugin.setProcessor(jobHandler) before starting the queue.'
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const helpers = {
|
|
266
|
+
puppeteer: this.puppeteerPlugin,
|
|
267
|
+
cookieFarm: this.cookieFarmPlugin,
|
|
268
|
+
queue: this.queuePlugin,
|
|
269
|
+
enqueue: this.enqueueJob.bind(this),
|
|
270
|
+
resource: this.jobsResource,
|
|
271
|
+
plugin: this
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
return await this.processor(record, context, helpers);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export default CookieFarmSuitePlugin;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { PluginError } from '../errors.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Custom errors for CookieFarmPlugin with actionable guidance.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export class CookieFarmError extends PluginError {
|
|
8
|
+
constructor(message, details = {}) {
|
|
9
|
+
const merged = {
|
|
10
|
+
pluginName: details.pluginName || 'CookieFarmPlugin',
|
|
11
|
+
operation: details.operation || 'unknown',
|
|
12
|
+
statusCode: details.statusCode ?? 500,
|
|
13
|
+
retriable: details.retriable ?? false,
|
|
14
|
+
suggestion: details.suggestion ?? 'Check CookieFarmPlugin configuration and persona storage before retrying.',
|
|
15
|
+
...details
|
|
16
|
+
};
|
|
17
|
+
super(message, merged);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class PersonaNotFoundError extends CookieFarmError {
|
|
22
|
+
constructor(personaId, details = {}) {
|
|
23
|
+
super(`Persona not found: ${personaId}`, {
|
|
24
|
+
code: 'PERSONA_NOT_FOUND',
|
|
25
|
+
personaId,
|
|
26
|
+
statusCode: 404,
|
|
27
|
+
retriable: false,
|
|
28
|
+
suggestion: 'Ensure the persona exists or create it before running CookieFarm operations.',
|
|
29
|
+
docs: details.docs || 'https://github.com/forattini-dev/s3db.js/blob/main/docs/plugins/cookie-farm.md',
|
|
30
|
+
...details
|
|
31
|
+
});
|
|
32
|
+
this.name = 'PersonaNotFoundError';
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export class WarmupError extends CookieFarmError {
|
|
37
|
+
constructor(message, details = {}) {
|
|
38
|
+
super(message, {
|
|
39
|
+
code: 'WARMUP_ERROR',
|
|
40
|
+
statusCode: details.statusCode ?? 500,
|
|
41
|
+
retriable: details.retriable ?? true,
|
|
42
|
+
suggestion: details.suggestion ?? 'Inspect warmup logs and retry; ensure upstream services are reachable.',
|
|
43
|
+
...details
|
|
44
|
+
});
|
|
45
|
+
this.name = 'WarmupError';
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export class GenerationError extends CookieFarmError {
|
|
50
|
+
constructor(message, details = {}) {
|
|
51
|
+
super(message, {
|
|
52
|
+
code: 'GENERATION_ERROR',
|
|
53
|
+
statusCode: details.statusCode ?? 500,
|
|
54
|
+
retriable: details.retriable ?? false,
|
|
55
|
+
suggestion: details.suggestion ?? 'Review persona generation inputs and plugin configuration before retrying.',
|
|
56
|
+
...details
|
|
57
|
+
});
|
|
58
|
+
this.name = 'GenerationError';
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export class QualityCalculationError extends CookieFarmError {
|
|
63
|
+
constructor(message, details = {}) {
|
|
64
|
+
super(message, {
|
|
65
|
+
code: 'QUALITY_CALCULATION_ERROR',
|
|
66
|
+
statusCode: details.statusCode ?? 500,
|
|
67
|
+
retriable: details.retriable ?? false,
|
|
68
|
+
suggestion: details.suggestion ?? 'Verify quality metrics configuration and input scores.',
|
|
69
|
+
...details
|
|
70
|
+
});
|
|
71
|
+
this.name = 'QualityCalculationError';
|
|
72
|
+
}
|
|
73
|
+
}
|