@ucptools/validator 1.0.1 → 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.js +96 -0
- package/dist/cli/index.js.map +1 -1
- 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/db/index.d.ts +8 -2
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +22 -5
- package/dist/db/index.js.map +1 -1
- package/dist/db/schema.d.ts +3570 -128
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/db/schema.js +377 -17
- package/dist/db/schema.js.map +1 -1
- 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.map +1 -1
- package/dist/feed-analyzer/feed-analyzer.js +218 -4
- package/dist/feed-analyzer/feed-analyzer.js.map +1 -1
- package/dist/feed-analyzer/types.d.ts +82 -1
- package/dist/feed-analyzer/types.d.ts.map +1 -1
- package/dist/feed-analyzer/types.js +13 -0
- package/dist/feed-analyzer/types.js.map +1 -1
- 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/dist/security/security-scanner.d.ts.map +1 -1
- package/dist/security/security-scanner.js +130 -2
- package/dist/security/security-scanner.js.map +1 -1
- package/dist/security/types.d.ts +32 -0
- package/dist/security/types.d.ts.map +1 -1
- package/dist/security/types.js.map +1 -1
- 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 +2 -0
- package/dist/services/directory.d.ts.map +1 -1
- package/dist/services/directory.js +45 -27
- package/dist/services/directory.js.map +1 -1
- 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.map +1 -1
- package/dist/simulator/agent-simulator.js +229 -9
- package/dist/simulator/agent-simulator.js.map +1 -1
- package/dist/simulator/types.d.ts +26 -0
- package/dist/simulator/types.d.ts.map +1 -1
- package/dist/simulator/types.js.map +1 -1
- 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/ucp-profile.d.ts +10 -2
- package/dist/types/ucp-profile.d.ts.map +1 -1
- package/dist/types/ucp-profile.js.map +1 -1
- package/dist/types/validation.d.ts +8 -0
- package/dist/types/validation.d.ts.map +1 -1
- package/dist/types/validation.js +10 -0
- package/dist/types/validation.js.map +1 -1
- 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/network-validator.d.ts.map +1 -1
- package/dist/validator/network-validator.js +4 -4
- package/dist/validator/network-validator.js.map +1 -1
- package/dist/validator/rules-validator.d.ts +8 -0
- package/dist/validator/rules-validator.d.ts.map +1 -1
- package/dist/validator/rules-validator.js +92 -43
- package/dist/validator/rules-validator.js.map +1 -1
- package/dist/validator/structural-validator.d.ts.map +1 -1
- package/dist/validator/structural-validator.js +187 -53
- package/dist/validator/structural-validator.js.map +1 -1
- 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/settings.local.json +0 -60
- package/.vercel/README.txt +0 -11
- package/.vercel/project.json +0 -1
- package/publish-output.txt +0 -0
- package/tsconfig.json +0 -20
|
@@ -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
|