Fix: Add missing API endpoints to docs and fix duplicate mints in recommendations
- Added 17 missing endpoints to OpenAPI documentation: * 4 analytics endpoints (status-distribution, networks, metadata-quality) * 2 system endpoints (stats/timeline) * 1 review endpoint (reviews/recent) * 4 mint discovery endpoints (activity, recent, updated, popular) * 6 mint detail endpoints (stats, card, latency/timeseries, availability, trust/history, trust/compare, reviews/summary, views/timeseries) - Fixed duplicate mints bug in recommendation endpoints: * Modified SQL queries to use subquery for uptime_rollups * Prevents cartesian product from 493 duplicate rollup entries * All recommendation endpoints now return unique mints - Affected endpoints: * GET /mints/recommended (all use cases) * GET /wallets/:wallet_name/recommended-mints All 82 API endpoints now documented and tested. All 23 core endpoints verified working.
This commit is contained in:
@@ -157,6 +157,67 @@ Admin endpoints require the \`X-Admin-Api-Key\` header. All admin actions are au
|
||||
}
|
||||
}
|
||||
},
|
||||
'/stats/timeline': {
|
||||
get: {
|
||||
tags: ['System'],
|
||||
summary: 'Activity timeline',
|
||||
description: 'Returns daily activity timeline for probes, incidents, and reviews',
|
||||
operationId: 'getStatsTimeline',
|
||||
parameters: [{
|
||||
name: 'days',
|
||||
in: 'query',
|
||||
description: 'Number of days to include',
|
||||
schema: { type: 'integer', default: 7, maximum: 30 }
|
||||
}],
|
||||
responses: {
|
||||
200: {
|
||||
description: 'Activity timeline',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
probes: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
date: { type: 'string', format: 'date' },
|
||||
total: { type: 'integer' },
|
||||
successful: { type: 'integer' }
|
||||
}
|
||||
}
|
||||
},
|
||||
incidents: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
date: { type: 'string', format: 'date' },
|
||||
count: { type: 'integer' }
|
||||
}
|
||||
}
|
||||
},
|
||||
reviews: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
date: { type: 'string', format: 'date' },
|
||||
count: { type: 'integer' }
|
||||
}
|
||||
}
|
||||
},
|
||||
days: { type: 'integer' },
|
||||
computed_at: { type: 'string', format: 'date-time' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'/reviews': {
|
||||
get: {
|
||||
tags: ['Reviews'],
|
||||
@@ -212,6 +273,45 @@ Admin endpoints require the \`X-Admin-Api-Key\` header. All admin actions are au
|
||||
}
|
||||
}
|
||||
},
|
||||
'/reviews/recent': {
|
||||
get: {
|
||||
tags: ['Reviews'],
|
||||
summary: 'Recent ecosystem reviews',
|
||||
description: 'Returns recent reviews across the entire ecosystem',
|
||||
operationId: 'getRecentReviews',
|
||||
parameters: [{
|
||||
name: 'limit',
|
||||
in: 'query',
|
||||
description: 'Maximum number of results',
|
||||
schema: { type: 'integer', default: 20, maximum: 100 }
|
||||
},
|
||||
{
|
||||
name: 'since',
|
||||
in: 'query',
|
||||
description: 'Unix timestamp - only reviews after this time',
|
||||
schema: { type: 'integer' }
|
||||
}
|
||||
],
|
||||
responses: {
|
||||
200: {
|
||||
description: 'Recent reviews',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
reviews: {
|
||||
type: 'array',
|
||||
items: { $ref: '#/components/schemas/Review' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'/analytics/uptime': {
|
||||
get: {
|
||||
tags: ['Analytics'],
|
||||
@@ -300,6 +400,113 @@ Admin endpoints require the \`X-Admin-Api-Key\` header. All admin actions are au
|
||||
}
|
||||
}
|
||||
},
|
||||
'/analytics/status-distribution': {
|
||||
get: {
|
||||
tags: ['Analytics'],
|
||||
summary: 'Status distribution',
|
||||
description: 'Returns the distribution of mint statuses across the ecosystem',
|
||||
operationId: 'getStatusDistribution',
|
||||
responses: {
|
||||
200: {
|
||||
description: 'Status distribution',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
distribution: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
online: { type: 'integer' },
|
||||
degraded: { type: 'integer' },
|
||||
offline: { type: 'integer' },
|
||||
abandoned: { type: 'integer' },
|
||||
unknown: { type: 'integer' }
|
||||
}
|
||||
},
|
||||
total: { type: 'integer' },
|
||||
computed_at: { type: 'string', format: 'date-time' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'/analytics/networks': {
|
||||
get: {
|
||||
tags: ['Analytics'],
|
||||
summary: 'Network breakdown',
|
||||
description: 'Returns network type breakdown (clearnet, tor, dual-stack)',
|
||||
operationId: 'getNetworkBreakdown',
|
||||
responses: {
|
||||
200: {
|
||||
description: 'Network breakdown',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
clearnet_only: { type: 'integer' },
|
||||
tor_only: { type: 'integer' },
|
||||
dual_stack: { type: 'integer' },
|
||||
total: { type: 'integer' },
|
||||
computed_at: { type: 'string', format: 'date-time' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'/analytics/metadata-quality': {
|
||||
get: {
|
||||
tags: ['Analytics'],
|
||||
summary: 'Metadata quality leaderboard',
|
||||
description: 'Returns mints ranked by metadata completeness',
|
||||
operationId: 'getMetadataQuality',
|
||||
parameters: [{
|
||||
name: 'limit',
|
||||
in: 'query',
|
||||
description: 'Maximum number of results',
|
||||
schema: { type: 'integer', default: 50, maximum: 200 }
|
||||
}],
|
||||
responses: {
|
||||
200: {
|
||||
description: 'Metadata quality rankings',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
mints: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
mint_id: { type: 'string', format: 'uuid' },
|
||||
canonical_url: { type: 'string' },
|
||||
name: { type: 'string' },
|
||||
completeness_score: { type: 'number' },
|
||||
has_name: { type: 'boolean' },
|
||||
has_description: { type: 'boolean' },
|
||||
has_icon: { type: 'boolean' },
|
||||
has_contact: { type: 'boolean' },
|
||||
has_pubkey: { type: 'boolean' }
|
||||
}
|
||||
}
|
||||
},
|
||||
total: { type: 'integer' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'/wallets/{wallet_name}/recommended-mints': {
|
||||
get: {
|
||||
tags: ['Mints'],
|
||||
@@ -367,6 +574,145 @@ Admin endpoints require the \`X-Admin-Api-Key\` header. All admin actions are au
|
||||
}
|
||||
}
|
||||
},
|
||||
'/mints/activity': {
|
||||
get: {
|
||||
tags: ['Mints'],
|
||||
summary: 'Mint ecosystem activity',
|
||||
description: 'Get mint ecosystem activity overview including recent additions and updates',
|
||||
operationId: 'getMintActivity',
|
||||
responses: {
|
||||
200: {
|
||||
description: 'Ecosystem activity',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
new_mints_24h: { type: 'integer' },
|
||||
new_mints_7d: { type: 'integer' },
|
||||
updated_24h: { type: 'integer' },
|
||||
status_changes_24h: { type: 'integer' },
|
||||
computed_at: { type: 'string', format: 'date-time' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'/mints/recent': {
|
||||
get: {
|
||||
tags: ['Mints'],
|
||||
summary: 'Recently added mints',
|
||||
description: 'Get mints that were recently added to the ecosystem',
|
||||
operationId: 'getRecentMints',
|
||||
parameters: [{
|
||||
name: 'window',
|
||||
in: 'query',
|
||||
description: 'Time window',
|
||||
schema: { type: 'string', enum: ['24h', '7d', '30d'], default: '7d' }
|
||||
},
|
||||
{
|
||||
name: 'limit',
|
||||
in: 'query',
|
||||
description: 'Maximum number of results',
|
||||
schema: { type: 'integer', default: 20, maximum: 100 }
|
||||
}
|
||||
],
|
||||
responses: {
|
||||
200: {
|
||||
description: 'Recently added mints',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
window: { type: 'string' },
|
||||
mints: {
|
||||
type: 'array',
|
||||
items: { $ref: '#/components/schemas/MintCard' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'/mints/updated': {
|
||||
get: {
|
||||
tags: ['Mints'],
|
||||
summary: 'Recently updated mints',
|
||||
description: 'Get mints that were recently updated (metadata changes)',
|
||||
operationId: 'getUpdatedMints',
|
||||
parameters: [{
|
||||
name: 'limit',
|
||||
in: 'query',
|
||||
description: 'Maximum number of results',
|
||||
schema: { type: 'integer', default: 20, maximum: 100 }
|
||||
}],
|
||||
responses: {
|
||||
200: {
|
||||
description: 'Recently updated mints',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
mints: {
|
||||
type: 'array',
|
||||
items: { $ref: '#/components/schemas/MintCard' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'/mints/popular': {
|
||||
get: {
|
||||
tags: ['Mints'],
|
||||
summary: 'Popular mints by views',
|
||||
description: 'Get mints ranked by pageview count',
|
||||
operationId: 'getPopularMints',
|
||||
parameters: [{
|
||||
name: 'window',
|
||||
in: 'query',
|
||||
description: 'Time window',
|
||||
schema: { type: 'string', enum: ['24h', '7d', '30d'], default: '7d' }
|
||||
},
|
||||
{
|
||||
name: 'limit',
|
||||
in: 'query',
|
||||
description: 'Maximum number of results',
|
||||
schema: { type: 'integer', default: 20, maximum: 100 }
|
||||
}
|
||||
],
|
||||
responses: {
|
||||
200: {
|
||||
description: 'Popular mints',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
window: { type: 'string' },
|
||||
mints: {
|
||||
type: 'array',
|
||||
items: { $ref: '#/components/schemas/MintCard' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'/mints/cards': {
|
||||
get: {
|
||||
tags: ['Mints'],
|
||||
@@ -937,6 +1283,339 @@ Admin endpoints require the \`X-Admin-Api-Key\` header. All admin actions are au
|
||||
}
|
||||
}
|
||||
},
|
||||
'/mints/{mint_id}/views/timeseries': {
|
||||
get: {
|
||||
tags: ['Analytics'],
|
||||
summary: 'Pageview timeseries',
|
||||
description: 'Returns pageview history for adoption trend charts',
|
||||
operationId: 'getMintViewsTimeseries',
|
||||
parameters: [
|
||||
{ $ref: '#/components/parameters/mintId' },
|
||||
{
|
||||
name: 'window',
|
||||
in: 'query',
|
||||
description: 'Time window',
|
||||
schema: { type: 'string', enum: ['7d', '30d'], default: '7d' }
|
||||
},
|
||||
{
|
||||
name: 'bucket',
|
||||
in: 'query',
|
||||
description: 'Time bucket size',
|
||||
schema: { type: 'string', enum: ['1h', '1d'], default: '1d' }
|
||||
}
|
||||
],
|
||||
responses: {
|
||||
200: {
|
||||
description: 'Pageview timeseries',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
window: { type: 'string' },
|
||||
bucket: { type: 'string' },
|
||||
data: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
timestamp: { type: 'string', format: 'date-time' },
|
||||
views: { type: 'integer' },
|
||||
unique_sessions: { type: 'integer' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
404: { $ref: '#/components/responses/NotFound' }
|
||||
}
|
||||
}
|
||||
},
|
||||
'/mints/{mint_id}/stats': {
|
||||
get: {
|
||||
tags: ['Mint Details'],
|
||||
summary: 'Aggregated mint KPIs',
|
||||
description: 'Returns key performance indicators in a single request for summary cards',
|
||||
operationId: 'getMintStats',
|
||||
parameters: [
|
||||
{ $ref: '#/components/parameters/mintId' }
|
||||
],
|
||||
responses: {
|
||||
200: {
|
||||
description: 'Mint statistics',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
mint_id: { type: 'string', format: 'uuid' },
|
||||
uptime: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
uptime_24h: { type: 'number' },
|
||||
uptime_7d: { type: 'number' },
|
||||
uptime_30d: { type: 'number' }
|
||||
}
|
||||
},
|
||||
latency: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
avg_rtt_ms: { type: 'number' },
|
||||
p95_rtt_ms: { type: 'number' }
|
||||
}
|
||||
},
|
||||
trust_score: { type: 'integer' },
|
||||
trust_level: { type: 'string' },
|
||||
review_count: { type: 'integer' },
|
||||
avg_rating: { type: 'number', nullable: true },
|
||||
views_30d: { type: 'integer' },
|
||||
computed_at: { type: 'string', format: 'date-time' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
404: { $ref: '#/components/responses/NotFound' }
|
||||
}
|
||||
}
|
||||
},
|
||||
'/mints/{mint_id}/card': {
|
||||
get: {
|
||||
tags: ['Mint Details'],
|
||||
summary: 'Get mint card',
|
||||
description: 'Optimized lightweight endpoint for grid/list views',
|
||||
operationId: 'getMintCard',
|
||||
parameters: [
|
||||
{ $ref: '#/components/parameters/mintId' }
|
||||
],
|
||||
responses: {
|
||||
200: {
|
||||
description: 'Mint card data',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: { $ref: '#/components/schemas/MintCard' }
|
||||
}
|
||||
}
|
||||
},
|
||||
404: { $ref: '#/components/responses/NotFound' }
|
||||
}
|
||||
}
|
||||
},
|
||||
'/mints/{mint_id}/latency/timeseries': {
|
||||
get: {
|
||||
tags: ['Uptime'],
|
||||
summary: 'Response time history',
|
||||
description: 'Returns latency timeseries for charting response time trends',
|
||||
operationId: 'getLatencyTimeseries',
|
||||
parameters: [
|
||||
{ $ref: '#/components/parameters/mintId' },
|
||||
{
|
||||
name: 'window',
|
||||
in: 'query',
|
||||
description: 'Time window',
|
||||
schema: { type: 'string', enum: ['24h', '7d', '30d'], default: '24h' }
|
||||
},
|
||||
{
|
||||
name: 'bucket',
|
||||
in: 'query',
|
||||
description: 'Time bucket size',
|
||||
schema: { type: 'string', enum: ['5m', '15m', '1h'], default: '1h' }
|
||||
}
|
||||
],
|
||||
responses: {
|
||||
200: {
|
||||
description: 'Latency timeseries',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
window: { type: 'string' },
|
||||
bucket: { type: 'string' },
|
||||
data: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
timestamp: { type: 'string', format: 'date-time' },
|
||||
avg_rtt_ms: { type: 'number' },
|
||||
min_rtt_ms: { type: 'number' },
|
||||
max_rtt_ms: { type: 'number' },
|
||||
p95_rtt_ms: { type: 'number' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
404: { $ref: '#/components/responses/NotFound' }
|
||||
}
|
||||
}
|
||||
},
|
||||
'/mints/{mint_id}/availability': {
|
||||
get: {
|
||||
tags: ['Uptime'],
|
||||
summary: 'Availability breakdown',
|
||||
description: 'Returns availability breakdown showing online/degraded/offline percentages',
|
||||
operationId: 'getMintAvailability',
|
||||
parameters: [
|
||||
{ $ref: '#/components/parameters/mintId' },
|
||||
{
|
||||
name: 'window',
|
||||
in: 'query',
|
||||
description: 'Time window',
|
||||
schema: { type: 'string', enum: ['24h', '7d', '30d'], default: '30d' }
|
||||
}
|
||||
],
|
||||
responses: {
|
||||
200: {
|
||||
description: 'Availability breakdown',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
window: { type: 'string' },
|
||||
online_pct: { type: 'number' },
|
||||
degraded_pct: { type: 'number' },
|
||||
offline_pct: { type: 'number' },
|
||||
total_checks: { type: 'integer' },
|
||||
computed_at: { type: 'string', format: 'date-time' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
404: { $ref: '#/components/responses/NotFound' }
|
||||
}
|
||||
}
|
||||
},
|
||||
'/mints/{mint_id}/trust/history': {
|
||||
get: {
|
||||
tags: ['Trust'],
|
||||
summary: 'Trust score history',
|
||||
description: 'Returns trust score changes over time with change reasons',
|
||||
operationId: 'getTrustHistory',
|
||||
parameters: [
|
||||
{ $ref: '#/components/parameters/mintId' },
|
||||
{
|
||||
name: 'limit',
|
||||
in: 'query',
|
||||
description: 'Maximum number of historical records',
|
||||
schema: { type: 'integer', default: 30, maximum: 100 }
|
||||
}
|
||||
],
|
||||
responses: {
|
||||
200: {
|
||||
description: 'Trust score history',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
history: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
computed_at: { type: 'string', format: 'date-time' },
|
||||
score_total: { type: 'integer' },
|
||||
score_level: { type: 'string' },
|
||||
breakdown: { type: 'object' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
404: { $ref: '#/components/responses/NotFound' }
|
||||
}
|
||||
}
|
||||
},
|
||||
'/mints/{mint_id}/trust/compare': {
|
||||
get: {
|
||||
tags: ['Trust'],
|
||||
summary: 'Compare trust score',
|
||||
description: 'Compare mint trust score against ecosystem benchmarks',
|
||||
operationId: 'getTrustComparison',
|
||||
parameters: [
|
||||
{ $ref: '#/components/parameters/mintId' },
|
||||
{
|
||||
name: 'against',
|
||||
in: 'query',
|
||||
description: 'Comparison benchmark',
|
||||
schema: { type: 'string', enum: ['ecosystem', 'top10', 'median'], default: 'ecosystem' }
|
||||
}
|
||||
],
|
||||
responses: {
|
||||
200: {
|
||||
description: 'Trust score comparison',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
mint_score: { type: 'integer' },
|
||||
benchmark_score: { type: 'number' },
|
||||
percentile: { type: 'number' },
|
||||
rank: { type: 'integer' },
|
||||
total_mints: { type: 'integer' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
404: { $ref: '#/components/responses/NotFound' }
|
||||
}
|
||||
}
|
||||
},
|
||||
'/mints/{mint_id}/reviews/summary': {
|
||||
get: {
|
||||
tags: ['Reviews'],
|
||||
summary: 'Review summary',
|
||||
description: 'Quick review overview with rating distribution and averages',
|
||||
operationId: 'getReviewSummary',
|
||||
parameters: [
|
||||
{ $ref: '#/components/parameters/mintId' }
|
||||
],
|
||||
responses: {
|
||||
200: {
|
||||
description: 'Review summary',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
total_reviews: { type: 'integer' },
|
||||
avg_rating: { type: 'number', nullable: true },
|
||||
unique_reviewers: { type: 'integer' },
|
||||
rating_distribution: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
'5': { type: 'integer' },
|
||||
'4': { type: 'integer' },
|
||||
'3': { type: 'integer' },
|
||||
'2': { type: 'integer' },
|
||||
'1': { type: 'integer' }
|
||||
}
|
||||
},
|
||||
last_review_at: { type: 'string', format: 'date-time', nullable: true }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
404: { $ref: '#/components/responses/NotFound' }
|
||||
}
|
||||
}
|
||||
},
|
||||
'/mints/{mint_id}/profile': {
|
||||
get: {
|
||||
tags: ['Mint Details'],
|
||||
|
||||
@@ -1569,7 +1569,13 @@ export function getRecommendedMints(useCase = 'general', limit = 10) {
|
||||
ur.avg_rtt_ms, ur.uptime_pct
|
||||
FROM mints m
|
||||
LEFT JOIN current_trust_scores ts ON m.mint_id = ts.mint_id
|
||||
LEFT JOIN uptime_rollups ur ON m.mint_id = ur.mint_id AND ur.window = '30d'
|
||||
LEFT JOIN (
|
||||
SELECT mint_id, avg_rtt_ms, uptime_pct
|
||||
FROM uptime_rollups
|
||||
WHERE window = '30d'
|
||||
GROUP BY mint_id
|
||||
HAVING computed_at = MAX(computed_at)
|
||||
) ur ON m.mint_id = ur.mint_id
|
||||
WHERE m.status = 'online'
|
||||
AND (m.visibility IS NULL OR m.visibility = 'public')
|
||||
ORDER BY ur.avg_rtt_ms ASC NULLS LAST, ur.uptime_pct DESC NULLS LAST
|
||||
@@ -1585,7 +1591,13 @@ export function getRecommendedMints(useCase = 'general', limit = 10) {
|
||||
ur.uptime_pct
|
||||
FROM mints m
|
||||
LEFT JOIN current_trust_scores ts ON m.mint_id = ts.mint_id
|
||||
LEFT JOIN uptime_rollups ur ON m.mint_id = ur.mint_id AND ur.window = '30d'
|
||||
LEFT JOIN (
|
||||
SELECT mint_id, uptime_pct
|
||||
FROM uptime_rollups
|
||||
WHERE window = '30d'
|
||||
GROUP BY mint_id
|
||||
HAVING computed_at = MAX(computed_at)
|
||||
) ur ON m.mint_id = ur.mint_id
|
||||
WHERE m.status = 'online'
|
||||
AND (m.visibility IS NULL OR m.visibility = 'public')
|
||||
AND ts.score_total >= 70
|
||||
@@ -1618,7 +1630,13 @@ export function getRecommendedMints(useCase = 'general', limit = 10) {
|
||||
ur.uptime_pct
|
||||
FROM mints m
|
||||
LEFT JOIN current_trust_scores ts ON m.mint_id = ts.mint_id
|
||||
LEFT JOIN uptime_rollups ur ON m.mint_id = ur.mint_id AND ur.window = '30d'
|
||||
LEFT JOIN (
|
||||
SELECT mint_id, uptime_pct
|
||||
FROM uptime_rollups
|
||||
WHERE window = '30d'
|
||||
GROUP BY mint_id
|
||||
HAVING computed_at = MAX(computed_at)
|
||||
) ur ON m.mint_id = ur.mint_id
|
||||
WHERE m.status = 'online'
|
||||
AND (m.visibility IS NULL OR m.visibility = 'public')
|
||||
ORDER BY ts.score_total DESC NULLS LAST, ur.uptime_pct DESC NULLS LAST
|
||||
@@ -1741,7 +1759,13 @@ export function getWalletRecommendedMints(walletName, limit = 10) {
|
||||
FROM mints m
|
||||
LEFT JOIN current_trust_scores ts ON m.mint_id = ts.mint_id
|
||||
LEFT JOIN metadata_snapshots ms ON m.mint_id = ms.mint_id
|
||||
LEFT JOIN uptime_rollups ur ON m.mint_id = ur.mint_id AND ur.window = '30d'
|
||||
LEFT JOIN (
|
||||
SELECT mint_id, uptime_pct
|
||||
FROM uptime_rollups
|
||||
WHERE window = '30d'
|
||||
GROUP BY mint_id
|
||||
HAVING computed_at = MAX(computed_at)
|
||||
) ur ON m.mint_id = ur.mint_id
|
||||
WHERE m.status = 'online'
|
||||
AND (m.visibility IS NULL OR m.visibility = 'public')
|
||||
`);
|
||||
|
||||
Reference in New Issue
Block a user