Initial commit

This commit is contained in:
michilis
2025-05-31 14:20:15 +02:00
commit fc7927e1c8
10 changed files with 5644 additions and 0 deletions

554
swagger.config.js Normal file
View File

@@ -0,0 +1,554 @@
const swaggerJsdoc = require('swagger-jsdoc');
const options = {
definition: {
openapi: '3.0.0',
info: {
title: 'Cashu Redeem API',
version: '1.0.0',
description: 'A production-grade API for redeeming Cashu tokens (ecash) to Lightning addresses using the cashu-ts library and LNURLp protocol.',
contact: {
name: 'API Support',
email: 'support@example.com'
},
license: {
name: 'MIT',
url: 'https://opensource.org/licenses/MIT'
}
},
servers: [
{
url: 'http://localhost:3000',
description: 'Development server'
},
{
url: 'https://api.example.com',
description: 'Production server'
}
],
components: {
schemas: {
// Error Response Schema
ErrorResponse: {
type: 'object',
required: ['success', 'error'],
properties: {
success: {
type: 'boolean',
example: false
},
error: {
type: 'string',
example: 'Error message description'
}
}
},
// Token Decode Schemas
DecodeRequest: {
type: 'object',
required: ['token'],
properties: {
token: {
type: 'string',
description: 'Cashu token to decode (supports v1 and v3 formats)',
example: 'cashuAeyJwcm9vZnMiOlt7ImFtb3VudCI6MSwiaWQiOiIwMGZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmIn0seyJhbW91bnQiOjEsImlkIjoiMDBmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmIn1dLCJtaW50IjoiaHR0cHM6Ly9taW50LmV4YW1wbGUuY29tIn0'
}
}
},
DecodeResponse: {
type: 'object',
properties: {
success: {
type: 'boolean',
example: true
},
decoded: {
type: 'object',
properties: {
mint: {
type: 'string',
format: 'uri',
example: 'https://mint.azzamo.net'
},
totalAmount: {
type: 'integer',
description: 'Total amount in satoshis',
example: 21000
},
numProofs: {
type: 'integer',
description: 'Number of proofs in the token',
example: 3
},
denominations: {
type: 'array',
items: {
type: 'integer'
},
description: 'Array of proof amounts',
example: [1000, 10000, 10000]
},
format: {
type: 'string',
enum: ['cashuA', 'cashuB'],
description: 'Token format version',
example: 'cashuA'
}
}
},
mint_url: {
type: 'string',
format: 'uri',
description: 'Mint URL extracted from token',
example: 'https://mint.azzamo.net'
}
}
},
// Redeem Schemas
RedeemRequest: {
type: 'object',
required: ['token'],
properties: {
token: {
type: 'string',
description: 'Cashu token to redeem',
example: 'cashuAeyJwcm9vZnMiOlt7ImFtb3VudCI6MSwiaWQiOiIwMGZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmIn0seyJhbW91bnQiOjEsImlkIjoiMDBmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmIn1dLCJtaW50IjoiaHR0cHM6Ly9taW50LmV4YW1wbGUuY29tIn0'
},
lightningAddress: {
type: 'string',
format: 'email',
description: 'Lightning address to send payment to (optional - uses default if not provided)',
example: 'user@ln.tips'
}
}
},
RedeemResponse: {
type: 'object',
properties: {
success: {
type: 'boolean',
example: true
},
redeemId: {
type: 'string',
format: 'uuid',
description: 'Unique redemption ID for tracking',
example: '8e99101e-d034-4d2e-9ccf-dfda24d26762'
},
paid: {
type: 'boolean',
description: 'Whether the payment was successful',
example: true
},
amount: {
type: 'integer',
description: 'Total amount redeemed in satoshis',
example: 21000
},
to: {
type: 'string',
description: 'Lightning address that received the payment',
example: 'user@ln.tips'
},
fee: {
type: 'integer',
description: 'Actual fee charged by mint in satoshis',
example: 1000
},
actualFee: {
type: 'integer',
description: 'Calculated fee according to NUT-05 (2% min 1 sat)',
example: 420
},
netAmount: {
type: 'integer',
description: 'Net amount after fees in satoshis',
example: 20000
},
mint_url: {
type: 'string',
format: 'uri',
description: 'Mint URL used for redemption',
example: 'https://mint.azzamo.net'
},
format: {
type: 'string',
enum: ['cashuA', 'cashuB'],
description: 'Token format that was redeemed',
example: 'cashuA'
},
preimage: {
type: 'string',
description: 'Lightning payment preimage (if available)',
example: 'abc123def456...'
},
change: {
type: 'array',
description: 'Change proofs returned (if any)',
items: {
type: 'object'
},
example: []
},
usingDefaultAddress: {
type: 'boolean',
description: 'Whether default Lightning address was used',
example: false
},
message: {
type: 'string',
description: 'Additional message (when using default address)',
example: 'Redeemed to default Lightning address: admin@example.com'
}
}
},
// Status Schemas
StatusRequest: {
type: 'object',
required: ['redeemId'],
properties: {
redeemId: {
type: 'string',
format: 'uuid',
description: 'Redemption ID to check status for',
example: '8e99101e-d034-4d2e-9ccf-dfda24d26762'
}
}
},
StatusResponse: {
type: 'object',
properties: {
success: {
type: 'boolean',
example: true
},
status: {
type: 'string',
enum: ['processing', 'parsing_token', 'checking_spendability', 'resolving_invoice', 'melting_token', 'paid', 'failed'],
description: 'Current redemption status',
example: 'paid'
},
details: {
type: 'object',
properties: {
amount: {
type: 'integer',
description: 'Amount in satoshis',
example: 21000
},
to: {
type: 'string',
description: 'Lightning address',
example: 'user@ln.tips'
},
paid: {
type: 'boolean',
example: true
},
createdAt: {
type: 'string',
format: 'date-time',
example: '2025-01-14T11:59:30Z'
},
updatedAt: {
type: 'string',
format: 'date-time',
example: '2025-01-14T12:00:00Z'
},
paidAt: {
type: 'string',
format: 'date-time',
example: '2025-01-14T12:00:00Z'
},
fee: {
type: 'integer',
description: 'Fee charged in satoshis',
example: 1000
},
error: {
type: 'string',
description: 'Error message if failed',
example: null
},
mint: {
type: 'string',
format: 'uri',
description: 'Mint URL',
example: 'https://mint.azzamo.net'
},
domain: {
type: 'string',
description: 'Lightning address domain',
example: 'ln.tips'
}
}
}
}
},
// Health Schema
HealthResponse: {
type: 'object',
properties: {
status: {
type: 'string',
example: 'ok'
},
timestamp: {
type: 'string',
format: 'date-time',
example: '2025-01-14T12:00:00Z'
},
uptime: {
type: 'number',
description: 'Server uptime in seconds',
example: 3600
},
memory: {
type: 'object',
description: 'Memory usage information',
example: {
"rss": 45678912,
"heapTotal": 12345678,
"heapUsed": 8765432
}
},
version: {
type: 'string',
example: '1.0.0'
}
}
},
// Stats Schema
StatsResponse: {
type: 'object',
properties: {
success: {
type: 'boolean',
example: true
},
stats: {
type: 'object',
properties: {
total: {
type: 'integer',
description: 'Total number of redemptions',
example: 150
},
paid: {
type: 'integer',
description: 'Number of successful redemptions',
example: 142
},
failed: {
type: 'integer',
description: 'Number of failed redemptions',
example: 8
},
processing: {
type: 'integer',
description: 'Number of currently processing redemptions',
example: 0
},
totalAmount: {
type: 'integer',
description: 'Total amount redeemed in satoshis',
example: 2500000
},
totalFees: {
type: 'integer',
description: 'Total fees collected in satoshis',
example: 15000
}
}
}
}
},
// Validate Address Schemas
ValidateAddressRequest: {
type: 'object',
required: ['lightningAddress'],
properties: {
lightningAddress: {
type: 'string',
format: 'email',
description: 'Lightning address to validate',
example: 'user@ln.tips'
}
}
},
ValidateAddressResponse: {
type: 'object',
properties: {
success: {
type: 'boolean',
example: true
},
valid: {
type: 'boolean',
example: true
},
domain: {
type: 'string',
example: 'ln.tips'
},
minSendable: {
type: 'integer',
description: 'Minimum sendable amount in satoshis',
example: 1
},
maxSendable: {
type: 'integer',
description: 'Maximum sendable amount in satoshis',
example: 100000000
},
commentAllowed: {
type: 'integer',
description: 'Maximum comment length allowed',
example: 144
},
error: {
type: 'string',
description: 'Error message if validation failed',
example: null
}
}
},
// Check Spendable Schemas
CheckSpendableRequest: {
type: 'object',
required: ['token'],
properties: {
token: {
type: 'string',
description: 'Cashu token to check spendability',
example: 'cashuAeyJwcm9vZnMiOlt7ImFtb3VudCI6MSwiaWQiOiIwMGZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmIn0seyJhbW91bnQiOjEsImlkIjoiMDBmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmIn1dLCJtaW50IjoiaHR0cHM6Ly9taW50LmV4YW1wbGUuY29tIn0'
}
}
},
CheckSpendableResponse: {
type: 'object',
properties: {
success: {
type: 'boolean',
example: true
},
spendable: {
type: 'array',
items: {
type: 'boolean'
},
description: 'Array indicating which proofs are spendable',
example: [true, true, false]
},
pending: {
type: 'array',
items: {
type: 'boolean'
},
description: 'Array indicating which proofs are pending',
example: []
},
mintUrl: {
type: 'string',
format: 'uri',
description: 'Mint URL where spendability was checked',
example: 'https://mint.azzamo.net'
},
totalAmount: {
type: 'integer',
description: 'Total amount of the token in satoshis',
example: 21000
},
message: {
type: 'string',
description: 'Human-readable status message',
example: 'Token is spendable'
}
}
}
},
responses: {
BadRequest: {
description: 'Bad request - invalid input',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/ErrorResponse'
}
}
}
},
NotFound: {
description: 'Resource not found',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/ErrorResponse'
}
}
}
},
InternalServerError: {
description: 'Internal server error',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/ErrorResponse'
}
}
}
},
TooManyRequests: {
description: 'Rate limit exceeded',
content: {
'application/json': {
schema: {
allOf: [
{ $ref: '#/components/schemas/ErrorResponse' },
{
properties: {
error: {
example: 'Rate limit exceeded. Please try again later.'
}
}
}
]
}
}
}
}
}
},
tags: [
{
name: 'Token Operations',
description: 'Operations for decoding and redeeming Cashu tokens'
},
{
name: 'Status & Monitoring',
description: 'Endpoints for checking redemption status and API health'
},
{
name: 'Validation',
description: 'Validation utilities for tokens and Lightning addresses'
}
]
},
apis: ['./server.js'], // paths to files containing OpenAPI definitions
};
const specs = swaggerJsdoc(options);
module.exports = specs;