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
|
@@ -32,9 +32,25 @@
|
|
|
32
32
|
*/
|
|
33
33
|
|
|
34
34
|
import { requirePluginDependency } from '../../concerns/plugin-dependencies.js';
|
|
35
|
+
import tryFn from '../../../concerns/try-fn.js';
|
|
36
|
+
import { resolveResourceNames } from '../../concerns/resource-names.js';
|
|
35
37
|
|
|
36
38
|
export class FailbanManager {
|
|
37
39
|
constructor(options = {}) {
|
|
40
|
+
this.namespace = options.namespace || null;
|
|
41
|
+
const resourceOverrides = options.resourceNames || options.resources || {};
|
|
42
|
+
this._resourceDescriptors = {
|
|
43
|
+
bans: {
|
|
44
|
+
defaultName: 'plg_api_failban_bans',
|
|
45
|
+
override: resourceOverrides.bans
|
|
46
|
+
},
|
|
47
|
+
violations: {
|
|
48
|
+
defaultName: 'plg_api_failban_violations',
|
|
49
|
+
override: resourceOverrides.violations
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
this.resourceNames = this._resolveResourceNames();
|
|
53
|
+
|
|
38
54
|
this.options = {
|
|
39
55
|
enabled: options.enabled !== false,
|
|
40
56
|
database: options.database,
|
|
@@ -52,7 +68,8 @@ export class FailbanManager {
|
|
|
52
68
|
blockedCountries: options.geo?.blockedCountries || [],
|
|
53
69
|
blockUnknown: options.geo?.blockUnknown || false,
|
|
54
70
|
cacheResults: options.geo?.cacheResults !== false
|
|
55
|
-
}
|
|
71
|
+
},
|
|
72
|
+
resources: this.resourceNames
|
|
56
73
|
};
|
|
57
74
|
|
|
58
75
|
this.database = options.database;
|
|
@@ -64,6 +81,18 @@ export class FailbanManager {
|
|
|
64
81
|
this.cleanupTimer = null;
|
|
65
82
|
}
|
|
66
83
|
|
|
84
|
+
_resolveResourceNames() {
|
|
85
|
+
return resolveResourceNames('api_failban', this._resourceDescriptors, {
|
|
86
|
+
namespace: this.namespace
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
setNamespace(namespace) {
|
|
91
|
+
this.namespace = namespace;
|
|
92
|
+
this.resourceNames = this._resolveResourceNames();
|
|
93
|
+
this.options.resources = this.resourceNames;
|
|
94
|
+
}
|
|
95
|
+
|
|
67
96
|
/**
|
|
68
97
|
* Initialize failban manager
|
|
69
98
|
*/
|
|
@@ -119,52 +148,61 @@ export class FailbanManager {
|
|
|
119
148
|
* @private
|
|
120
149
|
*/
|
|
121
150
|
async _createBansResource() {
|
|
122
|
-
const resourceName =
|
|
123
|
-
|
|
151
|
+
const resourceName = this.resourceNames.bans;
|
|
124
152
|
try {
|
|
125
153
|
return await this.database.getResource(resourceName);
|
|
126
154
|
} catch (err) {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
timestamps: true,
|
|
143
|
-
partitions: {
|
|
144
|
-
byExpiry: {
|
|
145
|
-
fields: { expiresAtCohort: 'string' }
|
|
146
|
-
}
|
|
155
|
+
// fall through
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const [created, createErr, resource] = await tryFn(() => this.database.createResource({
|
|
159
|
+
name: resourceName,
|
|
160
|
+
attributes: {
|
|
161
|
+
ip: 'string|required',
|
|
162
|
+
reason: 'string',
|
|
163
|
+
violations: 'number',
|
|
164
|
+
bannedAt: 'string',
|
|
165
|
+
expiresAt: 'string|required',
|
|
166
|
+
metadata: {
|
|
167
|
+
userAgent: 'string',
|
|
168
|
+
path: 'string',
|
|
169
|
+
lastViolation: 'string'
|
|
147
170
|
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
ttlPlugin.options.resources[resourceName] = {
|
|
155
|
-
enabled: true,
|
|
156
|
-
field: 'expiresAt'
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
if (this.options.verbose) {
|
|
160
|
-
console.log('[Failban] TTL configured for bans resource');
|
|
171
|
+
},
|
|
172
|
+
behavior: 'body-overflow',
|
|
173
|
+
timestamps: true,
|
|
174
|
+
partitions: {
|
|
175
|
+
byExpiry: {
|
|
176
|
+
fields: { expiresAtCohort: 'string' }
|
|
161
177
|
}
|
|
162
|
-
} else {
|
|
163
|
-
console.warn('[Failban] TTLPlugin not found - bans will not auto-expire from DB');
|
|
164
178
|
}
|
|
179
|
+
}));
|
|
165
180
|
|
|
166
|
-
|
|
181
|
+
if (!created) {
|
|
182
|
+
const existing = this.database.resources?.[resourceName];
|
|
183
|
+
if (existing) {
|
|
184
|
+
return existing;
|
|
185
|
+
}
|
|
186
|
+
throw createErr;
|
|
167
187
|
}
|
|
188
|
+
|
|
189
|
+
// Apply TTL plugin to this resource
|
|
190
|
+
const ttlPlugin = this.database.plugins?.ttl || this.database.plugins?.TTLPlugin;
|
|
191
|
+
if (ttlPlugin) {
|
|
192
|
+
ttlPlugin.options.resources = ttlPlugin.options.resources || {};
|
|
193
|
+
ttlPlugin.options.resources[resourceName] = {
|
|
194
|
+
enabled: true,
|
|
195
|
+
field: 'expiresAt'
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
if (this.options.verbose) {
|
|
199
|
+
console.log(`[Failban] TTL configured for bans resource (${resourceName})`);
|
|
200
|
+
}
|
|
201
|
+
} else {
|
|
202
|
+
console.warn('[Failban] TTLPlugin not found - bans will not auto-expire from DB');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return resource;
|
|
168
206
|
}
|
|
169
207
|
|
|
170
208
|
/**
|
|
@@ -172,29 +210,40 @@ export class FailbanManager {
|
|
|
172
210
|
* @private
|
|
173
211
|
*/
|
|
174
212
|
async _createViolationsResource() {
|
|
175
|
-
const resourceName =
|
|
176
|
-
|
|
213
|
+
const resourceName = this.resourceNames.violations;
|
|
177
214
|
try {
|
|
178
215
|
return await this.database.getResource(resourceName);
|
|
179
216
|
} catch (err) {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
217
|
+
// fall through
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const [created, createErr, resource] = await tryFn(() => this.database.createResource({
|
|
221
|
+
name: resourceName,
|
|
222
|
+
attributes: {
|
|
223
|
+
ip: 'string|required',
|
|
224
|
+
timestamp: 'string|required',
|
|
225
|
+
type: 'string',
|
|
226
|
+
path: 'string',
|
|
227
|
+
userAgent: 'string'
|
|
228
|
+
},
|
|
229
|
+
behavior: 'body-overflow',
|
|
230
|
+
timestamps: true,
|
|
231
|
+
partitions: {
|
|
232
|
+
byIp: {
|
|
233
|
+
fields: { ip: 'string' }
|
|
195
234
|
}
|
|
196
|
-
}
|
|
235
|
+
}
|
|
236
|
+
}));
|
|
237
|
+
|
|
238
|
+
if (!created) {
|
|
239
|
+
const existing = this.database.resources?.[resourceName];
|
|
240
|
+
if (existing) {
|
|
241
|
+
return existing;
|
|
242
|
+
}
|
|
243
|
+
throw createErr;
|
|
197
244
|
}
|
|
245
|
+
|
|
246
|
+
return resource;
|
|
198
247
|
}
|
|
199
248
|
|
|
200
249
|
/**
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenGraph Helper
|
|
3
|
+
*
|
|
4
|
+
* Generates OpenGraph and Twitter Card meta tags for social media previews.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* const og = new OpenGraphHelper({
|
|
8
|
+
* siteName: 'My Site',
|
|
9
|
+
* locale: 'en_US',
|
|
10
|
+
* twitterSite: '@mysite'
|
|
11
|
+
* });
|
|
12
|
+
*
|
|
13
|
+
* const tags = og.generateTags({
|
|
14
|
+
* title: 'Page Title',
|
|
15
|
+
* description: 'Page description',
|
|
16
|
+
* image: '/og-image.jpg',
|
|
17
|
+
* url: 'https://example.com/page'
|
|
18
|
+
* });
|
|
19
|
+
*/
|
|
20
|
+
export class OpenGraphHelper {
|
|
21
|
+
constructor(defaults = {}) {
|
|
22
|
+
this.defaults = {
|
|
23
|
+
siteName: defaults.siteName || 'My Site',
|
|
24
|
+
locale: defaults.locale || 'en_US',
|
|
25
|
+
type: defaults.type || 'website',
|
|
26
|
+
twitterCard: defaults.twitterCard || 'summary_large_image',
|
|
27
|
+
twitterSite: defaults.twitterSite || null,
|
|
28
|
+
defaultImage: defaults.defaultImage || null
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Generate OpenGraph meta tags
|
|
34
|
+
*
|
|
35
|
+
* @param {Object} data - OpenGraph data
|
|
36
|
+
* @param {string} data.title - Page title
|
|
37
|
+
* @param {string} data.description - Page description
|
|
38
|
+
* @param {string} data.image - Image URL (absolute or relative)
|
|
39
|
+
* @param {string} data.url - Canonical URL
|
|
40
|
+
* @param {string} data.type - Content type (default: 'website')
|
|
41
|
+
* @param {string} data.locale - Locale (default: from defaults)
|
|
42
|
+
* @param {string} data.siteName - Site name (default: from defaults)
|
|
43
|
+
* @param {string} data.imageAlt - Image alt text
|
|
44
|
+
* @param {number} data.imageWidth - Image width in pixels
|
|
45
|
+
* @param {number} data.imageHeight - Image height in pixels
|
|
46
|
+
* @param {string} data.twitterCard - Twitter card type (default: from defaults)
|
|
47
|
+
* @param {string} data.twitterSite - Twitter @username (default: from defaults)
|
|
48
|
+
* @param {string} data.twitterCreator - Twitter creator @username
|
|
49
|
+
* @returns {string} HTML meta tags
|
|
50
|
+
*/
|
|
51
|
+
generateTags(data = {}) {
|
|
52
|
+
const og = { ...this.defaults, ...data };
|
|
53
|
+
|
|
54
|
+
// Use default image if none provided
|
|
55
|
+
const image = og.image || og.defaultImage;
|
|
56
|
+
|
|
57
|
+
const tags = [
|
|
58
|
+
// Basic OpenGraph
|
|
59
|
+
og.title && `<meta property="og:title" content="${this._escape(og.title)}">`,
|
|
60
|
+
og.description && `<meta property="og:description" content="${this._escape(og.description)}">`,
|
|
61
|
+
image && `<meta property="og:image" content="${this._escape(image)}">`,
|
|
62
|
+
og.url && `<meta property="og:url" content="${this._escape(og.url)}">`,
|
|
63
|
+
`<meta property="og:type" content="${this._escape(og.type)}">`,
|
|
64
|
+
`<meta property="og:site_name" content="${this._escape(og.siteName)}">`,
|
|
65
|
+
`<meta property="og:locale" content="${this._escape(og.locale)}">`,
|
|
66
|
+
|
|
67
|
+
// Image metadata
|
|
68
|
+
og.imageAlt && `<meta property="og:image:alt" content="${this._escape(og.imageAlt)}">`,
|
|
69
|
+
og.imageWidth && `<meta property="og:image:width" content="${og.imageWidth}">`,
|
|
70
|
+
og.imageHeight && `<meta property="og:image:height" content="${og.imageHeight}">`,
|
|
71
|
+
|
|
72
|
+
// Twitter Cards
|
|
73
|
+
`<meta name="twitter:card" content="${this._escape(og.twitterCard)}">`,
|
|
74
|
+
og.twitterSite && `<meta name="twitter:site" content="${this._escape(og.twitterSite)}">`,
|
|
75
|
+
og.twitterCreator && `<meta name="twitter:creator" content="${this._escape(og.twitterCreator)}">`,
|
|
76
|
+
og.title && `<meta name="twitter:title" content="${this._escape(og.title)}">`,
|
|
77
|
+
og.description && `<meta name="twitter:description" content="${this._escape(og.description)}">`,
|
|
78
|
+
image && `<meta name="twitter:image" content="${this._escape(image)}">`,
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
return tags.filter(Boolean).join('\n ');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Create Hono middleware that injects OG helper into context
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* app.use('*', ogHelper.middleware());
|
|
89
|
+
*
|
|
90
|
+
* // In route handler:
|
|
91
|
+
* const ogTags = c.get('og')({ title: 'My Page', ... });
|
|
92
|
+
*/
|
|
93
|
+
middleware() {
|
|
94
|
+
return async (c, next) => {
|
|
95
|
+
c.set('og', (data) => this.generateTags(data));
|
|
96
|
+
await next();
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Escape HTML entities to prevent XSS
|
|
102
|
+
* @private
|
|
103
|
+
*/
|
|
104
|
+
_escape(str) {
|
|
105
|
+
if (str === null || str === undefined) return '';
|
|
106
|
+
|
|
107
|
+
return String(str)
|
|
108
|
+
.replace(/&/g, '&')
|
|
109
|
+
.replace(/</g, '<')
|
|
110
|
+
.replace(/>/g, '>')
|
|
111
|
+
.replace(/"/g, '"')
|
|
112
|
+
.replace(/'/g, ''');
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export default OpenGraphHelper;
|