@ucptools/validator 1.0.0 → 1.1.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/dist/auth/config.d.ts +20 -0
- package/dist/auth/config.d.ts.map +1 -0
- package/dist/auth/config.js +114 -0
- package/dist/auth/config.js.map +1 -0
- package/dist/auth/index.d.ts +5 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +17 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/middleware.d.ts +45 -0
- package/dist/auth/middleware.d.ts.map +1 -0
- package/dist/auth/middleware.js +170 -0
- package/dist/auth/middleware.js.map +1 -0
- package/dist/auth/service.d.ts +80 -0
- package/dist/auth/service.d.ts.map +1 -0
- package/dist/auth/service.js +298 -0
- package/dist/auth/service.js.map +1 -0
- package/dist/cli/index.d.ts +6 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +375 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/mock-server.d.ts +20 -0
- package/dist/cli/mock-server.d.ts.map +1 -0
- package/dist/cli/mock-server.js +261 -0
- package/dist/cli/mock-server.js.map +1 -0
- package/dist/compliance/compliance-generator.d.ts +34 -0
- package/dist/compliance/compliance-generator.d.ts.map +1 -0
- package/dist/compliance/compliance-generator.js +320 -0
- package/dist/compliance/compliance-generator.js.map +1 -0
- package/dist/compliance/index.d.ts +8 -0
- package/dist/compliance/index.d.ts.map +1 -0
- package/dist/compliance/index.js +17 -0
- package/dist/compliance/index.js.map +1 -0
- package/dist/compliance/templates.d.ts +34 -0
- package/dist/compliance/templates.d.ts.map +1 -0
- package/{src/compliance/templates.ts → dist/compliance/templates.js} +117 -155
- package/dist/compliance/templates.js.map +1 -0
- package/dist/compliance/types.d.ts +64 -0
- package/dist/compliance/types.d.ts.map +1 -0
- package/dist/compliance/types.js +64 -0
- package/dist/compliance/types.js.map +1 -0
- package/dist/db/index.d.ts +17 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +80 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/schema.d.ts +3886 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +425 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/db/utils.d.ts +252 -0
- package/dist/db/utils.d.ts.map +1 -0
- package/dist/db/utils.js +295 -0
- package/dist/db/utils.js.map +1 -0
- package/dist/feed-analyzer/feed-analyzer.d.ts +26 -0
- package/dist/feed-analyzer/feed-analyzer.d.ts.map +1 -0
- package/{src/feed-analyzer/feed-analyzer.ts → dist/feed-analyzer/feed-analyzer.js} +856 -726
- package/dist/feed-analyzer/feed-analyzer.js.map +1 -0
- package/dist/feed-analyzer/index.d.ts +8 -0
- package/dist/feed-analyzer/index.d.ts.map +1 -0
- package/dist/feed-analyzer/index.js +19 -0
- package/dist/feed-analyzer/index.js.map +1 -0
- package/dist/feed-analyzer/types.d.ts +285 -0
- package/dist/feed-analyzer/types.d.ts.map +1 -0
- package/dist/feed-analyzer/types.js +175 -0
- package/dist/feed-analyzer/types.js.map +1 -0
- package/{src/generator/index.ts → dist/generator/index.d.ts} +1 -1
- package/dist/generator/index.d.ts.map +1 -0
- package/dist/generator/index.js +13 -0
- package/dist/generator/index.js.map +1 -0
- package/dist/generator/key-generator.d.ts +24 -0
- package/dist/generator/key-generator.d.ts.map +1 -0
- package/dist/generator/key-generator.js +144 -0
- package/dist/generator/key-generator.js.map +1 -0
- package/dist/generator/profile-builder.d.ts +15 -0
- package/dist/generator/profile-builder.d.ts.map +1 -0
- package/dist/generator/profile-builder.js +338 -0
- package/dist/generator/profile-builder.js.map +1 -0
- package/dist/hosting/artifacts-generator.d.ts +10 -0
- package/dist/hosting/artifacts-generator.d.ts.map +1 -0
- package/{src/hosting/artifacts-generator.ts → dist/hosting/artifacts-generator.js} +191 -241
- package/dist/hosting/artifacts-generator.js.map +1 -0
- package/{src/hosting/index.ts → dist/hosting/index.d.ts} +1 -1
- package/dist/hosting/index.d.ts.map +1 -0
- package/dist/hosting/index.js +10 -0
- package/dist/hosting/index.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +78 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/analytics.d.ts +337 -0
- package/dist/lib/analytics.d.ts.map +1 -0
- package/dist/lib/analytics.js +188 -0
- package/dist/lib/analytics.js.map +1 -0
- package/{src/security/index.ts → dist/security/index.d.ts} +8 -15
- package/dist/security/index.d.ts.map +1 -0
- package/dist/security/index.js +12 -0
- package/dist/security/index.js.map +1 -0
- package/dist/security/security-scanner.d.ts +10 -0
- package/dist/security/security-scanner.d.ts.map +1 -0
- package/dist/security/security-scanner.js +669 -0
- package/dist/security/security-scanner.js.map +1 -0
- package/dist/security/types.d.ts +80 -0
- package/dist/security/types.d.ts.map +1 -0
- package/dist/security/types.js +21 -0
- package/dist/security/types.js.map +1 -0
- package/dist/services/analytics.d.ts +114 -0
- package/dist/services/analytics.d.ts.map +1 -0
- package/dist/services/analytics.js +862 -0
- package/dist/services/analytics.js.map +1 -0
- package/dist/services/badge.d.ts +31 -0
- package/dist/services/badge.d.ts.map +1 -0
- package/dist/services/badge.js +152 -0
- package/dist/services/badge.js.map +1 -0
- package/dist/services/cron.d.ts +125 -0
- package/dist/services/cron.d.ts.map +1 -0
- package/dist/services/cron.js +613 -0
- package/dist/services/cron.js.map +1 -0
- package/dist/services/directory.d.ts +106 -0
- package/dist/services/directory.d.ts.map +1 -0
- package/dist/services/directory.js +351 -0
- package/dist/services/directory.js.map +1 -0
- package/dist/services/email.d.ts +112 -0
- package/dist/services/email.d.ts.map +1 -0
- package/dist/services/email.js +772 -0
- package/dist/services/email.js.map +1 -0
- package/dist/services/hosted-profiles.d.ts +77 -0
- package/dist/services/hosted-profiles.d.ts.map +1 -0
- package/dist/services/hosted-profiles.js +433 -0
- package/dist/services/hosted-profiles.js.map +1 -0
- package/dist/services/latency.d.ts +67 -0
- package/dist/services/latency.d.ts.map +1 -0
- package/dist/services/latency.js +274 -0
- package/dist/services/latency.js.map +1 -0
- package/dist/services/manifest-compliance.d.ts +64 -0
- package/dist/services/manifest-compliance.d.ts.map +1 -0
- package/dist/services/manifest-compliance.js +271 -0
- package/dist/services/manifest-compliance.js.map +1 -0
- package/dist/services/monitoring-diff.d.ts +31 -0
- package/dist/services/monitoring-diff.d.ts.map +1 -0
- package/dist/services/monitoring-diff.js +189 -0
- package/dist/services/monitoring-diff.js.map +1 -0
- package/dist/services/notifications.d.ts +46 -0
- package/dist/services/notifications.d.ts.map +1 -0
- package/dist/services/notifications.js +88 -0
- package/dist/services/notifications.js.map +1 -0
- package/dist/services/stripe.d.ts +93 -0
- package/dist/services/stripe.d.ts.map +1 -0
- package/dist/services/stripe.js +490 -0
- package/dist/services/stripe.js.map +1 -0
- package/dist/services/validation-history.d.ts +99 -0
- package/dist/services/validation-history.d.ts.map +1 -0
- package/dist/services/validation-history.js +344 -0
- package/dist/services/validation-history.js.map +1 -0
- package/dist/services/validation-logging.d.ts +103 -0
- package/dist/services/validation-logging.d.ts.map +1 -0
- package/dist/services/validation-logging.js +210 -0
- package/dist/services/validation-logging.js.map +1 -0
- package/dist/services/validation.d.ts +119 -0
- package/dist/services/validation.d.ts.map +1 -0
- package/dist/services/validation.js +1185 -0
- package/dist/services/validation.js.map +1 -0
- package/dist/simulator/agent-simulator.d.ts +69 -0
- package/dist/simulator/agent-simulator.d.ts.map +1 -0
- package/dist/simulator/agent-simulator.js +870 -0
- package/dist/simulator/agent-simulator.js.map +1 -0
- package/{src/simulator/index.ts → dist/simulator/index.d.ts} +7 -7
- package/dist/simulator/index.d.ts.map +1 -0
- package/dist/simulator/index.js +23 -0
- package/dist/simulator/index.js.map +1 -0
- package/{src/simulator/types.ts → dist/simulator/types.d.ts} +171 -170
- package/dist/simulator/types.d.ts.map +1 -0
- package/dist/simulator/types.js +18 -0
- package/dist/simulator/types.js.map +1 -0
- package/dist/types/acp-validation.d.ts +87 -0
- package/dist/types/acp-validation.d.ts.map +1 -0
- package/dist/types/acp-validation.js +40 -0
- package/dist/types/acp-validation.js.map +1 -0
- package/dist/types/analytics.d.ts +182 -0
- package/dist/types/analytics.d.ts.map +1 -0
- package/dist/types/analytics.js +7 -0
- package/dist/types/analytics.js.map +1 -0
- package/dist/types/generator.d.ts +106 -0
- package/dist/types/generator.d.ts.map +1 -0
- package/dist/types/generator.js +6 -0
- package/dist/types/generator.js.map +1 -0
- package/{src/types/index.ts → dist/types/index.d.ts} +1 -1
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +23 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/ucp-profile.d.ts +111 -0
- package/dist/types/ucp-profile.d.ts.map +1 -0
- package/dist/types/ucp-profile.js +45 -0
- package/dist/types/ucp-profile.js.map +1 -0
- package/dist/types/validation.d.ts +76 -0
- package/dist/types/validation.d.ts.map +1 -0
- package/dist/types/validation.js +42 -0
- package/dist/types/validation.js.map +1 -0
- package/dist/validator/acp/index.d.ts +31 -0
- package/dist/validator/acp/index.d.ts.map +1 -0
- package/dist/validator/acp/index.js +574 -0
- package/dist/validator/acp/index.js.map +1 -0
- package/dist/validator/index.d.ts +26 -0
- package/dist/validator/index.d.ts.map +1 -0
- package/dist/validator/index.js +161 -0
- package/dist/validator/index.js.map +1 -0
- package/dist/validator/network-validator.d.ts +28 -0
- package/dist/validator/network-validator.d.ts.map +1 -0
- package/dist/validator/network-validator.js +319 -0
- package/dist/validator/network-validator.js.map +1 -0
- package/dist/validator/rules-validator.d.ts +19 -0
- package/dist/validator/rules-validator.d.ts.map +1 -0
- package/dist/validator/rules-validator.js +306 -0
- package/dist/validator/rules-validator.js.map +1 -0
- package/dist/validator/sdk-validator.d.ts +58 -0
- package/dist/validator/sdk-validator.d.ts.map +1 -0
- package/{src/validator/sdk-validator.ts → dist/validator/sdk-validator.js} +273 -330
- package/dist/validator/sdk-validator.js.map +1 -0
- package/dist/validator/structural-validator.d.ts +11 -0
- package/dist/validator/structural-validator.d.ts.map +1 -0
- package/dist/validator/structural-validator.js +549 -0
- package/dist/validator/structural-validator.js.map +1 -0
- package/dist/validator/utils.d.ts +51 -0
- package/dist/validator/utils.d.ts.map +1 -0
- package/dist/validator/utils.js +132 -0
- package/dist/validator/utils.js.map +1 -0
- package/package.json +44 -12
- package/CLAUDE.md +0 -109
- package/api/analyze-feed.js +0 -140
- package/api/badge.js +0 -185
- package/api/benchmark.js +0 -177
- package/api/directory-stats.ts +0 -29
- package/api/directory.ts +0 -73
- package/api/generate-compliance.js +0 -143
- package/api/generate-schema.js +0 -457
- package/api/generate.js +0 -132
- package/api/security-scan.js +0 -133
- package/api/simulate.js +0 -187
- package/api/tsconfig.json +0 -10
- package/api/validate.js +0 -1351
- package/apify-actor/.actor/actor.json +0 -68
- package/apify-actor/.actor/input_schema.json +0 -32
- package/apify-actor/APIFY-STORE-LISTING.md +0 -412
- package/apify-actor/Dockerfile +0 -8
- package/apify-actor/README.md +0 -166
- package/apify-actor/main.ts +0 -111
- package/apify-actor/package.json +0 -17
- package/apify-actor/src/main.js +0 -199
- package/docs/BRAND-IDENTITY.md +0 -238
- package/docs/BRAND-STYLE-GUIDE.md +0 -356
- package/drizzle/0000_black_king_cobra.sql +0 -39
- package/drizzle/meta/0000_snapshot.json +0 -309
- package/drizzle/meta/_journal.json +0 -13
- package/drizzle.config.ts +0 -10
- package/public/.well-known/ucp +0 -25
- package/public/android-chrome-192x192.png +0 -0
- package/public/android-chrome-512x512.png +0 -0
- package/public/apple-touch-icon.png +0 -0
- package/public/brand.css +0 -321
- package/public/directory.html +0 -701
- package/public/favicon-16x16.png +0 -0
- package/public/favicon-32x32.png +0 -0
- package/public/favicon.ico +0 -0
- package/public/guides/bigcommerce.html +0 -743
- package/public/guides/fastucp.html +0 -838
- package/public/guides/magento.html +0 -779
- package/public/guides/shopify.html +0 -726
- package/public/guides/squarespace.html +0 -749
- package/public/guides/wix.html +0 -747
- package/public/guides/woocommerce.html +0 -733
- package/public/index.html +0 -3835
- package/public/learn.html +0 -396
- package/public/logo.jpeg +0 -0
- package/public/og-image-icon.png +0 -0
- package/public/og-image.png +0 -0
- package/public/robots.txt +0 -6
- package/public/site.webmanifest +0 -31
- package/public/sitemap.xml +0 -69
- package/public/social/linkedin-banner-1128x191.png +0 -0
- package/public/social/temp.PNG +0 -0
- package/public/social/x-header-1500x500.png +0 -0
- package/public/verify.html +0 -410
- package/scripts/generate-favicons.js +0 -44
- package/scripts/generate-ico.js +0 -23
- package/scripts/generate-og-image.js +0 -45
- package/scripts/reset-db.ts +0 -77
- package/scripts/seed-db.ts +0 -71
- package/scripts/setup-benchmark-db.js +0 -70
- package/src/api/server.ts +0 -266
- package/src/cli/index.ts +0 -302
- package/src/compliance/compliance-generator.ts +0 -452
- package/src/compliance/index.ts +0 -28
- package/src/compliance/types.ts +0 -170
- package/src/db/index.ts +0 -28
- package/src/db/schema.ts +0 -84
- package/src/feed-analyzer/index.ts +0 -34
- package/src/feed-analyzer/types.ts +0 -354
- package/src/generator/key-generator.ts +0 -124
- package/src/generator/profile-builder.ts +0 -402
- package/src/index.ts +0 -105
- package/src/security/security-scanner.ts +0 -604
- package/src/security/types.ts +0 -55
- package/src/services/directory.ts +0 -434
- package/src/simulator/agent-simulator.ts +0 -941
- package/src/types/generator.ts +0 -140
- package/src/types/ucp-profile.ts +0 -140
- package/src/types/validation.ts +0 -89
- package/src/validator/index.ts +0 -194
- package/src/validator/network-validator.ts +0 -417
- package/src/validator/rules-validator.ts +0 -297
- package/src/validator/structural-validator.ts +0 -476
- package/tests/fixtures/non-compliant-profile.json +0 -25
- package/tests/fixtures/official-sample-profile.json +0 -75
- package/tests/integration/benchmark.test.ts +0 -207
- package/tests/integration/database.test.ts +0 -163
- package/tests/integration/directory-api.test.ts +0 -268
- package/tests/integration/simulate-api.test.ts +0 -230
- package/tests/integration/validate-api.test.ts +0 -269
- package/tests/setup.ts +0 -15
- package/tests/unit/agent-simulator.test.ts +0 -575
- package/tests/unit/compliance-generator.test.ts +0 -374
- package/tests/unit/directory-service.test.ts +0 -272
- package/tests/unit/feed-analyzer.test.ts +0 -517
- package/tests/unit/lint-suggestions.test.ts +0 -423
- package/tests/unit/official-samples.test.ts +0 -211
- package/tests/unit/pdf-report.test.ts +0 -390
- package/tests/unit/sdk-validator.test.ts +0 -531
- package/tests/unit/security-scanner.test.ts +0 -410
- package/tests/unit/validation.test.ts +0 -390
- package/tsconfig.json +0 -20
- package/vercel.json +0 -34
- package/vitest.config.ts +0 -22
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Validation History Service
|
|
4
|
+
*
|
|
5
|
+
* Manages storage and retrieval of validation history for domains.
|
|
6
|
+
* Supports both authenticated users (via monitoredDomains) and
|
|
7
|
+
* anonymous public access (with limited history).
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.storeValidationHistory = storeValidationHistory;
|
|
11
|
+
exports.getValidationHistory = getValidationHistory;
|
|
12
|
+
exports.calculateTrend = calculateTrend;
|
|
13
|
+
exports.getDomainHistory = getDomainHistory;
|
|
14
|
+
exports.getPublicDomainHistory = getPublicDomainHistory;
|
|
15
|
+
exports.getPreviousValidation = getPreviousValidation;
|
|
16
|
+
exports.updateHistorySeverity = updateHistorySeverity;
|
|
17
|
+
exports.validateAndStoreHistory = validateAndStoreHistory;
|
|
18
|
+
exports.getHistoryStats = getHistoryStats;
|
|
19
|
+
const index_js_1 = require("../db/index.js");
|
|
20
|
+
const schema_js_1 = require("../db/schema.js");
|
|
21
|
+
const drizzle_orm_1 = require("drizzle-orm");
|
|
22
|
+
const validation_js_1 = require("./validation.js");
|
|
23
|
+
/**
|
|
24
|
+
* Store a validation result in history
|
|
25
|
+
*/
|
|
26
|
+
async function storeValidationHistory(domainId, result) {
|
|
27
|
+
try {
|
|
28
|
+
const db = (0, index_js_1.getDb)();
|
|
29
|
+
const errorsCount = result.ucp.issues.filter(i => i.severity === 'error').length +
|
|
30
|
+
result.schema.issues.filter(i => i.severity === 'error').length;
|
|
31
|
+
const warningsCount = result.ucp.issues.filter(i => i.severity === 'warn').length +
|
|
32
|
+
result.schema.issues.filter(i => i.severity === 'warn').length;
|
|
33
|
+
const [entry] = await db
|
|
34
|
+
.insert(schema_js_1.validationHistory)
|
|
35
|
+
.values({
|
|
36
|
+
domainId,
|
|
37
|
+
score: result.ai_readiness.score,
|
|
38
|
+
grade: result.ai_readiness.grade,
|
|
39
|
+
hasUcp: result.ucp.found,
|
|
40
|
+
issuesCount: errorsCount + warningsCount,
|
|
41
|
+
errorsCount,
|
|
42
|
+
warningsCount,
|
|
43
|
+
resultJson: JSON.stringify(result),
|
|
44
|
+
})
|
|
45
|
+
.returning();
|
|
46
|
+
// Update the monitored domain with latest results
|
|
47
|
+
await db
|
|
48
|
+
.update(schema_js_1.monitoredDomains)
|
|
49
|
+
.set({
|
|
50
|
+
lastScore: result.ai_readiness.score,
|
|
51
|
+
lastGrade: result.ai_readiness.grade,
|
|
52
|
+
lastCheckedAt: new Date(),
|
|
53
|
+
updatedAt: new Date(),
|
|
54
|
+
})
|
|
55
|
+
.where((0, drizzle_orm_1.eq)(schema_js_1.monitoredDomains.id, domainId));
|
|
56
|
+
return entry;
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
console.error('Error storing validation history:', error);
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Get validation history for a monitored domain (authenticated user)
|
|
65
|
+
*/
|
|
66
|
+
async function getValidationHistory(domainId, limit = 100, daysBack = 90) {
|
|
67
|
+
try {
|
|
68
|
+
const db = (0, index_js_1.getDb)();
|
|
69
|
+
const cutoffDate = new Date();
|
|
70
|
+
cutoffDate.setDate(cutoffDate.getDate() - daysBack);
|
|
71
|
+
const history = await db
|
|
72
|
+
.select()
|
|
73
|
+
.from(schema_js_1.validationHistory)
|
|
74
|
+
.where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_js_1.validationHistory.domainId, domainId), (0, drizzle_orm_1.gte)(schema_js_1.validationHistory.validatedAt, cutoffDate)))
|
|
75
|
+
.orderBy((0, drizzle_orm_1.desc)(schema_js_1.validationHistory.validatedAt))
|
|
76
|
+
.limit(limit);
|
|
77
|
+
return history;
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
console.error('Error fetching validation history:', error);
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Calculate trend from history entries
|
|
86
|
+
*/
|
|
87
|
+
function calculateTrend(history) {
|
|
88
|
+
if (history.length < 2) {
|
|
89
|
+
return {
|
|
90
|
+
direction: 'insufficient_data',
|
|
91
|
+
scoreChange: 0,
|
|
92
|
+
periodDays: 0,
|
|
93
|
+
dataPoints: history.length,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
// Sort by date descending (newest first)
|
|
97
|
+
const sorted = [...history].sort((a, b) => new Date(b.validatedAt).getTime() - new Date(a.validatedAt).getTime());
|
|
98
|
+
const latest = sorted[0];
|
|
99
|
+
const oldest = sorted[sorted.length - 1];
|
|
100
|
+
const latestScore = latest.score ?? 0;
|
|
101
|
+
const oldestScore = oldest.score ?? 0;
|
|
102
|
+
const scoreChange = latestScore - oldestScore;
|
|
103
|
+
const periodMs = new Date(latest.validatedAt).getTime() - new Date(oldest.validatedAt).getTime();
|
|
104
|
+
const periodDays = Math.ceil(periodMs / (1000 * 60 * 60 * 24));
|
|
105
|
+
let direction;
|
|
106
|
+
if (Math.abs(scoreChange) <= 5) {
|
|
107
|
+
direction = 'stable';
|
|
108
|
+
}
|
|
109
|
+
else if (scoreChange > 0) {
|
|
110
|
+
direction = 'improving';
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
direction = 'declining';
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
direction,
|
|
117
|
+
scoreChange,
|
|
118
|
+
periodDays,
|
|
119
|
+
dataPoints: history.length,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Get domain history with trend analysis (for authenticated users)
|
|
124
|
+
*/
|
|
125
|
+
async function getDomainHistory(domainId, daysBack = 90) {
|
|
126
|
+
try {
|
|
127
|
+
const db = (0, index_js_1.getDb)();
|
|
128
|
+
// Get the domain info
|
|
129
|
+
const [domain] = await db
|
|
130
|
+
.select()
|
|
131
|
+
.from(schema_js_1.monitoredDomains)
|
|
132
|
+
.where((0, drizzle_orm_1.eq)(schema_js_1.monitoredDomains.id, domainId))
|
|
133
|
+
.limit(1);
|
|
134
|
+
if (!domain) {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
// Get history
|
|
138
|
+
const history = await getValidationHistory(domainId, 100, daysBack);
|
|
139
|
+
const trend = calculateTrend(history);
|
|
140
|
+
return {
|
|
141
|
+
domain: domain.domain,
|
|
142
|
+
currentScore: domain.lastScore,
|
|
143
|
+
currentGrade: domain.lastGrade,
|
|
144
|
+
trend,
|
|
145
|
+
history,
|
|
146
|
+
lastValidatedAt: domain.lastCheckedAt,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
console.error('Error getting domain history:', error);
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Get public history for a domain (limited to 3 entries for anonymous users)
|
|
156
|
+
* This looks up by domain name, not by user's monitored domain ID
|
|
157
|
+
*/
|
|
158
|
+
async function getPublicDomainHistory(domainName, isAuthenticated = false) {
|
|
159
|
+
try {
|
|
160
|
+
const db = (0, index_js_1.getDb)();
|
|
161
|
+
const cleanDomain = domainName.replace(/^https?:\/\//, '').replace(/\/$/, '').split('/')[0];
|
|
162
|
+
// Find all monitored domains with this domain name
|
|
163
|
+
const domains = await db
|
|
164
|
+
.select({ id: schema_js_1.monitoredDomains.id })
|
|
165
|
+
.from(schema_js_1.monitoredDomains)
|
|
166
|
+
.where((0, drizzle_orm_1.eq)(schema_js_1.monitoredDomains.domain, cleanDomain))
|
|
167
|
+
.limit(1);
|
|
168
|
+
if (domains.length === 0) {
|
|
169
|
+
// No history exists for this domain
|
|
170
|
+
return {
|
|
171
|
+
domain: cleanDomain,
|
|
172
|
+
history: [],
|
|
173
|
+
trend: {
|
|
174
|
+
direction: 'insufficient_data',
|
|
175
|
+
scoreChange: 0,
|
|
176
|
+
periodDays: 0,
|
|
177
|
+
dataPoints: 0,
|
|
178
|
+
},
|
|
179
|
+
limitedAccess: !isAuthenticated,
|
|
180
|
+
message: 'No validation history found for this domain. Run a validation to start tracking.',
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
// Get history from the first matching domain
|
|
184
|
+
// In a more sophisticated system, we might aggregate across all users
|
|
185
|
+
const domainId = domains[0].id;
|
|
186
|
+
const limit = isAuthenticated ? 100 : 3; // Free users get 3, authenticated get 100
|
|
187
|
+
const history = await db
|
|
188
|
+
.select({
|
|
189
|
+
score: schema_js_1.validationHistory.score,
|
|
190
|
+
grade: schema_js_1.validationHistory.grade,
|
|
191
|
+
hasUcp: schema_js_1.validationHistory.hasUcp,
|
|
192
|
+
validatedAt: schema_js_1.validationHistory.validatedAt,
|
|
193
|
+
})
|
|
194
|
+
.from(schema_js_1.validationHistory)
|
|
195
|
+
.where((0, drizzle_orm_1.eq)(schema_js_1.validationHistory.domainId, domainId))
|
|
196
|
+
.orderBy((0, drizzle_orm_1.desc)(schema_js_1.validationHistory.validatedAt))
|
|
197
|
+
.limit(limit);
|
|
198
|
+
// For trend calculation, get more data even for free users
|
|
199
|
+
const trendHistory = await db
|
|
200
|
+
.select({
|
|
201
|
+
score: schema_js_1.validationHistory.score,
|
|
202
|
+
validatedAt: schema_js_1.validationHistory.validatedAt,
|
|
203
|
+
})
|
|
204
|
+
.from(schema_js_1.validationHistory)
|
|
205
|
+
.where((0, drizzle_orm_1.eq)(schema_js_1.validationHistory.domainId, domainId))
|
|
206
|
+
.orderBy((0, drizzle_orm_1.desc)(schema_js_1.validationHistory.validatedAt))
|
|
207
|
+
.limit(10);
|
|
208
|
+
const trend = calculateTrend(trendHistory.map(h => ({
|
|
209
|
+
id: '',
|
|
210
|
+
domainId: '',
|
|
211
|
+
score: h.score,
|
|
212
|
+
grade: null,
|
|
213
|
+
hasUcp: null,
|
|
214
|
+
issuesCount: null,
|
|
215
|
+
errorsCount: null,
|
|
216
|
+
warningsCount: null,
|
|
217
|
+
validatedAt: h.validatedAt,
|
|
218
|
+
})));
|
|
219
|
+
return {
|
|
220
|
+
domain: cleanDomain,
|
|
221
|
+
history: history.map(h => ({
|
|
222
|
+
score: h.score,
|
|
223
|
+
grade: h.grade,
|
|
224
|
+
hasUcp: h.hasUcp,
|
|
225
|
+
validatedAt: h.validatedAt,
|
|
226
|
+
})),
|
|
227
|
+
trend,
|
|
228
|
+
limitedAccess: !isAuthenticated,
|
|
229
|
+
message: !isAuthenticated && history.length >= 3
|
|
230
|
+
? 'Sign up to see full validation history and trends.'
|
|
231
|
+
: undefined,
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
catch (error) {
|
|
235
|
+
console.error('Error getting public domain history:', error);
|
|
236
|
+
return {
|
|
237
|
+
domain: domainName,
|
|
238
|
+
history: [],
|
|
239
|
+
trend: {
|
|
240
|
+
direction: 'insufficient_data',
|
|
241
|
+
scoreChange: 0,
|
|
242
|
+
periodDays: 0,
|
|
243
|
+
dataPoints: 0,
|
|
244
|
+
},
|
|
245
|
+
limitedAccess: !isAuthenticated,
|
|
246
|
+
message: 'Error retrieving history.',
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Get the previous validation entry for a domain (for diffing)
|
|
252
|
+
* Returns the most recent entry excluding the given ID.
|
|
253
|
+
*/
|
|
254
|
+
async function getPreviousValidation(domainId, excludeId) {
|
|
255
|
+
try {
|
|
256
|
+
const db = (0, index_js_1.getDb)();
|
|
257
|
+
const entries = await db
|
|
258
|
+
.select({
|
|
259
|
+
id: schema_js_1.validationHistory.id,
|
|
260
|
+
resultJson: schema_js_1.validationHistory.resultJson,
|
|
261
|
+
score: schema_js_1.validationHistory.score,
|
|
262
|
+
grade: schema_js_1.validationHistory.grade,
|
|
263
|
+
})
|
|
264
|
+
.from(schema_js_1.validationHistory)
|
|
265
|
+
.where((0, drizzle_orm_1.eq)(schema_js_1.validationHistory.domainId, domainId))
|
|
266
|
+
.orderBy((0, drizzle_orm_1.desc)(schema_js_1.validationHistory.validatedAt))
|
|
267
|
+
.limit(2);
|
|
268
|
+
// If we have an excludeId, skip it and return the next one
|
|
269
|
+
if (excludeId) {
|
|
270
|
+
const filtered = entries.filter(e => e.id !== excludeId);
|
|
271
|
+
return filtered[0] ?? null;
|
|
272
|
+
}
|
|
273
|
+
// Otherwise return the second-most-recent (first is the "current")
|
|
274
|
+
return entries[1] ?? null;
|
|
275
|
+
}
|
|
276
|
+
catch (error) {
|
|
277
|
+
console.error('Error fetching previous validation:', error);
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Update a validation history entry with severity and change summary
|
|
283
|
+
*/
|
|
284
|
+
async function updateHistorySeverity(historyId, severity, changesSummary) {
|
|
285
|
+
try {
|
|
286
|
+
const db = (0, index_js_1.getDb)();
|
|
287
|
+
await db
|
|
288
|
+
.update(schema_js_1.validationHistory)
|
|
289
|
+
.set({ severity, changesSummary })
|
|
290
|
+
.where((0, drizzle_orm_1.eq)(schema_js_1.validationHistory.id, historyId));
|
|
291
|
+
}
|
|
292
|
+
catch (error) {
|
|
293
|
+
console.error('Error updating history severity:', error);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Run validation and store history for a monitored domain
|
|
298
|
+
*/
|
|
299
|
+
async function validateAndStoreHistory(domainId, domainName) {
|
|
300
|
+
// Run the validation
|
|
301
|
+
const result = await (0, validation_js_1.validateDomainReadiness)(domainName);
|
|
302
|
+
// Store in history
|
|
303
|
+
const historyEntry = await storeValidationHistory(domainId, result);
|
|
304
|
+
return { result, historyEntry };
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Get validation history statistics
|
|
308
|
+
*/
|
|
309
|
+
async function getHistoryStats(domainId) {
|
|
310
|
+
try {
|
|
311
|
+
const db = (0, index_js_1.getDb)();
|
|
312
|
+
const [stats] = await db
|
|
313
|
+
.select({
|
|
314
|
+
totalValidations: (0, drizzle_orm_1.sql) `count(*)`,
|
|
315
|
+
averageScore: (0, drizzle_orm_1.sql) `avg(${schema_js_1.validationHistory.score})`,
|
|
316
|
+
bestScore: (0, drizzle_orm_1.sql) `max(${schema_js_1.validationHistory.score})`,
|
|
317
|
+
worstScore: (0, drizzle_orm_1.sql) `min(${schema_js_1.validationHistory.score})`,
|
|
318
|
+
firstValidation: (0, drizzle_orm_1.sql) `min(${schema_js_1.validationHistory.validatedAt})`,
|
|
319
|
+
lastValidation: (0, drizzle_orm_1.sql) `max(${schema_js_1.validationHistory.validatedAt})`,
|
|
320
|
+
})
|
|
321
|
+
.from(schema_js_1.validationHistory)
|
|
322
|
+
.where((0, drizzle_orm_1.eq)(schema_js_1.validationHistory.domainId, domainId));
|
|
323
|
+
return {
|
|
324
|
+
totalValidations: Number(stats?.totalValidations) || 0,
|
|
325
|
+
averageScore: Math.round(Number(stats?.averageScore) || 0),
|
|
326
|
+
bestScore: Number(stats?.bestScore) || 0,
|
|
327
|
+
worstScore: Number(stats?.worstScore) || 0,
|
|
328
|
+
firstValidation: stats?.firstValidation || null,
|
|
329
|
+
lastValidation: stats?.lastValidation || null,
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
catch (error) {
|
|
333
|
+
console.error('Error getting history stats:', error);
|
|
334
|
+
return {
|
|
335
|
+
totalValidations: 0,
|
|
336
|
+
averageScore: 0,
|
|
337
|
+
bestScore: 0,
|
|
338
|
+
worstScore: 0,
|
|
339
|
+
firstValidation: null,
|
|
340
|
+
lastValidation: null,
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
//# sourceMappingURL=validation-history.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation-history.js","sourceRoot":"","sources":["../../src/services/validation-history.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validation Logging Service
|
|
3
|
+
*
|
|
4
|
+
* Tracks all validation attempts from all users for debugging and analytics.
|
|
5
|
+
* Privacy-focused: IPs are hashed, no personally identifiable information stored.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Extract IP from request headers (handles proxies)
|
|
9
|
+
*/
|
|
10
|
+
export declare function extractIp(req: {
|
|
11
|
+
headers: {
|
|
12
|
+
[key: string]: string | string[] | undefined;
|
|
13
|
+
};
|
|
14
|
+
ip?: string;
|
|
15
|
+
socket?: {
|
|
16
|
+
remoteAddress?: string;
|
|
17
|
+
};
|
|
18
|
+
}): string;
|
|
19
|
+
export interface LogValidationParams {
|
|
20
|
+
domain: string;
|
|
21
|
+
rawInput?: string;
|
|
22
|
+
success: boolean;
|
|
23
|
+
score?: number;
|
|
24
|
+
grade?: string;
|
|
25
|
+
hasUcp?: boolean;
|
|
26
|
+
errorCode?: string;
|
|
27
|
+
errorMessage?: string;
|
|
28
|
+
userAgent?: string;
|
|
29
|
+
ip?: string;
|
|
30
|
+
referer?: string;
|
|
31
|
+
durationMs?: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Log a validation attempt
|
|
35
|
+
* Fire-and-forget pattern - never blocks the main validation flow
|
|
36
|
+
*/
|
|
37
|
+
export declare function logValidation(params: LogValidationParams): Promise<void>;
|
|
38
|
+
export interface ValidationLogFilters {
|
|
39
|
+
domain?: string;
|
|
40
|
+
success?: boolean;
|
|
41
|
+
errorCode?: string;
|
|
42
|
+
startDate?: Date;
|
|
43
|
+
endDate?: Date;
|
|
44
|
+
limit?: number;
|
|
45
|
+
offset?: number;
|
|
46
|
+
}
|
|
47
|
+
export interface ValidationLogStats {
|
|
48
|
+
totalValidations: number;
|
|
49
|
+
successfulValidations: number;
|
|
50
|
+
failedValidations: number;
|
|
51
|
+
successRate: number;
|
|
52
|
+
uniqueDomains: number;
|
|
53
|
+
averageScore: number | null;
|
|
54
|
+
topErrors: {
|
|
55
|
+
code: string;
|
|
56
|
+
count: number;
|
|
57
|
+
}[];
|
|
58
|
+
topDomains: {
|
|
59
|
+
domain: string;
|
|
60
|
+
count: number;
|
|
61
|
+
}[];
|
|
62
|
+
recentTrend: {
|
|
63
|
+
date: string;
|
|
64
|
+
count: number;
|
|
65
|
+
successRate: number;
|
|
66
|
+
}[];
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Get validation logs with filtering
|
|
70
|
+
*/
|
|
71
|
+
export declare function getValidationLogs(filters?: ValidationLogFilters): Promise<{
|
|
72
|
+
logs: {
|
|
73
|
+
id: string;
|
|
74
|
+
domain: string;
|
|
75
|
+
rawInput: string | null;
|
|
76
|
+
success: boolean;
|
|
77
|
+
score: number | null;
|
|
78
|
+
grade: string | null;
|
|
79
|
+
hasUcp: boolean | null;
|
|
80
|
+
errorCode: string | null;
|
|
81
|
+
errorMessage: string | null;
|
|
82
|
+
userAgent: string | null;
|
|
83
|
+
ipHash: string | null;
|
|
84
|
+
referer: string | null;
|
|
85
|
+
durationMs: number | null;
|
|
86
|
+
createdAt: Date;
|
|
87
|
+
}[];
|
|
88
|
+
total: number;
|
|
89
|
+
limit: number;
|
|
90
|
+
offset: number;
|
|
91
|
+
}>;
|
|
92
|
+
/**
|
|
93
|
+
* Get validation statistics
|
|
94
|
+
*/
|
|
95
|
+
export declare function getValidationStats(startDate?: Date, endDate?: Date): Promise<ValidationLogStats>;
|
|
96
|
+
/**
|
|
97
|
+
* Get unique error codes for filtering UI
|
|
98
|
+
*/
|
|
99
|
+
export declare function getErrorCodes(): Promise<{
|
|
100
|
+
code: string;
|
|
101
|
+
count: number;
|
|
102
|
+
}[]>;
|
|
103
|
+
//# sourceMappingURL=validation-logging.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation-logging.d.ts","sourceRoot":"","sources":["../../src/services/validation-logging.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAgBH;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE;IAAE,OAAO,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAA;KAAE,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE;QAAE,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GAAG,MAAM,CActJ;AAED,MAAM,WAAW,mBAAmB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAwB9E;AAED,MAAM,WAAW,oBAAoB;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,OAAO,CAAC,EAAE,IAAI,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IAC/B,gBAAgB,EAAE,MAAM,CAAC;IACzB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC7C,UAAU,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAChD,WAAW,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACvE;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,GAAE,oBAAyB;;;;;;;;;;;;;;;;;;;;GAkDzE;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE,IAAI,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAoFtG;AAED;;GAEG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,CAAC,CAchF"}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Validation Logging Service
|
|
4
|
+
*
|
|
5
|
+
* Tracks all validation attempts from all users for debugging and analytics.
|
|
6
|
+
* Privacy-focused: IPs are hashed, no personally identifiable information stored.
|
|
7
|
+
*/
|
|
8
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
9
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.extractIp = extractIp;
|
|
13
|
+
exports.logValidation = logValidation;
|
|
14
|
+
exports.getValidationLogs = getValidationLogs;
|
|
15
|
+
exports.getValidationStats = getValidationStats;
|
|
16
|
+
exports.getErrorCodes = getErrorCodes;
|
|
17
|
+
const index_js_1 = require("../db/index.js");
|
|
18
|
+
const schema_js_1 = require("../db/schema.js");
|
|
19
|
+
const drizzle_orm_1 = require("drizzle-orm");
|
|
20
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
21
|
+
/**
|
|
22
|
+
* Hash IP address for privacy
|
|
23
|
+
*/
|
|
24
|
+
function hashIp(ip) {
|
|
25
|
+
// Use a salt to prevent rainbow table attacks
|
|
26
|
+
const salt = process.env.IP_HASH_SALT || 'ucptools-validation-logs-2026';
|
|
27
|
+
return crypto_1.default.createHash('sha256').update(salt + ip).digest('hex');
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Extract IP from request headers (handles proxies)
|
|
31
|
+
*/
|
|
32
|
+
function extractIp(req) {
|
|
33
|
+
// Check common proxy headers first
|
|
34
|
+
const forwarded = req.headers['x-forwarded-for'];
|
|
35
|
+
if (forwarded) {
|
|
36
|
+
const ip = Array.isArray(forwarded) ? forwarded[0] : forwarded.split(',')[0];
|
|
37
|
+
return ip.trim();
|
|
38
|
+
}
|
|
39
|
+
const realIp = req.headers['x-real-ip'];
|
|
40
|
+
if (realIp) {
|
|
41
|
+
return Array.isArray(realIp) ? realIp[0] : realIp;
|
|
42
|
+
}
|
|
43
|
+
return req.ip || req.socket?.remoteAddress || 'unknown';
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Log a validation attempt
|
|
47
|
+
* Fire-and-forget pattern - never blocks the main validation flow
|
|
48
|
+
*/
|
|
49
|
+
async function logValidation(params) {
|
|
50
|
+
try {
|
|
51
|
+
const db = (0, index_js_1.getDb)();
|
|
52
|
+
const logEntry = {
|
|
53
|
+
domain: params.domain,
|
|
54
|
+
rawInput: params.rawInput?.slice(0, 512), // Truncate to fit
|
|
55
|
+
success: params.success,
|
|
56
|
+
score: params.score,
|
|
57
|
+
grade: params.grade,
|
|
58
|
+
hasUcp: params.hasUcp,
|
|
59
|
+
errorCode: params.errorCode?.slice(0, 100),
|
|
60
|
+
errorMessage: params.errorMessage?.slice(0, 5000), // Reasonable limit
|
|
61
|
+
userAgent: params.userAgent?.slice(0, 512),
|
|
62
|
+
ipHash: params.ip ? hashIp(params.ip) : null,
|
|
63
|
+
referer: params.referer?.slice(0, 512),
|
|
64
|
+
durationMs: params.durationMs,
|
|
65
|
+
};
|
|
66
|
+
await db.insert(schema_js_1.validationLogs).values(logEntry);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
// Log to console but never throw - validation logging should never block
|
|
70
|
+
console.error('Failed to log validation:', error);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Get validation logs with filtering
|
|
75
|
+
*/
|
|
76
|
+
async function getValidationLogs(filters = {}) {
|
|
77
|
+
const db = (0, index_js_1.getDb)();
|
|
78
|
+
const { domain, success, errorCode, startDate, endDate, limit = 100, offset = 0 } = filters;
|
|
79
|
+
// Build conditions array
|
|
80
|
+
const conditions = [];
|
|
81
|
+
if (domain) {
|
|
82
|
+
conditions.push((0, drizzle_orm_1.like)(schema_js_1.validationLogs.domain, `%${domain}%`));
|
|
83
|
+
}
|
|
84
|
+
if (success !== undefined) {
|
|
85
|
+
conditions.push((0, drizzle_orm_1.eq)(schema_js_1.validationLogs.success, success));
|
|
86
|
+
}
|
|
87
|
+
if (errorCode) {
|
|
88
|
+
conditions.push((0, drizzle_orm_1.eq)(schema_js_1.validationLogs.errorCode, errorCode));
|
|
89
|
+
}
|
|
90
|
+
if (startDate) {
|
|
91
|
+
conditions.push((0, drizzle_orm_1.gte)(schema_js_1.validationLogs.createdAt, startDate));
|
|
92
|
+
}
|
|
93
|
+
if (endDate) {
|
|
94
|
+
conditions.push((0, drizzle_orm_1.lte)(schema_js_1.validationLogs.createdAt, endDate));
|
|
95
|
+
}
|
|
96
|
+
// Execute query
|
|
97
|
+
const whereClause = conditions.length > 0 ? (0, drizzle_orm_1.and)(...conditions) : undefined;
|
|
98
|
+
const logs = await db
|
|
99
|
+
.select()
|
|
100
|
+
.from(schema_js_1.validationLogs)
|
|
101
|
+
.where(whereClause)
|
|
102
|
+
.orderBy((0, drizzle_orm_1.desc)(schema_js_1.validationLogs.createdAt))
|
|
103
|
+
.limit(Math.min(limit, 1000)) // Cap at 1000
|
|
104
|
+
.offset(offset);
|
|
105
|
+
// Get total count for pagination
|
|
106
|
+
const [countResult] = await db
|
|
107
|
+
.select({ count: (0, drizzle_orm_1.sql) `count(*)::int` })
|
|
108
|
+
.from(schema_js_1.validationLogs)
|
|
109
|
+
.where(whereClause);
|
|
110
|
+
return {
|
|
111
|
+
logs,
|
|
112
|
+
total: countResult?.count || 0,
|
|
113
|
+
limit,
|
|
114
|
+
offset,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Get validation statistics
|
|
119
|
+
*/
|
|
120
|
+
async function getValidationStats(startDate, endDate) {
|
|
121
|
+
const db = (0, index_js_1.getDb)();
|
|
122
|
+
// Build date conditions
|
|
123
|
+
const dateConditions = [];
|
|
124
|
+
if (startDate) {
|
|
125
|
+
dateConditions.push((0, drizzle_orm_1.gte)(schema_js_1.validationLogs.createdAt, startDate));
|
|
126
|
+
}
|
|
127
|
+
if (endDate) {
|
|
128
|
+
dateConditions.push((0, drizzle_orm_1.lte)(schema_js_1.validationLogs.createdAt, endDate));
|
|
129
|
+
}
|
|
130
|
+
const whereClause = dateConditions.length > 0 ? (0, drizzle_orm_1.and)(...dateConditions) : undefined;
|
|
131
|
+
// Get basic stats
|
|
132
|
+
const [basicStats] = await db
|
|
133
|
+
.select({
|
|
134
|
+
total: (0, drizzle_orm_1.sql) `count(*)::int`,
|
|
135
|
+
successful: (0, drizzle_orm_1.sql) `count(*) filter (where ${schema_js_1.validationLogs.success} = true)::int`,
|
|
136
|
+
failed: (0, drizzle_orm_1.sql) `count(*) filter (where ${schema_js_1.validationLogs.success} = false)::int`,
|
|
137
|
+
uniqueDomains: (0, drizzle_orm_1.sql) `count(distinct ${schema_js_1.validationLogs.domain})::int`,
|
|
138
|
+
avgScore: (0, drizzle_orm_1.sql) `avg(${schema_js_1.validationLogs.score})`,
|
|
139
|
+
})
|
|
140
|
+
.from(schema_js_1.validationLogs)
|
|
141
|
+
.where(whereClause);
|
|
142
|
+
// Get top errors
|
|
143
|
+
const topErrors = await db
|
|
144
|
+
.select({
|
|
145
|
+
code: schema_js_1.validationLogs.errorCode,
|
|
146
|
+
count: (0, drizzle_orm_1.sql) `count(*)::int`,
|
|
147
|
+
})
|
|
148
|
+
.from(schema_js_1.validationLogs)
|
|
149
|
+
.where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_js_1.validationLogs.success, false), (0, drizzle_orm_1.sql) `${schema_js_1.validationLogs.errorCode} is not null`, ...(dateConditions.length > 0 ? dateConditions : [])))
|
|
150
|
+
.groupBy(schema_js_1.validationLogs.errorCode)
|
|
151
|
+
.orderBy((0, drizzle_orm_1.desc)((0, drizzle_orm_1.sql) `count(*)`))
|
|
152
|
+
.limit(10);
|
|
153
|
+
// Get top domains
|
|
154
|
+
const topDomains = await db
|
|
155
|
+
.select({
|
|
156
|
+
domain: schema_js_1.validationLogs.domain,
|
|
157
|
+
count: (0, drizzle_orm_1.sql) `count(*)::int`,
|
|
158
|
+
})
|
|
159
|
+
.from(schema_js_1.validationLogs)
|
|
160
|
+
.where(whereClause)
|
|
161
|
+
.groupBy(schema_js_1.validationLogs.domain)
|
|
162
|
+
.orderBy((0, drizzle_orm_1.desc)((0, drizzle_orm_1.sql) `count(*)`))
|
|
163
|
+
.limit(10);
|
|
164
|
+
// Get recent trend (last 7 days)
|
|
165
|
+
const recentTrend = await db
|
|
166
|
+
.select({
|
|
167
|
+
date: (0, drizzle_orm_1.sql) `date(${schema_js_1.validationLogs.createdAt})::text`,
|
|
168
|
+
count: (0, drizzle_orm_1.sql) `count(*)::int`,
|
|
169
|
+
successRate: (0, drizzle_orm_1.sql) `(count(*) filter (where ${schema_js_1.validationLogs.success} = true)::float / nullif(count(*), 0) * 100)`,
|
|
170
|
+
})
|
|
171
|
+
.from(schema_js_1.validationLogs)
|
|
172
|
+
.where((0, drizzle_orm_1.gte)(schema_js_1.validationLogs.createdAt, (0, drizzle_orm_1.sql) `now() - interval '7 days'`))
|
|
173
|
+
.groupBy((0, drizzle_orm_1.sql) `date(${schema_js_1.validationLogs.createdAt})`)
|
|
174
|
+
.orderBy((0, drizzle_orm_1.sql) `date(${schema_js_1.validationLogs.createdAt})`);
|
|
175
|
+
const total = basicStats?.total || 0;
|
|
176
|
+
const successful = basicStats?.successful || 0;
|
|
177
|
+
const failed = basicStats?.failed || 0;
|
|
178
|
+
return {
|
|
179
|
+
totalValidations: total,
|
|
180
|
+
successfulValidations: successful,
|
|
181
|
+
failedValidations: failed,
|
|
182
|
+
successRate: total > 0 ? Math.round((successful / total) * 100 * 10) / 10 : 0,
|
|
183
|
+
uniqueDomains: basicStats?.uniqueDomains || 0,
|
|
184
|
+
averageScore: basicStats?.avgScore ? Math.round(basicStats.avgScore * 10) / 10 : null,
|
|
185
|
+
topErrors: topErrors.map(e => ({ code: e.code || 'unknown', count: e.count })),
|
|
186
|
+
topDomains: topDomains.map(d => ({ domain: d.domain, count: d.count })),
|
|
187
|
+
recentTrend: recentTrend.map(t => ({
|
|
188
|
+
date: t.date,
|
|
189
|
+
count: t.count,
|
|
190
|
+
successRate: Math.round((t.successRate || 0) * 10) / 10,
|
|
191
|
+
})),
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Get unique error codes for filtering UI
|
|
196
|
+
*/
|
|
197
|
+
async function getErrorCodes() {
|
|
198
|
+
const db = (0, index_js_1.getDb)();
|
|
199
|
+
const errors = await db
|
|
200
|
+
.select({
|
|
201
|
+
code: schema_js_1.validationLogs.errorCode,
|
|
202
|
+
count: (0, drizzle_orm_1.sql) `count(*)::int`,
|
|
203
|
+
})
|
|
204
|
+
.from(schema_js_1.validationLogs)
|
|
205
|
+
.where((0, drizzle_orm_1.sql) `${schema_js_1.validationLogs.errorCode} is not null`)
|
|
206
|
+
.groupBy(schema_js_1.validationLogs.errorCode)
|
|
207
|
+
.orderBy((0, drizzle_orm_1.desc)((0, drizzle_orm_1.sql) `count(*)`));
|
|
208
|
+
return errors.map(e => ({ code: e.code || 'unknown', count: e.count }));
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=validation-logging.js.map
|