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': {
|
'/reviews': {
|
||||||
get: {
|
get: {
|
||||||
tags: ['Reviews'],
|
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': {
|
'/analytics/uptime': {
|
||||||
get: {
|
get: {
|
||||||
tags: ['Analytics'],
|
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': {
|
'/wallets/{wallet_name}/recommended-mints': {
|
||||||
get: {
|
get: {
|
||||||
tags: ['Mints'],
|
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': {
|
'/mints/cards': {
|
||||||
get: {
|
get: {
|
||||||
tags: ['Mints'],
|
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': {
|
'/mints/{mint_id}/profile': {
|
||||||
get: {
|
get: {
|
||||||
tags: ['Mint Details'],
|
tags: ['Mint Details'],
|
||||||
|
|||||||
@@ -1569,7 +1569,13 @@ export function getRecommendedMints(useCase = 'general', limit = 10) {
|
|||||||
ur.avg_rtt_ms, ur.uptime_pct
|
ur.avg_rtt_ms, ur.uptime_pct
|
||||||
FROM mints m
|
FROM mints m
|
||||||
LEFT JOIN current_trust_scores ts ON m.mint_id = ts.mint_id
|
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'
|
WHERE m.status = 'online'
|
||||||
AND (m.visibility IS NULL OR m.visibility = 'public')
|
AND (m.visibility IS NULL OR m.visibility = 'public')
|
||||||
ORDER BY ur.avg_rtt_ms ASC NULLS LAST, ur.uptime_pct DESC NULLS LAST
|
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
|
ur.uptime_pct
|
||||||
FROM mints m
|
FROM mints m
|
||||||
LEFT JOIN current_trust_scores ts ON m.mint_id = ts.mint_id
|
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'
|
WHERE m.status = 'online'
|
||||||
AND (m.visibility IS NULL OR m.visibility = 'public')
|
AND (m.visibility IS NULL OR m.visibility = 'public')
|
||||||
AND ts.score_total >= 70
|
AND ts.score_total >= 70
|
||||||
@@ -1618,7 +1630,13 @@ export function getRecommendedMints(useCase = 'general', limit = 10) {
|
|||||||
ur.uptime_pct
|
ur.uptime_pct
|
||||||
FROM mints m
|
FROM mints m
|
||||||
LEFT JOIN current_trust_scores ts ON m.mint_id = ts.mint_id
|
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'
|
WHERE m.status = 'online'
|
||||||
AND (m.visibility IS NULL OR m.visibility = 'public')
|
AND (m.visibility IS NULL OR m.visibility = 'public')
|
||||||
ORDER BY ts.score_total DESC NULLS LAST, ur.uptime_pct DESC NULLS LAST
|
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
|
FROM mints m
|
||||||
LEFT JOIN current_trust_scores ts ON m.mint_id = ts.mint_id
|
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 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'
|
WHERE m.status = 'online'
|
||||||
AND (m.visibility IS NULL OR m.visibility = 'public')
|
AND (m.visibility IS NULL OR m.visibility = 'public')
|
||||||
`);
|
`);
|
||||||
|
|||||||
Reference in New Issue
Block a user